Merge pull request #491 from adrianosela/golint

Address Go Lint messages
This commit is contained in:
Adrian Utrilla 2019-07-16 11:36:51 +02:00 коммит произвёл GitHub
Родитель 48f92ee92e 41b9e9f4b6
Коммит 9998e16c3e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 152 добавлений и 44 удалений

Просмотреть файл

@ -45,6 +45,7 @@ type Cipher struct {
stash map[stashKey][]byte stash map[stashKey][]byte
} }
// NewCipher is the constructor for a new Cipher object
func NewCipher() Cipher { func NewCipher() Cipher {
return Cipher{ return Cipher{
stash: make(map[stashKey][]byte), stash: make(map[stashKey][]byte),

Просмотреть файл

@ -9,7 +9,9 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
// empty import as per https://godoc.org/github.com/lib/pq
_ "github.com/lib/pq" _ "github.com/lib/pq"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"go.mozilla.org/sops/logging" "go.mozilla.org/sops/logging"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@ -67,36 +69,52 @@ type config struct {
var auditors []Auditor var auditors []Auditor
// SubmitEvent handles an event for all auditors
func SubmitEvent(event interface{}) { func SubmitEvent(event interface{}) {
for _, auditor := range auditors { for _, auditor := range auditors {
auditor.Handle(event) auditor.Handle(event)
} }
} }
// Register registers a new Auditor in the global auditor list
func Register(auditor Auditor) { func Register(auditor Auditor) {
auditors = append(auditors, auditor) auditors = append(auditors, auditor)
} }
// Auditor is notified when noteworthy events happen,
// for example when a file is encrypted or decrypted.
type Auditor interface { 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{}) Handle(event interface{})
} }
// DecryptEvent contains fields relevant to a decryption event
type DecryptEvent struct { type DecryptEvent struct {
File string File string
} }
// EncryptEvent contains fields relevant to an encryption event
type EncryptEvent struct { type EncryptEvent struct {
File string File string
} }
// RotateEvent contains fields relevant to a key rotation event
type RotateEvent struct { type RotateEvent struct {
File string File string
} }
// 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 { type PostgresAuditor struct {
DB *sql.DB DB *sql.DB
} }
// NewPostgresAuditor is the constructor for a new PostgresAuditor struct
// initialized with the given db connection string
func NewPostgresAuditor(connStr string) (*PostgresAuditor, error) { func NewPostgresAuditor(connStr string) (*PostgresAuditor, error) {
db, err := sql.Open("postgres", connStr) db, err := sql.Open("postgres", connStr)
pg := &PostgresAuditor{DB: db} pg := &PostgresAuditor{DB: db}
@ -113,6 +131,8 @@ func NewPostgresAuditor(connStr string) (*PostgresAuditor, error) {
return pg, nil return pg, nil
} }
// Handle persists the audit event by writing a row to the
// 'audit_event' postgres table
func (p *PostgresAuditor) Handle(event interface{}) { func (p *PostgresAuditor) Handle(event interface{}) {
u, err := user.Current() u, err := user.Current()
if err != nil { if err != nil {

Просмотреть файл

@ -73,7 +73,7 @@ func MasterKeysFromURLs(urls string) ([]*MasterKey, error) {
return keys, nil 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} // URL format is {vaultUrl}/keys/{key-name}/{key-version}
func NewMasterKeyFromURL(url string) (*MasterKey, error) { func NewMasterKeyFromURL(url string) (*MasterKey, error) {
k := &MasterKey{} k := &MasterKey{}

Просмотреть файл

@ -92,16 +92,16 @@ func TestKeyToMap(t *testing.T) {
}, key.ToMap()) }, 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) { func TestRoundtrip(t *testing.T) {
if *azureKeyAcceptanceTestUrl == "" { if *azureKeyAcceptanceTestURL == "" {
t.Skip("Azure URL not provided, skipping acceptance test") t.Skip("Azure URL not provided, skipping acceptance test")
} }
input := []byte("test-string") input := []byte("test-string")
key, err := NewMasterKeyFromURL(*azureKeyAcceptanceTestUrl) key, err := NewMasterKeyFromURL(*azureKeyAcceptanceTestURL)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

Просмотреть файл

@ -24,10 +24,13 @@ import (
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
) )
// 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 { type ExampleFileEmitter interface {
EmitExample() []byte EmitExample() []byte
} }
// Store handles marshaling and unmarshaling from SOPS files
type Store interface { type Store interface {
sops.Store sops.Store
ExampleFileEmitter ExampleFileEmitter
@ -107,6 +110,8 @@ func LoadEncryptedFile(loader sops.EncryptedFileLoader, inputPath string) (*sops
return &tree, err 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 { func NewExitError(i interface{}, exitCode int) *cli.ExitError {
if userErr, ok := i.(sops.UserError); ok { if userErr, ok := i.(sops.UserError); ok {
return NewExitError(userErr.UserError(), exitCode) return NewExitError(userErr.UserError(), exitCode)
@ -114,22 +119,28 @@ func NewExitError(i interface{}, exitCode int) *cli.ExitError {
return cli.NewExitError(i, exitCode) return cli.NewExitError(i, exitCode)
} }
// IsYAMLFile returns true if a given file path corresponds to a YAML file
func IsYAMLFile(path string) bool { func IsYAMLFile(path string) bool {
return strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") 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 { func IsJSONFile(path string) bool {
return strings.HasSuffix(path, ".json") return strings.HasSuffix(path, ".json")
} }
// IsEnvFile returns true if a given file path corresponds to a .env file
func IsEnvFile(path string) bool { func IsEnvFile(path string) bool {
return strings.HasSuffix(path, ".env") return strings.HasSuffix(path, ".env")
} }
// IsIniFile returns true if a given file path corresponds to a INI file
func IsIniFile(path string) bool { func IsIniFile(path string) bool {
return strings.HasSuffix(path, ".ini") 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 { func DefaultStoreForPath(path string) Store {
if IsYAMLFile(path) { if IsYAMLFile(path) {
return &yaml.Store{} return &yaml.Store{}
@ -143,8 +154,12 @@ func DefaultStoreForPath(path string) Store {
return &json.BinaryStore{} 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" 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) { func DetectKMSEncryptionContextBug(tree *sops.Tree) (bool, error) {
versionCheck, err := version.AIsNewerThanB(KMS_ENC_CTX_BUG_FIXED_VERSION, tree.Metadata.Version) versionCheck, err := version.AIsNewerThanB(KMS_ENC_CTX_BUG_FIXED_VERSION, tree.Metadata.Version)
if err != nil { if err != nil {
@ -161,6 +176,7 @@ func DetectKMSEncryptionContextBug(tree *sops.Tree) (bool, error) {
return false, nil 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) { func GetKMSKeyWithEncryptionCtx(tree *sops.Tree) (keyGroupIndex int, keyIndex int, key *kms.MasterKey) {
for i, kg := range tree.Metadata.KeyGroups { for i, kg := range tree.Metadata.KeyGroups {
for n, k := range kg { for n, k := range kg {
@ -181,6 +197,7 @@ func GetKMSKeyWithEncryptionCtx(tree *sops.Tree) (keyGroupIndex int, keyIndex in
return 0, 0, nil return 0, 0, nil
} }
// GenericDecryptOpts represents decryption options and config
type GenericDecryptOpts struct { type GenericDecryptOpts struct {
Cipher sops.Cipher Cipher sops.Cipher
InputStore sops.Store InputStore sops.Store
@ -235,10 +252,9 @@ func FixAWSKMSEncryptionContextBug(opts GenericDecryptOpts, tree *sops.Tree) (*s
} }
} }
if response == "n" { if response == "n" {
return nil, fmt.Errorf("Exiting. User responded no.") return nil, fmt.Errorf("Exiting. User responded no")
} else {
persistFix = true
} }
persistFix = true
} }
// If there is another key, then we should be able to just decrypt // If there is another key, then we should be able to just decrypt
@ -341,6 +357,7 @@ func RecoverDataKeyFromBuggyKMS(opts GenericDecryptOpts, tree *sops.Tree) []byte
return nil return nil
} }
// Diff represents a key diff
type Diff struct { type Diff struct {
Common []keys.MasterKey Common []keys.MasterKey
Added []keys.MasterKey Added []keys.MasterKey
@ -354,6 +371,7 @@ func max(a, b int) int {
return b return b
} }
// DiffKeyGroups returns the list of diffs found in two sops.keyGroup slices
func DiffKeyGroups(ours, theirs []sops.KeyGroup) []Diff { func DiffKeyGroups(ours, theirs []sops.KeyGroup) []Diff {
var diffs []Diff var diffs []Diff
for i := 0; i < max(len(ours), len(theirs)); i++ { for i := 0; i < max(len(ours), len(theirs)); i++ {
@ -388,6 +406,7 @@ func DiffKeyGroups(ours, theirs []sops.KeyGroup) []Diff {
return diffs return diffs
} }
// PrettyPrintDiffs prints a slice of Diff objects to stdout
func PrettyPrintDiffs(diffs []Diff) { func PrettyPrintDiffs(diffs []Diff) {
for i, diff := range diffs { for i, diff := range diffs {
color.New(color.Underline).Printf("Group %d\n", i+1) color.New(color.Underline).Printf("Group %d\n", i+1)

Просмотреть файл

@ -24,6 +24,7 @@ func init() {
log = logging.NewLogger("PUBLISH") log = logging.NewLogger("PUBLISH")
} }
// Opts represents publish options and config
type Opts struct { type Opts struct {
Interactive bool Interactive bool
Cipher sops.Cipher Cipher sops.Cipher
@ -33,6 +34,7 @@ type Opts struct {
InputStore sops.Store InputStore sops.Store
} }
// Run publish operation
func Run(opts Opts) error { func Run(opts Opts) error {
var fileContents []byte var fileContents []byte
path, err := filepath.Abs(opts.InputPath) path, err := filepath.Abs(opts.InputPath)

Просмотреть файл

@ -12,6 +12,7 @@ import (
"go.mozilla.org/sops/keyservice" "go.mozilla.org/sops/keyservice"
) )
// Opts represents key operation options and config
type Opts struct { type Opts struct {
InputPath string InputPath string
GroupQuorum int GroupQuorum int
@ -20,6 +21,7 @@ type Opts struct {
ConfigPath string ConfigPath string
} }
// UpdateKeys update the keys for a given file
func UpdateKeys(opts Opts) error { func UpdateKeys(opts Opts) error {
path, err := filepath.Abs(opts.InputPath) path, err := filepath.Abs(opts.InputPath)
if err != nil { if err != nil {

Просмотреть файл

@ -17,12 +17,14 @@ type TextFormatter struct {
logrus.TextFormatter logrus.TextFormatter
} }
// Format formats a log entry onto bytes
func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) { func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) {
bytes, err := f.TextFormatter.Format(entry) bytes, err := f.TextFormatter.Format(entry)
name := color.New(color.Bold).Sprintf("[%s]", f.LoggerName) name := color.New(color.Bold).Sprintf("[%s]", f.LoggerName)
return []byte(fmt.Sprintf("%s\t %s", name, bytes)), err 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 { func NewLogger(name string) *logrus.Logger {
log := logrus.New() log := logrus.New()
log.SetLevel(logrus.WarnLevel) log.SetLevel(logrus.WarnLevel)
@ -33,10 +35,12 @@ func NewLogger(name string) *logrus.Logger {
return log return log
} }
// SetLevel sets the given level for all current Loggers
func SetLevel(level logrus.Level) { func SetLevel(level logrus.Level) {
for k := range Loggers { for k := range Loggers {
Loggers[k].SetLevel(level) Loggers[k].SetLevel(level)
} }
} }
// Loggers is the runtime map of logger name to logger object
var Loggers map[string]*logrus.Logger var Loggers map[string]*logrus.Logger

Просмотреть файл

@ -7,19 +7,23 @@ import (
"cloud.google.com/go/storage" "cloud.google.com/go/storage"
) )
// GCSDestination represents the Google Cloud Storage destination
type GCSDestination struct { type GCSDestination struct {
gcsBucket string gcsBucket string
gcsPrefix string gcsPrefix string
} }
// NewGCSDestination is the constructor for a Google Cloud Storage destination
func NewGCSDestination(gcsBucket string, gcsPrefix string) *GCSDestination { func NewGCSDestination(gcsBucket string, gcsPrefix string) *GCSDestination {
return &GCSDestination{gcsBucket, gcsPrefix} return &GCSDestination{gcsBucket, gcsPrefix}
} }
// Path returns a the GCS path for a file within this GCS Destination
func (gcsd *GCSDestination) Path(fileName string) string { func (gcsd *GCSDestination) Path(fileName string) string {
return fmt.Sprintf("gcs://%s/%s%s", gcsd.gcsBucket, gcsd.gcsPrefix, fileName) return fmt.Sprintf("gcs://%s/%s%s", gcsd.gcsBucket, gcsd.gcsPrefix, fileName)
} }
// Upload uploads contents to a file in GCS
func (gcsd *GCSDestination) Upload(fileContents []byte, fileName string) error { func (gcsd *GCSDestination) Upload(fileContents []byte, fileName string) error {
ctx := context.Background() ctx := context.Background()
client, err := storage.NewClient(ctx) client, err := storage.NewClient(ctx)

Просмотреть файл

@ -1,5 +1,7 @@
package publish package publish
// Destination represents actions which all destination types
// must implement in order to be used by SOPS
type Destination interface { type Destination interface {
Upload(fileContents []byte, fileName string) error Upload(fileContents []byte, fileName string) error
Path(fileName string) string Path(fileName string) string

Просмотреть файл

@ -9,19 +9,23 @@ import (
"github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3"
) )
// S3Destination is the AWS S3 implementation of the Destination interface
type S3Destination struct { type S3Destination struct {
s3Bucket string s3Bucket string
s3Prefix string s3Prefix string
} }
// NewS3Destination is the constructor for an S3 Destination
func NewS3Destination(s3Bucket string, s3Prefix string) *S3Destination { func NewS3Destination(s3Bucket string, s3Prefix string) *S3Destination {
return &S3Destination{s3Bucket, s3Prefix} return &S3Destination{s3Bucket, s3Prefix}
} }
// Path returns the S3 path of a file in an S3 Destination (bucket)
func (s3d *S3Destination) Path(fileName string) string { func (s3d *S3Destination) Path(fileName string) string {
return fmt.Sprintf("s3://%s/%s%s", s3d.s3Bucket, s3d.s3Prefix, fileName) return fmt.Sprintf("s3://%s/%s%s", s3d.s3Bucket, s3d.s3Prefix, fileName)
} }
// Upload uploads contents to a file in an S3 Destination (bucket)
func (s3d *S3Destination) Upload(fileContents []byte, fileName string) error { func (s3d *S3Destination) Upload(fileContents []byte, fileName string) error {
sess := session.Must(session.NewSession()) sess := session.Must(session.NewSession())
svc := s3.New(sess) svc := s3.New(sess)

Просмотреть файл

@ -73,8 +73,8 @@ func (p *polynomial) evaluate(x uint8) uint8 {
// An implementation of Lagrange interpolation // An implementation of Lagrange interpolation
// <https://en.wikipedia.org/wiki/Lagrange_polynomial> // <https://en.wikipedia.org/wiki/Lagrange_polynomial>
// For this particular implementation, x is always 0 // For this particular implementation, x is always 0
func interpolatePolynomial(x_samples, y_samples []uint8, x uint8) uint8 { func interpolatePolynomial(xSamples, ySamples []uint8, x uint8) uint8 {
limit := len(x_samples) limit := len(xSamples)
var result, basis uint8 var result, basis uint8
for i := 0; i < limit; i++ { for i := 0; i < limit; i++ {
basis = 1 basis = 1
@ -82,12 +82,12 @@ func interpolatePolynomial(x_samples, y_samples []uint8, x uint8) uint8 {
if i == j { if i == j {
continue continue
} }
num := add(x, x_samples[j]) num := add(x, xSamples[j])
denom := add(x_samples[i], x_samples[j]) denom := add(xSamples[i], xSamples[j])
term := div(num, denom) term := div(num, denom)
basis = mult(basis, term) basis = mult(basis, term)
} }
group := mult(y_samples[i], basis) group := mult(ySamples[i], basis)
result = add(result, group) result = add(result, group)
} }
return result return result
@ -103,9 +103,9 @@ func div(a, b uint8) uint8 {
} }
var goodVal, zero uint8 var goodVal, zero uint8
log_a := logTable[a] logA := logTable[a]
log_b := logTable[b] logB := logTable[b]
diff := (int(log_a) - int(log_b)) % 255 diff := (int(logA) - int(logB)) % 255
if diff < 0 { if diff < 0 {
diff += 255 diff += 255
} }
@ -258,8 +258,8 @@ func Combine(parts [][]byte) ([]byte, error) {
secret := make([]byte, firstPartLen-1) secret := make([]byte, firstPartLen-1)
// Buffer to store the samples // Buffer to store the samples
x_samples := make([]uint8, len(parts)) xSamples := make([]uint8, len(parts))
y_samples := 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, // Set the x value for each sample and ensure no x_sample values are the same,
// otherwise div() can be unhappy // otherwise div() can be unhappy
@ -272,19 +272,19 @@ func Combine(parts [][]byte) ([]byte, error) {
return nil, fmt.Errorf("duplicate part detected") return nil, fmt.Errorf("duplicate part detected")
} }
checkMap[samp] = true checkMap[samp] = true
x_samples[i] = samp xSamples[i] = samp
} }
// Reconstruct each byte // Reconstruct each byte
for idx := range secret { for idx := range secret {
// Set the y value for each sample // Set the y value for each sample
for i, part := range parts { for i, part := range parts {
y_samples[i] = part[idx] ySamples[i] = part[idx]
} }
// Use Lagrange interpolation to retrieve the free term // Use Lagrange interpolation to retrieve the free term
// of the original polynomial // of the original polynomial
val := interpolatePolynomial(x_samples, y_samples, 0) val := interpolatePolynomial(xSamples, ySamples, 0)
// Evaluate the 0th value to get the intercept // Evaluate the 0th value to get the intercept
secret[idx] = val secret[idx] = val

Просмотреть файл

@ -188,9 +188,9 @@ func TestInterpolate_Rand(t *testing.T) {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
x_vals := []uint8{1, 2, 3} xVals := []uint8{1, 2, 3}
y_vals := []uint8{p.evaluate(1), p.evaluate(2), p.evaluate(3)} yVals := []uint8{p.evaluate(1), p.evaluate(2), p.evaluate(3)}
out := interpolatePolynomial(x_vals, y_vals, 0) out := interpolatePolynomial(xVals, yVals, 0)
if out != uint8(i) { if out != uint8(i) {
t.Fatalf("Bad: %v %d", out, i) t.Fatalf("Bad: %v %d", out, i)
} }

38
sops.go
Просмотреть файл

@ -100,6 +100,7 @@ type TreeItem struct {
// TreeBranch is a branch inside sops's tree. It is a slice of TreeItems and is therefore ordered // TreeBranch is a branch inside sops's tree. It is a slice of TreeItems and is therefore ordered
type TreeBranch []TreeItem 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 type TreeBranches []TreeBranch
@ -110,10 +111,9 @@ func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} {
return []interface{}{ return []interface{}{
leaf, leaf,
} }
} else { }
return []interface{}{ return []interface{}{
valueFromPathAndLeaf(path[1:], leaf), valueFromPathAndLeaf(path[1:], leaf),
}
} }
default: default:
if len(path) == 1 { if len(path) == 1 {
@ -123,13 +123,12 @@ func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} {
Value: leaf, Value: leaf,
}, },
} }
} else { }
return TreeBranch{ return TreeBranch{
TreeItem{ TreeItem{
Key: component, Key: component,
Value: valueFromPathAndLeaf(path[1:], leaf), 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 // Not found, need to add the next path entry to the branch
if len(path) == 1 { if len(path) == 1 {
return append(branch, TreeItem{Key: path[0], Value: value}) return append(branch, TreeItem{Key: path[0], Value: value})
} else {
return valueFromPathAndLeaf(path, value)
} }
return valueFromPathAndLeaf(path, value)
case []interface{}: case []interface{}:
position := path[0].(int) position := path[0].(int)
if len(path) == 1 { if len(path) == 1 {
if position >= len(branch) { if position >= len(branch) {
return append(branch, value) return append(branch, value)
} else {
branch[position] = value
} }
return branch branch[position] = value
} else { } else {
if position >= len(branch) { if position >= len(branch) {
branch = append(branch, valueFromPathAndLeaf(path[1:], value)) 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: default:
return valueFromPathAndLeaf(path, value) 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 { func (branch TreeBranch) Set(path []interface{}, value interface{}) TreeBranch {
return set(branch, path, value).(TreeBranch) return set(branch, path, value).(TreeBranch)
} }
@ -486,6 +482,8 @@ type PlainFileEmitter interface {
EmitPlainFile(TreeBranches) ([]byte, error) 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 { type ValueEmitter interface {
EmitValue(interface{}) ([]byte, error) 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. // of the key services, returning as soon as one key service succeeds.
func decryptKey(key keys.MasterKey, svcs []keyservice.KeyServiceClient) ([]byte, error) { func decryptKey(key keys.MasterKey, svcs []keyservice.KeyServiceClient) ([]byte, error) {
svcKey := keyservice.KeyFromMasterKey(key) svcKey := keyservice.KeyFromMasterKey(key)
var part []byte = nil var part []byte
decryptErr := decryptKeyError{ decryptErr := decryptKeyError{
keyName: key.ToString(), keyName: key.ToString(),
} }

Просмотреть файл

@ -10,12 +10,14 @@ import (
"go.mozilla.org/sops/stores" "go.mozilla.org/sops/stores"
) )
// SopsPrefix is the prefix for all metadatada entry keys
const SopsPrefix = "sops_" const SopsPrefix = "sops_"
// Store handles storage of dotenv data // Store handles storage of dotenv data
type Store struct { 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) { func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
branches, err := store.LoadPlainFile(in) branches, err := store.LoadPlainFile(in)
if err != nil { if err != nil {
@ -57,6 +59,8 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
}, nil }, nil
} }
// LoadPlainFile returns the contents of a plaintext file loaded onto a
// sops runtime object
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
var branches sops.TreeBranches var branches sops.TreeBranches
var branch sops.TreeBranch var branch sops.TreeBranch
@ -86,6 +90,8 @@ func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
return branches, nil 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) { func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
metadata := stores.MetadataFromInternal(in.Metadata) metadata := stores.MetadataFromInternal(in.Metadata)
mdItems, err := metadataToMap(metadata) mdItems, err := metadataToMap(metadata)
@ -101,6 +107,8 @@ func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
return store.EmitPlainFile(in.Branches) 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) { func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
buffer := bytes.Buffer{} buffer := bytes.Buffer{}
for _, item := range in[0] { for _, item := range in[0] {
@ -118,6 +126,7 @@ func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
return buffer.Bytes(), nil return buffer.Bytes(), nil
} }
// EmitValue returns a single value as bytes
func (Store) EmitValue(v interface{}) ([]byte, error) { func (Store) EmitValue(v interface{}) ([]byte, error) {
if s, ok := v.(string); ok { if s, ok := v.(string); ok {
return []byte(s), nil 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) 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 { func (store *Store) EmitExample() []byte {
bytes, err := store.EmitPlainFile(stores.ExampleFlatTree.Branches) bytes, err := store.EmitPlainFile(stores.ExampleFlatTree.Branches)
if err != nil { if err != nil {

Просмотреть файл

@ -106,7 +106,7 @@ func tokenize(path string) []token {
state = StateList state = StateList
i += len(listSeparator) i += len(listSeparator)
} else { } else {
i += 1 i++
} }
} }
finishPrevToken() finishPrevToken()

Просмотреть файл

@ -136,6 +136,7 @@ func (store Store) treeItemFromSection(section *ini.Section) (sops.TreeItem, err
return sectionItem, nil 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) { func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
iniFileOuter, err := ini.Load(in) iniFileOuter, err := ini.Load(in)
if err != nil { if err != nil {
@ -192,6 +193,7 @@ func (store *Store) iniSectionToMetadata(sopsSection *ini.Section) (stores.Metad
return md, err 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) { func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
branches, err := store.treeBranchesFromIni(in) branches, err := store.treeBranchesFromIni(in)
if err != nil { if err != nil {
@ -200,6 +202,8 @@ func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
return branches, nil 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) { func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
metadata := stores.MetadataFromInternal(in.Metadata) metadata := stores.MetadataFromInternal(in.Metadata)
@ -298,6 +302,7 @@ func encodeMetadataItem(prefix string, kind reflect.Kind, field reflect.Value) (
return result, nil 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) { func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
out, err := store.iniFromTreeBranches(in) out, err := store.iniFromTreeBranches(in)
if err != nil { 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) { func (store *Store) EmitValue(v interface{}) ([]byte, error) {
return store.encodeValue(v) return store.encodeValue(v)
} }
// EmitExample returns the plaintext INI file bytes corresponding to the SimpleTree example
func (store *Store) EmitExample() []byte { func (store *Store) EmitExample() []byte {
bytes, err := store.EmitPlainFile(stores.ExampleSimpleTree.Branches) bytes, err := store.EmitPlainFile(stores.ExampleSimpleTree.Branches)
if err != nil { if err != nil {

Просмотреть файл

@ -19,10 +19,13 @@ type BinaryStore struct {
store Store store Store
} }
// LoadEncryptedFile loads an encrypted json file onto a sops.Tree object
func (store BinaryStore) LoadEncryptedFile(in []byte) (sops.Tree, error) { func (store BinaryStore) LoadEncryptedFile(in []byte) (sops.Tree, error) {
return store.store.LoadEncryptedFile(in) 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) { func (store BinaryStore) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
return sops.TreeBranches{ return sops.TreeBranches{
sops.TreeBranch{ sops.TreeBranch{
@ -34,10 +37,12 @@ func (store BinaryStore) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
}, nil }, nil
} }
// EmitEncryptedFile produces an encrypted json file's bytes from its corresponding sops.Tree object
func (store BinaryStore) EmitEncryptedFile(in sops.Tree) ([]byte, error) { func (store BinaryStore) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
return store.store.EmitEncryptedFile(in) 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) { func (store BinaryStore) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
// JSON stores a single object per file // JSON stores a single object per file
for _, item := range in[0] { 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") 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) { 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") 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 { func (store BinaryStore) EmitExample() []byte {
return []byte("Welcome to SOPS! Edit this file as you please!") 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 return out.Bytes(), err
} }
// LoadEncryptedFile loads an encrypted secrets file onto a sops.Tree object
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) { func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
// Because we don't know what fields the input file will have, we have to // Because we don't know what fields the input file will have, we have to
// load the file in two steps. // load the file in two steps.
@ -252,6 +261,7 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
}, nil }, nil
} }
// LoadPlainFile loads plaintext json file bytes onto a sops.TreeBranches object
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
branch, err := store.treeBranchFromJSON(in) branch, err := store.treeBranchFromJSON(in)
if err != nil { if err != nil {
@ -262,6 +272,8 @@ func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
}, nil }, 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) { func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
tree := append(in.Branches[0], sops.TreeItem{Key: "sops", Value: stores.MetadataFromInternal(in.Metadata)}) tree := append(in.Branches[0], sops.TreeItem{Key: "sops", Value: stores.MetadataFromInternal(in.Metadata)})
out, err := store.jsonFromTreeBranch(tree) out, err := store.jsonFromTreeBranch(tree)
@ -271,6 +283,8 @@ func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
return out, nil 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) { func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
out, err := store.jsonFromTreeBranch(in[0]) out, err := store.jsonFromTreeBranch(in[0])
if err != nil { if err != nil {
@ -279,6 +293,8 @@ func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
return out, nil 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) { func (store *Store) EmitValue(v interface{}) ([]byte, error) {
s, err := store.encodeValue(v) s, err := store.encodeValue(v)
if err != nil { if err != nil {
@ -287,6 +303,7 @@ func (store *Store) EmitValue(v interface{}) ([]byte, error) {
return store.reindentJSON(s) return store.reindentJSON(s)
} }
// EmitExample returns the bytes corresponding to an example complex tree
func (store *Store) EmitExample() []byte { func (store *Store) EmitExample() []byte {
bytes, err := store.EmitPlainFile(stores.ExampleComplexTree.Branches) bytes, err := store.EmitPlainFile(stores.ExampleComplexTree.Branches)
if err != nil { if err != nil {

Просмотреть файл

@ -309,6 +309,7 @@ func (pgpKey *pgpkey) toInternal() (*pgp.MasterKey, error) {
}, nil }, nil
} }
// ExampleComplexTree is an example sops.Tree object exhibiting complex relationships
var ExampleComplexTree = sops.Tree{ var ExampleComplexTree = sops.Tree{
Branches: sops.TreeBranches{ Branches: sops.TreeBranches{
sops.TreeBranch{ 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{ var ExampleSimpleTree = sops.Tree{
Branches: sops.TreeBranches{ Branches: sops.TreeBranches{
sops.TreeBranch{ 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{ var ExampleFlatTree = sops.Tree{
Branches: sops.TreeBranches{ Branches: sops.TreeBranches{
sops.TreeBranch{ sops.TreeBranch{

Просмотреть файл

@ -101,6 +101,8 @@ func (store Store) treeBranchToYamlMap(in sops.TreeBranch) yaml.MapSlice {
return branch return branch
} }
// LoadEncryptedFile loads the contents of an encrypted yaml file onto a
// sops.Tree runtime object
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) { func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
var data []yaml.MapSlice var data []yaml.MapSlice
if err := (yaml.CommentUnmarshaler{}).UnmarshalDocuments(in, &data); err != nil { if err := (yaml.CommentUnmarshaler{}).UnmarshalDocuments(in, &data); err != nil {
@ -136,6 +138,8 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
}, nil }, nil
} }
// LoadPlainFile loads the contents of a plaintext yaml file onto a
// sops.Tree runtime obejct
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) { func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
var data []yaml.MapSlice var data []yaml.MapSlice
if err := (yaml.CommentUnmarshaler{}).UnmarshalDocuments(in, &data); err != nil { 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 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) { func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
out := []byte{} out := []byte{}
for i, branch := range in.Branches { for i, branch := range in.Branches {
@ -166,6 +172,8 @@ func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
return out, nil 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) { func (store *Store) EmitPlainFile(branches sops.TreeBranches) ([]byte, error) {
var out []byte var out []byte
for i, branch := range branches { for i, branch := range branches {
@ -182,11 +190,14 @@ func (store *Store) EmitPlainFile(branches sops.TreeBranches) ([]byte, error) {
return out, nil 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) { func (store *Store) EmitValue(v interface{}) ([]byte, error) {
v = store.treeValueToYamlValue(v) v = store.treeValueToYamlValue(v)
return (&yaml.YAMLMarshaler{Indent: 4}).Marshal(v) return (&yaml.YAMLMarshaler{Indent: 4}).Marshal(v)
} }
// EmitExample returns the bytes corresponding to an example complex tree
func (store *Store) EmitExample() []byte { func (store *Store) EmitExample() []byte {
bytes, err := store.EmitPlainFile(stores.ExampleComplexTree.Branches) bytes, err := store.EmitPlainFile(stores.ExampleComplexTree.Branches)
if err != nil { if err != nil {

Просмотреть файл

@ -10,8 +10,10 @@ import (
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
) )
// Version represents the value of the current semantic version
const Version = "3.3.1" const Version = "3.3.1"
// PrintVersion handles the version command for sops
func PrintVersion(c *cli.Context) { func PrintVersion(c *cli.Context) {
out := fmt.Sprintf("%s %s", c.App.Name, c.App.Version) out := fmt.Sprintf("%s %s", c.App.Name, c.App.Version)
upstreamVersion, err := RetrieveLatestVersionFromUpstream() upstreamVersion, err := RetrieveLatestVersionFromUpstream()