From 4b99fa18b3bf1317628303d86ffa0f0da3d07d12 Mon Sep 17 00:00:00 2001 From: Adriano Date: Mon, 8 Jul 2019 15:32:33 -0700 Subject: [PATCH 1/3] go lint --- aes/cipher.go | 1 + audit/audit.go | 14 +++++++ azkv/keysource.go | 2 +- azkv/keysource_test.go | 6 +-- cmd/sops/common/common.go | 25 ++++++++++-- cmd/sops/rotate.go | 2 +- cmd/sops/subcommand/publish/publish.go | 2 + cmd/sops/subcommand/updatekeys/updatekeys.go | 2 + logging/logging.go | 4 ++ publish/gcs.go | 4 ++ publish/publish.go | 2 + publish/s3.go | 4 ++ shamir/shamir.go | 26 ++++++------- shamir/shamir_test.go | 6 +-- sops.go | 40 ++++++++++---------- stores/dotenv/store.go | 10 +++++ stores/flatten.go | 2 +- stores/ini/store.go | 7 ++++ stores/json/store.go | 17 +++++++++ stores/stores.go | 5 +++ stores/yaml/store.go | 11 ++++++ version/version.go | 2 + 22 files changed, 148 insertions(+), 46 deletions(-) diff --git a/aes/cipher.go b/aes/cipher.go index b4ad8a422..6419daa76 100644 --- a/aes/cipher.go +++ b/aes/cipher.go @@ -45,6 +45,7 @@ type Cipher struct { stash map[stashKey][]byte } +// NewCipher is the constructor for a new Cipher object func NewCipher() Cipher { return Cipher{ stash: make(map[stashKey][]byte), diff --git a/audit/audit.go b/audit/audit.go index 7ec496cdf..2b33bca09 100644 --- a/audit/audit.go +++ b/audit/audit.go @@ -9,7 +9,9 @@ import ( "github.com/pkg/errors" + // empty import as per https://godoc.org/github.com/lib/pq _ "github.com/lib/pq" + "github.com/sirupsen/logrus" "go.mozilla.org/sops/logging" "gopkg.in/yaml.v2" @@ -67,36 +69,46 @@ type config struct { var auditors []Auditor +// SubmitEvent handles an event for all auditors func SubmitEvent(event interface{}) { for _, auditor := range auditors { auditor.Handle(event) } } +// Register registers a new Auditor in the global auditor list func Register(auditor Auditor) { auditors = append(auditors, auditor) } +// Auditor is notified when noteworthy events happen, for example when a file is encrypted or decrypted. type Auditor interface { Handle(event interface{}) } +// DecryptEvent contains fields relevant to a decryption event type DecryptEvent struct { File string } +// EncryptEvent contains fields relevant to an encryption event type EncryptEvent struct { File string } +// RotateEvent contains fields relevant to a key rotation event type RotateEvent struct { File string } +// PostgresAuditor is a Postgres SQL DB implementation of the +// Auditor interface type PostgresAuditor struct { DB *sql.DB } +// NewPostgresAuditor is the constructor for a new PostgresAuditor object +// initialized with the given db connection string func NewPostgresAuditor(connStr string) (*PostgresAuditor, error) { db, err := sql.Open("postgres", connStr) pg := &PostgresAuditor{DB: db} @@ -113,6 +125,8 @@ func NewPostgresAuditor(connStr string) (*PostgresAuditor, error) { return pg, nil } +// Handle is the PostgresAuditor implementation of the function required by the +// Auditor interface func (p *PostgresAuditor) Handle(event interface{}) { u, err := user.Current() if err != nil { diff --git a/azkv/keysource.go b/azkv/keysource.go index 23b3df8df..45c0fd5cc 100644 --- a/azkv/keysource.go +++ b/azkv/keysource.go @@ -73,7 +73,7 @@ func MasterKeysFromURLs(urls string) ([]*MasterKey, error) { return keys, nil } -// NewMasterKeyFromResourceID takes an Azure Key Vault key URL and returns a new MasterKey +// NewMasterKeyFromURL takes an Azure Key Vault key URL and returns a new MasterKey // URL format is {vaultUrl}/keys/{key-name}/{key-version} func NewMasterKeyFromURL(url string) (*MasterKey, error) { k := &MasterKey{} diff --git a/azkv/keysource_test.go b/azkv/keysource_test.go index d08bc51ff..17df440b4 100644 --- a/azkv/keysource_test.go +++ b/azkv/keysource_test.go @@ -92,16 +92,16 @@ func TestKeyToMap(t *testing.T) { }, key.ToMap()) } -var azureKeyAcceptanceTestUrl = flag.String("azure-key", "", "URL to Azure Key Vault (note that this can incur real costs!)") +var azureKeyAcceptanceTestURL = flag.String("azure-key", "", "URL to Azure Key Vault (note that this can incur real costs!)") func TestRoundtrip(t *testing.T) { - if *azureKeyAcceptanceTestUrl == "" { + if *azureKeyAcceptanceTestURL == "" { t.Skip("Azure URL not provided, skipping acceptance test") } input := []byte("test-string") - key, err := NewMasterKeyFromURL(*azureKeyAcceptanceTestUrl) + key, err := NewMasterKeyFromURL(*azureKeyAcceptanceTestURL) if err != nil { t.Fatal(err) } diff --git a/cmd/sops/common/common.go b/cmd/sops/common/common.go index d9b819b77..1fca82ebc 100644 --- a/cmd/sops/common/common.go +++ b/cmd/sops/common/common.go @@ -24,10 +24,13 @@ import ( "gopkg.in/urfave/cli.v1" ) +// ExampleFileEmitter represents actions needed for a struct to be comliant +// with the sops example-file emitter interface type ExampleFileEmitter interface { EmitExample() []byte } +// Store handles marshaling and unmarshaling from SOPS files type Store interface { sops.Store ExampleFileEmitter @@ -107,6 +110,8 @@ func LoadEncryptedFile(loader sops.EncryptedFileLoader, inputPath string) (*sops return &tree, err } +// NewExitError returns a cli.ExitError given an error (wrapped in a generic interface{}) +// and an exit code to represent the failure func NewExitError(i interface{}, exitCode int) *cli.ExitError { if userErr, ok := i.(sops.UserError); ok { return NewExitError(userErr.UserError(), exitCode) @@ -114,22 +119,28 @@ func NewExitError(i interface{}, exitCode int) *cli.ExitError { return cli.NewExitError(i, exitCode) } +// IsYAMLFile returns true if a given file path corresponds to a YAML file func IsYAMLFile(path string) bool { return strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") } +// IsJSONFile returns true if a given file path corresponds to a JSON file func IsJSONFile(path string) bool { return strings.HasSuffix(path, ".json") } +// IsEnvFile returns true if a given file path corresponds to a .env file func IsEnvFile(path string) bool { return strings.HasSuffix(path, ".env") } +// IsIniFile returns true if a given file path corresponds to a INI file func IsIniFile(path string) bool { return strings.HasSuffix(path, ".ini") } +// DefaultStoreForPath returns the correct format-specific implementation +// of the Store interface given the path to a file func DefaultStoreForPath(path string) Store { if IsYAMLFile(path) { return &yaml.Store{} @@ -143,8 +154,12 @@ func DefaultStoreForPath(path string) Store { return &json.BinaryStore{} } +// KMS_ENC_CTX_BUG_FIXED_VERSION represents the SOPS version in which the +// encryption context bug was fixed const KMS_ENC_CTX_BUG_FIXED_VERSION = "3.3.0" +// DetectKMSEncryptionContextBug returns true if the encryption context bug is detected +// in a given runtime sops.Tree object func DetectKMSEncryptionContextBug(tree *sops.Tree) (bool, error) { versionCheck, err := version.AIsNewerThanB(KMS_ENC_CTX_BUG_FIXED_VERSION, tree.Metadata.Version) if err != nil { @@ -161,6 +176,7 @@ func DetectKMSEncryptionContextBug(tree *sops.Tree) (bool, error) { return false, nil } +// GetKMSKeyWithEncryptionCtx returns the first KMS key affected by the encryption context bug as well as its location in the key groups. func GetKMSKeyWithEncryptionCtx(tree *sops.Tree) (keyGroupIndex int, keyIndex int, key *kms.MasterKey) { for i, kg := range tree.Metadata.KeyGroups { for n, k := range kg { @@ -181,6 +197,7 @@ func GetKMSKeyWithEncryptionCtx(tree *sops.Tree) (keyGroupIndex int, keyIndex in return 0, 0, nil } +// GenericDecryptOpts represents decryption options and config type GenericDecryptOpts struct { Cipher sops.Cipher InputStore sops.Store @@ -235,10 +252,9 @@ func FixAWSKMSEncryptionContextBug(opts GenericDecryptOpts, tree *sops.Tree) (*s } } if response == "n" { - return nil, fmt.Errorf("Exiting. User responded no.") - } else { - persistFix = true + return nil, fmt.Errorf("Exiting. User responded no") } + persistFix = true } dataKey := []byte{} @@ -342,6 +358,7 @@ func RecoverDataKeyFromBuggyKMS(opts GenericDecryptOpts, tree *sops.Tree) []byte return nil } +// Diff represents a key diff type Diff struct { Common []keys.MasterKey Added []keys.MasterKey @@ -355,6 +372,7 @@ func max(a, b int) int { return b } +// DiffKeyGroups returns the list of diffs found in two sops.keyGroup slices func DiffKeyGroups(ours, theirs []sops.KeyGroup) []Diff { var diffs []Diff for i := 0; i < max(len(ours), len(theirs)); i++ { @@ -389,6 +407,7 @@ func DiffKeyGroups(ours, theirs []sops.KeyGroup) []Diff { return diffs } +// PrettyPrintDiffs prints a slice of Diff objects to stdout func PrettyPrintDiffs(diffs []Diff) { for i, diff := range diffs { color.New(color.Underline).Printf("Group %d\n", i+1) diff --git a/cmd/sops/rotate.go b/cmd/sops/rotate.go index 373785a52..20221b266 100644 --- a/cmd/sops/rotate.go +++ b/cmd/sops/rotate.go @@ -38,7 +38,7 @@ func rotate(opts rotateOpts) ([]byte, error) { File: tree.FilePath, }) - dataKey, err := common.DecryptTree(common.DecryptTreeOpts{ + _, err = common.DecryptTree(common.DecryptTreeOpts{ Cipher: opts.Cipher, IgnoreMac: opts.IgnoreMAC, Tree: tree, KeyServices: opts.KeyServices, }) diff --git a/cmd/sops/subcommand/publish/publish.go b/cmd/sops/subcommand/publish/publish.go index fc4cb5afe..c8e303323 100644 --- a/cmd/sops/subcommand/publish/publish.go +++ b/cmd/sops/subcommand/publish/publish.go @@ -24,6 +24,7 @@ func init() { log = logging.NewLogger("PUBLISH") } +// Opts represents publish options and config type Opts struct { Interactive bool Cipher sops.Cipher @@ -33,6 +34,7 @@ type Opts struct { InputStore sops.Store } +// Run publish operation func Run(opts Opts) error { var fileContents []byte path, err := filepath.Abs(opts.InputPath) diff --git a/cmd/sops/subcommand/updatekeys/updatekeys.go b/cmd/sops/subcommand/updatekeys/updatekeys.go index 2f403770e..4763b045e 100644 --- a/cmd/sops/subcommand/updatekeys/updatekeys.go +++ b/cmd/sops/subcommand/updatekeys/updatekeys.go @@ -12,6 +12,7 @@ import ( "go.mozilla.org/sops/keyservice" ) +// Opts represents key operation options and config type Opts struct { InputPath string GroupQuorum int @@ -20,6 +21,7 @@ type Opts struct { ConfigPath string } +// UpdateKeys update the keys for a given file func UpdateKeys(opts Opts) error { path, err := filepath.Abs(opts.InputPath) if err != nil { diff --git a/logging/logging.go b/logging/logging.go index a13c9a7c0..1e89c85ee 100644 --- a/logging/logging.go +++ b/logging/logging.go @@ -17,12 +17,14 @@ type TextFormatter struct { logrus.TextFormatter } +// Format formats a log entry onto bytes func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) { bytes, err := f.TextFormatter.Format(entry) name := color.New(color.Bold).Sprintf("[%s]", f.LoggerName) return []byte(fmt.Sprintf("%s\t %s", name, bytes)), err } +// NewLogger is the constructor for a new Logger object with the given name func NewLogger(name string) *logrus.Logger { log := logrus.New() log.SetLevel(logrus.WarnLevel) @@ -33,10 +35,12 @@ func NewLogger(name string) *logrus.Logger { return log } +// SetLevel sets the given level for all current Loggers func SetLevel(level logrus.Level) { for k := range Loggers { Loggers[k].SetLevel(level) } } +// Loggers is the runtime map of logger name to logger object var Loggers map[string]*logrus.Logger diff --git a/publish/gcs.go b/publish/gcs.go index de5e574d5..fad62cfb8 100644 --- a/publish/gcs.go +++ b/publish/gcs.go @@ -7,19 +7,23 @@ import ( "cloud.google.com/go/storage" ) +// GCSDestination represents the Google Cloud Storage destination type GCSDestination struct { gcsBucket string gcsPrefix string } +// NewGCSDestination is the constructor for a new Google Cloud Storage destination func NewGCSDestination(gcsBucket string, gcsPrefix string) *GCSDestination { return &GCSDestination{gcsBucket, gcsPrefix} } +// Path returns a the GCS path for a file within this GCS Destination func (gcsd *GCSDestination) Path(fileName string) string { return fmt.Sprintf("gcs://%s/%s%s", gcsd.gcsBucket, gcsd.gcsPrefix, fileName) } +// Upload uploads contents to a new file in GCS func (gcsd *GCSDestination) Upload(fileContents []byte, fileName string) error { ctx := context.Background() client, err := storage.NewClient(ctx) diff --git a/publish/publish.go b/publish/publish.go index 9a559858c..f5784180d 100644 --- a/publish/publish.go +++ b/publish/publish.go @@ -1,5 +1,7 @@ package publish +// Destination represents actions which all destination types +// must implement in order to be used by SOPS type Destination interface { Upload(fileContents []byte, fileName string) error Path(fileName string) string diff --git a/publish/s3.go b/publish/s3.go index 65ca6ab5d..80fbe3763 100644 --- a/publish/s3.go +++ b/publish/s3.go @@ -9,19 +9,23 @@ import ( "github.com/aws/aws-sdk-go/service/s3" ) +// S3Destination is the AWS S3 implementation of the Destination interface type S3Destination struct { s3Bucket string s3Prefix string } +// NewS3Destination is the constructor for a new S3 Destination func NewS3Destination(s3Bucket string, s3Prefix string) *S3Destination { return &S3Destination{s3Bucket, s3Prefix} } +// Path returns the S3 path of a file in an S3 Destination (bucket) func (s3d *S3Destination) Path(fileName string) string { return fmt.Sprintf("s3://%s/%s%s", s3d.s3Bucket, s3d.s3Prefix, fileName) } +// Upload uploads contents to a new file in an S3 Destination (bucket) func (s3d *S3Destination) Upload(fileContents []byte, fileName string) error { sess := session.Must(session.NewSession()) svc := s3.New(sess) diff --git a/shamir/shamir.go b/shamir/shamir.go index 30cf2e77a..4fb63851e 100644 --- a/shamir/shamir.go +++ b/shamir/shamir.go @@ -73,8 +73,8 @@ func (p *polynomial) evaluate(x uint8) uint8 { // An implementation of Lagrange interpolation // // For this particular implementation, x is always 0 -func interpolatePolynomial(x_samples, y_samples []uint8, x uint8) uint8 { - limit := len(x_samples) +func interpolatePolynomial(xSamples, ySamples []uint8, x uint8) uint8 { + limit := len(xSamples) var result, basis uint8 for i := 0; i < limit; i++ { basis = 1 @@ -82,12 +82,12 @@ func interpolatePolynomial(x_samples, y_samples []uint8, x uint8) uint8 { if i == j { continue } - num := add(x, x_samples[j]) - denom := add(x_samples[i], x_samples[j]) + num := add(x, xSamples[j]) + denom := add(xSamples[i], xSamples[j]) term := div(num, denom) basis = mult(basis, term) } - group := mult(y_samples[i], basis) + group := mult(ySamples[i], basis) result = add(result, group) } return result @@ -103,9 +103,9 @@ func div(a, b uint8) uint8 { } var goodVal, zero uint8 - log_a := logTable[a] - log_b := logTable[b] - diff := (int(log_a) - int(log_b)) % 255 + logA := logTable[a] + logB := logTable[b] + diff := (int(logA) - int(logB)) % 255 if diff < 0 { diff += 255 } @@ -258,8 +258,8 @@ func Combine(parts [][]byte) ([]byte, error) { secret := make([]byte, firstPartLen-1) // Buffer to store the samples - x_samples := make([]uint8, len(parts)) - y_samples := make([]uint8, len(parts)) + xSamples := make([]uint8, len(parts)) + ySamples := make([]uint8, len(parts)) // Set the x value for each sample and ensure no x_sample values are the same, // otherwise div() can be unhappy @@ -272,19 +272,19 @@ func Combine(parts [][]byte) ([]byte, error) { return nil, fmt.Errorf("duplicate part detected") } checkMap[samp] = true - x_samples[i] = samp + xSamples[i] = samp } // Reconstruct each byte for idx := range secret { // Set the y value for each sample for i, part := range parts { - y_samples[i] = part[idx] + ySamples[i] = part[idx] } // Use Lagrange interpolation to retrieve the free term // of the original polynomial - val := interpolatePolynomial(x_samples, y_samples, 0) + val := interpolatePolynomial(xSamples, ySamples, 0) // Evaluate the 0th value to get the intercept secret[idx] = val diff --git a/shamir/shamir_test.go b/shamir/shamir_test.go index 09f90d5fc..7719ed01f 100644 --- a/shamir/shamir_test.go +++ b/shamir/shamir_test.go @@ -188,9 +188,9 @@ func TestInterpolate_Rand(t *testing.T) { t.Fatalf("err: %v", err) } - x_vals := []uint8{1, 2, 3} - y_vals := []uint8{p.evaluate(1), p.evaluate(2), p.evaluate(3)} - out := interpolatePolynomial(x_vals, y_vals, 0) + xVals := []uint8{1, 2, 3} + yVals := []uint8{p.evaluate(1), p.evaluate(2), p.evaluate(3)} + out := interpolatePolynomial(xVals, yVals, 0) if out != uint8(i) { t.Fatalf("Bad: %v %d", out, i) } diff --git a/sops.go b/sops.go index 69e01dc47..8cc244981 100644 --- a/sops.go +++ b/sops.go @@ -100,7 +100,8 @@ type TreeItem struct { // TreeBranch is a branch inside sops's tree. It is a slice of TreeItems and is therefore ordered type TreeBranch []TreeItem -// Trees usually have more than one branch +// TreeBranches is a collection of TreeBranch +// trees usually have more than one branch type TreeBranches []TreeBranch func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} { @@ -110,10 +111,9 @@ func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} { return []interface{}{ leaf, } - } else { - return []interface{}{ - valueFromPathAndLeaf(path[1:], leaf), - } + } + return []interface{}{ + valueFromPathAndLeaf(path[1:], leaf), } default: if len(path) == 1 { @@ -123,13 +123,12 @@ func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} { Value: leaf, }, } - } else { - return TreeBranch{ - TreeItem{ - Key: component, - Value: valueFromPathAndLeaf(path[1:], leaf), - }, - } + } + return TreeBranch{ + TreeItem{ + Key: component, + Value: valueFromPathAndLeaf(path[1:], leaf), + }, } } } @@ -150,31 +149,28 @@ func set(branch interface{}, path []interface{}, value interface{}) interface{} // Not found, need to add the next path entry to the branch if len(path) == 1 { return append(branch, TreeItem{Key: path[0], Value: value}) - } else { - return valueFromPathAndLeaf(path, value) } + return valueFromPathAndLeaf(path, value) case []interface{}: position := path[0].(int) if len(path) == 1 { if position >= len(branch) { return append(branch, value) - } else { - branch[position] = value } - return branch + branch[position] = value } else { if position >= len(branch) { branch = append(branch, valueFromPathAndLeaf(path[1:], value)) - } else { - branch[position] = set(branch[position], path[1:], value) } - return branch + branch[position] = set(branch[position], path[1:], value) } + return branch default: return valueFromPathAndLeaf(path, value) } } +// Set sets a value on a given tree for the specified path func (branch TreeBranch) Set(path []interface{}, value interface{}) TreeBranch { return set(branch, path, value).(TreeBranch) } @@ -486,6 +482,8 @@ type PlainFileEmitter interface { EmitPlainFile(TreeBranches) ([]byte, error) } +// ValueEmitter is the interface for emitting a value. It provides a way to emit +// values from the internal SOPS representation so that they can be shown type ValueEmitter interface { EmitValue(interface{}) ([]byte, error) } @@ -634,7 +632,7 @@ func decryptKeyGroup(group KeyGroup, svcs []keyservice.KeyServiceClient) ([]byte // of the key services, returning as soon as one key service succeeds. func decryptKey(key keys.MasterKey, svcs []keyservice.KeyServiceClient) ([]byte, error) { svcKey := keyservice.KeyFromMasterKey(key) - var part []byte = nil + var part []byte decryptErr := decryptKeyError{ keyName: key.ToString(), } diff --git a/stores/dotenv/store.go b/stores/dotenv/store.go index 770e7101f..b5a71344d 100644 --- a/stores/dotenv/store.go +++ b/stores/dotenv/store.go @@ -10,12 +10,14 @@ import ( "go.mozilla.org/sops/stores" ) +// SopsPrefix is the prefix for all metadatada entry keys const SopsPrefix = "sops_" // Store handles storage of dotenv data type Store struct { } +// LoadEncryptedFile loads an encrypted file's bytes onto a sops.Tree runtime object func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) { branches, err := store.LoadPlainFile(in) if err != nil { @@ -57,6 +59,8 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) { }, nil } +// LoadPlainFile returns the contents of a plaintext file loaded onto a +// sops runtime object func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { var branches sops.TreeBranches var branch sops.TreeBranch @@ -86,6 +90,8 @@ func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { return branches, nil } +// EmitEncryptedFile returns the encrypted file's bytes corresponding to a sops +// runtime object func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) { metadata := stores.MetadataFromInternal(in.Metadata) mdItems, err := metadataToMap(metadata) @@ -101,6 +107,8 @@ func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) { return store.EmitPlainFile(in.Branches) } +// EmitPlainFile returns the plaintext file's bytes corresponding to a sops +// runtime object func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) { buffer := bytes.Buffer{} for _, item := range in[0] { @@ -118,6 +126,7 @@ func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) { return buffer.Bytes(), nil } +// EmitValue returns a single value as bytes func (Store) EmitValue(v interface{}) ([]byte, error) { if s, ok := v.(string); ok { return []byte(s), nil @@ -125,6 +134,7 @@ func (Store) EmitValue(v interface{}) ([]byte, error) { return nil, fmt.Errorf("the dotenv store only supports emitting strings, got %T", v) } +// EmitExample returns the bytes corresponding to an example Flat Tree runtime object func (store *Store) EmitExample() []byte { bytes, err := store.EmitPlainFile(stores.ExampleFlatTree.Branches) if err != nil { diff --git a/stores/flatten.go b/stores/flatten.go index dbe3b2601..00d603302 100644 --- a/stores/flatten.go +++ b/stores/flatten.go @@ -106,7 +106,7 @@ func tokenize(path string) []token { state = StateList i += len(listSeparator) } else { - i += 1 + i++ } } finishPrevToken() diff --git a/stores/ini/store.go b/stores/ini/store.go index 823b031c1..19d631279 100644 --- a/stores/ini/store.go +++ b/stores/ini/store.go @@ -136,6 +136,7 @@ func (store Store) treeItemFromSection(section *ini.Section) (sops.TreeItem, err return sectionItem, nil } +// LoadEncryptedFile loads encrypted INI file's bytes onto a sops.Tree runtime object func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) { iniFileOuter, err := ini.Load(in) if err != nil { @@ -192,6 +193,7 @@ func (store *Store) iniSectionToMetadata(sopsSection *ini.Section) (stores.Metad return md, err } +// LoadPlainFile loads a plaintext INI file's bytes onto a sops.TreeBranches runtime object func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { branches, err := store.treeBranchesFromIni(in) if err != nil { @@ -200,6 +202,8 @@ func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { return branches, nil } +// EmitEncryptedFile returns encrypted INI file bytes corresponding to a sops.Tree +// runtime object func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) { metadata := stores.MetadataFromInternal(in.Metadata) @@ -298,6 +302,7 @@ func encodeMetadataItem(prefix string, kind reflect.Kind, field reflect.Value) ( return result, nil } +// EmitPlainFile returns the plaintext INI file bytes corresponding to a sops.TreeBranches object func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) { out, err := store.iniFromTreeBranches(in) if err != nil { @@ -315,10 +320,12 @@ func (store Store) encodeValue(v interface{}) ([]byte, error) { } } +// EmitValue returns a single value encoded in a generic interface{} as bytes func (store *Store) EmitValue(v interface{}) ([]byte, error) { return store.encodeValue(v) } +// EmitExample returns the plaintext INI file bytes corresponding to the SimpleTree example func (store *Store) EmitExample() []byte { bytes, err := store.EmitPlainFile(stores.ExampleSimpleTree.Branches) if err != nil { diff --git a/stores/json/store.go b/stores/json/store.go index 10fc46f68..a1e90d671 100644 --- a/stores/json/store.go +++ b/stores/json/store.go @@ -19,10 +19,13 @@ type BinaryStore struct { store Store } +// LoadEncryptedFile loads an encrypted json file onto a sops.Tree object func (store BinaryStore) LoadEncryptedFile(in []byte) (sops.Tree, error) { return store.store.LoadEncryptedFile(in) } +// LoadPlainFile loads a plaintext json file onto a sops.Tree encapsulated +// within a sops.TreeBranches object func (store BinaryStore) LoadPlainFile(in []byte) (sops.TreeBranches, error) { return sops.TreeBranches{ sops.TreeBranch{ @@ -34,10 +37,12 @@ func (store BinaryStore) LoadPlainFile(in []byte) (sops.TreeBranches, error) { }, nil } +// EmitEncryptedFile produces an encrypted json file's bytes from its corresponding sops.Tree object func (store BinaryStore) EmitEncryptedFile(in sops.Tree) ([]byte, error) { return store.store.EmitEncryptedFile(in) } +// EmitPlainFile produces plaintext json file's bytes from its corresponding sops.TreeBranches object func (store BinaryStore) EmitPlainFile(in sops.TreeBranches) ([]byte, error) { // JSON stores a single object per file for _, item := range in[0] { @@ -48,10 +53,13 @@ func (store BinaryStore) EmitPlainFile(in sops.TreeBranches) ([]byte, error) { return nil, fmt.Errorf("No binary data found in tree") } +// EmitValue extracts a value from a generic interface{} object representing a structured set +// of binary files func (store BinaryStore) EmitValue(v interface{}) ([]byte, error) { return nil, fmt.Errorf("Binary files are not structured and extracting a single value is not possible") } +// EmitExample returns the example's plaintext json file bytes func (store BinaryStore) EmitExample() []byte { return []byte("Welcome to SOPS! Edit this file as you please!") } @@ -207,6 +215,7 @@ func (store Store) reindentJSON(in []byte) ([]byte, error) { return out.Bytes(), err } +// LoadEncryptedFile loads an encrypted secrets file onto a sops.Tree object func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) { // Because we don't know what fields the input file will have, we have to // load the file in two steps. @@ -252,6 +261,7 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) { }, nil } +// LoadPlainFile loads plaintext json file bytes onto a sops.TreeBranches object func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { branch, err := store.treeBranchFromJSON(in) if err != nil { @@ -262,6 +272,8 @@ func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { }, nil } +// EmitEncryptedFile returns the encrypted bytes of the json file corresponding to a +// sops.Tree runtime object func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) { tree := append(in.Branches[0], sops.TreeItem{Key: "sops", Value: stores.MetadataFromInternal(in.Metadata)}) out, err := store.jsonFromTreeBranch(tree) @@ -271,6 +283,8 @@ func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) { return out, nil } +// EmitPlainFile returns the plaintext bytes of the json file corresponding to a +// sops.TreeBranches runtime object func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) { out, err := store.jsonFromTreeBranch(in[0]) if err != nil { @@ -279,6 +293,8 @@ func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) { return out, nil } +// EmitValue returns bytes corresponding to a single encoded value +// in a generic interface{} object func (store *Store) EmitValue(v interface{}) ([]byte, error) { s, err := store.encodeValue(v) if err != nil { @@ -287,6 +303,7 @@ func (store *Store) EmitValue(v interface{}) ([]byte, error) { return store.reindentJSON(s) } +// EmitExample returns the bytes corresponding to an example complex tree func (store *Store) EmitExample() []byte { bytes, err := store.EmitPlainFile(stores.ExampleComplexTree.Branches) if err != nil { diff --git a/stores/stores.go b/stores/stores.go index 5d32005a7..efe6f94cd 100644 --- a/stores/stores.go +++ b/stores/stores.go @@ -309,6 +309,7 @@ func (pgpKey *pgpkey) toInternal() (*pgp.MasterKey, error) { }, nil } +// ExampleComplexTree is an example sops.Tree object exhibiting complex relationships var ExampleComplexTree = sops.Tree{ Branches: sops.TreeBranches{ sops.TreeBranch{ @@ -343,6 +344,8 @@ var ExampleComplexTree = sops.Tree{ }, } +// ExampleSimpleTree is an example sops.Tree object exhibiting only simple relationships +// with only one nested branch and only simple string values var ExampleSimpleTree = sops.Tree{ Branches: sops.TreeBranches{ sops.TreeBranch{ @@ -367,6 +370,8 @@ var ExampleSimpleTree = sops.Tree{ }, } +// ExampleFlatTree is an example sops.Tree object exhibiting only simple relationships +// with no nested branches and only simple string values var ExampleFlatTree = sops.Tree{ Branches: sops.TreeBranches{ sops.TreeBranch{ diff --git a/stores/yaml/store.go b/stores/yaml/store.go index bbba20538..06a185207 100644 --- a/stores/yaml/store.go +++ b/stores/yaml/store.go @@ -101,6 +101,8 @@ func (store Store) treeBranchToYamlMap(in sops.TreeBranch) yaml.MapSlice { return branch } +// LoadEncryptedFile loads the contexts of an encrypted yaml file onto a +// sops.Tree runtime object func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) { var data []yaml.MapSlice if err := (yaml.CommentUnmarshaler{}).UnmarshalDocuments(in, &data); err != nil { @@ -136,6 +138,8 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) { }, nil } +// LoadPlainFile loads the contexts of a plaintext yaml file onto a +// sops.Tree runtime obejct func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { var data []yaml.MapSlice if err := (yaml.CommentUnmarshaler{}).UnmarshalDocuments(in, &data); err != nil { @@ -149,6 +153,8 @@ func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { return branches, nil } +// EmitEncryptedFile returns the encrypted bytes of the yaml file corresponding to a +// sops.Tree runtime object func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) { out := []byte{} for i, branch := range in.Branches { @@ -166,6 +172,8 @@ func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) { return out, nil } +// EmitPlainFile returns the plaintext bytes of the yaml file corresponding to a +// sops.TreeBranches runtime object func (store *Store) EmitPlainFile(branches sops.TreeBranches) ([]byte, error) { var out []byte for i, branch := range branches { @@ -182,11 +190,14 @@ func (store *Store) EmitPlainFile(branches sops.TreeBranches) ([]byte, error) { return out, nil } +// EmitValue returns bytes corresponding to a single encoded value +// in a generic interface{} object func (store *Store) EmitValue(v interface{}) ([]byte, error) { v = store.treeValueToYamlValue(v) return (&yaml.YAMLMarshaler{Indent: 4}).Marshal(v) } +// EmitExample returns the bytes corresponding to an example complex tree func (store *Store) EmitExample() []byte { bytes, err := store.EmitPlainFile(stores.ExampleComplexTree.Branches) if err != nil { diff --git a/version/version.go b/version/version.go index 996df12a4..e63d08cc0 100644 --- a/version/version.go +++ b/version/version.go @@ -10,8 +10,10 @@ import ( "gopkg.in/urfave/cli.v1" ) +// Version represents the value of the current semantic version const Version = "3.3.1" +// PrintVersion handles the version command for sops func PrintVersion(c *cli.Context) { out := fmt.Sprintf("%s %s", c.App.Name, c.App.Version) upstreamVersion, err := RetrieveLatestVersionFromUpstream() From 4d4891588d8d3bc9a5085372e0d27df17742778a Mon Sep 17 00:00:00 2001 From: Adriano Date: Thu, 11 Jul 2019 10:29:52 -0700 Subject: [PATCH 2/3] address comments --- audit/audit.go | 4 ++-- cmd/sops/common/common.go | 4 ++-- publish/gcs.go | 4 ++-- publish/s3.go | 4 ++-- sops.go | 2 +- stores/yaml/store.go | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/audit/audit.go b/audit/audit.go index 2b33bca09..21fca9fcc 100644 --- a/audit/audit.go +++ b/audit/audit.go @@ -81,7 +81,7 @@ func Register(auditor Auditor) { auditors = append(auditors, auditor) } -// Auditor is notified when noteworthy events happen, for example when a file is encrypted or decrypted. +// Auditor is notified when noteworthy events happen, for example when a file is encrypted or decrypted. type Auditor interface { Handle(event interface{}) } @@ -107,7 +107,7 @@ type PostgresAuditor struct { DB *sql.DB } -// NewPostgresAuditor is the constructor for a new PostgresAuditor object +// NewPostgresAuditor is the constructor for a new PostgresAuditor struct // initialized with the given db connection string func NewPostgresAuditor(connStr string) (*PostgresAuditor, error) { db, err := sql.Open("postgres", connStr) diff --git a/cmd/sops/common/common.go b/cmd/sops/common/common.go index 1fca82ebc..ebe057e9a 100644 --- a/cmd/sops/common/common.go +++ b/cmd/sops/common/common.go @@ -24,8 +24,8 @@ import ( "gopkg.in/urfave/cli.v1" ) -// ExampleFileEmitter represents actions needed for a struct to be comliant -// with the sops example-file emitter interface +// ExampleFileEmitter emits example files. This is used by the `sops` binary +// whenever a new file is created, in order to present the user with a non-empty file type ExampleFileEmitter interface { EmitExample() []byte } diff --git a/publish/gcs.go b/publish/gcs.go index fad62cfb8..44a3e8b3f 100644 --- a/publish/gcs.go +++ b/publish/gcs.go @@ -13,7 +13,7 @@ type GCSDestination struct { gcsPrefix string } -// NewGCSDestination is the constructor for a new Google Cloud Storage destination +// NewGCSDestination is the constructor for a Google Cloud Storage destination func NewGCSDestination(gcsBucket string, gcsPrefix string) *GCSDestination { return &GCSDestination{gcsBucket, gcsPrefix} } @@ -23,7 +23,7 @@ func (gcsd *GCSDestination) Path(fileName string) string { return fmt.Sprintf("gcs://%s/%s%s", gcsd.gcsBucket, gcsd.gcsPrefix, fileName) } -// Upload uploads contents to a new file in GCS +// Upload uploads contents to a file in GCS func (gcsd *GCSDestination) Upload(fileContents []byte, fileName string) error { ctx := context.Background() client, err := storage.NewClient(ctx) diff --git a/publish/s3.go b/publish/s3.go index 80fbe3763..2010c692e 100644 --- a/publish/s3.go +++ b/publish/s3.go @@ -15,7 +15,7 @@ type S3Destination struct { s3Prefix string } -// NewS3Destination is the constructor for a new S3 Destination +// NewS3Destination is the constructor for an S3 Destination func NewS3Destination(s3Bucket string, s3Prefix string) *S3Destination { return &S3Destination{s3Bucket, s3Prefix} } @@ -25,7 +25,7 @@ func (s3d *S3Destination) Path(fileName string) string { return fmt.Sprintf("s3://%s/%s%s", s3d.s3Bucket, s3d.s3Prefix, fileName) } -// Upload uploads contents to a new file in an S3 Destination (bucket) +// Upload uploads contents to a file in an S3 Destination (bucket) func (s3d *S3Destination) Upload(fileContents []byte, fileName string) error { sess := session.Must(session.NewSession()) svc := s3.New(sess) diff --git a/sops.go b/sops.go index 8cc244981..bc46f79f8 100644 --- a/sops.go +++ b/sops.go @@ -101,7 +101,7 @@ type TreeItem struct { type TreeBranch []TreeItem // TreeBranches is a collection of TreeBranch -// trees usually have more than one branch +// Trees usually have more than one branch type TreeBranches []TreeBranch func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} { diff --git a/stores/yaml/store.go b/stores/yaml/store.go index 06a185207..c897f537e 100644 --- a/stores/yaml/store.go +++ b/stores/yaml/store.go @@ -101,7 +101,7 @@ func (store Store) treeBranchToYamlMap(in sops.TreeBranch) yaml.MapSlice { return branch } -// LoadEncryptedFile loads the contexts of an encrypted yaml file onto a +// LoadEncryptedFile loads the contents of an encrypted yaml file onto a // sops.Tree runtime object func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) { var data []yaml.MapSlice @@ -138,7 +138,7 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) { }, nil } -// LoadPlainFile loads the contexts of a plaintext yaml file onto a +// LoadPlainFile loads the contents of a plaintext yaml file onto a // sops.Tree runtime obejct func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { var data []yaml.MapSlice From 41b9e9f4b6f868d2dd78de2cc1ae5a458fc51cb2 Mon Sep 17 00:00:00 2001 From: Adriano Date: Fri, 12 Jul 2019 12:06:56 -0700 Subject: [PATCH 3/3] update auditor interface comments --- audit/audit.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/audit/audit.go b/audit/audit.go index 21fca9fcc..6d9fb4f8a 100644 --- a/audit/audit.go +++ b/audit/audit.go @@ -81,8 +81,12 @@ func Register(auditor Auditor) { auditors = append(auditors, auditor) } -// Auditor is notified when noteworthy events happen, for example when a file is encrypted or decrypted. +// Auditor is notified when noteworthy events happen, +// for example when a file is encrypted or decrypted. type Auditor interface { + // Handle() takes an audit event and attempts to persists it; + // how it is persisted and how errors are handled is up to the + // implementation of this interface. Handle(event interface{}) } @@ -101,8 +105,10 @@ type RotateEvent struct { File string } -// PostgresAuditor is a Postgres SQL DB implementation of the -// Auditor interface +// PostgresAuditor is a Postgres SQL DB implementation of the Auditor interface. +// It persists the audit event by writing a row to the 'audit_event' table. +// Errors with writing to the database will output a log message and the +// process will exit with status set to 1 type PostgresAuditor struct { DB *sql.DB } @@ -125,8 +131,8 @@ func NewPostgresAuditor(connStr string) (*PostgresAuditor, error) { return pg, nil } -// Handle is the PostgresAuditor implementation of the function required by the -// Auditor interface +// Handle persists the audit event by writing a row to the +// 'audit_event' postgres table func (p *PostgresAuditor) Handle(event interface{}) { u, err := user.Current() if err != nil {