├── test-secrets ├── testsecret.notsupported ├── yaml │ ├── sopsfile.yaml │ ├── sopsfile.yml │ ├── sopsfile-complex-parameters.yaml │ ├── sopsfile-complex.yaml │ ├── sopsfile-complex.yml │ ├── sopsfile-complex-parameters.enc-age.yaml │ ├── sopsfile.enc-age.yaml │ ├── sopsfile.enc-age.yml │ ├── sopsfile.enc-kms.yaml │ ├── sopsfile.enc-kms.yml │ ├── sopsfile.enc-kms-alias.yaml │ ├── sopsfile-complex.enc-age.yaml │ ├── sopsfile-complex.enc-age.yml │ └── sopsfile.enc-multikms.yaml ├── json │ ├── sopsfile.json │ ├── sopsfile-complex.json │ ├── sopsfile.enc-age.json │ ├── complex.sops.binary │ ├── complex.sops.json │ ├── sopsfile.enc-kms.json │ └── sopsfile-complex.enc-age.json ├── README.md ├── _testsecret.yaml ├── _testsecret.env ├── _testsecret.json ├── README.gz.sops.binary ├── README.sops.binary ├── testsecret.sops.env ├── testsecret.sops.yaml └── testsecret.sops.json ├── .github ├── pull_request_template.md ├── workflows │ ├── build.yml │ ├── pull-request-lint.yml │ └── create-release.yml └── SECURITY.md ├── lambda ├── internal │ ├── event │ │ ├── testdata │ │ │ ├── invalid_empty.json │ │ │ ├── valid_minimal.json │ │ │ └── valid_full.json │ │ ├── __snapshots__ │ │ │ ├── invalid_empty.json.snap │ │ │ ├── valid_full.json.snap │ │ │ ├── valid_minimal.json.snap │ │ │ ├── invalid_empty.json_err.snap │ │ │ ├── valid_minimal.json_err.snap │ │ │ └── valid_full.json_err.snap │ │ ├── event_test.go │ │ └── config-schema.json │ ├── data │ │ └── __snapshots__ │ │ │ ├── 1.simple.toYaml.snap │ │ │ ├── 1.simple.toJson.snap │ │ │ ├── 3.array.toYaml.snap │ │ │ ├── 2.nested.toYaml.snap │ │ │ ├── 2.nested.toJson.snap │ │ │ ├── 3.array.toJson.snap │ │ │ ├── 1.simple.toStringMap.snap │ │ │ ├── 1.simple.stringify.snap │ │ │ ├── 1.simple.flatten.snap │ │ │ ├── 3.array.flatten.snap │ │ │ ├── 2.nested.flatten.snap │ │ │ ├── 2.nested.stringify.snap │ │ │ ├── 3.array.stringify.snap │ │ │ ├── 1.simple.fromYaml.snap │ │ │ ├── 1.simple.fromJson.snap │ │ │ ├── 3.array.fromYaml.snap │ │ │ ├── 2.nested.fromYaml.snap │ │ │ ├── 2.nested.fromJson.snap │ │ │ └── 3.array.fromJson.snap │ ├── client │ │ ├── __snapshots__ │ │ │ ├── secrets_manager_put_secret_value.snap │ │ │ ├── s3_get_object_etag.snap │ │ │ ├── s3_get_object_etag_not_found.snap │ │ │ ├── s3_get_object.snap │ │ │ ├── s3_get_object_reader_error.snap │ │ │ └── ssm_update_update_parameter.snap │ │ ├── mock.go │ │ └── client.go │ └── sops │ │ ├── __snapshots__ │ │ ├── README.sops.binary.snap │ │ ├── testsecret.sops.env.snap │ │ ├── testsecret.sops.yaml.snap │ │ ├── testsecret.sops.json.snap │ │ └── sops_test.snap │ │ ├── sops.go │ │ └── sops_test.go ├── events │ ├── 01-SECRET-json.json │ ├── 02-SECRET-yaml.json │ ├── 03-SECRET-dotenv.json │ ├── 11-SECRET_RAW-json.json │ ├── 12-SECRET_RAW-yaml.json │ ├── 31-PARAMETER-json.json │ ├── 32-PARAMETER-yaml.json │ ├── 13-SECRET_RAW-dotenv.json │ ├── 33-PARAMETER-dotenv.json │ ├── 34-PARAMETER-binary.json │ ├── 21-SECRET_BINARY-json.json │ ├── 22-SECRET_BINARY-yaml.json │ ├── 23-SECRET_BINARY-dotenv.json │ ├── 24-SECRET_BINARY-binary.json │ ├── 41-PARAMETER_MULTI-json.json │ ├── 42-PARAMETER_MULTI-yaml.json │ └── 43-PARAMETER_MULTI-dotenv.json ├── __snapshots__ │ ├── TestIntegration_HandleRequestWithClients │ │ ├── 34-PARAMETER-binary.json.snap │ │ ├── 32-PARAMETER-yaml.json.snap │ │ ├── 33-PARAMETER-dotenv.json.snap │ │ ├── 12-SECRET_RAW-yaml.json.snap │ │ ├── 13-SECRET_RAW-dotenv.json.snap │ │ ├── 31-PARAMETER-json.json.snap │ │ ├── 11-SECRET_RAW-json.json.snap │ │ ├── 01-SECRET-json.json.snap │ │ ├── 02-SECRET-yaml.json.snap │ │ ├── 03-SECRET-dotenv.json.snap │ │ ├── 24-SECRET_BINARY-binary.json.snap │ │ ├── 41-PARAMETER_MULTI-json.json.snap │ │ ├── 42-PARAMETER_MULTI-yaml.json.snap │ │ ├── 43-PARAMETER_MULTI-dotenv.json.snap │ │ ├── 22-SECRET_BINARY-yaml.json.snap │ │ ├── 23-SECRET_BINARY-dotenv.json.snap │ │ └── 21-SECRET_BINARY-json.json.snap │ └── TestHandleRequestWithClients │ │ ├── 34-PARAMETER-binary.json │ │ └── 34-PARAMETER-binary.json.snap │ │ ├── 12-SECRET_RAW-yaml.json │ │ └── 12-SECRET_RAW-yaml.json.snap │ │ ├── 13-SECRET_RAW-dotenv.json │ │ └── 13-SECRET_RAW-dotenv.json.snap │ │ ├── 22-SECRET_BINARY-yaml.json │ │ └── 22-SECRET_BINARY-yaml.json.snap │ │ ├── 23-SECRET_BINARY-dotenv.json │ │ └── 23-SECRET_BINARY-dotenv.json.snap │ │ ├── 32-PARAMETER-yaml.json │ │ └── 32-PARAMETER-yaml.json.snap │ │ ├── 33-PARAMETER-dotenv.json │ │ └── 33-PARAMETER-dotenv.json.snap │ │ ├── 11-SECRET_RAW-json.json │ │ └── 11-SECRET_RAW-json.json.snap │ │ ├── 21-SECRET_BINARY-json.json │ │ └── 21-SECRET_BINARY-json.json.snap │ │ ├── 01-SECRET-json.json │ │ └── 01-SECRET-json.json.snap │ │ ├── 02-SECRET-yaml.json │ │ └── 02-SECRET-yaml.json.snap │ │ ├── 03-SECRET-dotenv.json │ │ └── 03-SECRET-dotenv.json.snap │ │ ├── 31-PARAMETER-json.json │ │ └── 31-PARAMETER-json.json.snap │ │ ├── 24-SECRET_BINARY-binary.json │ │ └── 24-SECRET_BINARY-binary.json.snap │ │ ├── 41-PARAMETER_MULTI-json.json │ │ └── 41-PARAMETER_MULTI-json.json.snap │ │ ├── 42-PARAMETER_MULTI-yaml.json │ │ └── 42-PARAMETER_MULTI-yaml.json.snap │ │ └── 43-PARAMETER_MULTI-dotenv.json │ │ └── 43-PARAMETER_MULTI-dotenv.json.snap ├── README.md ├── main.go └── main_test.go ├── cdk.context.json ├── .versionrc ├── .DS_Store ├── img ├── icon.png ├── banner.png ├── banner-dl.png ├── banner-dark.png └── banner-dl-small.png ├── .vscode ├── ltex.dictionary.en-US.txt └── settings.json ├── .prettierrc.json ├── src ├── index.ts ├── LambdaInterface.ts └── SopsStringParameter.ts ├── MAINTAINERS.md ├── .prettierignore ├── .devcontainer └── devcontainer.json ├── .gitattributes ├── CHANGELOG.md ├── tsconfig.dev.json ├── .npmignore ├── .gitleaks.toml ├── .gitignore ├── renovate.json ├── LICENSE-3RD-PARTY ├── test ├── PARAMETER_MULTI.integ.ts ├── PARAMETER.integ.ts ├── ConstantAssetHashAspect.ts ├── SECRET.integ.ts └── __snapshots__ │ └── permissions.test.ts.snap ├── CONTRIBUTING.md ├── package.json └── eslint.config.js /test-secrets/testsecret.notsupported: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # -------------------------------------------------------------------------------- /lambda/internal/event/testdata/invalid_empty.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /cdk.context.json: -------------------------------------------------------------------------------- 1 | { 2 | "acknowledged-issue-numbers": [34892] 3 | } 4 | -------------------------------------------------------------------------------- /.versionrc: -------------------------------------------------------------------------------- 1 | { 2 | "bumpFiles": ["package.json", "package-lock.json"] 3 | } -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbsystel/cdk-sops-secrets/HEAD/.DS_Store -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile.yaml: -------------------------------------------------------------------------------- 1 | key1: value1 2 | key2: 12345 3 | key3: false 4 | -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile.yml: -------------------------------------------------------------------------------- 1 | key1: value1 2 | key2: 12345 3 | key3: false 4 | -------------------------------------------------------------------------------- /img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbsystel/cdk-sops-secrets/HEAD/img/icon.png -------------------------------------------------------------------------------- /img/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbsystel/cdk-sops-secrets/HEAD/img/banner.png -------------------------------------------------------------------------------- /img/banner-dl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbsystel/cdk-sops-secrets/HEAD/img/banner-dl.png -------------------------------------------------------------------------------- /img/banner-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbsystel/cdk-sops-secrets/HEAD/img/banner-dark.png -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile-complex-parameters.yaml: -------------------------------------------------------------------------------- 1 | foo: 2 | bar: 3 | key1: foo 4 | key2: bar -------------------------------------------------------------------------------- /img/banner-dl-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbsystel/cdk-sops-secrets/HEAD/img/banner-dl-small.png -------------------------------------------------------------------------------- /test-secrets/json/sopsfile.json: -------------------------------------------------------------------------------- 1 | { 2 | "key1": "value1", 3 | "key2": 12345, 4 | "key3": false 5 | } -------------------------------------------------------------------------------- /.vscode/ltex.dictionary.en-US.txt: -------------------------------------------------------------------------------- 1 | SecretsManager 2 | SopsSecret 3 | SopsSync 4 | projen 5 | stringify 6 | ConstructHub 7 | -------------------------------------------------------------------------------- /lambda/internal/event/__snapshots__/invalid_empty.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestFromCfnEvent - 1] 3 | (*event.SopsSyncResourcePropertys)(nil) 4 | --- 5 | -------------------------------------------------------------------------------- /lambda/internal/event/__snapshots__/valid_full.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestFromCfnEvent - 1] 3 | (*event.SopsSyncResourcePropertys)(nil) 4 | --- 5 | -------------------------------------------------------------------------------- /lambda/internal/event/__snapshots__/valid_minimal.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestFromCfnEvent - 1] 3 | (*event.SopsSyncResourcePropertys)(nil) 4 | --- 5 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "tabWidth": 2, 6 | "overrides": [] 7 | } 8 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/1.simple.toYaml.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_ToYAML/1.simple - 1] 3 | bool: true 4 | number: 1.1 5 | string: examplestring 6 | 7 | --- 8 | -------------------------------------------------------------------------------- /lambda/internal/event/__snapshots__/invalid_empty.json_err.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestFromCfnEvent - 1] 3 | invalid resource properties (more details in log): 4 | map[] 5 | --- 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SopsSync'; 2 | export * from './SopsStringParameter'; 3 | export * from './SopsSecret'; 4 | export * from './MultiStringParameter'; 5 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/1.simple.toJson.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_ToJSON/1.simple - 1] 3 | { 4 | "bool": true, 5 | "number": 1.1, 6 | "string": "examplestring" 7 | } 8 | --- 9 | -------------------------------------------------------------------------------- /lambda/internal/event/testdata/valid_minimal.json: -------------------------------------------------------------------------------- 1 | { 2 | "OutputFormat": "json", 3 | "Flatten": false, 4 | "FlattenSeparator": "", 5 | "Format": "yaml", 6 | "ResourceType": "SECRET", 7 | "StringifyValues": false 8 | } -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/3.array.toYaml.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_ToYAML/3.array - 1] 3 | - bool: true 4 | number: 1.1 5 | string: examplestring 6 | - bool: true 7 | number: 1.1 8 | string: examplestring 9 | 10 | --- 11 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/2.nested.toYaml.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_ToYAML/2.nested - 1] 3 | bool: true 4 | nested: 5 | bool: true 6 | number: 1.1 7 | string: examplestring 8 | number: 1.1 9 | string: examplestring 10 | 11 | --- 12 | -------------------------------------------------------------------------------- /lambda/internal/event/__snapshots__/valid_minimal.json_err.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestFromCfnEvent - 1] 3 | invalid resource properties (more details in log): 4 | map[Flatten:false FlattenSeparator: Format:yaml OutputFormat:json ResourceType:SECRET StringifyValues:false] 5 | --- 6 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/2.nested.toJson.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_ToJSON/2.nested - 1] 3 | { 4 | "bool": true, 5 | "nested": { 6 | "bool": true, 7 | "number": 1.1, 8 | "string": "examplestring" 9 | }, 10 | "number": 1.1, 11 | "string": "examplestring" 12 | } 13 | --- 14 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/3.array.toJson.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_ToJSON/3.array - 1] 3 | [ 4 | { 5 | "bool": true, 6 | "number": 1.1, 7 | "string": "examplestring" 8 | }, 9 | { 10 | "bool": true, 11 | "number": 1.1, 12 | "string": "examplestring" 13 | } 14 | ] 15 | --- 16 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/1.simple.toStringMap.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_ToStringMap/1.simple - 1] 3 | map[string][]uint8{ 4 | "bool": {0x74, 0x72, 0x75, 0x65}, 5 | "number": {0x31, 0x2e, 0x31}, 6 | "string": {0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67}, 7 | } 8 | --- 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "json.schemas": [ 3 | { 4 | "fileMatch": [ 5 | "lambda/internal/event/testdata/*.json", 6 | "lambda/events/*.json" 7 | ], 8 | "url": "./lambda/internal/event/config-schema.json" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | This page lists all active maintainers of this repository in alphabetical order. 2 | 3 | [dariozachow](https://github.com/dariozachow) 4 | [henrysachs](https://github.com/henrysachs) 5 | [lennartrommeiss](https://github.com/lenderom) 6 | [markussiebert](https://github.com/markussiebert) 7 | [thomaskrause](https://github.com/obirah) 8 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/1.simple.stringify.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_Stringify/1.simple - 1] 3 | &data.Data{ 4 | parsed: &map[string]interface {}{ 5 | "bool": "true", 6 | "number": "1.1", 7 | "string": "examplestring", 8 | }, 9 | raw: (*[]uint8)(nil), 10 | Hash: (*string)(nil), 11 | } 12 | --- 13 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/1.simple.flatten.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_Flatten/1.simple - 1] 3 | &data.Data{ 4 | parsed: &map[string]interface {}{ 5 | "bool": bool(true), 6 | "number": float64(1.1), 7 | "string": "examplestring", 8 | }, 9 | raw: (*[]uint8)(nil), 10 | Hash: (*string)(nil), 11 | } 12 | --- 13 | -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile-complex.yaml: -------------------------------------------------------------------------------- 1 | some: 2 | deep: 3 | nested: 4 | object: structure 5 | arrays: 6 | - with 7 | - several 8 | - values: 9 | and: objects 10 | notsodeep: struct 11 | and now: 12 | some: 13 | - basic: false 14 | - nested: 12345 15 | - type: 1.2345 16 | - tests: Finish! 17 | -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile-complex.yml: -------------------------------------------------------------------------------- 1 | some: 2 | deep: 3 | nested: 4 | object: structure 5 | arrays: 6 | - with 7 | - several 8 | - values: 9 | and: objects 10 | notsodeep: struct 11 | and now: 12 | some: 13 | - basic: false 14 | - nested: 12345 15 | - type: 1.2345 16 | - tests: Finish! 17 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Test secrets and snapshots 2 | /test-secrets/ 3 | /test/*snapshot 4 | /test/__snapshots__/ 5 | /lambda/__snapshots__/ 6 | 7 | # Generated files 8 | API.md 9 | package.json 10 | tsconfig.json 11 | .jsii 12 | src/LambdaInterface.ts 13 | 14 | # Build artifacts 15 | /dist/ 16 | /lib/ 17 | /coverage/ 18 | /test-reports/ 19 | 20 | # Dependencies 21 | /node_modules/ 22 | -------------------------------------------------------------------------------- /lambda/events/01-SECRET-json.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "json", 4 | "ResourceType": "SECRET", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.json" 8 | }, 9 | "Target": "01-SECRET-json" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/02-SECRET-yaml.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "yaml", 4 | "ResourceType": "SECRET", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.yaml" 8 | }, 9 | "Target": "02-SECRET-yaml" 10 | } 11 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mise", 3 | "image": "mcr.microsoft.com/devcontainers/base:ubuntu", 4 | "features": { 5 | "ghcr.io/devcontainers-extra/features/mise:1": {} 6 | }, 7 | "customizations": { 8 | "vscode": { 9 | "extensions": [ 10 | "hverlin.mise-vscode" 11 | ] 12 | } 13 | }, 14 | "mounts": [], 15 | "containerEnv": {} 16 | } -------------------------------------------------------------------------------- /lambda/events/03-SECRET-dotenv.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "dotenv", 4 | "ResourceType": "SECRET", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.env" 8 | }, 9 | "Target": "03-SECRET-dotenv" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/11-SECRET_RAW-json.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "json", 4 | "ResourceType": "SECRET_RAW", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.json" 8 | }, 9 | "Target": "11-SECRET_RAW-json" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/12-SECRET_RAW-yaml.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "yaml", 4 | "ResourceType": "SECRET_RAW", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.yaml" 8 | }, 9 | "Target": "12-SECRET_RAW-yaml" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/31-PARAMETER-json.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "json", 4 | "ResourceType": "PARAMETER", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.json" 8 | }, 9 | "Target": "/31-PARAMETER-json" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/32-PARAMETER-yaml.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "yaml", 4 | "ResourceType": "PARAMETER", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.yaml" 8 | }, 9 | "Target": "/32-PARAMETER-yaml" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/13-SECRET_RAW-dotenv.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "dotenv", 4 | "ResourceType": "SECRET_RAW", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.env" 8 | }, 9 | "Target": "13-SECRET_RAW-dotenv" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/33-PARAMETER-dotenv.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "dotenv", 4 | "ResourceType": "PARAMETER", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.env" 8 | }, 9 | "Target": "/33-PARAMETER-dotenv" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/34-PARAMETER-binary.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "binary", 4 | "ResourceType": "PARAMETER", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/README.sops.binary" 8 | }, 9 | "Target": "/34-PARAMETER-binary" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/21-SECRET_BINARY-json.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "json", 4 | "ResourceType": "SECRET_BINARY", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.json" 8 | }, 9 | "Target": "21-SECRET_BINARY-json" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/22-SECRET_BINARY-yaml.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "yaml", 4 | "ResourceType": "SECRET_BINARY", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.yaml" 8 | }, 9 | "Target": "22-SECRET_BINARY-yaml" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/23-SECRET_BINARY-dotenv.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "dotenv", 4 | "ResourceType": "SECRET_BINARY", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.env" 8 | }, 9 | "Target": "23-SECRET_BINARY-dotenv" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/24-SECRET_BINARY-binary.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "binary", 4 | "ResourceType": "SECRET_BINARY", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/README.gz.sops.binary" 8 | }, 9 | "Target": "24-SECRET_BINARY-binary" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/41-PARAMETER_MULTI-json.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "json", 4 | "ResourceType": "PARAMETER_MULTI", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.json" 8 | }, 9 | "Target": "/41-PARAMETER_MULTI-json/" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/42-PARAMETER_MULTI-yaml.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "yaml", 4 | "ResourceType": "PARAMETER_MULTI", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.yaml" 8 | }, 9 | "Target": "/42-PARAMETER_MULTI-yaml/" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/events/43-PARAMETER_MULTI-dotenv.json: -------------------------------------------------------------------------------- 1 | { 2 | "EncryptionKey": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 3 | "Format": "dotenv", 4 | "ResourceType": "PARAMETER_MULTI", 5 | "SopsS3File": { 6 | "Bucket": "cdk-hnb659fds-assets-505755377845-eu-central-1", 7 | "Key": "test-secrets/testsecret.sops.env" 8 | }, 9 | "Target": "/43-PARAMETER_MULTI-dotenv/" 10 | } 11 | -------------------------------------------------------------------------------- /lambda/internal/event/__snapshots__/valid_full.json_err.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestFromCfnEvent - 1] 3 | invalid resource properties (more details in log): 4 | map[EncryptionKey:string Flatten:false FlattenSeparator:string Format:json OutputFormat:json ParameterKeyPrefix:string ParameterName:string ResourceType:SECRET SecretARN:string SopsInline:map[Content:string Hash:string] SopsS3File:map[Bucket:string Key:string] StringifyValues:false] 5 | --- 6 | -------------------------------------------------------------------------------- /lambda/internal/client/__snapshots__/secrets_manager_put_secret_value.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestSecretsManagerPutSecretValue - 1] 3 | &secretsmanager.PutSecretValueInput{ 4 | SecretId: &"arn", 5 | ClientRequestToken: &"hash", 6 | RotationToken: (*string)(nil), 7 | SecretBinary: nil, 8 | SecretString: &"content", 9 | VersionStages: nil, 10 | noSmithyDocumentSerde: document.NoSerde{}, 11 | } 12 | --- 13 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/3.array.flatten.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_Flatten/3.array - 1] 3 | &data.Data{ 4 | parsed: &map[string]interface {}{ 5 | "[0].bool": bool(true), 6 | "[0].number": float64(1.1), 7 | "[0].string": "examplestring", 8 | "[1].bool": bool(true), 9 | "[1].number": float64(1.1), 10 | "[1].string": "examplestring", 11 | }, 12 | raw: (*[]uint8)(nil), 13 | Hash: (*string)(nil), 14 | } 15 | --- 16 | -------------------------------------------------------------------------------- /test-secrets/README.md: -------------------------------------------------------------------------------- 1 | # SOPS files for testing purpose 2 | 3 | The sops files in this directory were created for testing purpose. As encryption algorithm [age](https://age-encryption.org/) is used, as it allows easy sharing of encryption information. 4 | 5 | The following keys were used: 6 | 7 | ```yaml 8 | public: age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu 9 | private: AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3 10 | ``` 11 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/2.nested.flatten.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_Flatten/2.nested - 1] 3 | &data.Data{ 4 | parsed: &map[string]interface {}{ 5 | "bool": bool(true), 6 | "nested.bool": bool(true), 7 | "nested.number": float64(1.1), 8 | "nested.string": "examplestring", 9 | "number": float64(1.1), 10 | "string": "examplestring", 11 | }, 12 | raw: (*[]uint8)(nil), 13 | Hash: (*string)(nil), 14 | } 15 | --- 16 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/2.nested.stringify.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_Stringify/2.nested - 1] 3 | &data.Data{ 4 | parsed: &map[string]interface {}{ 5 | "bool": "true", 6 | "nested": map[string]interface {}{ 7 | "bool": "true", 8 | "number": "1.1", 9 | "string": "examplestring", 10 | }, 11 | "number": "1.1", 12 | "string": "examplestring", 13 | }, 14 | raw: (*[]uint8)(nil), 15 | Hash: (*string)(nil), 16 | } 17 | --- 18 | -------------------------------------------------------------------------------- /test-secrets/_testsecret.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiKey: sk-1234567890abcdef 3 | database: 4 | user: admin 5 | password: P@ssw0rd! 6 | host: db.example.com 7 | tokens: 8 | - service: service1 9 | token: token1 10 | - service: service2 11 | token: token2 12 | someOtherKey: base64:aGFsbG8gd2VsdAo= 13 | specific: 14 | SpecialCharacters: '"ajkscbuiuXA34%%&&=' 15 | HTMLEncodingTest1: test 16 | HTMLEncodingTest2: "&" 17 | HTLMEncodingTest3: "@" 18 | 'null': 19 | boolean: true 20 | number1: 123 21 | number2: 123.456 22 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/3.array.stringify.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_Stringify/3.array - 1] 3 | &data.Data{ 4 | parsed: &[]interface {}{ 5 | map[string]interface {}{ 6 | "bool": "true", 7 | "number": "1.1", 8 | "string": "examplestring", 9 | }, 10 | map[string]interface {}{ 11 | "bool": "true", 12 | "number": "1.1", 13 | "string": "examplestring", 14 | }, 15 | }, 16 | raw: (*[]uint8)(nil), 17 | Hash: (*string)(nil), 18 | } 19 | --- 20 | -------------------------------------------------------------------------------- /test-secrets/_testsecret.env: -------------------------------------------------------------------------------- 1 | apiKey=sk-1234567890abcdef 2 | database:host=db.example.com 3 | database:password=P@ssw0rd! 4 | database:user=admin 5 | someOtherKey=base64:aGFsbG8gd2VsdAo= 6 | specific:boolean=True 7 | specific:SpecialCharacters="ajkscbuiuXA34%%&&= 8 | specific:HTLMEncodingTest3=@ 9 | specific:HTMLEncodingTest1=test 10 | specific:HTMLEncodingTest2=& 11 | specific:null= 12 | specific:number1=123 13 | specific:number2=123.456 14 | tokens:0:service=service1 15 | tokens:0:token=token1 16 | tokens:1:service=service2 17 | tokens:1:token=token2 18 | -------------------------------------------------------------------------------- /lambda/internal/sops/__snapshots__/README.sops.binary.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestDecrypt/README.sops.binary - 1] 3 | # SOPS files for testing purpose 4 | 5 | The sops files in this directory were created for testing purpose. As encryption algorithm [age](https://age-encryption.org/) is used, as it allows easy sharing of encryption information. 6 | 7 | The following keys were used: 8 | 9 | ```yaml 10 | public: age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu 11 | private: AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3 12 | ``` 13 | 14 | --- 15 | -------------------------------------------------------------------------------- /lambda/internal/event/testdata/valid_full.json: -------------------------------------------------------------------------------- 1 | { 2 | "OutputFormat": "json", 3 | "EncryptionKey": "string", 4 | "Flatten": false, 5 | "FlattenSeparator": "string", 6 | "Format": "json", 7 | "ParameterKeyPrefix": "string", 8 | "ParameterName": "string", 9 | "ResourceType": "SECRET", 10 | "SecretARN": "string", 11 | "SopsInline": { 12 | "Content": "string", 13 | "Hash": "string" 14 | }, 15 | "SopsS3File": { 16 | "Bucket": "string", 17 | "Key": "string" 18 | }, 19 | "StringifyValues": false 20 | } -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/1.simple.fromYaml.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_FromYAML/1.simple - 1] 3 | &data.Data{ 4 | parsed: &map[string]interface {}{ 5 | "bool": bool(true), 6 | "number": float64(1.1), 7 | "string": "examplestring", 8 | }, 9 | raw: &[]uint8{0x62, 0x6f, 0x6f, 0x6c, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0xa, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x20, 0x31, 0x2e, 0x31, 0xa, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xa}, 10 | Hash: (*string)(nil), 11 | } 12 | --- 13 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/34-PARAMETER-binary.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/34-PARAMETER-binary.json - 1] 3 | &"/34-PARAMETER-binary" 4 | &"# SOPS files for testing purpose\n\nThe sops files in this directory were created for testing purpose. As encryption algorithm [age](https://age-encryption.org/) is used, as it allows easy sharing of encryption information.\n\nThe following keys were used:\n\n```yaml\npublic: age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu\nprivate: AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3\n```\n" 5 | --- 6 | -------------------------------------------------------------------------------- /lambda/internal/sops/__snapshots__/testsecret.sops.env.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestDecrypt/testsecret.sops.env - 1] 3 | apiKey=sk-1234567890abcdef 4 | database:host=db.example.com 5 | database:password=P@ssw0rd! 6 | database:user=admin 7 | someOtherKey=base64:aGFsbG8gd2VsdAo= 8 | specific:boolean=True 9 | specific:SpecialCharacters="ajkscbuiuXA34%%&&= 10 | specific:HTLMEncodingTest3=@ 11 | specific:HTMLEncodingTest1=test 12 | specific:HTMLEncodingTest2=& 13 | specific:null= 14 | specific:number1=123 15 | specific:number2=123.456 16 | tokens:0:service=service1 17 | tokens:0:token=token1 18 | tokens:1:service=service2 19 | tokens:1:token=token2 20 | 21 | --- 22 | -------------------------------------------------------------------------------- /lambda/internal/sops/__snapshots__/testsecret.sops.yaml.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestDecrypt/testsecret.sops.yaml - 1] 3 | apiKey: sk-1234567890abcdef 4 | database: 5 | user: admin 6 | password: P@ssw0rd! 7 | host: db.example.com 8 | tokens: 9 | - service: service1 10 | token: token1 11 | - service: service2 12 | token: token2 13 | someOtherKey: base64:aGFsbG8gd2VsdAo= 14 | specific: 15 | SpecialCharacters: '"ajkscbuiuXA34%%&&=' 16 | HTMLEncodingTest1: test 17 | HTMLEncodingTest2: '&' 18 | HTLMEncodingTest3: '@' 19 | "null": null 20 | boolean: true 21 | number1: 123 22 | number2: 123.456 23 | 24 | --- 25 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/32-PARAMETER-yaml.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/32-PARAMETER-yaml.json - 1] 3 | &"/32-PARAMETER-yaml" 4 | &"apiKey: sk-1234567890abcdef\ndatabase:\n user: admin\n password: P@ssw0rd!\n host: db.example.com\ntokens:\n - service: service1\n token: token1\n - service: service2\n token: token2\nsomeOtherKey: base64:aGFsbG8gd2VsdAo=\nspecific:\n SpecialCharacters: '\"ajkscbuiuXA34%%&&='\n HTMLEncodingTest1: test \n HTMLEncodingTest2: '&'\n HTLMEncodingTest3: '@'\n \"null\": null\n boolean: true\n number1: 123\n number2: 123.456\n" 5 | --- 6 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/33-PARAMETER-dotenv.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/33-PARAMETER-dotenv.json - 1] 3 | &"/33-PARAMETER-dotenv" 4 | &"apiKey=sk-1234567890abcdef\ndatabase:host=db.example.com\ndatabase:password=P@ssw0rd!\ndatabase:user=admin\nsomeOtherKey=base64:aGFsbG8gd2VsdAo=\nspecific:boolean=True\nspecific:SpecialCharacters=\"ajkscbuiuXA34%%&&=\nspecific:HTLMEncodingTest3=@\nspecific:HTMLEncodingTest1=test \nspecific:HTMLEncodingTest2=&\nspecific:null=\nspecific:number1=123\nspecific:number2=123.456\ntokens:0:service=service1\ntokens:0:token=token1\ntokens:1:service=service2\ntokens:1:token=token2\n" 5 | --- 6 | -------------------------------------------------------------------------------- /lambda/internal/client/__snapshots__/s3_get_object_etag.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestS3GetObjectETAG - 1] 3 | &s3.GetObjectAttributesInput{ 4 | Bucket: &"test-bucket", 5 | Key: &"test-key", 6 | ObjectAttributes: {"ETag"}, 7 | ExpectedBucketOwner: (*string)(nil), 8 | MaxParts: (*int32)(nil), 9 | PartNumberMarker: (*string)(nil), 10 | RequestPayer: "", 11 | SSECustomerAlgorithm: (*string)(nil), 12 | SSECustomerKey: (*string)(nil), 13 | SSECustomerKeyMD5: (*string)(nil), 14 | VersionId: (*string)(nil), 15 | noSmithyDocumentSerde: document.NoSerde{}, 16 | } 17 | --- 18 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/12-SECRET_RAW-yaml.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/12-SECRET_RAW-yaml.json - 1] 3 | &"12-SECRET_RAW-yaml" 4 | []uint8(nil) 5 | &"apiKey: sk-1234567890abcdef\ndatabase:\n user: admin\n password: P@ssw0rd!\n host: db.example.com\ntokens:\n - service: service1\n token: token1\n - service: service2\n token: token2\nsomeOtherKey: base64:aGFsbG8gd2VsdAo=\nspecific:\n SpecialCharacters: '\"ajkscbuiuXA34%%&&='\n HTMLEncodingTest1: test \n HTMLEncodingTest2: '&'\n HTLMEncodingTest3: '@'\n \"null\": null\n boolean: true\n number1: 123\n number2: 123.456\n" 6 | --- 7 | -------------------------------------------------------------------------------- /lambda/internal/client/__snapshots__/s3_get_object_etag_not_found.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestS3GetObjectETAG - 1] 3 | &s3.GetObjectAttributesInput{ 4 | Bucket: &"test-bucket", 5 | Key: &"test-key", 6 | ObjectAttributes: {"ETag"}, 7 | ExpectedBucketOwner: (*string)(nil), 8 | MaxParts: (*int32)(nil), 9 | PartNumberMarker: (*string)(nil), 10 | RequestPayer: "", 11 | SSECustomerAlgorithm: (*string)(nil), 12 | SSECustomerKey: (*string)(nil), 13 | SSECustomerKeyMD5: (*string)(nil), 14 | VersionId: (*string)(nil), 15 | noSmithyDocumentSerde: document.NoSerde{}, 16 | } 17 | --- 18 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/13-SECRET_RAW-dotenv.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/13-SECRET_RAW-dotenv.json - 1] 3 | &"13-SECRET_RAW-dotenv" 4 | []uint8(nil) 5 | &"apiKey=sk-1234567890abcdef\ndatabase:host=db.example.com\ndatabase:password=P@ssw0rd!\ndatabase:user=admin\nsomeOtherKey=base64:aGFsbG8gd2VsdAo=\nspecific:boolean=True\nspecific:SpecialCharacters=\"ajkscbuiuXA34%%&&=\nspecific:HTLMEncodingTest3=@\nspecific:HTMLEncodingTest1=test \nspecific:HTMLEncodingTest2=&\nspecific:null=\nspecific:number1=123\nspecific:number2=123.456\ntokens:0:service=service1\ntokens:0:token=token1\ntokens:1:service=service2\ntokens:1:token=token2\n" 6 | --- 7 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/1.simple.fromJson.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_FromJSON/1.simple - 1] 3 | &data.Data{ 4 | parsed: &map[string]interface {}{ 5 | "bool": bool(true), 6 | "number": float64(1.1), 7 | "string": "examplestring", 8 | }, 9 | raw: &[]uint8{0x7b, 0xa, 0x20, 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x22, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0xa, 0x20, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3a, 0x20, 0x31, 0x2e, 0x31, 0x2c, 0xa, 0x20, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0xa, 0x7d}, 10 | Hash: (*string)(nil), 11 | } 12 | --- 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Default to LF line endings 2 | * text=auto eol=lf 3 | 4 | # Mark generated files to reduce diff noise and collapse in PR reviews 5 | API.md linguist-generated=true 6 | tsconfig.json linguist-generated=true 7 | .jsii linguist-generated=true 8 | 9 | # Test snapshots - collapse diffs in PR reviews 10 | *.snap linguist-generated=true 11 | test/**/*.snap linguist-generated=true 12 | test/__snapshots__/**/* linguist-generated=true 13 | lambda/__snapshots__/**/* linguist-generated=true 14 | 15 | # Package lock files 16 | package-lock.json linguist-generated=true merge=ours 17 | 18 | # Binary files 19 | *.gz binary 20 | *.zip binary 21 | *.tgz binary 22 | *.jar binary 23 | *.whl binary 24 | *.nupkg binary 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. 4 | 5 | ## [2.4.7](https://github.com/dbsystel/cdk-sops-secrets/compare/v2.4.6...v2.4.7) (2025-11-26) 6 | 7 | ## [2.4.6](https://github.com/dbsystel/cdk-sops-secrets/compare/v2.4.5...v2.4.6) (2025-11-26) 8 | 9 | 10 | ### Bug Fixes 11 | 12 | * **package-lock.json:** update packages ([f93b485](https://github.com/dbsystel/cdk-sops-secrets/commit/f93b485123f4ae6814a6ac6aadb0e3c4a4cc1957)) 13 | * **SopsStringParameter:** add parameterRef to match IStringParameter ([ea04275](https://github.com/dbsystel/cdk-sops-secrets/commit/ea04275a6ef082b7548fc484a213e85641d6c8e5)) 14 | -------------------------------------------------------------------------------- /src/LambdaInterface.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * This file was automatically generated by json-schema-to-typescript. 4 | * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, 5 | * and run json-schema-to-typescript to regenerate this file. 6 | */ 7 | 8 | export interface SopsSyncResourcePropertys { 9 | ResourceType: "SECRET" | "SECRET_RAW" | "SECRET_BINARY" | "PARAMETER_MULTI" | "PARAMETER"; 10 | Format: "json" | "yaml" | "dotenv" | "binary"; 11 | Target: string; 12 | EncryptionKey?: string; 13 | SopsS3File?: SopsS3File; 14 | SopsInline?: SopsInline; 15 | FlattenSeparator?: string; 16 | ServiceToken?: string; 17 | } 18 | export interface SopsS3File { 19 | Bucket: string; 20 | Key: string; 21 | } 22 | export interface SopsInline { 23 | Content: string; 24 | Hash: string; 25 | } 26 | -------------------------------------------------------------------------------- /test-secrets/_testsecret.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiKey": "sk-1234567890abcdef", 3 | "database": { 4 | "user": "admin", 5 | "password": "P@ssw0rd!", 6 | "host": "db.example.com" 7 | }, 8 | "tokens": [ 9 | { 10 | "service": "service1", 11 | "token": "token1" 12 | }, 13 | { 14 | "service": "service2", 15 | "token": "token2" 16 | } 17 | ], 18 | "someOtherKey": "base64:aGFsbG8gd2VsdAo=", 19 | "specific": { 20 | "SpecialCharacters": "\"ajkscbuiuXA34%%&&=", 21 | "HTMLEncodingTest1": "test ", 22 | "HTMLEncodingTest2": "&", 23 | "HTLMEncodingTest3": "@", 24 | "null": null, 25 | "boolean": true, 26 | "number1": 123, 27 | "number2": 123.456 28 | } 29 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | pull_request: {} 5 | workflow_dispatch: {} 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: read 15 | env: 16 | CI: 'true' 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 20 | with: 21 | ref: ${{ github.event.pull_request.head.ref }} 22 | repository: ${{ github.event.pull_request.head.repo.full_name }} 23 | 24 | - name: Install mise 25 | uses: jdx/mise-action@146a28175021df8ca24f8ee1828cc2a60f980bd5 # v3 26 | with: 27 | experimental: true 28 | 29 | - name: Build (includes lambda test, lambda build, compile, test, integ verify) 30 | run: mise run build 31 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/31-PARAMETER-json.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/31-PARAMETER-json.json - 1] 3 | &"/31-PARAMETER-json" 4 | &"{\n\t\"apiKey\": \"sk-1234567890abcdef\",\n\t\"database\": {\n\t\t\"host\": \"db.example.com\",\n\t\t\"password\": \"P@ssw0rd!\",\n\t\t\"user\": \"admin\"\n\t},\n\t\"someOtherKey\": \"base64:aGFsbG8gd2VsdAo=\",\n\t\"specific\": {\n\t\t\"HTLMEncodingTest3\": \"@\",\n\t\t\"HTMLEncodingTest1\": \"test \",\n\t\t\"HTMLEncodingTest2\": \"&\",\n\t\t\"SpecialCharacters\": \"\\\"ajkscbuiuXA34%%&&=\",\n\t\t\"boolean\": true,\n\t\t\"null\": null,\n\t\t\"number1\": 123,\n\t\t\"number2\": 123.456\n\t},\n\t\"tokens\": [\n\t\t{\n\t\t\t\"service\": \"service1\",\n\t\t\t\"token\": \"token1\"\n\t\t},\n\t\t{\n\t\t\t\"service\": \"service2\",\n\t\t\t\"token\": \"token2\"\n\t\t}\n\t]\n}" 5 | --- 6 | -------------------------------------------------------------------------------- /test-secrets/json/sopsfile-complex.json: -------------------------------------------------------------------------------- 1 | { 2 | "some": { 3 | "deep": { 4 | "nested": { 5 | "object": "structure", 6 | "arrays": [ 7 | "with", 8 | "several", 9 | { 10 | "values": { 11 | "and": "objects" 12 | } 13 | } 14 | ] 15 | } 16 | }, 17 | "notsodeep": "struct" 18 | }, 19 | "with": { 20 | "TestValue": "test ", 21 | "TestValue2": "&", 22 | "TestValue3": "@" 23 | }, 24 | "and now": { 25 | "some": [ 26 | { 27 | "basic": false 28 | }, 29 | { 30 | "nested": 12345 31 | }, 32 | { 33 | "type": 1.2345 34 | }, 35 | { 36 | "tests": "Finish!" 37 | } 38 | ] 39 | } 40 | } -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/11-SECRET_RAW-json.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/11-SECRET_RAW-json.json - 1] 3 | &"11-SECRET_RAW-json" 4 | []uint8(nil) 5 | &"{\n\t\"apiKey\": \"sk-1234567890abcdef\",\n\t\"database\": {\n\t\t\"host\": \"db.example.com\",\n\t\t\"password\": \"P@ssw0rd!\",\n\t\t\"user\": \"admin\"\n\t},\n\t\"someOtherKey\": \"base64:aGFsbG8gd2VsdAo=\",\n\t\"specific\": {\n\t\t\"HTLMEncodingTest3\": \"@\",\n\t\t\"HTMLEncodingTest1\": \"test \",\n\t\t\"HTMLEncodingTest2\": \"&\",\n\t\t\"SpecialCharacters\": \"\\\"ajkscbuiuXA34%%&&=\",\n\t\t\"boolean\": true,\n\t\t\"null\": null,\n\t\t\"number1\": 123,\n\t\t\"number2\": 123.456\n\t},\n\t\"tokens\": [\n\t\t{\n\t\t\t\"service\": \"service1\",\n\t\t\t\"token\": \"token1\"\n\t\t},\n\t\t{\n\t\t\t\"service\": \"service2\",\n\t\t\t\"token\": \"token2\"\n\t\t}\n\t]\n}" 6 | --- 7 | -------------------------------------------------------------------------------- /tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "experimentalDecorators": true, 7 | "inlineSourceMap": true, 8 | "inlineSources": true, 9 | "lib": [ 10 | "es2020" 11 | ], 12 | "module": "CommonJS", 13 | "noEmitOnError": false, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitAny": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "resolveJsonModule": true, 21 | "strict": true, 22 | "strictNullChecks": true, 23 | "strictPropertyInitialization": true, 24 | "stripInternal": true, 25 | "target": "ES2020" 26 | }, 27 | "include": [ 28 | "src/**/*.ts", 29 | "test/**/*.ts" 30 | ], 31 | "exclude": [ 32 | "node_modules" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | We release patches for the latest major.minor version. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | Please report security issues via GitHub Security Advisories ("Report a vulnerability" in the repository Security tab) or e-mail the CODEOWNERS directly. 10 | DO NOT open an issue. 11 | 12 | ## Dependency Management 13 | 14 | We use Renovate to automatically keep dependencies up to date. Routine updates are merged only after a 3-day delay ("cool-down") to reduce supply-chain risk from freshly compromised releases. High or critical severity vulnerabilities may be upgraded immediately; feel free to open an issue if urgent remediation is needed or if an automatic PR has not appeared. 15 | 16 | ## Public Disclosure 17 | 18 | We kindly request you avoid public disclosure until a fix is available. We will coordinate a CVE if appropriate. 19 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-lint.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: pull-request-lint 4 | on: 5 | pull_request_target: 6 | types: 7 | - labeled 8 | - opened 9 | - synchronize 10 | - reopened 11 | - ready_for_review 12 | - edited 13 | merge_group: {} 14 | jobs: 15 | validate: 16 | name: Validate PR title 17 | runs-on: ubuntu-latest 18 | permissions: 19 | pull-requests: write 20 | if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') 21 | steps: 22 | - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | with: 26 | types: |- 27 | feat 28 | fix 29 | chore 30 | requireScope: false 31 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/01-SECRET-json.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/01-SECRET-json.json - 1] 3 | &"01-SECRET-json" 4 | []uint8(nil) 5 | &"{\n \"apiKey\": \"sk-1234567890abcdef\",\n \"database.host\": \"db.example.com\",\n \"database.password\": \"P@ssw0rd!\",\n \"database.user\": \"admin\",\n \"someOtherKey\": \"base64:aGFsbG8gd2VsdAo=\",\n \"specific.HTLMEncodingTest3\": \"@\",\n \"specific.HTMLEncodingTest1\": \"test \\u003ctest@test.test\\u003e\",\n \"specific.HTMLEncodingTest2\": \"\\u0026\",\n \"specific.SpecialCharacters\": \"\\\"ajkscbuiuXA34%%\\u0026\\u0026=\",\n \"specific.boolean\": \"true\",\n \"specific.null\": \"\",\n \"specific.number1\": \"123\",\n \"specific.number2\": \"123.456\",\n \"tokens[0].service\": \"service1\",\n \"tokens[0].token\": \"token1\",\n \"tokens[1].service\": \"service2\",\n \"tokens[1].token\": \"token2\"\n}" 6 | --- 7 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/02-SECRET-yaml.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/02-SECRET-yaml.json - 1] 3 | &"02-SECRET-yaml" 4 | []uint8(nil) 5 | &"{\n \"apiKey\": \"sk-1234567890abcdef\",\n \"database.host\": \"db.example.com\",\n \"database.password\": \"P@ssw0rd!\",\n \"database.user\": \"admin\",\n \"someOtherKey\": \"base64:aGFsbG8gd2VsdAo=\",\n \"specific.HTLMEncodingTest3\": \"@\",\n \"specific.HTMLEncodingTest1\": \"test \\u003ctest@test.test\\u003e\",\n \"specific.HTMLEncodingTest2\": \"\\u0026\",\n \"specific.SpecialCharacters\": \"\\\"ajkscbuiuXA34%%\\u0026\\u0026=\",\n \"specific.boolean\": \"true\",\n \"specific.null\": \"\",\n \"specific.number1\": \"123\",\n \"specific.number2\": \"123.456\",\n \"tokens[0].service\": \"service1\",\n \"tokens[0].token\": \"token1\",\n \"tokens[1].service\": \"service2\",\n \"tokens[1].token\": \"token2\"\n}" 6 | --- 7 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/03-SECRET-dotenv.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/03-SECRET-dotenv.json - 1] 3 | &"03-SECRET-dotenv" 4 | []uint8(nil) 5 | &"{\n \"apiKey\": \"sk-1234567890abcdef\",\n \"database:host\": \"db.example.com\",\n \"database:password\": \"P@ssw0rd!\",\n \"database:user\": \"admin\",\n \"someOtherKey\": \"base64:aGFsbG8gd2VsdAo=\",\n \"specific:HTLMEncodingTest3\": \"@\",\n \"specific:HTMLEncodingTest1\": \"test \\u003ctest@test.test\\u003e\",\n \"specific:HTMLEncodingTest2\": \"\\u0026\",\n \"specific:SpecialCharacters\": \"\\\"ajkscbuiuXA34%%\\u0026\\u0026=\",\n \"specific:boolean\": \"True\",\n \"specific:null\": \"\",\n \"specific:number1\": \"123\",\n \"specific:number2\": \"123.456\",\n \"tokens:0:service\": \"service1\",\n \"tokens:0:token\": \"token1\",\n \"tokens:1:service\": \"service2\",\n \"tokens:1:token\": \"token2\"\n}" 6 | --- 7 | -------------------------------------------------------------------------------- /lambda/internal/sops/__snapshots__/testsecret.sops.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestDecrypt/testsecret.sops.json - 1] 3 | { 4 | "apiKey": "sk-1234567890abcdef", 5 | "database": { 6 | "host": "db.example.com", 7 | "password": "P@ssw0rd!", 8 | "user": "admin" 9 | }, 10 | "someOtherKey": "base64:aGFsbG8gd2VsdAo=", 11 | "specific": { 12 | "HTLMEncodingTest3": "@", 13 | "HTMLEncodingTest1": "test ", 14 | "HTMLEncodingTest2": "&", 15 | "SpecialCharacters": "\"ajkscbuiuXA34%%&&=", 16 | "boolean": true, 17 | "null": null, 18 | "number1": 123, 19 | "number2": 123.456 20 | }, 21 | "tokens": [ 22 | { 23 | "service": "service1", 24 | "token": "token1" 25 | }, 26 | { 27 | "service": "service2", 28 | "token": "token2" 29 | } 30 | ] 31 | } 32 | --- 33 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Build/Test artifacts 2 | *.snap 3 | coverage/ 4 | test-reports/ 5 | junit.xml 6 | permissions-backup.acl 7 | 8 | # Source files (exclude - only ship compiled) 9 | src/ 10 | test/ 11 | lambda/ 12 | 13 | # Development secrets and test data 14 | test-secrets/ 15 | 16 | # Config files 17 | .prettierignore 18 | .prettierrc.json 19 | eslint.config.js 20 | tsconfig.dev.json 21 | tsconfig.json 22 | .versionrc-* 23 | mise.toml 24 | renovate.json 25 | .gitleaks.toml 26 | 27 | # IDE and OS files 28 | .vscode/ 29 | .idea/ 30 | .DS_Store 31 | .devcontainer/ 32 | 33 | # CI/CD 34 | .github/ 35 | 36 | # Documentation 37 | CONTRIBUTING.md 38 | MAINTAINERS.md 39 | 40 | # Scripts 41 | scripts/ 42 | 43 | # Git metadata 44 | .gitattributes 45 | .gitignore 46 | 47 | # Build info that shouldn't be in lib/ 48 | lib/tsconfig.tsbuildinfo 49 | 50 | # Vibe AI 51 | .vibe/ 52 | 53 | # Include these important files 54 | !.jsii 55 | !assets/ 56 | !lib/**/*.js 57 | !lib/**/*.d.ts 58 | !dist/ 59 | 60 | 61 | # Exclude jsii outdir 62 | dist 63 | -------------------------------------------------------------------------------- /.gitleaks.toml: -------------------------------------------------------------------------------- 1 | [extend] 2 | useDefault = true 3 | 4 | [[rules]] 5 | id = "generic-api-key" 6 | # all the other attributes from the default rule are inherited 7 | 8 | [[rules.allowlists]] 9 | regexTarget = "line" 10 | regexes = [ 11 | '''objectKey''', 12 | '''S3Key''', 13 | '''SopsAgeKey''', 14 | '''s3Key''', 15 | ] 16 | 17 | [[rules]] 18 | id = "private-key" 19 | 20 | [[rules.allowlists]] 21 | regexTarget = "line" 22 | regexes = [ 23 | '''(.*)OAdqlMznWINBDoyR\+PESgQJlUptwnh(.*)''', 24 | ] 25 | 26 | [allowlist] 27 | description = "global allow list" 28 | paths = [ 29 | '''\.gitleaks\.toml''', 30 | '''lambda/events/(.*?)json''', 31 | '''lambda/__snapshots__/(.*?)snap''', 32 | '''test-secrets/(.*?)(json|yaml|yml|env|binary)''', 33 | '''test/(.*)\.integ\.snapshot/(.*?)json''' 34 | ] 35 | 36 | regexTarget = "match" 37 | regexes = [ 38 | '''AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3''', 39 | '''ghp_abcd1234''', 40 | '''sk-1234567890abcdef''', 41 | ] 42 | 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Coverage directory 17 | lib-cov 18 | coverage 19 | *.lcov 20 | .nyc_output 21 | 22 | # Dependency directories 23 | node_modules/ 24 | jspm_packages/ 25 | 26 | # TypeScript cache 27 | *.tsbuildinfo 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional eslint cache 33 | .eslintcache 34 | 35 | # Yarn Integrity file 36 | .yarn-integrity 37 | 38 | # Package files 39 | *.tgz 40 | 41 | # dotenv environment variables file 42 | .env 43 | .env.test 44 | 45 | # Build output 46 | /lib 47 | /dist/ 48 | build/Release 49 | 50 | # Test output 51 | /test-reports/ 52 | junit.xml 53 | /coverage/ 54 | 55 | # Generated files 56 | .jsii 57 | tsconfig.json 58 | /dist/changelog.md 59 | /dist/version.txt 60 | 61 | # IDE 62 | *.iml 63 | .idea 64 | .vscode/ 65 | .DS_Store 66 | 67 | # Assets 68 | /assets 69 | 70 | # Cache 71 | .cache 72 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:recommended"], 4 | "enabledManagers": ["gomod", "npm", "mise", "github-actions"], 5 | "packageRules": [ 6 | { 7 | "matchManagers": ["gomod"], 8 | "groupName": "go-deps", 9 | "automerge": true 10 | }, 11 | { 12 | "matchManagers": ["npm"], 13 | "groupName": "npm-deps", 14 | "automerge": true, 15 | "postUpgradeTasks": { 16 | "commands": [ 17 | "npm ci", 18 | "npx jsii --silence-warnings=reserved-word", 19 | "npx jsii-docgen -o API.md" 20 | ], 21 | "fileFilters": ["package.json", "package-lock.json", "API.md"], 22 | "executionMode": "update" 23 | } 24 | }, 25 | { 26 | "matchManagers": ["mise"], 27 | "groupName": "mise-tools", 28 | "automerge": true 29 | }, 30 | { 31 | "matchManagers": ["github-actions"], 32 | "groupName": "github-actions", 33 | "automerge": true 34 | } 35 | ], 36 | "postUpdateOptions": ["gomodTidy"], 37 | "pinDigests": true 38 | } 39 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/3.array.fromYaml.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_FromYAML/3.array - 1] 3 | &data.Data{ 4 | parsed: &[]interface {}{ 5 | map[string]interface {}{ 6 | "bool": bool(true), 7 | "number": float64(1.1), 8 | "string": "examplestring", 9 | }, 10 | map[string]interface {}{ 11 | "bool": bool(true), 12 | "number": float64(1.1), 13 | "string": "examplestring", 14 | }, 15 | }, 16 | raw: &[]uint8{0x2d, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0xa, 0x20, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x20, 0x31, 0x2e, 0x31, 0xa, 0x20, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xa, 0x2d, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0xa, 0x20, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x20, 0x31, 0x2e, 0x31, 0xa, 0x20, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xa}, 17 | Hash: (*string)(nil), 18 | } 19 | --- 20 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/2.nested.fromYaml.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_FromYAML/2.nested - 1] 3 | &data.Data{ 4 | parsed: &map[string]interface {}{ 5 | "bool": bool(true), 6 | "nested": map[string]interface {}{ 7 | "bool": bool(true), 8 | "number": float64(1.1), 9 | "string": "examplestring", 10 | }, 11 | "number": float64(1.1), 12 | "string": "examplestring", 13 | }, 14 | raw: &[]uint8{0x62, 0x6f, 0x6f, 0x6c, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0xa, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x3a, 0xa, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0xa, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x20, 0x31, 0x2e, 0x31, 0xa, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xa, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x20, 0x31, 0x2e, 0x31, 0xa, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xa}, 15 | Hash: (*string)(nil), 16 | } 17 | --- 18 | -------------------------------------------------------------------------------- /lambda/internal/event/event_test.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "testing" 8 | 9 | "github.com/aws/aws-lambda-go/cfn" 10 | "github.com/gkampitakis/go-snaps/snaps" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestGenerateSchema(t *testing.T) { 15 | GenerateSchema() 16 | } 17 | 18 | func createTestSchema(t *testing.T, filename string) cfn.Event { 19 | file, err := os.Open("testdata/" + filename) 20 | assert.NoError(t, err) 21 | defer file.Close() 22 | var i map[string]interface{} 23 | err = json.NewDecoder(file).Decode(&i) 24 | assert.NoError(t, err) 25 | return cfn.Event{ 26 | ResourceProperties: i, 27 | } 28 | } 29 | 30 | func TestFromCfnEvent(t *testing.T) { 31 | tests := []string{ 32 | "valid_minimal.json", 33 | "valid_full.json", 34 | "invalid_empty.json", 35 | } 36 | 37 | for _, test := range tests { 38 | mockEvent := createTestSchema(t, test) 39 | props, err := FromCfnEvent(mockEvent) 40 | snaps.WithConfig(snaps.Filename(test)).MatchSnapshot(t, props) 41 | snaps.WithConfig(snaps.Filename(test+"_err")).MatchSnapshot(t, fmt.Sprintf("%v", err)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/34-PARAMETER-binary.json/34-PARAMETER-binary.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/34-PARAMETER-binary.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/README.sops.binary": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/README.sops.binary"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/README.sops.binary": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/README.sops.binary"}, 8 | } 9 | main.putParameterCalls{ 10 | "/34-PARAMETER-binary": map[string]interface {}{ 11 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 12 | "parameterContent": "# SOPS files for testing purpose\n\nThe sops files in this directory were created for testing purpose. As encryption algorithm [age](https://age-encryption.org/) is used, as it allows easy sharing of encryption information.\n\nThe following keys were used:\n\n```yaml\npublic: age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu\nprivate: AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3\n```\n", 13 | }, 14 | } 15 | main.putSecretValueCalls{ 16 | } 17 | --- 18 | -------------------------------------------------------------------------------- /lambda/internal/client/__snapshots__/s3_get_object.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestS3GetObject - 1] 3 | &s3.GetObjectInput{ 4 | Bucket: &"test-bucket", 5 | Key: &"test-key", 6 | ChecksumMode: "", 7 | ExpectedBucketOwner: (*string)(nil), 8 | IfMatch: (*string)(nil), 9 | IfModifiedSince: (*time.Time)(nil), 10 | IfNoneMatch: (*string)(nil), 11 | IfUnmodifiedSince: (*time.Time)(nil), 12 | PartNumber: (*int32)(nil), 13 | Range: (*string)(nil), 14 | RequestPayer: "", 15 | ResponseCacheControl: (*string)(nil), 16 | ResponseContentDisposition: (*string)(nil), 17 | ResponseContentEncoding: (*string)(nil), 18 | ResponseContentLanguage: (*string)(nil), 19 | ResponseContentType: (*string)(nil), 20 | ResponseExpires: (*time.Time)(nil), 21 | SSECustomerAlgorithm: (*string)(nil), 22 | SSECustomerKey: (*string)(nil), 23 | SSECustomerKeyMD5: (*string)(nil), 24 | VersionId: (*string)(nil), 25 | noSmithyDocumentSerde: document.NoSerde{}, 26 | } 27 | --- 28 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/12-SECRET_RAW-yaml.json/12-SECRET_RAW-yaml.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/12-SECRET_RAW-yaml.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.yaml": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.yaml"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.yaml": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.yaml"}, 8 | } 9 | main.putParameterCalls{ 10 | } 11 | main.putSecretValueCalls{ 12 | "12-SECRET_RAW-yaml": map[string]interface {}{ 13 | "binary": bool(false), 14 | "secretContent": "apiKey: sk-1234567890abcdef\ndatabase:\n user: admin\n password: P@ssw0rd!\n host: db.example.com\ntokens:\n - service: service1\n token: token1\n - service: service2\n token: token2\nsomeOtherKey: base64:aGFsbG8gd2VsdAo=\nspecific:\n SpecialCharacters: '\"ajkscbuiuXA34%%&&='\n HTMLEncodingTest1: test \n HTMLEncodingTest2: '&'\n HTLMEncodingTest3: '@'\n \"null\": null\n boolean: true\n number1: 123\n number2: 123.456\n", 15 | "sopsHash": "mock-etag", 16 | }, 17 | } 18 | --- 19 | -------------------------------------------------------------------------------- /lambda/internal/client/__snapshots__/s3_get_object_reader_error.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestS3GetObject - 1] 3 | &s3.GetObjectInput{ 4 | Bucket: &"test-bucket", 5 | Key: &"test-key", 6 | ChecksumMode: "", 7 | ExpectedBucketOwner: (*string)(nil), 8 | IfMatch: (*string)(nil), 9 | IfModifiedSince: (*time.Time)(nil), 10 | IfNoneMatch: (*string)(nil), 11 | IfUnmodifiedSince: (*time.Time)(nil), 12 | PartNumber: (*int32)(nil), 13 | Range: (*string)(nil), 14 | RequestPayer: "", 15 | ResponseCacheControl: (*string)(nil), 16 | ResponseContentDisposition: (*string)(nil), 17 | ResponseContentEncoding: (*string)(nil), 18 | ResponseContentLanguage: (*string)(nil), 19 | ResponseContentType: (*string)(nil), 20 | ResponseExpires: (*time.Time)(nil), 21 | SSECustomerAlgorithm: (*string)(nil), 22 | SSECustomerKey: (*string)(nil), 23 | SSECustomerKeyMD5: (*string)(nil), 24 | VersionId: (*string)(nil), 25 | noSmithyDocumentSerde: document.NoSerde{}, 26 | } 27 | --- 28 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/13-SECRET_RAW-dotenv.json/13-SECRET_RAW-dotenv.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/13-SECRET_RAW-dotenv.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.env": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.env"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.env": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.env"}, 8 | } 9 | main.putParameterCalls{ 10 | } 11 | main.putSecretValueCalls{ 12 | "13-SECRET_RAW-dotenv": map[string]interface {}{ 13 | "binary": bool(false), 14 | "secretContent": "apiKey=sk-1234567890abcdef\ndatabase:host=db.example.com\ndatabase:password=P@ssw0rd!\ndatabase:user=admin\nsomeOtherKey=base64:aGFsbG8gd2VsdAo=\nspecific:boolean=True\nspecific:SpecialCharacters=\"ajkscbuiuXA34%%&&=\nspecific:HTLMEncodingTest3=@\nspecific:HTMLEncodingTest1=test \nspecific:HTMLEncodingTest2=&\nspecific:null=\nspecific:number1=123\nspecific:number2=123.456\ntokens:0:service=service1\ntokens:0:token=token1\ntokens:1:service=service2\ntokens:1:token=token2\n", 15 | "sopsHash": "mock-etag", 16 | }, 17 | } 18 | --- 19 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/22-SECRET_BINARY-yaml.json/22-SECRET_BINARY-yaml.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/22-SECRET_BINARY-yaml.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.yaml": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.yaml"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.yaml": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.yaml"}, 8 | } 9 | main.putParameterCalls{ 10 | } 11 | main.putSecretValueCalls{ 12 | "22-SECRET_BINARY-yaml": map[string]interface {}{ 13 | "binary": bool(true), 14 | "secretContent": "apiKey: sk-1234567890abcdef\ndatabase:\n user: admin\n password: P@ssw0rd!\n host: db.example.com\ntokens:\n - service: service1\n token: token1\n - service: service2\n token: token2\nsomeOtherKey: base64:aGFsbG8gd2VsdAo=\nspecific:\n SpecialCharacters: '\"ajkscbuiuXA34%%&&='\n HTMLEncodingTest1: test \n HTMLEncodingTest2: '&'\n HTLMEncodingTest3: '@'\n \"null\": null\n boolean: true\n number1: 123\n number2: 123.456\n", 15 | "sopsHash": "mock-etag", 16 | }, 17 | } 18 | --- 19 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/23-SECRET_BINARY-dotenv.json/23-SECRET_BINARY-dotenv.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/23-SECRET_BINARY-dotenv.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.env": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.env"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.env": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.env"}, 8 | } 9 | main.putParameterCalls{ 10 | } 11 | main.putSecretValueCalls{ 12 | "23-SECRET_BINARY-dotenv": map[string]interface {}{ 13 | "binary": bool(true), 14 | "secretContent": "apiKey=sk-1234567890abcdef\ndatabase:host=db.example.com\ndatabase:password=P@ssw0rd!\ndatabase:user=admin\nsomeOtherKey=base64:aGFsbG8gd2VsdAo=\nspecific:boolean=True\nspecific:SpecialCharacters=\"ajkscbuiuXA34%%&&=\nspecific:HTLMEncodingTest3=@\nspecific:HTMLEncodingTest1=test \nspecific:HTMLEncodingTest2=&\nspecific:null=\nspecific:number1=123\nspecific:number2=123.456\ntokens:0:service=service1\ntokens:0:token=token1\ntokens:1:service=service2\ntokens:1:token=token2\n", 15 | "sopsHash": "mock-etag", 16 | }, 17 | } 18 | --- 19 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/32-PARAMETER-yaml.json/32-PARAMETER-yaml.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/32-PARAMETER-yaml.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.yaml": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.yaml"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.yaml": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.yaml"}, 8 | } 9 | main.putParameterCalls{ 10 | "/32-PARAMETER-yaml": map[string]interface {}{ 11 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 12 | "parameterContent": "apiKey: sk-1234567890abcdef\ndatabase:\n user: admin\n password: P@ssw0rd!\n host: db.example.com\ntokens:\n - service: service1\n token: token1\n - service: service2\n token: token2\nsomeOtherKey: base64:aGFsbG8gd2VsdAo=\nspecific:\n SpecialCharacters: '\"ajkscbuiuXA34%%&&='\n HTMLEncodingTest1: test \n HTMLEncodingTest2: '&'\n HTLMEncodingTest3: '@'\n \"null\": null\n boolean: true\n number1: 123\n number2: 123.456\n", 13 | }, 14 | } 15 | main.putSecretValueCalls{ 16 | } 17 | --- 18 | -------------------------------------------------------------------------------- /lambda/internal/client/__snapshots__/ssm_update_update_parameter.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestSsmUpdateUpdateParameter - 1] 3 | &ssm.PutParameterInput{ 4 | Name: &"parameter", 5 | Value: &"content", 6 | AllowedPattern: (*string)(nil), 7 | DataType: (*string)(nil), 8 | Description: (*string)(nil), 9 | KeyId: &"key", 10 | Overwrite: &bool(true), 11 | Policies: (*string)(nil), 12 | Tags: nil, 13 | Tier: "", 14 | Type: "SecureString", 15 | noSmithyDocumentSerde: document.NoSerde{}, 16 | } 17 | --- 18 | 19 | [TestSsmPutParameter - 1] 20 | &ssm.PutParameterInput{ 21 | Name: &"parameter", 22 | Value: &"content", 23 | AllowedPattern: (*string)(nil), 24 | DataType: (*string)(nil), 25 | Description: (*string)(nil), 26 | KeyId: &"key", 27 | Overwrite: &bool(true), 28 | Policies: (*string)(nil), 29 | Tags: nil, 30 | Tier: "", 31 | Type: "SecureString", 32 | noSmithyDocumentSerde: document.NoSerde{}, 33 | } 34 | --- 35 | -------------------------------------------------------------------------------- /lambda/internal/sops/__snapshots__/sops_test.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestToData/Test_JSON_ToData - 1] 3 | &data.Data{ 4 | parsed: &map[string]interface {}{ 5 | "key": "value", 6 | }, 7 | raw: &[]uint8{0x7b, 0x22, 0x6b, 0x65, 0x79, 0x22, 0x3a, 0x20, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x7d}, 8 | Hash: &"", 9 | } 10 | --- 11 | 12 | [TestToData/Test_YAML_ToData - 1] 13 | &data.Data{ 14 | parsed: &map[string]interface {}{ 15 | "key": "value", 16 | }, 17 | raw: &[]uint8{0x6b, 0x65, 0x79, 0x3a, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65}, 18 | Hash: &"", 19 | } 20 | --- 21 | 22 | [TestToData/Test_DotEnv_ToData - 1] 23 | &data.Data{ 24 | parsed: &map[string]string{"KEY":"value"}, 25 | raw: &[]uint8{0x4b, 0x45, 0x59, 0x3d, 0x76, 0x61, 0x6c, 0x75, 0x65}, 26 | Hash: &"", 27 | } 28 | --- 29 | 30 | [TestToData/Test_Binary_ToData - 1] 31 | &data.Data{ 32 | parsed: (*interface {})(nil), 33 | raw: &[]uint8{0x0, 0x1, 0x2, 0x3}, 34 | Hash: &"", 35 | } 36 | --- 37 | 38 | [TestCreateEncryptedSopsSecret/Valid_JSON_Secret - 1] 39 | &sops.EncryptedSopsSecret{ 40 | Content: {0x7b, 0x22, 0x6b, 0x65, 0x79, 0x22, 0x3a, 0x20, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x7d}, 41 | Format: "json", 42 | Hash: "somehash", 43 | } 44 | --- 45 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/33-PARAMETER-dotenv.json/33-PARAMETER-dotenv.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/33-PARAMETER-dotenv.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.env": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.env"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.env": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.env"}, 8 | } 9 | main.putParameterCalls{ 10 | "/33-PARAMETER-dotenv": map[string]interface {}{ 11 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 12 | "parameterContent": "apiKey=sk-1234567890abcdef\ndatabase:host=db.example.com\ndatabase:password=P@ssw0rd!\ndatabase:user=admin\nsomeOtherKey=base64:aGFsbG8gd2VsdAo=\nspecific:boolean=True\nspecific:SpecialCharacters=\"ajkscbuiuXA34%%&&=\nspecific:HTLMEncodingTest3=@\nspecific:HTMLEncodingTest1=test \nspecific:HTMLEncodingTest2=&\nspecific:null=\nspecific:number1=123\nspecific:number2=123.456\ntokens:0:service=service1\ntokens:0:token=token1\ntokens:1:service=service2\ntokens:1:token=token2\n", 13 | }, 14 | } 15 | main.putSecretValueCalls{ 16 | } 17 | --- 18 | -------------------------------------------------------------------------------- /LICENSE-3RD-PARTY: -------------------------------------------------------------------------------- 1 | 3RD PARTY LICENSES 2 | 3 | Note that not all files in the cdk-sops-secrets repository and in the released 4 | software packages belong to the cdk-sops-secrets project. For 3rd party files, 5 | the individual licenses apply. 6 | 7 | * mozilla/sops 8 | https://github.com/mozilla/sops 9 | Mozilla Public License Version 2.0, https://github.com/mozilla/sops/blob/master/LICENSE 10 | 11 | * aws/aws-cdk 12 | https://github.com/aws/aws-cdk 13 | Apache License Version 2.0, January 2004, https://github.com/aws/aws-cdk/blob/master/LICENSE 14 | 15 | * aws/aws-sdk-go 16 | https://github.com/aws/aws-sdk-go 17 | Apache License Version 2.0, January 2004, https://github.com/aws/aws-sdk-go/blob/main/LICENSE.txt 18 | 19 | * aws/aws-lambda-go 20 | Apache License Version 2.0, January 2004, https://github.com/aws/aws-lambda-go/blob/main/LICENSE 21 | https://github.com/aws/aws-lambda-go 22 | 23 | * gkampitakis/go-snaps 24 | https://github.com/gkampitakis/go-snaps 25 | MIT License, https://github.com/gkampitakis/go-snaps/blob/master/LICENSE 26 | 27 | * go-test/deep 28 | https://github.com/go-test/deep 29 | MIT License, https://github.com/go-test/deep/blob/master/LICENSE 30 | 31 | * go-yaml/yaml 32 | https://github.com/go-yaml/yaml/tree/v3 33 | MIT License, https://github.com/go-yaml/yaml/blob/v3/LICENSE 34 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/2.nested.fromJson.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_FromJSON/2.nested - 1] 3 | &data.Data{ 4 | parsed: &map[string]interface {}{ 5 | "bool": bool(true), 6 | "nested": map[string]interface {}{ 7 | "bool": bool(true), 8 | "number": float64(1.1), 9 | "string": "examplestring", 10 | }, 11 | "number": float64(1.1), 12 | "string": "examplestring", 13 | }, 14 | raw: &[]uint8{0x7b, 0xa, 0x20, 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x22, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0xa, 0x20, 0x20, 0x22, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x22, 0x3a, 0x20, 0x7b, 0xa, 0x20, 0x20, 0x20, 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x22, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0xa, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3a, 0x20, 0x31, 0x2e, 0x31, 0x2c, 0xa, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0xa, 0x20, 0x20, 0x7d, 0x2c, 0xa, 0x20, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3a, 0x20, 0x31, 0x2e, 0x31, 0x2c, 0xa, 0x20, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0xa, 0x7d}, 15 | Hash: (*string)(nil), 16 | } 17 | --- 18 | -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile-complex-parameters.enc-age.yaml: -------------------------------------------------------------------------------- 1 | foo: 2 | bar: 3 | key1: ENC[AES256_GCM,data:LIwF,iv:v7TRbCSDNeCfOscfIyDRC4FGRj/uVXEK31ita3jpdbw=,tag:YuzSgwmdVvD1Tz/nD1z3dg==,type:str] 4 | key2: ENC[AES256_GCM,data:405+,iv:J/pSdnus1CDU+KxFYneVPBz3gBDM4YlifayrSMb6vtY=,tag:WS5IQ8QktEhnfKBX2RIQWQ==,type:str] 5 | sops: 6 | kms: [] 7 | gcp_kms: [] 8 | azure_kv: [] 9 | hc_vault: [] 10 | age: 11 | - recipient: age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu 12 | enc: | 13 | -----BEGIN AGE ENCRYPTED FILE----- 14 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIcHZ4T1RwaHZCN3FQbjgy 15 | aTB0aGlQblU3dDVPRjhGZHVQQ2ZiYTFQTnlZClZxSDd0Zno3NlVYOFNVUU9HbDBR 16 | eG1UQ1RLM0dPbEFNZ2tDbWJDejFMSmcKLS0tIGV5TklodVd4RkNJR2ZzU2xKMEJE 17 | eDFWZU9vZWo2SUd2L2NWOHlPMy9YM0kKAY9EAJl7/BEUF2mB3C7dMhvft92osLms 18 | quBZMa3D79SB9+HyTJ+2U/D+Wvt47CzWYi0VZ5EZepQ5s+8iiKw1xw== 19 | -----END AGE ENCRYPTED FILE----- 20 | lastmodified: "2024-07-25T12:48:11Z" 21 | mac: ENC[AES256_GCM,data:+VbmLna14pXLB5zNQkKUvOoEk1R8rqnKlW9h7DWG7Sv1ksIO+fMpJRSGVa3A2kUj6HeZlhOo6wNcna2LC51oOsnuWFdx8RQ7ZwCVOY1qUGMgNcBaXuI9L5WRbqhLRzYjqElg6duiir+jYqZyi1IDN1kuiLNpDqdqZRzd11LZyGQ=,iv:Xwl/Gz+qToEB7EEAXah5+jOB4bK3EdKVJ4EpzdD/XFA=,tag:CQpmhglflkANA33fZDHUsA==,type:str] 22 | pgp: [] 23 | unencrypted_suffix: _unencrypted 24 | version: 3.8.1 25 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/11-SECRET_RAW-json.json/11-SECRET_RAW-json.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/11-SECRET_RAW-json.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.json": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.json"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.json": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.json"}, 8 | } 9 | main.putParameterCalls{ 10 | } 11 | main.putSecretValueCalls{ 12 | "11-SECRET_RAW-json": map[string]interface {}{ 13 | "binary": bool(false), 14 | "secretContent": "{\n\t\"apiKey\": \"sk-1234567890abcdef\",\n\t\"database\": {\n\t\t\"host\": \"db.example.com\",\n\t\t\"password\": \"P@ssw0rd!\",\n\t\t\"user\": \"admin\"\n\t},\n\t\"someOtherKey\": \"base64:aGFsbG8gd2VsdAo=\",\n\t\"specific\": {\n\t\t\"HTLMEncodingTest3\": \"@\",\n\t\t\"HTMLEncodingTest1\": \"test \",\n\t\t\"HTMLEncodingTest2\": \"&\",\n\t\t\"SpecialCharacters\": \"\\\"ajkscbuiuXA34%%&&=\",\n\t\t\"boolean\": true,\n\t\t\"null\": null,\n\t\t\"number1\": 123,\n\t\t\"number2\": 123.456\n\t},\n\t\"tokens\": [\n\t\t{\n\t\t\t\"service\": \"service1\",\n\t\t\t\"token\": \"token1\"\n\t\t},\n\t\t{\n\t\t\t\"service\": \"service2\",\n\t\t\t\"token\": \"token2\"\n\t\t}\n\t]\n}", 15 | "sopsHash": "mock-etag", 16 | }, 17 | } 18 | --- 19 | -------------------------------------------------------------------------------- /lambda/internal/data/__snapshots__/3.array.fromJson.snap: -------------------------------------------------------------------------------- 1 | 2 | [Test_FromJSON/3.array - 1] 3 | &data.Data{ 4 | parsed: &[]interface {}{ 5 | map[string]interface {}{ 6 | "bool": bool(true), 7 | "number": float64(1.1), 8 | "string": "examplestring", 9 | }, 10 | map[string]interface {}{ 11 | "bool": bool(true), 12 | "number": float64(1.1), 13 | "string": "examplestring", 14 | }, 15 | }, 16 | raw: &[]uint8{0x5b, 0xa, 0x20, 0x20, 0x7b, 0xa, 0x20, 0x20, 0x20, 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x22, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0xa, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3a, 0x20, 0x31, 0x2e, 0x31, 0x2c, 0xa, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0xa, 0x20, 0x20, 0x7d, 0x2c, 0xa, 0x20, 0x20, 0x7b, 0xa, 0x20, 0x20, 0x20, 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x22, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0xa, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3a, 0x20, 0x31, 0x2e, 0x31, 0x2c, 0xa, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0xa, 0x20, 0x20, 0x7d, 0xa, 0x5d}, 17 | Hash: (*string)(nil), 18 | } 19 | --- 20 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/21-SECRET_BINARY-json.json/21-SECRET_BINARY-json.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/21-SECRET_BINARY-json.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.json": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.json"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.json": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.json"}, 8 | } 9 | main.putParameterCalls{ 10 | } 11 | main.putSecretValueCalls{ 12 | "21-SECRET_BINARY-json": map[string]interface {}{ 13 | "binary": bool(true), 14 | "secretContent": "{\n\t\"apiKey\": \"sk-1234567890abcdef\",\n\t\"database\": {\n\t\t\"host\": \"db.example.com\",\n\t\t\"password\": \"P@ssw0rd!\",\n\t\t\"user\": \"admin\"\n\t},\n\t\"someOtherKey\": \"base64:aGFsbG8gd2VsdAo=\",\n\t\"specific\": {\n\t\t\"HTLMEncodingTest3\": \"@\",\n\t\t\"HTMLEncodingTest1\": \"test \",\n\t\t\"HTMLEncodingTest2\": \"&\",\n\t\t\"SpecialCharacters\": \"\\\"ajkscbuiuXA34%%&&=\",\n\t\t\"boolean\": true,\n\t\t\"null\": null,\n\t\t\"number1\": 123,\n\t\t\"number2\": 123.456\n\t},\n\t\"tokens\": [\n\t\t{\n\t\t\t\"service\": \"service1\",\n\t\t\t\"token\": \"token1\"\n\t\t},\n\t\t{\n\t\t\t\"service\": \"service2\",\n\t\t\t\"token\": \"token2\"\n\t\t}\n\t]\n}", 15 | "sopsHash": "mock-etag", 16 | }, 17 | } 18 | --- 19 | -------------------------------------------------------------------------------- /test/PARAMETER_MULTI.integ.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { Key } from 'aws-cdk-lib/aws-kms'; 3 | import { ConstantAssetHashAspect } from './ConstantAssetHashAspect'; 4 | import { MultiStringParameter } from '../src/index'; 5 | 6 | const app = new cdk.App(); 7 | const stack = new cdk.Stack(app, 'PARAMETERMULTI'); 8 | 9 | // Apply aspect for constant asset IDs 10 | cdk.Aspects.of(stack).add(new ConstantAssetHashAspect()); 11 | 12 | interface TestCase { 13 | name: string; 14 | sopsFilePath: string; 15 | } 16 | 17 | const tc = [ 18 | { 19 | name: 'Json', 20 | sopsFilePath: 'test-secrets/testsecret.sops.json', 21 | }, 22 | { 23 | name: 'Yaml', 24 | sopsFilePath: 'test-secrets/testsecret.sops.yaml', 25 | }, 26 | // { NotSupported right now 27 | // name: 'DotEnv', 28 | // sopsFilePath: 'test-secrets/testsecret.sops.env', 29 | // }, 30 | ] satisfies TestCase[]; 31 | 32 | const encryptionKey = Key.fromKeyArn( 33 | stack, 34 | 'Key', 35 | 'arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb', 36 | ); 37 | 38 | tc.forEach((t) => { 39 | new MultiStringParameter(stack, t.name, { 40 | keyPrefix: `/${t.name}/`, 41 | keySeparator: '/', 42 | sopsFilePath: t.sopsFilePath, 43 | sopsAgeKey: cdk.SecretValue.unsafePlainText( 44 | 'AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3', 45 | ), 46 | encryptionKey, 47 | }); 48 | }); 49 | 50 | app.synth(); 51 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/01-SECRET-json.json/01-SECRET-json.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/01-SECRET-json.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.json": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.json"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.json": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.json"}, 8 | } 9 | main.putParameterCalls{ 10 | } 11 | main.putSecretValueCalls{ 12 | "01-SECRET-json": map[string]interface {}{ 13 | "binary": bool(false), 14 | "secretContent": "{\n \"apiKey\": \"sk-1234567890abcdef\",\n \"database.host\": \"db.example.com\",\n \"database.password\": \"P@ssw0rd!\",\n \"database.user\": \"admin\",\n \"someOtherKey\": \"base64:aGFsbG8gd2VsdAo=\",\n \"specific.HTLMEncodingTest3\": \"@\",\n \"specific.HTMLEncodingTest1\": \"test \\u003ctest@test.test\\u003e\",\n \"specific.HTMLEncodingTest2\": \"\\u0026\",\n \"specific.SpecialCharacters\": \"\\\"ajkscbuiuXA34%%\\u0026\\u0026=\",\n \"specific.boolean\": \"true\",\n \"specific.null\": \"\",\n \"specific.number1\": \"123\",\n \"specific.number2\": \"123.456\",\n \"tokens[0].service\": \"service1\",\n \"tokens[0].token\": \"token1\",\n \"tokens[1].service\": \"service2\",\n \"tokens[1].token\": \"token2\"\n}", 15 | "sopsHash": "mock-etag", 16 | }, 17 | } 18 | --- 19 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/02-SECRET-yaml.json/02-SECRET-yaml.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/02-SECRET-yaml.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.yaml": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.yaml"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.yaml": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.yaml"}, 8 | } 9 | main.putParameterCalls{ 10 | } 11 | main.putSecretValueCalls{ 12 | "02-SECRET-yaml": map[string]interface {}{ 13 | "binary": bool(false), 14 | "secretContent": "{\n \"apiKey\": \"sk-1234567890abcdef\",\n \"database.host\": \"db.example.com\",\n \"database.password\": \"P@ssw0rd!\",\n \"database.user\": \"admin\",\n \"someOtherKey\": \"base64:aGFsbG8gd2VsdAo=\",\n \"specific.HTLMEncodingTest3\": \"@\",\n \"specific.HTMLEncodingTest1\": \"test \\u003ctest@test.test\\u003e\",\n \"specific.HTMLEncodingTest2\": \"\\u0026\",\n \"specific.SpecialCharacters\": \"\\\"ajkscbuiuXA34%%\\u0026\\u0026=\",\n \"specific.boolean\": \"true\",\n \"specific.null\": \"\",\n \"specific.number1\": \"123\",\n \"specific.number2\": \"123.456\",\n \"tokens[0].service\": \"service1\",\n \"tokens[0].token\": \"token1\",\n \"tokens[1].service\": \"service2\",\n \"tokens[1].token\": \"token2\"\n}", 15 | "sopsHash": "mock-etag", 16 | }, 17 | } 18 | --- 19 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/03-SECRET-dotenv.json/03-SECRET-dotenv.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/03-SECRET-dotenv.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.env": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.env"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.env": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.env"}, 8 | } 9 | main.putParameterCalls{ 10 | } 11 | main.putSecretValueCalls{ 12 | "03-SECRET-dotenv": map[string]interface {}{ 13 | "binary": bool(false), 14 | "secretContent": "{\n \"apiKey\": \"sk-1234567890abcdef\",\n \"database:host\": \"db.example.com\",\n \"database:password\": \"P@ssw0rd!\",\n \"database:user\": \"admin\",\n \"someOtherKey\": \"base64:aGFsbG8gd2VsdAo=\",\n \"specific:HTLMEncodingTest3\": \"@\",\n \"specific:HTMLEncodingTest1\": \"test \\u003ctest@test.test\\u003e\",\n \"specific:HTMLEncodingTest2\": \"\\u0026\",\n \"specific:SpecialCharacters\": \"\\\"ajkscbuiuXA34%%\\u0026\\u0026=\",\n \"specific:boolean\": \"True\",\n \"specific:null\": \"\",\n \"specific:number1\": \"123\",\n \"specific:number2\": \"123.456\",\n \"tokens:0:service\": \"service1\",\n \"tokens:0:token\": \"token1\",\n \"tokens:1:service\": \"service2\",\n \"tokens:1:token\": \"token2\"\n}", 15 | "sopsHash": "mock-etag", 16 | }, 17 | } 18 | --- 19 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/31-PARAMETER-json.json/31-PARAMETER-json.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/31-PARAMETER-json.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.json": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.json"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.json": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.json"}, 8 | } 9 | main.putParameterCalls{ 10 | "/31-PARAMETER-json": map[string]interface {}{ 11 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 12 | "parameterContent": "{\n\t\"apiKey\": \"sk-1234567890abcdef\",\n\t\"database\": {\n\t\t\"host\": \"db.example.com\",\n\t\t\"password\": \"P@ssw0rd!\",\n\t\t\"user\": \"admin\"\n\t},\n\t\"someOtherKey\": \"base64:aGFsbG8gd2VsdAo=\",\n\t\"specific\": {\n\t\t\"HTLMEncodingTest3\": \"@\",\n\t\t\"HTMLEncodingTest1\": \"test \",\n\t\t\"HTMLEncodingTest2\": \"&\",\n\t\t\"SpecialCharacters\": \"\\\"ajkscbuiuXA34%%&&=\",\n\t\t\"boolean\": true,\n\t\t\"null\": null,\n\t\t\"number1\": 123,\n\t\t\"number2\": 123.456\n\t},\n\t\"tokens\": [\n\t\t{\n\t\t\t\"service\": \"service1\",\n\t\t\t\"token\": \"token1\"\n\t\t},\n\t\t{\n\t\t\t\"service\": \"service2\",\n\t\t\t\"token\": \"token2\"\n\t\t}\n\t]\n}", 13 | }, 14 | } 15 | main.putSecretValueCalls{ 16 | } 17 | --- 18 | -------------------------------------------------------------------------------- /test-secrets/json/sopsfile.enc-age.json: -------------------------------------------------------------------------------- 1 | { 2 | "key1": "ENC[AES256_GCM,data:ArSjC0Vd,iv:NFrIj1ekM2dU8JCU6NpmnHFmwcFaEQagCw/fxKV2b6A=,tag:GrGgeNmtqFuyiTjKJ/78kw==,type:str]", 3 | "key2": "ENC[AES256_GCM,data:NeoandY=,iv:2oCcBk0R/XxvH/O+aaCIa7V9pFsy7dhCKZ70ebjc5Ds=,tag:GVZXUFFwbpMVY/KCLNENAQ==,type:float]", 4 | "key3": "ENC[AES256_GCM,data:y4tBaNg=,iv:mHuYrtf0Ke/oHBcunpgdPpzaYkBx7UjMcOQgpmVc8/Q=,tag:cuEg2UE17Po6opfpH8ikog==,type:bool]", 5 | "sops": { 6 | "kms": null, 7 | "gcp_kms": null, 8 | "azure_kv": null, 9 | "hc_vault": null, 10 | "age": [ 11 | { 12 | "recipient": "age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu", 13 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOWlAxekVNNXdjenV5cmdR\nZDlQU3N5WC9oeENUbUpYcWxkeU9kVS9TeUR3Cm5zbGhIL0J6WG9TWi9KR3ZUTVph\nblpiUDZtQklnWlRrSGZzTXlZN2Z6WjgKLS0tIDI3ZHNHR0NmWTMzdTg0dEtXcU93\nbEdNWndKdWROWGdLdHAwTGFtRGlsaDQKV7HJG629rPWDBY046Hxj4utxUkex3SwU\nVUQRX00p6r9ffc+iC5DGUm/KOketAHunO4Kn0uOS4WHg+Jg2Cwu72Q==\n-----END AGE ENCRYPTED FILE-----\n" 14 | } 15 | ], 16 | "lastmodified": "2022-03-29T21:02:32Z", 17 | "mac": "ENC[AES256_GCM,data:2vmzwW74OSJq18GEcScpMH76AWrhJdENlKw2vRzGrQfPixTNcrwJ0neTH+BvGKfR9OlLR2EZTheRphQ0Ikb4SdhfInoDjHo+0jXN7RQ4neTlko4j/YRVek61oePgAKAqcmvL2IhzRGO2VR8nDPuJuvRCmP5wOMOBkerydRKHeDc=,iv:NHWPNWDmuhekJUIJpC1cDJ51lLKTsXLE6dC4tpw/qSI=,tag:yV5ngRdIYZfUgj6ntsY5CQ==,type:str]", 18 | "pgp": null, 19 | "unencrypted_suffix": "_unencrypted", 20 | "version": "3.7.2" 21 | } 22 | } -------------------------------------------------------------------------------- /test/PARAMETER.integ.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { Key } from 'aws-cdk-lib/aws-kms'; 3 | import { ConstantAssetHashAspect } from './ConstantAssetHashAspect'; 4 | import { SopsStringParameter } from '../src/index'; 5 | 6 | const app = new cdk.App(); 7 | const stack = new cdk.Stack(app, 'PARAMETER'); 8 | 9 | // Apply aspect for constant asset IDs 10 | cdk.Aspects.of(stack).add(new ConstantAssetHashAspect()); 11 | 12 | interface TestCase { 13 | name: string; 14 | sopsFilePath: string; 15 | } 16 | 17 | const tc = [ 18 | { 19 | name: 'Json', 20 | sopsFilePath: 'test-secrets/testsecret.sops.json', 21 | }, 22 | { 23 | name: 'Yaml', 24 | sopsFilePath: 'test-secrets/testsecret.sops.yaml', 25 | }, 26 | { 27 | name: 'DotEnv', 28 | sopsFilePath: 'test-secrets/testsecret.sops.env', 29 | }, 30 | { 31 | name: 'Binary', 32 | sopsFilePath: 'test-secrets/README.sops.binary', 33 | }, 34 | ] satisfies TestCase[]; 35 | 36 | const encryptionKey = Key.fromKeyArn( 37 | stack, 38 | 'Key', 39 | 'arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb', 40 | ); 41 | 42 | tc.forEach((t) => { 43 | new SopsStringParameter(stack, t.name, { 44 | parameterName: `/${t.name}`, 45 | sopsFilePath: t.sopsFilePath, 46 | sopsAgeKey: cdk.SecretValue.unsafePlainText( 47 | 'AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3', 48 | ), 49 | encryptionKey, 50 | }); 51 | }); 52 | 53 | app.synth(); 54 | -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile.enc-age.yaml: -------------------------------------------------------------------------------- 1 | key1: ENC[AES256_GCM,data:hwfM3bD0,iv:5OBD288sPyLhWtO6oaVNGnOcmk1NraBCd2VxopWvKDw=,tag:kRkbsp+OIwIwhe/iXJlPxQ==,type:str] 2 | key2: ENC[AES256_GCM,data:H+vZVMU=,iv:lnoO1rG8/64054Gpvzp7Hdowh6NhIAcaanXeFw+6V3s=,tag:MfD+feJx6Cpkg13r2L5lCg==,type:int] 3 | key3: ENC[AES256_GCM,data:Rq8caa0=,iv:9nzsP+HIX7z5F3styRkU447tXN86GZ/yynsxBQK3YoM=,tag:8NQmGwc4HF9kwyd1xti9hw==,type:bool] 4 | sops: 5 | kms: [] 6 | gcp_kms: [] 7 | azure_kv: [] 8 | hc_vault: [] 9 | age: 10 | - recipient: age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu 11 | enc: | 12 | -----BEGIN AGE ENCRYPTED FILE----- 13 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNeWd3dGV6MU1sdDBYNDVt 14 | c2F6S1JSSk5jSnE2M0VVS081dFJOWWNYemxVCm5MUUZUUDVNOUhwdWFaYjZQd1Vw 15 | NXkrK2IvbmgxQVNiUlJkZVhTalRKSkkKLS0tIFJHaXgyTk1Hd2NreExWWHFuNUJz 16 | RUpZTURyV1dETE05clVuQ3RreGsrSm8KZ910VWzFZj7uAOhE4onPpzefokuH+xnz 17 | BPJPThvD80QwPWUdjJ7QnkZzW8FhdN3MISd0G6vLIjq3SqKacdcr6g== 18 | -----END AGE ENCRYPTED FILE----- 19 | lastmodified: "2022-04-01T20:40:19Z" 20 | mac: ENC[AES256_GCM,data:39e/EmWXxeYdzHgMVDd8YKQ5o36OCdgFJOGje5rnnEU6n/+ihfP8TBXAa62JitXRDQmyVn0mlv+s+b1Io0zLscwwydpK9B6knehZA29pwWIS/aFAuY+F08ckg9N8HQHQqqZ/AKwD8RtT1gDOHWxnx0JgCh+vL6gCN2VHW2iC9H4=,iv:tHgn3jpBIQ72LxQV2G10kW21zC0mSmA6e55AQ3Fr2a0=,tag:KkhXaTZEKi/HnqUzgYRUNw==,type:str] 21 | pgp: [] 22 | unencrypted_suffix: _unencrypted 23 | version: 3.7.2 24 | -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile.enc-age.yml: -------------------------------------------------------------------------------- 1 | key1: ENC[AES256_GCM,data:hwfM3bD0,iv:5OBD288sPyLhWtO6oaVNGnOcmk1NraBCd2VxopWvKDw=,tag:kRkbsp+OIwIwhe/iXJlPxQ==,type:str] 2 | key2: ENC[AES256_GCM,data:H+vZVMU=,iv:lnoO1rG8/64054Gpvzp7Hdowh6NhIAcaanXeFw+6V3s=,tag:MfD+feJx6Cpkg13r2L5lCg==,type:int] 3 | key3: ENC[AES256_GCM,data:Rq8caa0=,iv:9nzsP+HIX7z5F3styRkU447tXN86GZ/yynsxBQK3YoM=,tag:8NQmGwc4HF9kwyd1xti9hw==,type:bool] 4 | sops: 5 | kms: [] 6 | gcp_kms: [] 7 | azure_kv: [] 8 | hc_vault: [] 9 | age: 10 | - recipient: age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu 11 | enc: | 12 | -----BEGIN AGE ENCRYPTED FILE----- 13 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNeWd3dGV6MU1sdDBYNDVt 14 | c2F6S1JSSk5jSnE2M0VVS081dFJOWWNYemxVCm5MUUZUUDVNOUhwdWFaYjZQd1Vw 15 | NXkrK2IvbmgxQVNiUlJkZVhTalRKSkkKLS0tIFJHaXgyTk1Hd2NreExWWHFuNUJz 16 | RUpZTURyV1dETE05clVuQ3RreGsrSm8KZ910VWzFZj7uAOhE4onPpzefokuH+xnz 17 | BPJPThvD80QwPWUdjJ7QnkZzW8FhdN3MISd0G6vLIjq3SqKacdcr6g== 18 | -----END AGE ENCRYPTED FILE----- 19 | lastmodified: "2022-04-01T20:40:19Z" 20 | mac: ENC[AES256_GCM,data:39e/EmWXxeYdzHgMVDd8YKQ5o36OCdgFJOGje5rnnEU6n/+ihfP8TBXAa62JitXRDQmyVn0mlv+s+b1Io0zLscwwydpK9B6knehZA29pwWIS/aFAuY+F08ckg9N8HQHQqqZ/AKwD8RtT1gDOHWxnx0JgCh+vL6gCN2VHW2iC9H4=,iv:tHgn3jpBIQ72LxQV2G10kW21zC0mSmA6e55AQ3Fr2a0=,tag:KkhXaTZEKi/HnqUzgYRUNw==,type:str] 21 | pgp: [] 22 | unencrypted_suffix: _unencrypted 23 | version: 3.7.2 24 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/24-SECRET_BINARY-binary.json/24-SECRET_BINARY-binary.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/24-SECRET_BINARY-binary.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/README.gz.sops.binary": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/README.gz.sops.binary"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/README.gz.sops.binary": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/README.gz.sops.binary"}, 8 | } 9 | main.putParameterCalls{ 10 | } 11 | main.putSecretValueCalls{ 12 | "24-SECRET_BINARY-binary": map[string]interface {}{ 13 | "binary": bool(true), 14 | "secretContent": "\x1f\x8b\b\b+Ʃg\x00\x03README.md\x00m\x90\xc1n\x82@\x14E\xf7|\xc5K\xbai\x93\xa2(\xa0\x84\x1d\xb1`\xa3!U\xa1\xb5\xb5i\"\x1d\x06\x18\x1d\x98qf\xc0\xe0\xd7\x17j\x93v\xd1\xe5K\xee=\xf7\xe4\xdd@\xf4\xb4\x8a #\x14KȘ\x00\x85\xa5\"U\x0e\xbc\x16\x9cI\xaciq\x81A2.\x7f2\xa4\x02U\x10\t)\x11\x18)&Z8c\x81\x01\t\x9c(\x9c\xfe\x87\x18\x80'\x01WH\xb4\\\x11VABs&\x88*JxOr\xfcq[(ť;\x1cv\x87\xfe\x1b\x1b0\x91\x0f\xef\xa0[\xaa%N\xef!\xe9\xa6Uץ\xec\xdc\xd1\x12ق,\x12\xd1ϰ\xec/\x9dT\x9dB\x99|#\xae\xf2\x19\xebK}\xf2\x88[y\xd5홮\xa6\xed\xf7\xfb6)\xa9\xc6\xebOJ\x90\v\x9d\xc2(=Pz\x1e\xf3Kͅ8!\x03Wv\xe94\xc89\xda96Uid\x93|\x8a\x0e\x062r\x9a]\xb8e5)\xb2Nԩ\xf2\xa6ָ M\xf7\x05\x17\xbc\xb9\xafG\xfel\xe3\xc7\xfa\xd2\x7f\xd3G~\xf0\xbc]\x18\xf3\xf1\xeb\"^\x04\xdb\xd8\v\xc7\x0f\xf3г^fKsc\xd8[\xdb\t\xa2\xf0q\xb72\xc3\xdd\xda\xd8\xc5\xde\xda\xf7\x82\xf5l\x12Oc\xb3WԾ\x00\x9a\x91g\xf1\xa5\x01\x00\x00", 15 | "sopsHash": "mock-etag", 16 | }, 17 | } 18 | --- 19 | -------------------------------------------------------------------------------- /test-secrets/README.gz.sops.binary: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:EjjTw976sTEpeSvA0cMYF4VruJFLRpi4Hfk3+eEb6i545QOyHUEkknnrjsr3DN1RpvyppbAmVq33WHnB3FL9CKuXfNc9k0hhTjWFAgGjYVZqLzRZJIyjbKFkxYVdpFjbCxk6L5hII1d/SRh61/WmKktEZ18eBNf88f5E5WrznPOjJfq4Zto50kkOcnn6zL7U/LuWh6iqb9Zfnc2aQpSX5JPMhc6wVp8xOMFfBT2n0bOeUHVMZzf0/jiNTcKURy+ENyqibc8zEtAGqFHVGsK/uC3YsE0zGntr3Tn40gcAkfmewz8YEU2YrnOA+KJgHoBA6hjLdCsjAdYdQ5kQRL2iZKA1AK3TosKGwjIQfS/kOi2xY5Q43tNuEkPuTjvY4ugEeuXGXxfQRz8RyoRsKIk+xbP6Pv6SoYvM5tWGwd5pzb0NDXJx7oJpilX40YFFdFegqYa9gfOhSQ==,iv:vLpG2x1eJ+HUj/VAyEnTzQYku+Tu7E+Ea2qnYMMJy00=,tag:41WDFsgPC7AEJzy49kOwGQ==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvcUpWa0IvQjc4Zk1jaXEz\nVFF1TURUSkdxdkp6QUpKSWkrL21pWGZjajJZClVaOGJTd0FOMU5yanRMOHJ5REdS\nS0RoOC9PcUEyWmZ5NERqRFl0UjhyREUKLS0tIGJrc3lINVFsZDNSUjZjS2lwdnpV\nbXFzd1gvSnVUKzZTcHJiemNRTEEwOUEKZ+L9VZbrFOw1qWjUFX9YCOawR/WqPXEp\n167H2YaedW7JfgEX6LZNwrccMQEEzKS1aogrqdM0xrq8gy9wmlRf0g==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-02-10T09:56:02Z", 15 | "mac": "ENC[AES256_GCM,data:ctF4yVtqwUoo69pqguDM51pTeD1v+oF5vjhxygIuWGlXllM+eTnhtv1KMf6bXqveEcHhbI7eNmqbOI2Ts+v+1a3Tekj32yk0YjyQ9YGxqdkJo8HW6/J4rwmobe8EFujAMs7K7vT0BOFB/KOWbk1De3VWlLC7R3FRsVFGV0hcyGw=,iv:A/sVJc4+bmWVzRT7kHgRn/zbwJfHeCsnXt9VhH4+AwE=,tag:eXWJKToJDMUYg/WLeMLfvg==,type:str]", 16 | "pgp": null, 17 | "encrypted_regex": "^(data|stringData)$", 18 | "version": "3.9.1" 19 | } 20 | } -------------------------------------------------------------------------------- /test-secrets/README.sops.binary: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:M9gzDsRd9oCruM1DhYiUA4YCAGvSuAWORbpXepreT3NxIfD8Y63pz00LHh/t9Y+WrTBApJuOOD3UYhU1Jrvu638HedrB1A5sq1tOuAVsYz9+KUaOjaoyeyMhvwrNzvlzaH6J06S2qb2q/G1pUHWVPkoEOUhiGNyXTbQch4nHUBbUZpUSkmqwtvzkIYOx0FbxRhf0FwZXay4S+XOMcDuKbfQx8hA1gXXdgZal2nleujE4kXWah7gB/q0AcfjIjhytPaeieElPXrNcJdyIRKllOWtpmowDxaRmqU4iqUYg5k/OybtKwPvbg2mjYxLwJZr57xjzEX2tFJi9kuoxl4HP04qVERPbaPuy3q61Zb9+FwtW85ebnViOAITp//R78KkmrQVzGx6VKY+2JxbPGsQ6HfeSm7WWWGCXteuTDYAgpoEKlZHMd/6hY07D92tlna8vadBPOcLAl7+cxVfCJaZ7Ob63jFgzPo6/LOTzYjqDKvLIEKdFGj08PwiED3vUiX4hO4oi2MUkfNuI1jyjP9Aqwj/H2QwvqpbtGDjD2+CdyOdw5pBEQQ==,iv:ntwa7TSFVDDAhd79X5GLD2B61FM19QajO2BKROjF7sk=,tag:4NfwoTRNzKK1UP2GxaQRXw==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXNUEyT09PR3pZYmN1ZU9S\neURCWUdZYnhmMzBiSm1FcENYNGliNGtNZzFRCk52TVZCSDRBOWk5TTB2dGRGTFgz\nMklsZUV0ZForUkxaM0ZiSTRIcW5id3MKLS0tIHdFMHFMMUdEYllUaGRRSEl6UGZw\ndFF2ODBPZGRrTnBGbWJvdVZvdWVGUW8Km5PYXRuTBvjmMDYmg6NEKhIbUU/SHPSZ\nTtgwos8id+MYTpudG+aKVTKSx91Gnx9ekLcTkafEw/YULU9crybYtg==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-02-07T14:50:25Z", 15 | "mac": "ENC[AES256_GCM,data:NjLPZL5bdkc+ebNrpT9sjsOZjUE/Z1t/Oq1q4CEs731jgbW0lolLZ3Dr7EFVNLoC5eunhQxvn7E6ZA1bpmqpijtMNSkqqVQdG1JszF7YGhF9Yj+Tyz9RHhvNFuH8AFcl4f1ksD7GmnV7dYsj1r1YP3wwFY+UhEqlq4+Kd87Vna0=,iv:8IMB37V4kxLHrNiIV0cVZtB2VCfuq8SYANsyDyl3m1o=,tag:EGBXVOIayzn2lh5Zi5+Gxg==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.4" 19 | } 20 | } -------------------------------------------------------------------------------- /lambda/internal/event/config-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft-07/schema#", 3 | "$id": "SopsSyncResourcePropertys", 4 | "$defs": { 5 | "SopsInline": { 6 | "properties": { 7 | "Content": { 8 | "type": "string" 9 | }, 10 | "Hash": { 11 | "type": "string" 12 | } 13 | }, 14 | "additionalProperties": false, 15 | "type": "object", 16 | "required": [ 17 | "Content", 18 | "Hash" 19 | ] 20 | }, 21 | "SopsS3File": { 22 | "properties": { 23 | "Bucket": { 24 | "type": "string" 25 | }, 26 | "Key": { 27 | "type": "string" 28 | } 29 | }, 30 | "additionalProperties": false, 31 | "type": "object", 32 | "required": [ 33 | "Bucket", 34 | "Key" 35 | ] 36 | } 37 | }, 38 | "properties": { 39 | "ResourceType": { 40 | "type": "string", 41 | "enum": [ 42 | "SECRET", 43 | "SECRET_RAW", 44 | "SECRET_BINARY", 45 | "PARAMETER_MULTI", 46 | "PARAMETER" 47 | ] 48 | }, 49 | "Format": { 50 | "type": "string", 51 | "enum": [ 52 | "json", 53 | "yaml", 54 | "dotenv", 55 | "binary" 56 | ] 57 | }, 58 | "Target": { 59 | "type": "string" 60 | }, 61 | "EncryptionKey": { 62 | "type": "string" 63 | }, 64 | "SopsS3File": { 65 | "$ref": "#/$defs/SopsS3File" 66 | }, 67 | "SopsInline": { 68 | "$ref": "#/$defs/SopsInline" 69 | }, 70 | "FlattenSeparator": { 71 | "type": "string" 72 | }, 73 | "ServiceToken": { 74 | "type": "string" 75 | } 76 | }, 77 | "additionalProperties": false, 78 | "type": "object", 79 | "required": [ 80 | "ResourceType", 81 | "Format", 82 | "Target" 83 | ] 84 | } -------------------------------------------------------------------------------- /test-secrets/json/complex.sops.binary: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:zomC/advyRjbi+7BhnHjYGPiRKGgpsY2GbBVUi/7A4EKYOXOf4870xhRkxAHBUA+CllzjfLsVc3GbYPsdJ1VzymbQL5dCNOMMbn/KgMIq1eSYpnYWZ3kOMPpVJZi9HVMEOVa4rRQbmjqnY640XEvv1mjN0/8AmJdp1vPABLmCiLCka++sIrkdFzFOwI3GlKa7mfrn137Q5VaDb3RnOzfT9lvC2ymuvFVu/Wpk6WANpID0PwGa8AZD5EHBqTYG2LbFY3CDkKMgRRurrRsRr8dih4KyF4CckyE1mo2WoxpaN9JFAS985IEYvVZZbzxSfVxved2K4qLp5xCBlE8mp3rvkDIdtGywQlm56B8Vra7sXije4/RuS8yhxW3HG3K0mZ1rvxKdZigZaWM/1De6H842QmkuIHABkbi8G60HtIiGkTCoqeS0MrqmoFS+c5+Fb3wA/cfTgstcFWbYVafhZUOkO8KfoEbWFnvCnKhCBsJWq0bve2rGKMSCFcXX5I1DDJ/XC7WQBc5PcsImKnuByu+4pgrMimz1Ah50SJM3eM/GYpcJvvhfZyEf5ustnzKvVSK04RKIXIX+G2hFawgj8gm2uw079vF2WkWslrJepA7xh338D5f0DMurvTkujiLq9DtkrW4FrxB1dsplpcl7CHyUdXPK7/Ii1V9BTvpdvLqPR7by75s64xClJt5GijkWBWUJitxrwG+Szufnv3Bbj++MRUrhEiSvGoR052bpmk=,iv:ei7B9c6t9hLtnDYJWnkRl1Wf7SWDUPlKkisDjYORZds=,tag:sMBkA3dZ8hMZWA6dF7EOyQ==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFMVBqMWNaanp1MktqRXBE\nYlVWWWxjUW1NZS9oVmMwMDdnWko5UzRjMGlJCjJaS0FWVDQvS2RoMHBHMEV3VG1M\nTml0dVhlUGdENzk2bzFTS05CSE5OYXcKLS0tIDRLRHBHYTQ3aXROV3VwMis3UVlZ\nZnNZQkZqWENjaGRwcWJ6SDB2VmJOcHMKlqxMDLaL4vdsmpKZct986r5CqlnwROXB\neYzxlSDP/48hTej56BzCsVYOHHOcFBud255HaVINXu9mHjsov0XYGA==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-02-07T14:07:37Z", 15 | "mac": "ENC[AES256_GCM,data:dJGBOZbUlZ1TCq2ZCzl7Giwwkjvh9nCaqkj4ZfSSxbsstOg1cUPUhwYzyxBLwj0tl/tmPjMiyz0MlY8ZtL/CFfjktWJbzZL3AGGU+Eq2WRt0sH6skpymy07w381HiWJfzz10GC0+yRXCQWWSmdQ3bNlyGIrXIT9eAtU83VnvTmE=,iv:tIe0mulg3snFbpfW7Qk1W7tSkM3tOIFv5OQwK3wVLz4=,tag:pRkSBsjR/1WZ5QLKITd2Mg==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.4" 19 | } 20 | } -------------------------------------------------------------------------------- /test-secrets/json/complex.sops.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:9PHZv0Okb/u2GbUKLtimcD+e/j+QbV8f5F7GBaE7BYmFX0Ft1p58b83AyBALjcKmldUQTQVLvkVEROHyZvQXM5HMEgGMJqu2aaCJgvaUVd7d0c8L4WppylxvzfPLzZFtE+m+H07Ab6FgmL/EtEmzLaNJX+d31gyfCNXO7svY8VnctDMMHWflXNGMmQbo5UPTsKasx9U5xmQJRFEPJXWJPXBWtpYXRiMYn0t5ZTrrPJf9I2suKzCFx0I+t2yga4iJEdTPiW8Xcdu2P8VTY3YboEBgvMMqj9W2ZlWYy0uPt9ACcmec1EhNB1IfB6lQM+siLlGZyrI/4gBtmX+Z5fIMYYBcm2oj2VSdSFErAHtmbQVJ3vYKVuT/ztGHx42SNExpeS5pIHYlKkCFsbvHo222r1d4FelbxLOcIaXs9a0bdIco4s5tYDy92vRTjYp6MjozzWixpou/bZzY3kwHVTSFXv7MxD/o+vN/W+Cgw/XnYY/VdLjyWmdYWOc+zKncZC0NuCxSWOGmS3C/nH1w3lnm8HQ4trksrm0/XFzyqbsDLfGaxz1LQJiEnTC3QS8plcWit2SZYIWH9azc/PGa4095/gCUcvGDfCqoswNAuKPyFDEPHqQPkh4x3qLQY6xHLC9IO2kWWyxsFWM5EGXMTxeKFW3q5N+3xbr5KuK3yyoFf9Rk/2wd2qDEhWPx0RwuGxXv+rjadwIkZxWAVLY8iKHAiKqrCTDt67cO/T/eTm8=,iv:CQtbsFV0L+VkrsG2HyYDqMyXZiaAPDLCyYYJjUMw2mY=,tag:GfZwBt/vJim2jZX9WYEdNA==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": [ 9 | { 10 | "recipient": "age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu", 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvcDcrc0pFbXJkbHk0K00x\nNkRMcmlQQVlVRGlDTEoyeVFwMWdUSHU5cTM0CmxhWGdGVDZndzlxYzNlS0xuSElv\nOU9JM1I2WERIY3hmcTdFTzRoSTgreEEKLS0tIGJJUkdMeUluMGJ5Z3dUM3lrakJh\nMVd4UENwQ1dvNFdkeWVFL05uU3I2cGcKuM1BUeQilKGIVstjC6wU368ovRx9upJr\nTga8aeE62fxDrp4gRRHqAw6KhT5OOnDdaZs5Ze5pFxkeO90csLj55Q==\n-----END AGE ENCRYPTED FILE-----\n" 12 | } 13 | ], 14 | "lastmodified": "2025-02-07T14:07:08Z", 15 | "mac": "ENC[AES256_GCM,data:8ztXnA+XO8v74aLFeeCOn2VBIeXvk4vMiASOPftwQgZFTaesVvQRszAPNpVrdr0JmMiTHNfyMOeJd4pEOSClzcynYLkEurxJIAXIWgFaGg2pZS9yO0Ybg/gJLsApV+s4FPtFSLSJjd42QlNwCUex6noUIR/kSchXOMs9qbyxdrc=,iv:5L71jZk+jVrK8vNCCKESj4mBavZ0RCTqio445TKkr8I=,tag:PVBvVFXtgt0SetwjxUy2fw==,type:str]", 16 | "pgp": null, 17 | "unencrypted_suffix": "_unencrypted", 18 | "version": "3.9.4" 19 | } 20 | } -------------------------------------------------------------------------------- /lambda/README.md: -------------------------------------------------------------------------------- 1 | # Tested combinations 2 | 3 | | Test Case | Resource Type | Input Format | Expected Behavior | 4 | | --------- | --------------- | ------------ | ----------------------------------------------- | 5 | | 01 | SECRET | json | Save json as stringified, flattened JSON | 6 | | 02 | SECRET | yaml | Save yaml as stringified, flattened JSON | 7 | | 03 | SECRET | dotenv | Save dotenv as stringified, flattened JSON | 8 | | 11 | SECRET_RAW | json | Save json as raw String SecretValue | 9 | | 12 | SECRET_RAW | yaml | Save yaml as raw String SecretValue | 10 | | 13 | SECRET_RAW | dotenv | Save dotenv as raw String SecretValue | 11 | | 14 | SECRET_RAW | binary | Save binary as raw String SecretValue | 12 | | 21 | SECRET_BINARY | json | Save json as Binary SecretValue | 13 | | 22 | SECRET_BINARY | yaml | Save yaml as Binary SecretValue | 14 | | 23 | SECRET_BINARY | dotenv | Save dotenv as Binary SecretValue | 15 | | 24 | SECRET_BINARY | binary | Save binary as Binary SecretValue | 16 | | 31 | PARAMETER | json | Save raw json into StringParameter | 17 | | 32 | PARAMETER | yaml | Save raw yaml into StringParameter | 18 | | 33 | PARAMETER | dotenv | Save raw dotenv into StringParameter | 19 | | 34 | PARAMETER | binary | Save raw binary into StringParameter | 20 | | 41 | PARAMETER_MULTI | json | Create multpiple Parameters (from json input) | 21 | | 42 | PARAMETER_MULTI | yaml | Create multpiple Parameters (from yaml input) | 22 | | 43 | PARAMETER_MULTI | dotenv | Create multpiple Parameters (from dotenv input) | 23 | -------------------------------------------------------------------------------- /test-secrets/json/sopsfile.enc-kms.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "ENC[AES256_GCM,data:yWrCCP1r9pxnGHG1rXo9x3RVNN/ixH1XUlalwhwgiyVkntYD5A3piTf1utgdXw==,iv:RwYflr/xU7aV3Z0KwG81rNgfYXagy7EwgllckdB/Ee4=,tag:CZw5Z6ZkQ3TJeFNSwwPR0g==,type:str]", 3 | "example_key": "ENC[AES256_GCM,data:SBHJz9qh9xxwcaTLVA==,iv:hSFx/KZgqL6Judeup9ugKa/r/Roi+reqaj2hrw7vU+A=,tag:Mp312nEw40LAsrNXW9XrCA==,type:str]", 4 | "example_array": [ 5 | "ENC[AES256_GCM,data:8yyhUHB9nutSmeLpfhM=,iv:a0LuvAJa3QVpOzj21H8nsyA/tlpzTGr16wfN6qGkrYM=,tag:nyNMg75Q+qvh8Hg6ZY8Pqw==,type:str]", 6 | "ENC[AES256_GCM,data:VEu37SOPbcPSlLCalEI=,iv:FzSP6KRkvEoMR/9Ow3u8TyTwaFA8QHct5F1YnmU48aE=,tag:1N0XQ88kIaKPCH3oZ+nf3Q==,type:str]" 7 | ], 8 | "example_number": "ENC[AES256_GCM,data:pA48gjh/gzgugg==,iv:n/G99KOP5wdZwP5k0HIR3vr1X3gHgjVbsT2BlKNlWoQ=,tag:jrWB7DpEIkgQ0Yfqk1dOyw==,type:float]", 9 | "example_booleans": [ 10 | "ENC[AES256_GCM,data:IdnQxQ==,iv:P8pnmm2I6dHmUswxSzK7l1k1qiOPFN7RvxBhtbjRtcg=,tag:IltVGFpbOZgnZNQicne/TA==,type:bool]", 11 | "ENC[AES256_GCM,data:JQ+ia6k=,iv:3T1qT67J5CxuibjIPjOdBxTANyg98lhhKh6jt7R8o/c=,tag:mlY5LelvJcLv6e+xbjKVDQ==,type:bool]" 12 | ], 13 | "sops": { 14 | "kms": [ 15 | { 16 | "arn": "arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab", 17 | "created_at": "2022-04-03T17:33:26Z", 18 | "enc": "AQICAHiSgZoLP6fDrUBYYPU2oJOB/3qFAR5mEYuZY2DQqzYrBgHxiBGKVwObuNv1iy4ZWWVwAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMoqaPIN/AFY0HvtVpAgEQgDvs0NairSxSUSr8QzJIwdXX6mGg2Aez/0CvFA4Teo6J6gBnSfsYGnuTi+eX/CRdSaSvcFfXzLo5nCn0ew==", 19 | "aws_profile": "" 20 | } 21 | ], 22 | "gcp_kms": null, 23 | "azure_kv": null, 24 | "hc_vault": null, 25 | "age": null, 26 | "lastmodified": "2022-04-03T17:33:31Z", 27 | "mac": "ENC[AES256_GCM,data:xY4meibKM8AaovVwOMAQ1xz8Czj2D9TkgP642IXteN84o3ydPIE5sT5JZzCvSFgc9kHi3ke93xhRNYuQKolyEEv5PQSNQFAxW9aq0nDtUVTzJ640C57ab0NcgIWzxZGsVnoBcrFhHR+t2Ltbe8KwhUxpE6cuYy/zJ2UYgG600T8=,iv:LfL+1ng6rfH+5Zt7xrlpGyy8bAfcvS8YCFJ0e9sNbjI=,tag:CpZsbynJv7Ctj0M7zo1HSA==,type:str]", 28 | "pgp": null, 29 | "unencrypted_suffix": "_unencrypted", 30 | "version": "3.7.2" 31 | } 32 | } -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile.enc-kms.yaml: -------------------------------------------------------------------------------- 1 | hello: ENC[AES256_GCM,data:sswbRHccIuisbGyO+mG/EWb+1JctqNUorOzQUaMRn3HkAyWtreNuZyhYYlkvcg==,iv:XpHilhyCRXa8clGiEckpquCqSZGWA80H0eDUmR80xhE=,tag:WsuxI4lUHwJEas76ODQxUA==,type:str] 2 | example_key: ENC[AES256_GCM,data:/4OtiH3nooTVZuzC+g==,iv:R6qrNY7GAxcIwR42bvE2774uf+IfQ9pv9UA78LmitU0=,tag:jFogVsycufSXRvBKXLVA9g==,type:str] 3 | #ENC[AES256_GCM,data:aFjHl+KK37OIsFkhBqbWAa0=,iv:ZSQP65Qh6isgQL6aQqk9Q4bPCxqd/k0w+9qznaum1PA=,tag:7CUuqndG4b3J3v/g5OKO2Q==,type:comment] 4 | example_array: 5 | - ENC[AES256_GCM,data:UxWfsBaGcgregmDfKMI=,iv:XBs2OCoHWOmobj1OPd/clbUJFbPTdAdotFHVGQm1TP0=,tag:CTT15ZYd1nG8kJarDDLZLA==,type:str] 6 | - ENC[AES256_GCM,data:QP8ZKa88n7SnJaK/ZLk=,iv:Q7aBsWrIv0G8D33Iw65KRqORwqyUWjaAvIBSbWNHAWw=,tag:wBB7tRcc6yRv5cA53k5jog==,type:str] 7 | example_number: ENC[AES256_GCM,data:41QvJoCJ5MuELQ==,iv:QexJM+rwhuZcxDL3TDEswpk8qwUeU6bGk5i5TCduiEk=,tag:rzykMYrmHyv3UHjEGRrNHg==,type:float] 8 | example_booleans: 9 | - ENC[AES256_GCM,data:2sFhDg==,iv:hBMZ0x/1RJC/2NeQ/BEugPDVCConvVHCZk1Tak9vln8=,tag:CJqyx7yAtNnFADvDakfXbA==,type:bool] 10 | - ENC[AES256_GCM,data:51T3h9A=,iv:0VuwMJwURT8AyZEAptQC5kd5k+dfZDHR58Mr4Z4TVII=,tag:mTK1vQE9eXlhAkLQogjdfw==,type:bool] 11 | sops: 12 | kms: 13 | - arn: arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab 14 | created_at: "2022-04-03T17:34:45Z" 15 | enc: AQICAHiSgZoLP6fDrUBYYPU2oJOB/3qFAR5mEYuZY2DQqzYrBgGAPS+SiO5yb/bXbUdoUPeZAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMXZPpPU3GibIONK6VAgEQgDu154YpnYoe2f8YFuWeBpGXfDdaud5oMDfquwqY2UWG8clnZV9355z+V7cqCi+4PEBoaveMLLcLYsLOAA== 16 | aws_profile: "" 17 | gcp_kms: [] 18 | azure_kv: [] 19 | hc_vault: [] 20 | age: [] 21 | lastmodified: "2022-04-03T17:34:56Z" 22 | mac: ENC[AES256_GCM,data:qDi/CaAGfXbcCxPXxHRVnDiP3ayLhL5+Ordv+rCyF/M0iBlO6PEBez4mW+0JE4F8E8a3lMJORhaEGTmRtGyzPXm62DoNLpVepqj8fdBNgzxDQdbTX2yfPoSltSmyCp7lpnbUjGwSHVHsGK8+1EEoocTn81efP0hytRSocYEKONY=,iv:8vZ417Nhp6BO2kE5Ic4HRkzKjS79enrfVJlsYeq59Fo=,tag:mpE+Oto+HPLd2tsWuteuSA==,type:str] 23 | pgp: [] 24 | unencrypted_suffix: _unencrypted 25 | version: 3.7.2 26 | -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile.enc-kms.yml: -------------------------------------------------------------------------------- 1 | hello: ENC[AES256_GCM,data:sswbRHccIuisbGyO+mG/EWb+1JctqNUorOzQUaMRn3HkAyWtreNuZyhYYlkvcg==,iv:XpHilhyCRXa8clGiEckpquCqSZGWA80H0eDUmR80xhE=,tag:WsuxI4lUHwJEas76ODQxUA==,type:str] 2 | example_key: ENC[AES256_GCM,data:/4OtiH3nooTVZuzC+g==,iv:R6qrNY7GAxcIwR42bvE2774uf+IfQ9pv9UA78LmitU0=,tag:jFogVsycufSXRvBKXLVA9g==,type:str] 3 | #ENC[AES256_GCM,data:aFjHl+KK37OIsFkhBqbWAa0=,iv:ZSQP65Qh6isgQL6aQqk9Q4bPCxqd/k0w+9qznaum1PA=,tag:7CUuqndG4b3J3v/g5OKO2Q==,type:comment] 4 | example_array: 5 | - ENC[AES256_GCM,data:UxWfsBaGcgregmDfKMI=,iv:XBs2OCoHWOmobj1OPd/clbUJFbPTdAdotFHVGQm1TP0=,tag:CTT15ZYd1nG8kJarDDLZLA==,type:str] 6 | - ENC[AES256_GCM,data:QP8ZKa88n7SnJaK/ZLk=,iv:Q7aBsWrIv0G8D33Iw65KRqORwqyUWjaAvIBSbWNHAWw=,tag:wBB7tRcc6yRv5cA53k5jog==,type:str] 7 | example_number: ENC[AES256_GCM,data:41QvJoCJ5MuELQ==,iv:QexJM+rwhuZcxDL3TDEswpk8qwUeU6bGk5i5TCduiEk=,tag:rzykMYrmHyv3UHjEGRrNHg==,type:float] 8 | example_booleans: 9 | - ENC[AES256_GCM,data:2sFhDg==,iv:hBMZ0x/1RJC/2NeQ/BEugPDVCConvVHCZk1Tak9vln8=,tag:CJqyx7yAtNnFADvDakfXbA==,type:bool] 10 | - ENC[AES256_GCM,data:51T3h9A=,iv:0VuwMJwURT8AyZEAptQC5kd5k+dfZDHR58Mr4Z4TVII=,tag:mTK1vQE9eXlhAkLQogjdfw==,type:bool] 11 | sops: 12 | kms: 13 | - arn: arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab 14 | created_at: "2022-04-03T17:34:45Z" 15 | enc: AQICAHiSgZoLP6fDrUBYYPU2oJOB/3qFAR5mEYuZY2DQqzYrBgGAPS+SiO5yb/bXbUdoUPeZAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMXZPpPU3GibIONK6VAgEQgDu154YpnYoe2f8YFuWeBpGXfDdaud5oMDfquwqY2UWG8clnZV9355z+V7cqCi+4PEBoaveMLLcLYsLOAA== 16 | aws_profile: "" 17 | gcp_kms: [] 18 | azure_kv: [] 19 | hc_vault: [] 20 | age: [] 21 | lastmodified: "2022-04-03T17:34:56Z" 22 | mac: ENC[AES256_GCM,data:qDi/CaAGfXbcCxPXxHRVnDiP3ayLhL5+Ordv+rCyF/M0iBlO6PEBez4mW+0JE4F8E8a3lMJORhaEGTmRtGyzPXm62DoNLpVepqj8fdBNgzxDQdbTX2yfPoSltSmyCp7lpnbUjGwSHVHsGK8+1EEoocTn81efP0hytRSocYEKONY=,iv:8vZ417Nhp6BO2kE5Ic4HRkzKjS79enrfVJlsYeq59Fo=,tag:mpE+Oto+HPLd2tsWuteuSA==,type:str] 23 | pgp: [] 24 | unencrypted_suffix: _unencrypted 25 | version: 3.7.2 26 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/24-SECRET_BINARY-binary.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/24-SECRET_BINARY-binary.json - 1] 3 | &"24-SECRET_BINARY-binary" 4 | []uint8{0x1f, 0x8b, 0x8, 0x8, 0x2b, 0xc6, 0xa9, 0x67, 0x0, 0x3, 0x52, 0x45, 0x41, 0x44, 0x4d, 0x45, 0x2e, 0x6d, 0x64, 0x0, 0x6d, 0x90, 0xc1, 0x6e, 0x82, 0x40, 0x14, 0x45, 0xf7, 0x7c, 0xc5, 0x4b, 0xba, 0x69, 0x93, 0xa2, 0x28, 0xa0, 0x84, 0x1d, 0xb1, 0x60, 0xa3, 0x21, 0x55, 0xa1, 0xb5, 0xb5, 0x69, 0x22, 0x1d, 0x6, 0x18, 0x1d, 0x98, 0x71, 0x66, 0xc0, 0xe0, 0xd7, 0x17, 0x6a, 0x93, 0x76, 0xd1, 0xe5, 0x4b, 0xee, 0x3d, 0xf7, 0xe4, 0xdd, 0x40, 0xf4, 0xb4, 0x8a, 0x20, 0x23, 0x14, 0x4b, 0xc8, 0x98, 0x0, 0x85, 0xa5, 0x22, 0x55, 0xe, 0xbc, 0x16, 0x9c, 0x49, 0xac, 0x69, 0x71, 0x81, 0x41, 0x32, 0x2e, 0x7f, 0x32, 0xa4, 0x2, 0x55, 0x10, 0x9, 0x29, 0x11, 0x18, 0x29, 0x26, 0x5a, 0x38, 0x63, 0x81, 0x1, 0x9, 0x9c, 0x28, 0x9c, 0xfe, 0x87, 0x18, 0x80, 0x27, 0x1, 0x57, 0x48, 0xb4, 0x5c, 0x11, 0x56, 0x41, 0x42, 0x73, 0x26, 0x88, 0x2a, 0x4a, 0x78, 0x4f, 0x72, 0xfc, 0x71, 0x5b, 0x28, 0xc5, 0xa5, 0x3b, 0x1c, 0x76, 0x87, 0xfe, 0x1b, 0x1b, 0x30, 0x91, 0xf, 0xef, 0xa0, 0x5b, 0xaa, 0x25, 0x4e, 0xef, 0x21, 0xe9, 0xa6, 0x55, 0xd7, 0xa5, 0xec, 0xdc, 0xd1, 0x12, 0xd9, 0x82, 0x2c, 0x12, 0xd1, 0xcf, 0xb0, 0xec, 0x2f, 0x9d, 0x54, 0x9d, 0x42, 0x99, 0x7c, 0x23, 0xae, 0xf2, 0x19, 0xeb, 0x4b, 0x7d, 0xf2, 0x88, 0x5b, 0x79, 0xd5, 0xed, 0x99, 0xae, 0xa6, 0xed, 0xf7, 0xfb, 0x36, 0x29, 0xa9, 0xc6, 0xeb, 0x4f, 0x4a, 0x90, 0xb, 0x9d, 0xc2, 0x28, 0x3d, 0x50, 0x7a, 0x1e, 0xf3, 0x4b, 0xcd, 0x85, 0x38, 0x21, 0x3, 0x57, 0x76, 0xe9, 0x34, 0xc8, 0x39, 0xda, 0x39, 0x36, 0x55, 0x69, 0x64, 0x93, 0x7c, 0x8a, 0xe, 0x6, 0x32, 0x72, 0x9a, 0x5d, 0xb8, 0x65, 0x35, 0x29, 0xb2, 0x4e, 0xd4, 0xa9, 0xf2, 0xa6, 0xd6, 0xb8, 0x20, 0x4d, 0xf7, 0x5, 0x17, 0xbc, 0xb9, 0xaf, 0x47, 0xfe, 0x6c, 0xe3, 0xc7, 0xfa, 0xd2, 0x7f, 0xd3, 0x47, 0x7e, 0xf0, 0xbc, 0x5d, 0x18, 0xf3, 0xf1, 0xeb, 0x22, 0x5e, 0x4, 0xdb, 0xd8, 0xb, 0xc7, 0xf, 0xf3, 0xd0, 0xb3, 0x5e, 0x66, 0x4b, 0x73, 0x63, 0xd8, 0x5b, 0xdb, 0x9, 0xa2, 0xf0, 0x71, 0xb7, 0x32, 0xc3, 0xdd, 0xda, 0xd8, 0xc5, 0xde, 0xda, 0xf7, 0x82, 0xf5, 0x6c, 0x12, 0x4f, 0x63, 0xb3, 0x57, 0xd4, 0xbe, 0x0, 0x9a, 0x91, 0x67, 0xf1, 0xa5, 0x1, 0x0, 0x0} 5 | (*string)(nil) 6 | --- 7 | -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile.enc-kms-alias.yaml: -------------------------------------------------------------------------------- 1 | # This secret is not working, just for testing recognition of several kms keys 2 | 3 | hello: ENC[AES256_GCM,data:sswbRHccIuisbGyO+mG/EWb+1JctqNUorOzQUaMRn3HkAyWtreNuZyhYYlkvcg==,iv:XpHilhyCRXa8clGiEckpquCqSZGWA80H0eDUmR80xhE=,tag:WsuxI4lUHwJEas76ODQxUA==,type:str] 4 | example_key: ENC[AES256_GCM,data:/4OtiH3nooTVZuzC+g==,iv:R6qrNY7GAxcIwR42bvE2774uf+IfQ9pv9UA78LmitU0=,tag:jFogVsycufSXRvBKXLVA9g==,type:str] 5 | #ENC[AES256_GCM,data:aFjHl+KK37OIsFkhBqbWAa0=,iv:ZSQP65Qh6isgQL6aQqk9Q4bPCxqd/k0w+9qznaum1PA=,tag:7CUuqndG4b3J3v/g5OKO2Q==,type:comment] 6 | example_array: 7 | - ENC[AES256_GCM,data:UxWfsBaGcgregmDfKMI=,iv:XBs2OCoHWOmobj1OPd/clbUJFbPTdAdotFHVGQm1TP0=,tag:CTT15ZYd1nG8kJarDDLZLA==,type:str] 8 | - ENC[AES256_GCM,data:QP8ZKa88n7SnJaK/ZLk=,iv:Q7aBsWrIv0G8D33Iw65KRqORwqyUWjaAvIBSbWNHAWw=,tag:wBB7tRcc6yRv5cA53k5jog==,type:str] 9 | example_number: ENC[AES256_GCM,data:41QvJoCJ5MuELQ==,iv:QexJM+rwhuZcxDL3TDEswpk8qwUeU6bGk5i5TCduiEk=,tag:rzykMYrmHyv3UHjEGRrNHg==,type:float] 10 | example_booleans: 11 | - ENC[AES256_GCM,data:2sFhDg==,iv:hBMZ0x/1RJC/2NeQ/BEugPDVCConvVHCZk1Tak9vln8=,tag:CJqyx7yAtNnFADvDakfXbA==,type:bool] 12 | - ENC[AES256_GCM,data:51T3h9A=,iv:0VuwMJwURT8AyZEAptQC5kd5k+dfZDHR58Mr4Z4TVII=,tag:mTK1vQE9eXlhAkLQogjdfw==,type:bool] 13 | sops: 14 | kms: 15 | - arn: arn:aws:kms:aws-region-1:123456789011:alias/myalias 16 | created_at: "2022-04-03T17:34:45Z" 17 | enc: AQICAHiSgZoLP6fDrUBYYPU2oJOB/3qFAR5mEYuZY2DQqzYrBgGAPS+SiO5yb/bXbUdoUPeZAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMXZPpPU3GibIONK6VAgEQgDu154YpnYoe2f8YFuWeBpGXfDdaud5oMDfquwqY2UWG8clnZV9355z+V7cqCi+4PEBoaveMLLcLYsLOAA== 18 | aws_profile: "" 19 | gcp_kms: [] 20 | azure_kv: [] 21 | hc_vault: [] 22 | age: [] 23 | lastmodified: "2022-04-03T17:34:56Z" 24 | mac: ENC[AES256_GCM,data:qDi/CaAGfXbcCxPXxHRVnDiP3ayLhL5+Ordv+rCyF/M0iBlO6PEBez4mW+0JE4F8E8a3lMJORhaEGTmRtGyzPXm62DoNLpVepqj8fdBNgzxDQdbTX2yfPoSltSmyCp7lpnbUjGwSHVHsGK8+1EEoocTn81efP0hytRSocYEKONY=,iv:8vZ417Nhp6BO2kE5Ic4HRkzKjS79enrfVJlsYeq59Fo=,tag:mpE+Oto+HPLd2tsWuteuSA==,type:str] 25 | pgp: [] 26 | unencrypted_suffix: _unencrypted 27 | version: 3.7.2 28 | -------------------------------------------------------------------------------- /test/ConstantAssetHashAspect.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { IConstruct } from 'constructs'; 3 | 4 | /** 5 | * Aspect that ensures constant asset IDs by overriding asset hashes. 6 | * This is useful for integration tests to prevent snapshot changes due to asset hash variations. 7 | */ 8 | export class ConstantAssetHashAspect implements cdk.IAspect { 9 | public visit(node: IConstruct): void { 10 | // Check if the node is a Lambda Function 11 | if (node instanceof cdk.aws_lambda.Function) { 12 | const cfnFunction = node.node.defaultChild as cdk.aws_lambda.CfnFunction; 13 | if (cfnFunction && cfnFunction.code) { 14 | // Override the asset hash to a constant value 15 | const code = cfnFunction.code as any; 16 | if (code.s3Bucket || code.s3Key) { 17 | // Set a constant hash based on the function ID 18 | const constantHash = this.generateConstantHash(node.node.id); 19 | 20 | // Override asset parameters 21 | cfnFunction.addPropertyOverride( 22 | 'Code.S3Key', 23 | `asset.${constantHash}.zip`, 24 | ); 25 | } 26 | } 27 | } 28 | 29 | // Check if the node is an Asset 30 | if (node instanceof cdk.aws_s3_assets.Asset) { 31 | // Override the asset hash 32 | const cfnResource = node.node.defaultChild as cdk.CfnResource; 33 | if (cfnResource) { 34 | const constantHash = this.generateConstantHash(node.node.id); 35 | cfnResource.addPropertyOverride('AssetHash', constantHash); 36 | } 37 | } 38 | } 39 | 40 | private generateConstantHash(id: string): string { 41 | // Generate a deterministic hash based on the construct ID 42 | // Using a simple string-to-hex conversion for reproducibility 43 | let hash = 0; 44 | for (let i = 0; i < id.length; i++) { 45 | const char = id.charCodeAt(i); 46 | // eslint-disable-next-line no-bitwise 47 | hash = (hash << 5) - hash + char; 48 | // eslint-disable-next-line no-bitwise 49 | hash = hash & hash; // Convert to 32bit integer 50 | } 51 | // Return a hex string padded to 64 characters (SHA256 length) 52 | return Math.abs(hash).toString(16).padStart(64, '0'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/SECRET.integ.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { ConstantAssetHashAspect } from './ConstantAssetHashAspect'; 3 | import { RawOutput, SopsSecret } from '../src/index'; 4 | 5 | const app = new cdk.App(); 6 | const stack = new cdk.Stack(app, 'SECRET'); 7 | 8 | // Apply aspect for constant asset IDs 9 | cdk.Aspects.of(stack).add(new ConstantAssetHashAspect()); 10 | 11 | interface TestCase { 12 | name: string; 13 | sopsFilePath: string; 14 | additionalProperties: Record; 15 | } 16 | 17 | const tc = [ 18 | { 19 | name: 'Json2Json', 20 | sopsFilePath: 'test-secrets/testsecret.sops.json', 21 | additionalProperties: {}, 22 | }, 23 | { 24 | name: 'Json2RawString', 25 | sopsFilePath: 'test-secrets/testsecret.sops.json', 26 | additionalProperties: { 27 | rawOutput: RawOutput.STRING, 28 | }, 29 | }, 30 | { 31 | name: 'Yaml2Json', 32 | sopsFilePath: 'test-secrets/testsecret.sops.yaml', 33 | additionalProperties: {}, 34 | }, 35 | { 36 | name: 'Yaml2RawString', 37 | sopsFilePath: 'test-secrets/testsecret.sops.yaml', 38 | additionalProperties: { 39 | rawOutput: RawOutput.STRING, 40 | }, 41 | }, 42 | { 43 | name: 'DotEnv2Json', 44 | sopsFilePath: 'test-secrets/testsecret.sops.env', 45 | additionalProperties: {}, 46 | }, 47 | { 48 | name: 'DotEnv2RawString', 49 | sopsFilePath: 'test-secrets/README.sops.binary', 50 | additionalProperties: { 51 | rawOutput: RawOutput.STRING, 52 | }, 53 | }, 54 | { 55 | name: 'Binary2RawString', 56 | sopsFilePath: 'test-secrets/README.sops.binary', 57 | additionalProperties: { 58 | rawOutput: RawOutput.STRING, 59 | }, 60 | }, 61 | { 62 | name: 'Binary2RawBinary', 63 | sopsFilePath: 'test-secrets/README.gz.sops.binary', 64 | additionalProperties: { 65 | rawOutput: RawOutput.BINARY, 66 | }, 67 | }, 68 | ] satisfies TestCase[]; 69 | 70 | tc.forEach((t) => { 71 | new SopsSecret(stack, t.name, { 72 | secretName: t.name, 73 | sopsFilePath: t.sopsFilePath, 74 | sopsAgeKey: cdk.SecretValue.unsafePlainText( 75 | 'AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3', 76 | ), 77 | ...t.additionalProperties, 78 | }); 79 | }); 80 | 81 | app.synth(); 82 | -------------------------------------------------------------------------------- /test/__snapshots__/permissions.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing 2 | 3 | exports[`parameters 1 parameter - full snapshot 1`] = ` 4 | { 5 | "SopsSecretParameterProviderManagedPolicyParameterAccess03C7CE5D3": { 6 | "Properties": { 7 | "Description": "Policy to grant parameter provider permissions to put parameter", 8 | "Path": "/", 9 | "PolicyDocument": { 10 | "Statement": [ 11 | { 12 | "Action": "ssm:PutParameter", 13 | "Effect": "Allow", 14 | "Resource": { 15 | "Fn::Join": [ 16 | "", 17 | [ 18 | "arn:aws:ssm:", 19 | { 20 | "Ref": "AWS::Region", 21 | }, 22 | ":", 23 | { 24 | "Ref": "AWS::AccountId", 25 | }, 26 | ":parameter/parameter0", 27 | ], 28 | ], 29 | }, 30 | }, 31 | ], 32 | "Version": "2012-10-17", 33 | }, 34 | "Roles": [ 35 | "test-role", 36 | ], 37 | }, 38 | "Type": "AWS::IAM::ManagedPolicy", 39 | }, 40 | } 41 | `; 42 | 43 | exports[`parameters 100 parameter - snapshot count 1`] = ` 44 | [ 45 | "SopsSecretParameterProviderManagedPolicyParameterAccess03C7CE5D3", 46 | "SopsSecretParameterProviderManagedPolicyParameterAccess1E27103A2", 47 | ] 48 | `; 49 | 50 | exports[`parameters 1000 parameter - snapshot count 1`] = ` 51 | [ 52 | "SopsSecretParameterProviderManagedPolicyParameterAccess03C7CE5D3", 53 | "SopsSecretParameterProviderManagedPolicyParameterAccess1E27103A2", 54 | "SopsSecretParameterProviderManagedPolicyParameterAccess204C56AEE", 55 | "SopsSecretParameterProviderManagedPolicyParameterAccess39C2C7238", 56 | "SopsSecretParameterProviderManagedPolicyParameterAccess4810168FC", 57 | "SopsSecretParameterProviderManagedPolicyParameterAccess5A4020F4D", 58 | "SopsSecretParameterProviderManagedPolicyParameterAccess6D18916A5", 59 | "SopsSecretParameterProviderManagedPolicyParameterAccess7C1D2D5DD", 60 | "SopsSecretParameterProviderManagedPolicyParameterAccess881B83B3B", 61 | "SopsSecretParameterProviderManagedPolicyParameterAccess9F6C18C5F", 62 | "SopsSecretParameterProviderManagedPolicyParameterAccess10B80D3BA8", 63 | "SopsSecretParameterProviderManagedPolicyParameterAccess118D826D5D", 64 | ] 65 | `; 66 | -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile-complex.enc-age.yaml: -------------------------------------------------------------------------------- 1 | some: 2 | deep: 3 | nested: 4 | object: ENC[AES256_GCM,data:YmdeRFEGKKUT,iv:8O9QUvU7g1E+awYKXSxkkzUKzBc8adUDtmPiwjtPPb0=,tag:3Ot8yNCo2p3RH0DT5+AkbQ==,type:str] 5 | arrays: 6 | - ENC[AES256_GCM,data:haaDZw==,iv:4/aUgsEmsUpE/1K5iGbB2IBcbZm0P7k3iL4IR9kOnAI=,tag:nAasS//1/XAASs2sb6vITA==,type:str] 7 | - ENC[AES256_GCM,data:VojTYmEilw==,iv:i2/sFm9MpLb4L/WYykB4bxLuo7qZv5NrdSHcM+gYhhw=,tag:5/+M6eEIdsb/b3wrXPjsmA==,type:str] 8 | - values: 9 | and: ENC[AES256_GCM,data:Bg924tLTZg==,iv:k8AEbL8VGt5ubKKNWRXUPeMQx84cxPCoQ0yWx6Dvwoc=,tag:o8cRtGJxtCtJa7Bj3Z0m2A==,type:str] 10 | notsodeep: ENC[AES256_GCM,data:wpiMypLh,iv:hiLoAdgzE9PbSk212u3IjigEzxiNgaKVg+2SjjEFunE=,tag:dLl1u5R9F105J7qI5c3oEw==,type:str] 11 | and now: 12 | some: 13 | - basic: ENC[AES256_GCM,data:mOhwGnc=,iv:dgMjIZNIzBVTdM0LyeTwJbOvrhtL3CeEVJQQaZeFGoY=,tag:oBSwDjyDAyFglITYBvsHeQ==,type:bool] 14 | - nested: ENC[AES256_GCM,data:sgsq2Kc=,iv:2j1CDzqwCSwo+Q8xzNWaiQPweC/8VR0ip25fxQn6+no=,tag:9xDqrdDcH07VEhrOaK7Lzg==,type:int] 15 | - type: ENC[AES256_GCM,data:t9iIJUk0,iv:5Yu+kCWPI76uC4t9Fj17gEWVc+RppvysYHYn1RPWVQ0=,tag:TBhz0oGvhbOEfhSAl0Alww==,type:float] 16 | - tests: ENC[AES256_GCM,data:KlhkmQDD1A==,iv:msOWAoGnsB8lcky0HeBuXYX7BXclqVfM2oyrWJrnE70=,tag:apCS6QmaVbUzdsul9l9WCg==,type:str] 17 | sops: 18 | kms: [] 19 | gcp_kms: [] 20 | azure_kv: [] 21 | hc_vault: [] 22 | age: 23 | - recipient: age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu 24 | enc: | 25 | -----BEGIN AGE ENCRYPTED FILE----- 26 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaUVBCRVh1SmVqbG5DenFa 27 | by9IU24rOHdVMkc0Y1NCaCtqQTEwdzZqWW1vCkx5NXBMZkZCeTkvQW1tUEdvR2ly 28 | MWlSTE1jVWtHd0Roa3RPS3VKQjRTYlUKLS0tIDhCZ3M2QVdVb2l0S0NPMDNSalVS 29 | NVo4ZVVSNHFPRGFyenBqN2tPNWRwNzgKxpTfiJFBmN9OwQyH2kIS1PphNieCKMpZ 30 | ykWAthKZ2a35vzGpdb0UVTUc3hQxow6nJCGePLgvLGCNEK+BX9nQ5w== 31 | -----END AGE ENCRYPTED FILE----- 32 | lastmodified: "2022-04-03T17:25:24Z" 33 | mac: ENC[AES256_GCM,data:sYDj9xk+RKXgzBGBNTzsqCcHgLV0jjOI1q5SySLhlbsG63ch7fXedZLsOJtWhF5x1v22ffUSYU+HHMQgBpPRIlkHuGQCSXk8Kyth4yIfRpYreJbU3h/Y6cD272qcXXrj+EyBQD3y5w6bSdFatKxX/Vi4TmngrM/azj4jLzu/u+I=,iv:j0b0WvwJZktn5l1pouWdE8Mq6Yl1yjulC59JlCT0hEw=,tag:f6G0DB77mDpqloWnadc3Ug==,type:str] 34 | pgp: [] 35 | unencrypted_suffix: _unencrypted 36 | version: 3.7.2 37 | -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile-complex.enc-age.yml: -------------------------------------------------------------------------------- 1 | some: 2 | deep: 3 | nested: 4 | object: ENC[AES256_GCM,data:YmdeRFEGKKUT,iv:8O9QUvU7g1E+awYKXSxkkzUKzBc8adUDtmPiwjtPPb0=,tag:3Ot8yNCo2p3RH0DT5+AkbQ==,type:str] 5 | arrays: 6 | - ENC[AES256_GCM,data:haaDZw==,iv:4/aUgsEmsUpE/1K5iGbB2IBcbZm0P7k3iL4IR9kOnAI=,tag:nAasS//1/XAASs2sb6vITA==,type:str] 7 | - ENC[AES256_GCM,data:VojTYmEilw==,iv:i2/sFm9MpLb4L/WYykB4bxLuo7qZv5NrdSHcM+gYhhw=,tag:5/+M6eEIdsb/b3wrXPjsmA==,type:str] 8 | - values: 9 | and: ENC[AES256_GCM,data:Bg924tLTZg==,iv:k8AEbL8VGt5ubKKNWRXUPeMQx84cxPCoQ0yWx6Dvwoc=,tag:o8cRtGJxtCtJa7Bj3Z0m2A==,type:str] 10 | notsodeep: ENC[AES256_GCM,data:wpiMypLh,iv:hiLoAdgzE9PbSk212u3IjigEzxiNgaKVg+2SjjEFunE=,tag:dLl1u5R9F105J7qI5c3oEw==,type:str] 11 | and now: 12 | some: 13 | - basic: ENC[AES256_GCM,data:mOhwGnc=,iv:dgMjIZNIzBVTdM0LyeTwJbOvrhtL3CeEVJQQaZeFGoY=,tag:oBSwDjyDAyFglITYBvsHeQ==,type:bool] 14 | - nested: ENC[AES256_GCM,data:sgsq2Kc=,iv:2j1CDzqwCSwo+Q8xzNWaiQPweC/8VR0ip25fxQn6+no=,tag:9xDqrdDcH07VEhrOaK7Lzg==,type:int] 15 | - type: ENC[AES256_GCM,data:t9iIJUk0,iv:5Yu+kCWPI76uC4t9Fj17gEWVc+RppvysYHYn1RPWVQ0=,tag:TBhz0oGvhbOEfhSAl0Alww==,type:float] 16 | - tests: ENC[AES256_GCM,data:KlhkmQDD1A==,iv:msOWAoGnsB8lcky0HeBuXYX7BXclqVfM2oyrWJrnE70=,tag:apCS6QmaVbUzdsul9l9WCg==,type:str] 17 | sops: 18 | kms: [] 19 | gcp_kms: [] 20 | azure_kv: [] 21 | hc_vault: [] 22 | age: 23 | - recipient: age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu 24 | enc: | 25 | -----BEGIN AGE ENCRYPTED FILE----- 26 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaUVBCRVh1SmVqbG5DenFa 27 | by9IU24rOHdVMkc0Y1NCaCtqQTEwdzZqWW1vCkx5NXBMZkZCeTkvQW1tUEdvR2ly 28 | MWlSTE1jVWtHd0Roa3RPS3VKQjRTYlUKLS0tIDhCZ3M2QVdVb2l0S0NPMDNSalVS 29 | NVo4ZVVSNHFPRGFyenBqN2tPNWRwNzgKxpTfiJFBmN9OwQyH2kIS1PphNieCKMpZ 30 | ykWAthKZ2a35vzGpdb0UVTUc3hQxow6nJCGePLgvLGCNEK+BX9nQ5w== 31 | -----END AGE ENCRYPTED FILE----- 32 | lastmodified: "2022-04-03T17:25:24Z" 33 | mac: ENC[AES256_GCM,data:sYDj9xk+RKXgzBGBNTzsqCcHgLV0jjOI1q5SySLhlbsG63ch7fXedZLsOJtWhF5x1v22ffUSYU+HHMQgBpPRIlkHuGQCSXk8Kyth4yIfRpYreJbU3h/Y6cD272qcXXrj+EyBQD3y5w6bSdFatKxX/Vi4TmngrM/azj4jLzu/u+I=,iv:j0b0WvwJZktn5l1pouWdE8Mq6Yl1yjulC59JlCT0hEw=,tag:f6G0DB77mDpqloWnadc3Ug==,type:str] 34 | pgp: [] 35 | unencrypted_suffix: _unencrypted 36 | version: 3.7.2 37 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 1] 3 | &"/41-PARAMETER_MULTI-json/apiKey" 4 | &"sk-1234567890abcdef" 5 | --- 6 | 7 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 2] 8 | &"/41-PARAMETER_MULTI-json/database/host" 9 | &"db.example.com" 10 | --- 11 | 12 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 3] 13 | &"/41-PARAMETER_MULTI-json/database/password" 14 | &"P@ssw0rd!" 15 | --- 16 | 17 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 4] 18 | &"/41-PARAMETER_MULTI-json/database/user" 19 | &"admin" 20 | --- 21 | 22 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 5] 23 | &"/41-PARAMETER_MULTI-json/someOtherKey" 24 | &"base64:aGFsbG8gd2VsdAo=" 25 | --- 26 | 27 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 6] 28 | &"/41-PARAMETER_MULTI-json/specific/HTMLEncodingTest2" 29 | &"&" 30 | --- 31 | 32 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 7] 33 | &"/41-PARAMETER_MULTI-json/specific/HTLMEncodingTest3" 34 | &"@" 35 | --- 36 | 37 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 8] 38 | &"/41-PARAMETER_MULTI-json/specific/HTMLEncodingTest1" 39 | &"test " 40 | --- 41 | 42 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 9] 43 | &"/41-PARAMETER_MULTI-json/specific/SpecialCharacters" 44 | &"\"ajkscbuiuXA34%%&&=" 45 | --- 46 | 47 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 10] 48 | &"/41-PARAMETER_MULTI-json/specific/boolean" 49 | &"true" 50 | --- 51 | 52 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 11] 53 | &"/41-PARAMETER_MULTI-json/specific/null" 54 | &"" 55 | --- 56 | 57 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 12] 58 | &"/41-PARAMETER_MULTI-json/specific/number1" 59 | &"123" 60 | --- 61 | 62 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 13] 63 | &"/41-PARAMETER_MULTI-json/specific/number2" 64 | &"123.456" 65 | --- 66 | 67 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 14] 68 | &"/41-PARAMETER_MULTI-json/tokens/0/service" 69 | &"service1" 70 | --- 71 | 72 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 15] 73 | &"/41-PARAMETER_MULTI-json/tokens/0/token" 74 | &"token1" 75 | --- 76 | 77 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 16] 78 | &"/41-PARAMETER_MULTI-json/tokens/1/service" 79 | &"service2" 80 | --- 81 | 82 | [TestIntegration_HandleRequestWithClients/41-PARAMETER_MULTI-json.json - 17] 83 | &"/41-PARAMETER_MULTI-json/tokens/1/token" 84 | &"token2" 85 | --- 86 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 1] 3 | &"/42-PARAMETER_MULTI-yaml/apiKey" 4 | &"sk-1234567890abcdef" 5 | --- 6 | 7 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 2] 8 | &"/42-PARAMETER_MULTI-yaml/database/host" 9 | &"db.example.com" 10 | --- 11 | 12 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 3] 13 | &"/42-PARAMETER_MULTI-yaml/database/password" 14 | &"P@ssw0rd!" 15 | --- 16 | 17 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 4] 18 | &"/42-PARAMETER_MULTI-yaml/database/user" 19 | &"admin" 20 | --- 21 | 22 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 5] 23 | &"/42-PARAMETER_MULTI-yaml/someOtherKey" 24 | &"base64:aGFsbG8gd2VsdAo=" 25 | --- 26 | 27 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 6] 28 | &"/42-PARAMETER_MULTI-yaml/specific/HTMLEncodingTest2" 29 | &"&" 30 | --- 31 | 32 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 7] 33 | &"/42-PARAMETER_MULTI-yaml/specific/boolean" 34 | &"true" 35 | --- 36 | 37 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 8] 38 | &"/42-PARAMETER_MULTI-yaml/specific/null" 39 | &"" 40 | --- 41 | 42 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 9] 43 | &"/42-PARAMETER_MULTI-yaml/specific/number2" 44 | &"123.456" 45 | --- 46 | 47 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 10] 48 | &"/42-PARAMETER_MULTI-yaml/specific/HTLMEncodingTest3" 49 | &"@" 50 | --- 51 | 52 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 11] 53 | &"/42-PARAMETER_MULTI-yaml/specific/HTMLEncodingTest1" 54 | &"test " 55 | --- 56 | 57 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 12] 58 | &"/42-PARAMETER_MULTI-yaml/specific/SpecialCharacters" 59 | &"\"ajkscbuiuXA34%%&&=" 60 | --- 61 | 62 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 13] 63 | &"/42-PARAMETER_MULTI-yaml/specific/number1" 64 | &"123" 65 | --- 66 | 67 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 14] 68 | &"/42-PARAMETER_MULTI-yaml/tokens/0/service" 69 | &"service1" 70 | --- 71 | 72 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 15] 73 | &"/42-PARAMETER_MULTI-yaml/tokens/0/token" 74 | &"token1" 75 | --- 76 | 77 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 16] 78 | &"/42-PARAMETER_MULTI-yaml/tokens/1/service" 79 | &"service2" 80 | --- 81 | 82 | [TestIntegration_HandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 17] 83 | &"/42-PARAMETER_MULTI-yaml/tokens/1/token" 84 | &"token2" 85 | --- 86 | -------------------------------------------------------------------------------- /.github/workflows/create-release.yml: -------------------------------------------------------------------------------- 1 | name: create-release 2 | 3 | on: 4 | workflow_dispatch: {} 5 | 6 | permissions: 7 | contents: write 8 | actions: write 9 | 10 | jobs: 11 | release: 12 | runs-on: ubuntu-latest 13 | env: 14 | CI: true 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: Setup Node 22 | uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6 23 | with: 24 | node-version: 'lts/*' 25 | 26 | - name: Install mise 27 | uses: jdx/mise-action@146a28175021df8ca24f8ee1828cc2a60f980bd5 # v3 28 | with: 29 | experimental: true 30 | 31 | - name: Install dependencies 32 | run: mise run install 33 | 34 | - name: Set git identity 35 | run: | 36 | git config user.name "github-actions[bot]" 37 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" 38 | 39 | - name: Create version and tag 40 | id: version 41 | run: | 42 | # Run commit-and-tag-version to bump version, update changelog, and create tag 43 | npx commit-and-tag-version 44 | 45 | # Get the new version tag 46 | NEW_TAG=$(git describe --tags --abbrev=0) 47 | echo "new_tag=$NEW_TAG" >> $GITHUB_OUTPUT 48 | 49 | # Extract changelog for this version 50 | # This extracts content between the first two version headers 51 | CHANGELOG_CONTENT=$(awk '/^## \[/{if(++count==2) exit; if(count==1) next} count==1' CHANGELOG.md) 52 | 53 | # Save changelog to file for multiline handling 54 | echo "$CHANGELOG_CONTENT" > /tmp/release_notes.md 55 | 56 | echo "Prepared release $NEW_TAG" 57 | 58 | - name: Push changes and tag 59 | env: 60 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 61 | run: | 62 | # Push the version bump commit and tag 63 | git push --follow-tags origin main 64 | 65 | - name: Create GitHub Release 66 | env: 67 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 68 | run: | 69 | # Create GitHub release using the changelog content 70 | gh release create ${{ steps.version.outputs.new_tag }} \ 71 | --title "${{ steps.version.outputs.new_tag }}" \ 72 | --notes-file /tmp/release_notes.md 73 | 74 | - name: Trigger release workflow on tag 75 | env: 76 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 77 | run: | 78 | # Manually trigger the 'release' workflow on the new tag ref 79 | # Using workflow_dispatch with ref to run on the tag 80 | gh workflow run release.yml --ref ${{ steps.version.outputs.new_tag }} 81 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 1] 3 | &"/43-PARAMETER_MULTI-dotenv/specific_number1" 4 | &"123" 5 | --- 6 | 7 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 2] 8 | &"/43-PARAMETER_MULTI-dotenv/tokens_1_service" 9 | &"service2" 10 | --- 11 | 12 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 3] 13 | &"/43-PARAMETER_MULTI-dotenv/apiKey" 14 | &"sk-1234567890abcdef" 15 | --- 16 | 17 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 4] 18 | &"/43-PARAMETER_MULTI-dotenv/database_password" 19 | &"P@ssw0rd!" 20 | --- 21 | 22 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 5] 23 | &"/43-PARAMETER_MULTI-dotenv/database_user" 24 | &"admin" 25 | --- 26 | 27 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 6] 28 | &"/43-PARAMETER_MULTI-dotenv/specific_HTLMEncodingTest3" 29 | &"@" 30 | --- 31 | 32 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 7] 33 | &"/43-PARAMETER_MULTI-dotenv/specific_HTMLEncodingTest1" 34 | &"test " 35 | --- 36 | 37 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 8] 38 | &"/43-PARAMETER_MULTI-dotenv/specific_HTMLEncodingTest2" 39 | &"&" 40 | --- 41 | 42 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 9] 43 | &"/43-PARAMETER_MULTI-dotenv/specific_null" 44 | &"" 45 | --- 46 | 47 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 10] 48 | &"/43-PARAMETER_MULTI-dotenv/specific_number2" 49 | &"123.456" 50 | --- 51 | 52 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 11] 53 | &"/43-PARAMETER_MULTI-dotenv/tokens_0_service" 54 | &"service1" 55 | --- 56 | 57 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 12] 58 | &"/43-PARAMETER_MULTI-dotenv/tokens_1_token" 59 | &"token2" 60 | --- 61 | 62 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 13] 63 | &"/43-PARAMETER_MULTI-dotenv/database_host" 64 | &"db.example.com" 65 | --- 66 | 67 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 14] 68 | &"/43-PARAMETER_MULTI-dotenv/someOtherKey" 69 | &"base64:aGFsbG8gd2VsdAo=" 70 | --- 71 | 72 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 15] 73 | &"/43-PARAMETER_MULTI-dotenv/specific_SpecialCharacters" 74 | &"\"ajkscbuiuXA34%%&&=" 75 | --- 76 | 77 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 16] 78 | &"/43-PARAMETER_MULTI-dotenv/specific_boolean" 79 | &"True" 80 | --- 81 | 82 | [TestIntegration_HandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 17] 83 | &"/43-PARAMETER_MULTI-dotenv/tokens_0_token" 84 | &"token1" 85 | --- 86 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/22-SECRET_BINARY-yaml.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/22-SECRET_BINARY-yaml.json - 1] 3 | &"22-SECRET_BINARY-yaml" 4 | []uint8{0x61, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x3a, 0x20, 0x73, 0x6b, 0x2d, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0xa, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x3a, 0xa, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x65, 0x72, 0x3a, 0x20, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3a, 0x20, 0x50, 0x40, 0x73, 0x73, 0x77, 0x30, 0x72, 0x64, 0x21, 0xa, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x64, 0x62, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0xa, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x3a, 0xa, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x3a, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x31, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x3a, 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x31, 0xa, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x3a, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x32, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x3a, 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0xa, 0x73, 0x6f, 0x6d, 0x65, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x3a, 0x20, 0x62, 0x61, 0x73, 0x65, 0x36, 0x34, 0x3a, 0x61, 0x47, 0x46, 0x73, 0x62, 0x47, 0x38, 0x67, 0x64, 0x32, 0x56, 0x73, 0x64, 0x41, 0x6f, 0x3d, 0xa, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x3a, 0xa, 0x20, 0x20, 0x20, 0x20, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x3a, 0x20, 0x27, 0x22, 0x61, 0x6a, 0x6b, 0x73, 0x63, 0x62, 0x75, 0x69, 0x75, 0x58, 0x41, 0x33, 0x34, 0x25, 0x25, 0x26, 0x26, 0x3d, 0x27, 0xa, 0x20, 0x20, 0x20, 0x20, 0x48, 0x54, 0x4d, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x73, 0x74, 0x31, 0x3a, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x3c, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x48, 0x54, 0x4d, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x73, 0x74, 0x32, 0x3a, 0x20, 0x27, 0x26, 0x27, 0xa, 0x20, 0x20, 0x20, 0x20, 0x48, 0x54, 0x4c, 0x4d, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x73, 0x74, 0x33, 0x3a, 0x20, 0x27, 0x40, 0x27, 0xa, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6e, 0x75, 0x6c, 0x6c, 0x22, 0x3a, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0xa, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0xa, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x31, 0x3a, 0x20, 0x31, 0x32, 0x33, 0xa, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x32, 0x3a, 0x20, 0x31, 0x32, 0x33, 0x2e, 0x34, 0x35, 0x36, 0xa} 5 | (*string)(nil) 6 | --- 7 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/23-SECRET_BINARY-dotenv.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/23-SECRET_BINARY-dotenv.json - 1] 3 | &"23-SECRET_BINARY-dotenv" 4 | []uint8{0x61, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x3d, 0x73, 0x6b, 0x2d, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0xa, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x3a, 0x68, 0x6f, 0x73, 0x74, 0x3d, 0x64, 0x62, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0xa, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x3a, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x3d, 0x50, 0x40, 0x73, 0x73, 0x77, 0x30, 0x72, 0x64, 0x21, 0xa, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x3a, 0x75, 0x73, 0x65, 0x72, 0x3d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0xa, 0x73, 0x6f, 0x6d, 0x65, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x3d, 0x62, 0x61, 0x73, 0x65, 0x36, 0x34, 0x3a, 0x61, 0x47, 0x46, 0x73, 0x62, 0x47, 0x38, 0x67, 0x64, 0x32, 0x56, 0x73, 0x64, 0x41, 0x6f, 0x3d, 0xa, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x3a, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x3d, 0x54, 0x72, 0x75, 0x65, 0xa, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x3a, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x3d, 0x22, 0x61, 0x6a, 0x6b, 0x73, 0x63, 0x62, 0x75, 0x69, 0x75, 0x58, 0x41, 0x33, 0x34, 0x25, 0x25, 0x26, 0x26, 0x3d, 0xa, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x3a, 0x48, 0x54, 0x4c, 0x4d, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x73, 0x74, 0x33, 0x3d, 0x40, 0xa, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x3a, 0x48, 0x54, 0x4d, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x73, 0x74, 0x31, 0x3d, 0x74, 0x65, 0x73, 0x74, 0x20, 0x3c, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x3e, 0xa, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x3a, 0x48, 0x54, 0x4d, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x73, 0x74, 0x32, 0x3d, 0x26, 0xa, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x3a, 0x6e, 0x75, 0x6c, 0x6c, 0x3d, 0xa, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x3a, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x31, 0x3d, 0x31, 0x32, 0x33, 0xa, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x3a, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x32, 0x3d, 0x31, 0x32, 0x33, 0x2e, 0x34, 0x35, 0x36, 0xa, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x3a, 0x30, 0x3a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x3d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x31, 0xa, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x3a, 0x30, 0x3a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x3d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x31, 0xa, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x3a, 0x31, 0x3a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x3d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x32, 0xa, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x3a, 0x31, 0x3a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x3d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0xa} 5 | (*string)(nil) 6 | --- 7 | -------------------------------------------------------------------------------- /lambda/internal/client/mock.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/aws/aws-sdk-go-v2/service/s3" 9 | "github.com/aws/aws-sdk-go-v2/service/secretsmanager" 10 | "github.com/aws/aws-sdk-go-v2/service/ssm" 11 | "github.com/gkampitakis/go-snaps/snaps" 12 | ) 13 | 14 | type mockErrorReader struct{} 15 | 16 | func (r *mockErrorReader) Read(p []byte) (n int, err error) { 17 | return 0, fmt.Errorf("mock read error") 18 | } 19 | 20 | // MockAwsClient is a mock for the real AWS client 21 | type MockAwsClient struct { 22 | t *testing.T 23 | snapsFileName string 24 | ReturnError error 25 | GetObjectRet *s3.GetObjectOutput 26 | GetObjectAttributesRet *s3.GetObjectAttributesOutput 27 | PutParameterRet *ssm.PutParameterOutput 28 | PutSecretValueRet *secretsmanager.PutSecretValueOutput 29 | } 30 | 31 | func (s *MockAwsClient) GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) { 32 | if s.ReturnError == nil { 33 | snaps.WithConfig(snaps.Filename(s.snapsFileName)).MatchSnapshot(s.t, params) 34 | } 35 | return s.GetObjectRet, s.ReturnError 36 | } 37 | 38 | func (s *MockAwsClient) GetObjectAttributes(ctx context.Context, params *s3.GetObjectAttributesInput, optFns ...func(*s3.Options)) (*s3.GetObjectAttributesOutput, error) { 39 | if s.ReturnError == nil { 40 | snaps.WithConfig(snaps.Filename(s.snapsFileName)).MatchSnapshot(s.t, params) 41 | } 42 | return s.GetObjectAttributesRet, s.ReturnError 43 | } 44 | 45 | func (s *MockAwsClient) PutParameter(ctx context.Context, params *ssm.PutParameterInput, optFns ...func(*ssm.Options)) (*ssm.PutParameterOutput, error) { 46 | if s.ReturnError == nil { 47 | snaps.WithConfig(snaps.Filename(s.snapsFileName)).MatchSnapshot(s.t, params) 48 | } 49 | return s.PutParameterRet, s.ReturnError 50 | } 51 | 52 | func (s *MockAwsClient) PutSecretValue(ctx context.Context, params *secretsmanager.PutSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.PutSecretValueOutput, error) { 53 | if s.ReturnError == nil { 54 | snaps.WithConfig(snaps.Filename(s.snapsFileName)).MatchSnapshot(s.t, params) 55 | } 56 | return s.PutSecretValueRet, s.ReturnError 57 | } 58 | 59 | // MockClient is a mock for "our" AWS client 60 | type MockClient struct { 61 | S3Content []byte 62 | S3Etag string 63 | S3Err error 64 | S3EtagErr error 65 | } 66 | 67 | func (m *MockClient) S3GetObject(file SopsS3File) ([]byte, error) { 68 | return m.S3Content, m.S3Err 69 | } 70 | 71 | func (m *MockClient) S3GetObjectETAG(file SopsS3File) (*string, error) { 72 | return &m.S3Etag, m.S3EtagErr 73 | } 74 | 75 | func (m *MockClient) SecretsManagerPutSecretValue(sopsHash string, secretArn string, secretContent *[]byte) (*secretsmanager.PutSecretValueOutput, error) { 76 | return nil, nil 77 | } 78 | 79 | func (m *MockClient) SsmPutParameter(parameterName string, parameterContent *[]byte, keyId string) (*ssm.PutParameterOutput, error) { 80 | return nil, nil 81 | } 82 | -------------------------------------------------------------------------------- /test-secrets/json/sopsfile-complex.enc-age.json: -------------------------------------------------------------------------------- 1 | { 2 | "some": { 3 | "deep": { 4 | "nested": { 5 | "object": "ENC[AES256_GCM,data:KqcODVlKoN+h,iv:0cIx2pZoTzfGq6JaSD72yVYLbzThijiEyDCnle7m8Bo=,tag:m4HgUT9//lEjgxxWKcwTlQ==,type:str]", 6 | "arrays": [ 7 | "ENC[AES256_GCM,data:cwdZGg==,iv:nHtR+SK2qPPwvkWIuLSQR0NOy7vm53KXHg8A7WOjVyM=,tag:EW/JaUnr3n43H3ZNC4AAbA==,type:str]", 8 | "ENC[AES256_GCM,data:zqWmSAyFuw==,iv:M/0wJ2xjwWdlNJ4XSMOHpUT6bPlh21jl3inR19qj8qc=,tag:60Hb9dVqXphkHl48cU+skA==,type:str]", 9 | { 10 | "values": { 11 | "and": "ENC[AES256_GCM,data:9q+bY2/Fxg==,iv:oGUlni+NO9BE1FSvVhp3mGs64DhNErVES3tMVY1geFU=,tag:9a//E2BWpBnzJNBkshNVPQ==,type:str]" 12 | } 13 | } 14 | ] 15 | } 16 | }, 17 | "notsodeep": "ENC[AES256_GCM,data:sSIRj3Fd,iv:hvi5Yygsf+czctpvuvAItgioIIfGBhFuvse9CsF7BVM=,tag:drZ9yZCsQZ+N9f78gYe/sg==,type:str]" 18 | }, 19 | "with": { 20 | "TestValue": "ENC[AES256_GCM,data:bto965a3UQy4xhGhrnLHp6UOlLVY,iv:jfyb6zm1S4DxsEDnwNx17PjpNoLoM1GjWycNAYLIiTo=,tag:p5NKNN/4gfPK4wbxalrMgg==,type:str]", 21 | "TestValue2": "ENC[AES256_GCM,data:2A==,iv:5pptPCrBWBdZ2dyEE7fb53kdF9AXSBO3U2zQJ36SEFo=,tag:R4p7PdwCQVJzkFtpHcWzyA==,type:str]", 22 | "TestValue3": "ENC[AES256_GCM,data:pg==,iv:qAzebSlwqDH75A4ptv0b5u2nX01Q+K+I22lyHXfOGg8=,tag:TS/dSzzkIXsoNyyqfu9dbw==,type:str]" 23 | }, 24 | "and now": { 25 | "some": [ 26 | { 27 | "basic": "ENC[AES256_GCM,data:LLfe+cU=,iv:KK/akV0TnHIKxi+ZVRRuqGW9/BZeaVq+bVoWjbw8V94=,tag:fZKNID18dnK+TTcUC02iBQ==,type:bool]" 28 | }, 29 | { 30 | "nested": "ENC[AES256_GCM,data:94TT9qM=,iv:suWnfGAoEJ8oRbh6guaX5ZmaBJii9k9s+1Rktn3PeUQ=,tag:eAgf5Id1PiU2nVo3Q+h67w==,type:float]" 31 | }, 32 | { 33 | "type": "ENC[AES256_GCM,data:9MGS4J8e,iv:LIza4w0iCTY13SBtIkL/zXXvcot1NRkhQoXQCO6YI1E=,tag:U3tdtG7sCOgJuX0HNwd6Bw==,type:float]" 34 | }, 35 | { 36 | "tests": "ENC[AES256_GCM,data:ZywWxDM6mQ==,iv:cbJtn9JN7UvB7qcvu6/x+BAE6IKZR28ec1295tvbo3g=,tag:9T8Q3PiisNnwKbJyGXsI/w==,type:str]" 37 | } 38 | ] 39 | }, 40 | "sops": { 41 | "kms": null, 42 | "gcp_kms": null, 43 | "azure_kv": null, 44 | "hc_vault": null, 45 | "age": [ 46 | { 47 | "recipient": "age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu", 48 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3RzBneS9sWnFmWnl3NDBO\nV0FFQ3lhYzZTZmQ1U3dmbUgrR2dUaTEybzJZCmxyaEhQT2hLalM5WWlXMjVCdWpS\nWHI5dUE5MjhyM2VlWjRWY1FMd3Zub0EKLS0tIGFiZlhrMTlJY1MrMXJGMS9xbUVQ\nY2d1czhGOGdsdzIzVjNsSDNRWE93bWMKTBl/gzy3HxvBfdwBkTQ8x0zaAHflC+DU\nDZu42K3OgMSJ16s7Abbk7Bp5uzUx+UEIJB+Fb5IzVIFAvovFq2fhNA==\n-----END AGE ENCRYPTED FILE-----\n" 49 | } 50 | ], 51 | "lastmodified": "2025-02-07T10:08:49Z", 52 | "mac": "ENC[AES256_GCM,data:HYmEvL9BQFp8AQNzRP6QoY8/7lsYpceP5qCPmgL/SCokgVqazV7BJp58b/6eHI5yUSxBOsvtqse0fo7R4POoOEqzVh6oSLGfT7hB0sXjn9G34MQCz6aQZiOyz41uV6/tzsOoivH+/xSLV0I5sosY8jHxa6HdD/d3V7ke+wJOSXs=,iv:VEYU+FMfdAZMha1ny9rD7GeimMyiB/E6uM3mXpaYvVU=,tag:mGwHGZFLem7Xz0dDzj0rvQ==,type:str]", 53 | "pgp": null, 54 | "unencrypted_suffix": "_unencrypted", 55 | "version": "3.9.4" 56 | } 57 | } -------------------------------------------------------------------------------- /lambda/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log/slog" 7 | 8 | "github.com/aws/aws-lambda-go/cfn" 9 | runtime "github.com/aws/aws-lambda-go/lambda" 10 | "github.com/markussiebert/cdk-sops-secrets/internal/client" 11 | "github.com/markussiebert/cdk-sops-secrets/internal/event" 12 | ) 13 | 14 | func HandleRequestWithClients(clients client.AwsClient, e cfn.Event) (physicalResourceID string, data map[string]interface{}, err error) { 15 | logger := slog.With("Package", "main", "Function", "HandleRequestWithClients") 16 | logger.Debug("Incoming Event", "Event", e) 17 | 18 | // If it's a delete request, we don't have to do anything 19 | if e.RequestType == cfn.RequestDelete { 20 | return event.GenerateTempPhysicalResourceId(), nil, nil 21 | } 22 | // We have to run this code only, if it is a CloudFormation Create or Update request 23 | if e.RequestType != cfn.RequestCreate && e.RequestType != cfn.RequestUpdate { 24 | return event.GenerateTempPhysicalResourceId(), nil, fmt.Errorf("requestType '%s' not supported", e.RequestType) 25 | } 26 | 27 | // Get the event input from the cloudformation event 28 | props, err := event.FromCfnEvent(e) 29 | if err != nil { 30 | return "", nil, err 31 | } 32 | 33 | // Get the encrypted secret input provided by the user 34 | secretEncrypted, secretEncryptedErr := props.GetEncryptedSopsSecret(clients) 35 | if secretEncryptedErr != nil { 36 | return props.GeneratePhysicalResourceId(), nil, secretEncryptedErr 37 | } 38 | 39 | // Decrypt the secret input with sops 40 | secretDecrypted, secretDecryptedErr := secretEncrypted.Decrypt() 41 | if secretDecryptedErr != nil { 42 | return props.GeneratePhysicalResourceId(), nil, secretDecryptedErr 43 | } 44 | 45 | // Generate a data object by parsing the decrypted secret depending on the data input type 46 | secretDecryptedData, secretDecryptedDataErr := secretDecrypted.ToData() 47 | if secretDecryptedDataErr != nil { 48 | return props.GeneratePhysicalResourceId(), nil, secretDecryptedDataErr 49 | } 50 | 51 | baseProps := BaseProps{ 52 | properties: props, 53 | clients: clients, 54 | secretDecryptedData: secretDecryptedData, 55 | } 56 | 57 | // Fill the secret values in the ressource depending on the ressource type 58 | switch props.ResourceType { 59 | case event.SECRET, event.SECRET_RAW, event.SECRET_BINARY: 60 | return handleSecret(baseProps) 61 | case event.PARAMETER_MULTI: 62 | return handleParameterMulti(baseProps) 63 | case event.PARAMETER: 64 | return handleParameter(baseProps) 65 | default: 66 | return props.GeneratePhysicalResourceId(), nil, fmt.Errorf("unsupported resource type %s", props.ResourceType) 67 | } 68 | } 69 | 70 | // Just a Wrapper function to allow injecting clients for testing 71 | func HandleRequest(ctx context.Context, event cfn.Event) (physicalResourceID string, data map[string]interface{}, err error) { 72 | 73 | clients := client.CreateAwsClients(ctx) 74 | 75 | return HandleRequestWithClients(clients, event) 76 | } 77 | 78 | func main() { 79 | runtime.Start(cfn.LambdaWrap(HandleRequest)) 80 | } 81 | -------------------------------------------------------------------------------- /test-secrets/testsecret.sops.env: -------------------------------------------------------------------------------- 1 | apiKey=ENC[AES256_GCM,data:yMjVmvg8ngplsw6A2Ed/L9xrFg==,iv:CqIREmvd4vtMmXR0sKFW4BzZVa9g0rTj558RfwBAyp8=,tag:uDdkA/HTENFIaFzqlHUTdw==,type:str] 2 | database:host=ENC[AES256_GCM,data:NQGBJcEmb3Mk96XLPKs=,iv:lhkq8kcH/axV+wR8d/qcXD0isSn+BAFT+j4gD1kCqVc=,tag:WK9Cd9xQo20C726TYNBwbw==,type:str] 3 | database:password=ENC[AES256_GCM,data:o6Cw6lRCKwW3,iv:ab1xLcrf9XWrjXAddW/vFhzOQHqbOyRiMT1WDZ2E94U=,tag:xay4qqpXVFG5SAXswtKuWg==,type:str] 4 | database:user=ENC[AES256_GCM,data:YakZvFU=,iv:/UkM6EE2v9miMngg5hrFW+TNC6O9SHgJvpEpbBS4iZk=,tag:cQ7BgN/rlfppBaI2eXNB4A==,type:str] 5 | someOtherKey=ENC[AES256_GCM,data:jbrUQ2ZN3G+2ms1ktUbB4n4GKMhzs1Q=,iv:nxBr7hYaKWIQ+w2R9ydqRgE8WY3wWJYHRLEML4oXvCE=,tag:h8xX0ENw30JvPUBVbn3RcA==,type:str] 6 | specific:boolean=ENC[AES256_GCM,data:6hJZpA==,iv:Jn7vcGhG/KHuIvnWtLm4KutBcXOMGGa03VexDEfoELw=,tag:yVYbrdDc8lZQcr5M90hLpA==,type:str] 7 | specific:SpecialCharacters=ENC[AES256_GCM,data:iVnV8R4wUo4Hmj+hNBgVhqsK/Q==,iv:2qnJFP07q+bidV6QHva8xg/6Kut01vySqoUbrJiVdd4=,tag:ODPHGLs3yU9BKAXjvH8aaQ==,type:str] 8 | specific:HTLMEncodingTest3=ENC[AES256_GCM,data:Og==,iv:OsijRbaUtX2fNNzsI02Bm81Bx9ZpMCLl9oEYfEgoygM=,tag:W5yrTTCt8xpEyDWpmCrSbg==,type:str] 9 | specific:HTMLEncodingTest1=ENC[AES256_GCM,data:Gtc5yOSZrpRQZRWpbFP9QCk52Dpq,iv:zs/FMO5S92w/DOmT/RhEuWQ4WXDtHYWmglyp8bmW6SQ=,tag:4nQCdkMJLnRK5RAWTmbNVA==,type:str] 10 | specific:HTMLEncodingTest2=ENC[AES256_GCM,data:Kg==,iv:RQRy8LWFvoKd4UaWSIKL/5js6I8Wvi+r+T7uF/ox+rA=,tag:TKMdUm/83MkBnP9VtPAHWg==,type:str] 11 | specific:null= 12 | specific:number1=ENC[AES256_GCM,data:236O,iv:/X1jDyMOvcqfve673BjCP5r1zjj0h1KtVjMD+4rbIZI=,tag:rWBT0Rs5hhlKBTyvNwBqiw==,type:str] 13 | specific:number2=ENC[AES256_GCM,data:iIqKlmwRCA==,iv:usF+X0flWIg9Ki7O18K01x8m/WT/kQd/aVWBSdXabww=,tag:BPHFXTeq7qru6OWG3DfUsQ==,type:str] 14 | tokens:0:service=ENC[AES256_GCM,data:MlgbuTr1d4s=,iv:e/YjEvEtEhMQdu4obTW3BzkStSk843krefETISmxxVw=,tag:cg4E2clFJ5GWUc+iFm78Tw==,type:str] 15 | tokens:0:token=ENC[AES256_GCM,data:Kde9qHN8,iv:HWGei+RhxWfEY+YgKT75QfwWh1piE1UJ8q4lVc4Gu7M=,tag:DLYPwmYlhRlJK6COxd9waw==,type:str] 16 | tokens:1:service=ENC[AES256_GCM,data:d0AWlJz+nt4=,iv:BQzKHbBDTlR+8z1LTkhBLLo/7vAcOjLTfB/9qR7MdGg=,tag:s2NeOPlR1IwZiUnCs4E1eg==,type:str] 17 | tokens:1:token=ENC[AES256_GCM,data:G4vIDVtl,iv:ihV/tjPSwSazsc/Df+dXpnepwSkP6n1KccDG8GKWRMg=,tag:RDl9c3P9kok1zWWUbifEcA==,type:str] 18 | sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRcTUrRWtwSHdPOTNGVXVp\nemJQWlY4aXg0ZEVSSGxsUzl4MW9ydlVYR2lBCkFDdkl4bjVVYWpja3FObC9nVFVK\nMks5ck5mSXI4QUk5R1R0c2JCVC9ZK0EKLS0tIERNODdLaWJmZ1R3NFZET3F1YVMy\nRitBcEVhNGt6UUNlc2szZUJjd1RTT1kKAYk+D7gqF+prdNAF7OdOHima/A92Njpw\nqh57Rk5C7NXvVeeJk0xKdS2zFm86IIST/KgSw7QK80IUx8iGCHf6kg==\n-----END AGE ENCRYPTED FILE-----\n 19 | sops_age__list_0__map_recipient=age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu 20 | sops_lastmodified=2025-02-07T14:46:04Z 21 | sops_mac=ENC[AES256_GCM,data:vmJOTGI56UNeTSU5f3OFQ2Kqgl6N51TKHpCZ2eGD9W0sBzvri4usD9cOiiOsr1cOIVk9tjfdpjcs8/SMVLPY2D4nMfBdUO6e32tNo3V0DpR1BUPYcUykPogDF4psrJ3h1WG8+n1Y1KgVDo0+RawHp4WxeBi8/myxFvE+m067rso=,iv:MeiPPTwepzHU0rrtiVpOIngVMrGO/LGXu/5FDKe+k6A=,tag:vYSKk1/g7TPaNrninFx1Gg==,type:str] 22 | sops_unencrypted_suffix=_unencrypted 23 | sops_version=3.9.4 24 | -------------------------------------------------------------------------------- /lambda/internal/sops/sops.go: -------------------------------------------------------------------------------- 1 | package sops 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "log/slog" 8 | 9 | "github.com/getsops/sops/v3/decrypt" 10 | "github.com/markussiebert/cdk-sops-secrets/internal/data" 11 | ) 12 | 13 | type Format string 14 | 15 | const ( 16 | JSON Format = "json" 17 | YAML Format = "yaml" 18 | DOTENV Format = "dotenv" 19 | BINARY Format = "binary" 20 | ) 21 | 22 | type EncryptedSopsSecret struct { 23 | Content []byte 24 | Format Format 25 | Hash string 26 | } 27 | 28 | func CreateEncryptedSopsSecret(content []byte, format Format, hash string) (*EncryptedSopsSecret, error) { 29 | if len(content) == 0 { 30 | return nil, fmt.Errorf("content cannot be empty") 31 | } 32 | if format == "" { 33 | return nil, fmt.Errorf("format cannot be empty") 34 | } 35 | if hash == "" { 36 | return nil, fmt.Errorf("hash cannot be empty") 37 | } 38 | return &EncryptedSopsSecret{ 39 | Content: content, 40 | Format: format, 41 | Hash: hash, 42 | }, nil 43 | } 44 | 45 | type DecryptedSopsSecret struct { 46 | content []byte 47 | format Format 48 | hash string 49 | } 50 | 51 | func (e EncryptedSopsSecret) Decrypt() (*DecryptedSopsSecret, error) { 52 | // Create a logger with context information 53 | logger := slog.With("Package", "sops", "Function", "Decrypt") 54 | logger.Info("Decrypting content", "Format", e.Format) 55 | 56 | // Decrypt the content using the specified format 57 | cleartext, err := decrypt.Data(e.Content, string(e.Format)) 58 | if err != nil { 59 | return nil, fmt.Errorf("decryption error:\n%v", err) 60 | } 61 | logger.Info("Decryption successful") 62 | 63 | // If the format is JSON, process the cleartext to ensure proper formatting 64 | if e.Format == JSON { 65 | var jsonObj interface{} 66 | var buf bytes.Buffer 67 | 68 | // Unmarshal the JSON content into an interface 69 | err := json.Unmarshal(cleartext, &jsonObj) 70 | if err != nil { 71 | return nil, fmt.Errorf("decoding error:\n%v", err) 72 | } 73 | 74 | // Create a JSON encoder with specific settings 75 | encoder := json.NewEncoder(&buf) 76 | encoder.SetEscapeHTML(false) 77 | encoder.SetIndent("", " ") // tab inside 78 | 79 | // Encode the JSON object back into bytes 80 | err = encoder.Encode(jsonObj) 81 | if err != nil { 82 | return nil, fmt.Errorf("encoding error:\n%v", err) 83 | } 84 | 85 | // Trim any extra whitespace from the encoded JSON 86 | cleartext = bytes.TrimSpace(buf.Bytes()) 87 | } 88 | 89 | return &DecryptedSopsSecret{ 90 | content: cleartext, 91 | format: e.Format, 92 | hash: e.Hash, 93 | }, nil 94 | } 95 | 96 | func (d DecryptedSopsSecret) ToData() (*data.Data, error) { 97 | // Generate a data object by parsing the decrypted secret depending on the data input type 98 | switch d.format { 99 | case JSON: 100 | return data.FromJSON(d.content, &d.hash) 101 | case YAML: 102 | return data.FromYAML(d.content, &d.hash) 103 | case DOTENV: 104 | return data.FromDotEnv(d.content, &d.hash) 105 | case BINARY: 106 | return data.FromBinary(d.content, &d.hash) 107 | default: 108 | return nil, fmt.Errorf("unsupported format %s", d.format) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestIntegration_HandleRequestWithClients/21-SECRET_BINARY-json.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestIntegration_HandleRequestWithClients/21-SECRET_BINARY-json.json - 1] 3 | &"21-SECRET_BINARY-json" 4 | []uint8{0x7b, 0xa, 0x9, 0x22, 0x61, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x22, 0x3a, 0x20, 0x22, 0x73, 0x6b, 0x2d, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x22, 0x2c, 0xa, 0x9, 0x22, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x22, 0x3a, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x22, 0x68, 0x6f, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x64, 0x62, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x22, 0x2c, 0xa, 0x9, 0x9, 0x22, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3a, 0x20, 0x22, 0x50, 0x40, 0x73, 0x73, 0x77, 0x30, 0x72, 0x64, 0x21, 0x22, 0x2c, 0xa, 0x9, 0x9, 0x22, 0x75, 0x73, 0x65, 0x72, 0x22, 0x3a, 0x20, 0x22, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x22, 0xa, 0x9, 0x7d, 0x2c, 0xa, 0x9, 0x22, 0x73, 0x6f, 0x6d, 0x65, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x22, 0x3a, 0x20, 0x22, 0x62, 0x61, 0x73, 0x65, 0x36, 0x34, 0x3a, 0x61, 0x47, 0x46, 0x73, 0x62, 0x47, 0x38, 0x67, 0x64, 0x32, 0x56, 0x73, 0x64, 0x41, 0x6f, 0x3d, 0x22, 0x2c, 0xa, 0x9, 0x22, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x22, 0x3a, 0x20, 0x7b, 0xa, 0x9, 0x9, 0x22, 0x48, 0x54, 0x4c, 0x4d, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x73, 0x74, 0x33, 0x22, 0x3a, 0x20, 0x22, 0x40, 0x22, 0x2c, 0xa, 0x9, 0x9, 0x22, 0x48, 0x54, 0x4d, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x73, 0x74, 0x31, 0x22, 0x3a, 0x20, 0x22, 0x74, 0x65, 0x73, 0x74, 0x20, 0x3c, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x3e, 0x22, 0x2c, 0xa, 0x9, 0x9, 0x22, 0x48, 0x54, 0x4d, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x73, 0x74, 0x32, 0x22, 0x3a, 0x20, 0x22, 0x26, 0x22, 0x2c, 0xa, 0x9, 0x9, 0x22, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x5c, 0x22, 0x61, 0x6a, 0x6b, 0x73, 0x63, 0x62, 0x75, 0x69, 0x75, 0x58, 0x41, 0x33, 0x34, 0x25, 0x25, 0x26, 0x26, 0x3d, 0x22, 0x2c, 0xa, 0x9, 0x9, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x22, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0xa, 0x9, 0x9, 0x22, 0x6e, 0x75, 0x6c, 0x6c, 0x22, 0x3a, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0xa, 0x9, 0x9, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x31, 0x22, 0x3a, 0x20, 0x31, 0x32, 0x33, 0x2c, 0xa, 0x9, 0x9, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x32, 0x22, 0x3a, 0x20, 0x31, 0x32, 0x33, 0x2e, 0x34, 0x35, 0x36, 0xa, 0x9, 0x7d, 0x2c, 0xa, 0x9, 0x22, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x22, 0x3a, 0x20, 0x5b, 0xa, 0x9, 0x9, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x22, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x31, 0x22, 0x2c, 0xa, 0x9, 0x9, 0x9, 0x22, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x31, 0x22, 0xa, 0x9, 0x9, 0x7d, 0x2c, 0xa, 0x9, 0x9, 0x7b, 0xa, 0x9, 0x9, 0x9, 0x22, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x32, 0x22, 0x2c, 0xa, 0x9, 0x9, 0x9, 0x22, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0x22, 0xa, 0x9, 0x9, 0x7d, 0xa, 0x9, 0x5d, 0xa, 0x7d} 5 | (*string)(nil) 6 | --- 7 | -------------------------------------------------------------------------------- /test-secrets/testsecret.sops.yaml: -------------------------------------------------------------------------------- 1 | apiKey: ENC[AES256_GCM,data:r6YfrwGkVFxBF8mHEcsPaxMtUQ==,iv:/8rr4K5mBxLi3jR2lHfeeyVO/wQY00pc//Ow5vqY17Q=,tag:+dgLaKeSxNNBuOEFIftOZw==,type:str] 2 | database: 3 | user: ENC[AES256_GCM,data:sdsUyvo=,iv:IVioJCVyROehAtG6xbmAZzd52HQvk0eZItMBjBAvqx8=,tag:0pumzdyH5wiEArMmmra3UQ==,type:str] 4 | password: ENC[AES256_GCM,data:CSQ8jFrxnLQY,iv:hXLENmG8qLGsGdWv3wbrj5Lf2zbF1ToNIwS4y1uWEXg=,tag:8QDCMyA69dYl58xIuP1DcA==,type:str] 5 | host: ENC[AES256_GCM,data:xfU2eC+/+uvCkHY8QHY=,iv:ULWdvoa6r8F1g2+ZF3xPwak1EdhDWmndsO5RUxAVPE8=,tag:KGlJG1IMSWn4nEtMaOSsdg==,type:str] 6 | tokens: 7 | - service: ENC[AES256_GCM,data:tRSUpGKIEmc=,iv:hi/38iC/Glc4nxUkRYBV2cy946k+azXFtEdJONP1g/w=,tag:GQ0/WEhteyqCpsEv/SFrbA==,type:str] 8 | token: ENC[AES256_GCM,data:CGnSO4HE,iv:LH0xVWNTbBmHPQ1OiP5+uRFfG/E7K7OGYqKHXOKje8Q=,tag:sOYhgKKV1ogOs7PJrBrsjw==,type:str] 9 | - service: ENC[AES256_GCM,data:ohqXvZ/7HWw=,iv:CpjUdTbGL4TGRIWtEJxZ8923tw/Qomw+kCtiHuFiGQc=,tag:95TQUa3NSW1qGT8LNsEgbQ==,type:str] 10 | token: ENC[AES256_GCM,data:I0I/XgWl,iv:87E/c7DiGKT22tddCFqBuPOC736yv5YD9M/+g3PiZX4=,tag:Z1prIHfH39m2U6i2KeV22Q==,type:str] 11 | someOtherKey: ENC[AES256_GCM,data:HSMFwMXHni18tTOPw2DAE0g5XQR9oUw=,iv:4xTRO8TYhbla01gzathf7UBHh8Gthd97GIxX/0kCXwg=,tag:4psEFW3O2RqqYN68U23Pxw==,type:str] 12 | specific: 13 | SpecialCharacters: ENC[AES256_GCM,data:E/jqkPvUSbhZPbm0vrCjX1soLA==,iv:Rto0EyQT2ozFmk1tvk3xRDwVnNprA5xjpCNGTZieb0A=,tag:pRvhsXnVrb2qGG1ocu5mKw==,type:str] 14 | HTMLEncodingTest1: ENC[AES256_GCM,data:Zdc5vZOBIQR8mclrPytvmyg4HQeD,iv:WGzS0+LSP9XaFeqh1aZmmgO54ZDn8Tf2YIOiwmJHp0I=,tag:ZdA/xX72JzBYxGVcV2a2RA==,type:str] 15 | HTMLEncodingTest2: ENC[AES256_GCM,data:Xw==,iv:blYJXwT+wErgVswaCW8oWYnny88oL1yvFx8e3JcE09Q=,tag:+YUy02xaJhqqatBtkj972A==,type:str] 16 | HTLMEncodingTest3: ENC[AES256_GCM,data:/w==,iv:/4eSVtFQ7cGoNI7UOMw8ly0+aiX7WivU4ihMlVKUD5k=,tag:cLSO8CR0IM+Y5LZfdM43BQ==,type:str] 17 | "null": null 18 | boolean: ENC[AES256_GCM,data:D1iaUA==,iv:SurUABhqM+UYiCZaEmjyGKZai2jDJL3nnZJ41tMNHBQ=,tag:gewFlccUdb3IyX7kP+SP3w==,type:bool] 19 | number1: ENC[AES256_GCM,data:McBM,iv:VjYEpdVgLsjXGm2VRD9Lu0BXyOMFjkUMQIjvuVod5cs=,tag:7EfnK0qcITRg1Obriu7Egg==,type:int] 20 | number2: ENC[AES256_GCM,data:wLcu0XjyJg==,iv:tZT/AMYMToZi5nuo6jUxwB01HPX4HhHpdt6v7uzZ9SE=,tag:fgK4Gfi5YBA1LupJBX2sBA==,type:float] 21 | sops: 22 | kms: [] 23 | gcp_kms: [] 24 | azure_kv: [] 25 | hc_vault: [] 26 | age: 27 | - recipient: age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu 28 | enc: | 29 | -----BEGIN AGE ENCRYPTED FILE----- 30 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYc3F6ZFY4RFllbStpMzEz 31 | WmxuNzduMytjRkh1Y24xMUNoVndCYUNhaGtJCjJwZ1AzZlBVcVd6Y2NFbGR4MmVF 32 | bW9OWWh2bmRjaFRrOFoyeWVWbnJTeWMKLS0tIExyVDNFRXpFTEpRMzYvWG1PaWNz 33 | TW96d0tqajBLc01paVlEZHFXR1F2bXcKLIJg7yTm0uCiiPiKsnsAyRM/aDZ/fNvw 34 | iilKTB5wP1qds7StcVLJ18RGUo9vuAA9iP0NZCP6kKLVgZbcJ/aSdw== 35 | -----END AGE ENCRYPTED FILE----- 36 | lastmodified: "2025-02-07T14:47:09Z" 37 | mac: ENC[AES256_GCM,data:Sx2D6SF+pQHQ+oux7lNexshPodcge3w2g6ixLTjK/NkEgyBMmgsVyBGWMl+kSk6PY58HT4AQpyBtuiUKj3jSJ4pZmepgWIAdxKpB3slqy38gghYP2/AHSbHYRzhLQIOxxDO0inUwroHa13HttJCzDfXKNiKrbMRAQKk+FWF6x3w=,iv:7sBjShVWrW177wZBv+rxW6eqK0VQcjjGWVtHw9dbGTY=,tag:Bo3oYFgbATbNVwDtP2RAaw==,type:str] 38 | pgp: [] 39 | unencrypted_suffix: _unencrypted 40 | version: 3.9.4 41 | -------------------------------------------------------------------------------- /test-secrets/testsecret.sops.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiKey": "ENC[AES256_GCM,data:lLbXVEhBRKXiGGkBh5KltgUBoA==,iv:luayJniiXHwA3Z0rZyxeF00xlHfMwrYMiYh+ahP6Ayo=,tag:l3k3pSnzRTAKeAHOxw+fGw==,type:str]", 3 | "database": { 4 | "user": "ENC[AES256_GCM,data:kQtkQas=,iv:+qyJ9FUXoMtcpcfdT7ZTONHf3SsGbAfJznJFt5e/pVE=,tag:2CinmzVeqn654MfYdhkqxg==,type:str]", 5 | "password": "ENC[AES256_GCM,data:jHwCEJh02A8s,iv:imwHW4U7DV09m5NCzO5k0AuRZdY/TK2+F28VbWFH6yg=,tag:pcQ3h40D9AvBFQEniRxMow==,type:str]", 6 | "host": "ENC[AES256_GCM,data:U3Jx/GkByKLPnebsp5U=,iv:cCuW99ktyAI4dVQzDt1/jsgLP5pMdYYCsi6AhqsgVXs=,tag:v16zyYu5A1TVBP4baOxB1g==,type:str]" 7 | }, 8 | "tokens": [ 9 | { 10 | "service": "ENC[AES256_GCM,data:MLQW8tocgCc=,iv:+/foQyR7q0hvbOkGivauUAWdihfoHKzwgSYdv1Gq29c=,tag:O2ebmT2u9NfVqoo0q4J97w==,type:str]", 11 | "token": "ENC[AES256_GCM,data:pcrFQQDk,iv:+q/3i7niTwn/jyOqeHJ8hUVA4jN8CqPXufWsIRkkpJA=,tag:buIs8G4OfiuJPf108/sDhQ==,type:str]" 12 | }, 13 | { 14 | "service": "ENC[AES256_GCM,data:gyRnIigPXh8=,iv:oTNrXTZKX77yynvcQwhzXio2rEJXsIBMUD6DOCRr4fA=,tag:2OchZmbqdcHdljC6CKkulA==,type:str]", 15 | "token": "ENC[AES256_GCM,data:EwGCYT3k,iv:1HMzUDqmXrI3bhGaxknMftwEZ8Cc1szxaMV6A164mfc=,tag:0b5eRwxZD4j9ujixTjfFZg==,type:str]" 16 | } 17 | ], 18 | "someOtherKey": "ENC[AES256_GCM,data:vVlp3EE1mgTQP9JSDGdPf/+h2FSqajY=,iv:xYGIoTYPEn+4ax2LTKixlQb5NaotVw+0QVfFSm9QNWo=,tag:+U4JQbm/8mFWAmlMryg7LA==,type:str]", 19 | "specific": { 20 | "SpecialCharacters": "ENC[AES256_GCM,data:uPqFOSjUMgVdroUBIM6qernOsA==,iv:rSScNgl3g66+ITAYYjjyU2HzBWYDy8lBcCGGXFvHIgo=,tag:JEw3yYdiLK+F+MCyWcth6w==,type:str]", 21 | "HTMLEncodingTest1": "ENC[AES256_GCM,data:ksCu3G0rG2Nf5+c/9vSs0aol+xnv,iv:7+PJt3Q91CHmvRHqDIbApSMaYLoZq3D1/BnaA3CfsMw=,tag:S/At2clSsGAvHSkjLwetqQ==,type:str]", 22 | "HTMLEncodingTest2": "ENC[AES256_GCM,data:Zw==,iv:jH3RkRJsMDL+gPPzpLv1gPX/TLy9U1zSPqIpF97wTb4=,tag:3Xaf7JWYYxIQQR88eb+FDg==,type:str]", 23 | "HTLMEncodingTest3": "ENC[AES256_GCM,data:UA==,iv:ZYqChMLBfQoKHclu94sbcI98sVGvymM37Esshvz0Iuw=,tag:UV1zFimcgETD0ncTjpgDvg==,type:str]", 24 | "null": null, 25 | "boolean": "ENC[AES256_GCM,data:w2sY/A==,iv:K+2AL8Da70+1mBVzuq2eOgzkEFwembEcyX8HXKIAqR8=,tag:KYCw3m6PT8rcu1i/mGZJ5g==,type:bool]", 26 | "number1": "ENC[AES256_GCM,data:3Y1f,iv:MCCT+EwNnpVLeu/KkiQNlIGGokimh6U7PNEFEyr1u4A=,tag:jZVaMNn4rgh+5kWgNziIig==,type:float]", 27 | "number2": "ENC[AES256_GCM,data:EPgTD7ZxVQ==,iv:+ZJgfD2V/s3XBjQuzq0ysS5fYtS2P2hX72dj9qmuHCE=,tag:fM9g+wtubjAfjXPHCvsXVA==,type:float]" 28 | }, 29 | "sops": { 30 | "kms": null, 31 | "gcp_kms": null, 32 | "azure_kv": null, 33 | "hc_vault": null, 34 | "age": [ 35 | { 36 | "recipient": "age1djllw2pzuprrqc0en5m8vc8k5ge3tm0f6g7cj0c0glfzp44vdc4ql8ngvu", 37 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjSCttMFlYZE9FZjdHS2gw\nQjdjeFNmRmlQVnVYMHIvUzlLTEJFZVFxZ1E0CmFxWnRwejNMZXpBNnplaUVDdzZt\nQWZGVEJ5QzMzUUhud0hkSW5vdGFiU00KLS0tIDVpRTJHMlhqTGJZSytranZaSW1a\nclA0YWFUNXIrY2x2emh5dGFTd1VjYzAKA9R9C+42bZ2bKDUn8K6SDfQ5KkrICY3r\n0N27V58ZPAo48tqkhea/o0DBtkdllrBHOxGeOIJm/wFOfnETrZ68Vw==\n-----END AGE ENCRYPTED FILE-----\n" 38 | } 39 | ], 40 | "lastmodified": "2025-02-07T14:47:14Z", 41 | "mac": "ENC[AES256_GCM,data:7CuCj7sJeKFZFpFp6ji3BfGzDY+X6Z5efccpTsbDj4ck/QYu+x3iLkirhhrGH0hEuN6xHKAjry/jmCRJpGh+7EGm3ffQoSYu9oC+ujSX3+7aWfhOv8ROt7R38Z9jzEn1qphKEX9lK3O+IruXHfUE+6t/zqwlIVjMGaI1ARbLBKA=,iv:n8ceHb4WwXy4VIx/mJNWNMwGbF6IvMuOw/YQa7JxRiY=,tag:hfU3SbZE7cAJ9QTXRGZj7w==,type:str]", 42 | "pgp": null, 43 | "unencrypted_suffix": "_unencrypted", 44 | "version": "3.9.4" 45 | } 46 | } -------------------------------------------------------------------------------- /test-secrets/yaml/sopsfile.enc-multikms.yaml: -------------------------------------------------------------------------------- 1 | # This secret is not working, just for testing recognition of several kms keys 2 | 3 | hello: ENC[AES256_GCM,data:sswbRHccIuisbGyO+mG/EWb+1JctqNUorOzQUaMRn3HkAyWtreNuZyhYYlkvcg==,iv:XpHilhyCRXa8clGiEckpquCqSZGWA80H0eDUmR80xhE=,tag:WsuxI4lUHwJEas76ODQxUA==,type:str] 4 | example_key: ENC[AES256_GCM,data:/4OtiH3nooTVZuzC+g==,iv:R6qrNY7GAxcIwR42bvE2774uf+IfQ9pv9UA78LmitU0=,tag:jFogVsycufSXRvBKXLVA9g==,type:str] 5 | #ENC[AES256_GCM,data:aFjHl+KK37OIsFkhBqbWAa0=,iv:ZSQP65Qh6isgQL6aQqk9Q4bPCxqd/k0w+9qznaum1PA=,tag:7CUuqndG4b3J3v/g5OKO2Q==,type:comment] 6 | example_array: 7 | - ENC[AES256_GCM,data:UxWfsBaGcgregmDfKMI=,iv:XBs2OCoHWOmobj1OPd/clbUJFbPTdAdotFHVGQm1TP0=,tag:CTT15ZYd1nG8kJarDDLZLA==,type:str] 8 | - ENC[AES256_GCM,data:QP8ZKa88n7SnJaK/ZLk=,iv:Q7aBsWrIv0G8D33Iw65KRqORwqyUWjaAvIBSbWNHAWw=,tag:wBB7tRcc6yRv5cA53k5jog==,type:str] 9 | example_number: ENC[AES256_GCM,data:41QvJoCJ5MuELQ==,iv:QexJM+rwhuZcxDL3TDEswpk8qwUeU6bGk5i5TCduiEk=,tag:rzykMYrmHyv3UHjEGRrNHg==,type:float] 10 | example_booleans: 11 | - ENC[AES256_GCM,data:2sFhDg==,iv:hBMZ0x/1RJC/2NeQ/BEugPDVCConvVHCZk1Tak9vln8=,tag:CJqyx7yAtNnFADvDakfXbA==,type:bool] 12 | - ENC[AES256_GCM,data:51T3h9A=,iv:0VuwMJwURT8AyZEAptQC5kd5k+dfZDHR58Mr4Z4TVII=,tag:mTK1vQE9eXlhAkLQogjdfw==,type:bool] 13 | sops: 14 | kms: 15 | - arn: arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab 16 | created_at: "2022-04-03T17:34:45Z" 17 | enc: AQICAHiSgZoLP6fDrUBYYPU2oJOB/3qFAR5mEYuZY2DQqzYrBgGAPS+SiO5yb/bXbUdoUPeZAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMXZPpPU3GibIONK6VAgEQgDu154YpnYoe2f8YFuWeBpGXfDdaud5oMDfquwqY2UWG8clnZV9355z+V7cqCi+4PEBoaveMLLcLYsLOAA== 18 | aws_profile: "" 19 | - arn: arn:aws:kms:aws-region-1:123456789011:key/00000001-1234-4321-abcd-1234abcd12ab 20 | created_at: "2022-04-03T17:34:45Z" 21 | enc: AQICAHiSgZoLP6fDrUBYYPU2oJOB/3qFAR5mEYuZY2DQqzYrBgGAPS+SiO5yb/bXbUdoUPeZAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMXZPpPU3GibIONK6VAgEQgDu154YpnYoe2f8YFuWeBpGXfDdaud5oMDfquwqY2UWG8clnZV9355z+V7cqCi+4PEBoaveMLLcLYsLOAA== 22 | aws_profile: "" 23 | - arn: arn:aws:kms:aws-region-1:123456789011:key/00000002-1234-4321-abcd-1234abcd12ab 24 | created_at: "2022-04-03T17:34:45Z" 25 | enc: AQICAHiSgZoLP6fDrUBYYPU2oJOB/3qFAR5mEYuZY2DQqzYrBgGAPS+SiO5yb/bXbUdoUPeZAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMXZPpPU3GibIONK6VAgEQgDu154YpnYoe2f8YFuWeBpGXfDdaud5oMDfquwqY2UWG8clnZV9355z+V7cqCi+4PEBoaveMLLcLYsLOAA== 26 | aws_profile: "" 27 | - arn: arn:aws:kms:aws-region-1:123456789011:key/00000003-1234-4321-abcd-1234abcd12ab 28 | created_at: "2022-04-03T17:34:45Z" 29 | enc: AQICAHiSgZoLP6fDrUBYYPU2oJOB/3qFAR5mEYuZY2DQqzYrBgGAPS+SiO5yb/bXbUdoUPeZAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMXZPpPU3GibIONK6VAgEQgDu154YpnYoe2f8YFuWeBpGXfDdaud5oMDfquwqY2UWG8clnZV9355z+V7cqCi+4PEBoaveMLLcLYsLOAA== 30 | aws_profile: "" 31 | gcp_kms: [] 32 | azure_kv: [] 33 | hc_vault: [] 34 | age: [] 35 | lastmodified: "2022-04-03T17:34:56Z" 36 | mac: ENC[AES256_GCM,data:qDi/CaAGfXbcCxPXxHRVnDiP3ayLhL5+Ordv+rCyF/M0iBlO6PEBez4mW+0JE4F8E8a3lMJORhaEGTmRtGyzPXm62DoNLpVepqj8fdBNgzxDQdbTX2yfPoSltSmyCp7lpnbUjGwSHVHsGK8+1EEoocTn81efP0hytRSocYEKONY=,iv:8vZ417Nhp6BO2kE5Ic4HRkzKjS79enrfVJlsYeq59Fo=,tag:mpE+Oto+HPLd2tsWuteuSA==,type:str] 37 | pgp: [] 38 | unencrypted_suffix: _unencrypted 39 | version: 3.7.2 40 | -------------------------------------------------------------------------------- /lambda/internal/sops/sops_test.go: -------------------------------------------------------------------------------- 1 | package sops 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/gkampitakis/go-snaps/snaps" 8 | ) 9 | 10 | func read(filename string) ([]byte, error) { 11 | filePath := "../../../test-secrets/" + filename 12 | existingContent, err := os.ReadFile(filePath) 13 | if err != nil { 14 | return nil, err 15 | } 16 | return existingContent, nil 17 | } 18 | 19 | func TestDecrypt(t *testing.T) { 20 | tests := map[Format]string{ 21 | BINARY: "README.sops.binary", 22 | DOTENV: "testsecret.sops.env", 23 | JSON: "testsecret.sops.json", 24 | YAML: "testsecret.sops.yaml", 25 | } 26 | 27 | t.Setenv("SOPS_AGE_KEY", "AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3") 28 | 29 | for format, file := range tests { 30 | t.Run(file, func(t *testing.T) { 31 | 32 | content, err := read(file) 33 | 34 | if err != nil { 35 | t.Fatalf("Failed to read file: %v", err) 36 | } 37 | 38 | encryptedSecret := EncryptedSopsSecret{ 39 | Content: content, 40 | Format: format, 41 | } 42 | 43 | decryptedSecret, err := encryptedSecret.Decrypt() 44 | if err != nil { 45 | t.Fatalf("Failed to decrypt: %v", err) 46 | } 47 | 48 | snaps.WithConfig( 49 | snaps.Filename(file), 50 | ).MatchSnapshot(t, string(decryptedSecret.content)) 51 | }) 52 | } 53 | } 54 | 55 | func TestToData(t *testing.T) { 56 | tests := []struct { 57 | name string 58 | content []byte 59 | format Format 60 | }{ 61 | { 62 | name: "Test JSON ToData", 63 | content: []byte(`{"key": "value"}`), 64 | format: JSON, 65 | }, 66 | { 67 | name: "Test YAML ToData", 68 | content: []byte(`key: value`), 69 | format: YAML, 70 | }, 71 | { 72 | name: "Test DotEnv ToData", 73 | content: []byte(`KEY=value`), 74 | format: DOTENV, 75 | }, 76 | { 77 | name: "Test Binary ToData", 78 | content: []byte{0x00, 0x01, 0x02, 0x03}, 79 | format: BINARY, 80 | }, 81 | } 82 | 83 | for _, tt := range tests { 84 | t.Run(tt.name, func(t *testing.T) { 85 | decryptedSecret := DecryptedSopsSecret{ 86 | content: tt.content, 87 | format: tt.format, 88 | } 89 | 90 | data, err := decryptedSecret.ToData() 91 | if err != nil { 92 | t.Fatalf("Failed to convert to data: %v", err) 93 | } 94 | 95 | snaps.MatchSnapshot(t, data) 96 | }) 97 | } 98 | } 99 | 100 | func TestCreateEncryptedSopsSecret(t *testing.T) { 101 | tests := []struct { 102 | name string 103 | content []byte 104 | format Format 105 | hash string 106 | wantErr bool 107 | }{ 108 | { 109 | name: "Valid JSON Secret", 110 | content: []byte(`{"key": "value"}`), 111 | format: JSON, 112 | hash: "somehash", 113 | wantErr: false, 114 | }, 115 | { 116 | name: "Empty Content", 117 | content: []byte{}, 118 | format: JSON, 119 | hash: "somehash", 120 | wantErr: true, 121 | }, 122 | { 123 | name: "Empty Format", 124 | content: []byte(`{"key": "value"}`), 125 | format: "", 126 | hash: "somehash", 127 | wantErr: true, 128 | }, 129 | { 130 | name: "Empty Hash", 131 | content: []byte(`{"key": "value"}`), 132 | format: JSON, 133 | hash: "", 134 | wantErr: true, 135 | }, 136 | } 137 | 138 | for _, tt := range tests { 139 | t.Run(tt.name, func(t *testing.T) { 140 | encryptedSecret, err := CreateEncryptedSopsSecret(tt.content, tt.format, tt.hash) 141 | if (err != nil) != tt.wantErr { 142 | t.Fatalf("CreateEncryptedSopsSecret() error = %v, wantErr %v", err, tt.wantErr) 143 | } 144 | if err == nil { 145 | snaps.MatchSnapshot(t, encryptedSecret) 146 | } 147 | }) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/SopsStringParameter.ts: -------------------------------------------------------------------------------- 1 | import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam'; 2 | import { IKey } from 'aws-cdk-lib/aws-kms'; 3 | import { 4 | IStringParameter, 5 | ParameterReference, 6 | ParameterTier, 7 | StringParameter, 8 | } from 'aws-cdk-lib/aws-ssm'; 9 | import { RemovalPolicy, ResourceEnvironment, Stack } from 'aws-cdk-lib/core'; 10 | import { Construct } from 'constructs'; 11 | import { ResourceType, SopsSync, SopsSyncOptions } from './SopsSync'; 12 | 13 | /** 14 | * The configuration options of the StringParameter 15 | */ 16 | export interface SopsCommonParameterProps extends SopsSyncOptions { 17 | /** 18 | * The tier of the string parameter 19 | * 20 | * @default - undefined 21 | */ 22 | readonly tier?: ParameterTier; 23 | /** 24 | * Information about the parameter that you want to add to the system. 25 | * 26 | * @default none 27 | */ 28 | readonly description?: string; 29 | /** 30 | * The customer-managed encryption key to use for encrypting the secret value. 31 | * 32 | * @default - A default KMS key for the account and region is used. 33 | */ 34 | readonly encryptionKey: IKey; 35 | } 36 | 37 | export interface SopsStringParameterProps extends SopsCommonParameterProps { 38 | /** 39 | * The name of the parameter. 40 | * 41 | * @default - a name will be generated by CloudFormation 42 | */ 43 | readonly parameterName?: string; 44 | } 45 | 46 | /** 47 | * A drop in replacement for the normal String Parameter, that is populated with the encrypted 48 | * content of the given sops file. 49 | */ 50 | export class SopsStringParameter extends Construct implements IStringParameter { 51 | private readonly parameter: StringParameter; 52 | readonly sync: SopsSync; 53 | readonly encryptionKey: IKey; 54 | readonly stack: Stack; 55 | readonly env: ResourceEnvironment; 56 | readonly parameterArn: string; 57 | readonly parameterName: string; 58 | readonly parameterType: string; 59 | readonly stringValue: string; 60 | readonly parameterRef: ParameterReference; 61 | 62 | public constructor( 63 | scope: Construct, 64 | id: string, 65 | props: SopsStringParameterProps, 66 | ) { 67 | super(scope, id); 68 | 69 | this.encryptionKey = props.encryptionKey; 70 | this.stack = Stack.of(scope); 71 | this.env = { 72 | account: this.stack.account, 73 | region: this.stack.region, 74 | }; 75 | 76 | this.parameter = new StringParameter(this, 'Resource', { 77 | parameterName: props.parameterName, 78 | description: props.description, 79 | tier: props.tier, 80 | stringValue: ' ', 81 | }); 82 | 83 | this.parameterArn = this.parameter.parameterArn; 84 | this.parameterName = this.parameter.parameterName; 85 | this.parameterType = this.parameter.parameterType; 86 | this.stringValue = this.parameter.stringValue; 87 | this.parameterRef = this.parameter.parameterRef; 88 | 89 | this.sync = new SopsSync(this, 'SopsSync', { 90 | encryptionKey: this.parameter.encryptionKey, 91 | target: this.parameter.parameterName, 92 | resourceType: ResourceType.PARAMETER, 93 | parameterNames: [props.parameterName ?? this.parameter.parameterName], 94 | ...(props as SopsSyncOptions), 95 | }); 96 | } 97 | grantRead(grantee: IGrantable): Grant { 98 | if (this.encryptionKey) { 99 | this.encryptionKey.grantDecrypt(grantee); 100 | } 101 | return this.parameter.grantRead(grantee); 102 | } 103 | grantWrite(grantee: IGrantable): Grant { 104 | if (this.encryptionKey) { 105 | this.encryptionKey.grantEncrypt(grantee); 106 | } 107 | return this.parameter.grantWrite(grantee); 108 | } 109 | applyRemovalPolicy(policy: RemovalPolicy): void { 110 | this.parameter.applyRemovalPolicy(policy); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for your interest in our project. Contributions are welcome. Feel free to [open an issue](issues) with questions or reporting ideas and bugs, or [open pull requests](pulls) to contribute code. 4 | 5 | We are committed to fostering a welcoming, respectful, and harassment-free environment. Be kind! 6 | 7 | ## How to build/deploy local 8 | 9 | ### Prerequisites 10 | 11 | This project uses [mise](https://mise.jdx.dev/) for task management and tool versioning. 12 | 13 | **Install mise:** 14 | 15 | On macOS using Homebrew: 16 | 17 | ```bash 18 | brew install mise 19 | ``` 20 | 21 | For other platforms, follow the [installation guide](https://mise.jdx.dev/getting-started.html). 22 | 23 | Tool versions (Node.js, Go, etc.) are automatically managed via `mise.toml` ([tools] section). 24 | 25 | ### Dev Container Setup (Recommended) 26 | 27 | For the simplest development environment setup, use the included dev container configuration. This provides a pre-configured environment with all necessary tools: 28 | 29 | 1. Open the project in VS Code (or any IDE that supports dev containers) 30 | 2. When prompted, select "Reopen in Container" (or use the Command Palette: "Dev Containers: Reopen in Container") 31 | 3. The dev container will automatically set up the environment with mise managing all required tools and dependencies 32 | 33 | This approach ensures a consistent development environment across all contributors without manual tool installation. 34 | 35 | ### Install Dependencies 36 | 37 | ```bash 38 | mise run install 39 | ``` 40 | 41 | ### Building 42 | 43 | Build the complete project (includes lambda, TypeScript compilation, and tests): 44 | 45 | ```bash 46 | mise run build 47 | ``` 48 | 49 | Build only the Go lambda code: 50 | 51 | ```bash 52 | mise run lambda:build 53 | ``` 54 | 55 | ### Testing 56 | 57 | #### Integration (CDK) Tests 58 | 59 | Use generic tasks (optionally set TEST to limit to one): 60 | 61 | Deploy all: 62 | 63 | ```bash 64 | mise run integ:deploy 65 | ``` 66 | 67 | Deploy single: 68 | 69 | ```bash 70 | TEST=SECRET mise run integ:deploy 71 | ``` 72 | 73 | Assert: 74 | 75 | ```bash 76 | mise run integ:assert 77 | # or single 78 | TEST=SECRET mise run integ:assert 79 | ``` 80 | 81 | Update snapshots (failed only): 82 | 83 | ```bash 84 | mise run integ:snapshot 85 | ``` 86 | 87 | Destroy stacks: 88 | 89 | ```bash 90 | mise run integ:destroy 91 | # or single 92 | TEST=SECRET mise run integ:destroy 93 | ``` 94 | 95 | Combined workflow (deploy, assert, snapshot): 96 | 97 | ```bash 98 | mise run integ:all 99 | ``` 100 | 101 | Run TypeScript tests: 102 | 103 | ```bash 104 | mise run test 105 | ``` 106 | 107 | Run Go lambda unit tests (default set): 108 | 109 | ```bash 110 | mise run lambda:test 111 | ``` 112 | 113 | Run Go lambda integration tests (explicit only): 114 | 115 | ```bash 116 | mise run lambda:test:integration 117 | ``` 118 | 119 | ### Packaging 120 | 121 | Package for JavaScript/npm (default in CI): 122 | 123 | ```bash 124 | mise run package:js 125 | ``` 126 | 127 | Package for all targets (Java, Python, .NET, JavaScript): 128 | 129 | ```bash 130 | mise run package:all 131 | ``` 132 | 133 | ### Local Development 134 | 135 | You can still use `npm link` if desired, but the recommended workflow is to rely on `mise` tasks directly for building and testing. 136 | 137 | To build and watch during local development: 138 | 139 | ```bash 140 | mise run build 141 | mise run watch 142 | ``` 143 | 144 | If you need to link the package (optional): 145 | 146 | ```bash 147 | npm link 148 | npm link "cdk-sops-secrets" 149 | ``` 150 | 151 | ### Other Useful Tasks 152 | 153 | Format code: 154 | 155 | ```bash 156 | mise run format 157 | ``` 158 | 159 | Lint code: 160 | 161 | ```bash 162 | mise run lint 163 | ``` 164 | 165 | See all available tasks: 166 | 167 | ```bash 168 | mise tasks 169 | ``` 170 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk-sops-secrets", 3 | "description": "CDK Constructs that syncs your sops secrets into AWS SecretsManager secrets.", 4 | "version": "2.4.7", 5 | "license": "Apache-2.0", 6 | "author": { 7 | "name": "Markus Siebert", 8 | "email": "markus.siebert@deutschebahn.com", 9 | "organization": false 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/dbsystel/cdk-sops-secrets.git" 14 | }, 15 | "homepage": "https://constructs.dev/packages/cdk-sops-secrets", 16 | "keywords": [ 17 | "cdk", 18 | "getsops/sops", 19 | "gitops", 20 | "kms", 21 | "mozilla/sops", 22 | "secrets", 23 | "secrets management", 24 | "sops" 25 | ], 26 | "main": "lib/index.js", 27 | "types": "lib/index.d.ts", 28 | "stability": "stable", 29 | "peerDependencies": { 30 | "aws-cdk-lib": "^2.227.0", 31 | "constructs": "^10.0.5" 32 | }, 33 | "dependencies": { 34 | "yaml": "^2.8.1" 35 | }, 36 | "devDependencies": { 37 | "@eslint/js": "^9", 38 | "@types/jest": "^30.0.0", 39 | "@types/node": "^24.10.3", 40 | "@typescript-eslint/eslint-plugin": "^8", 41 | "@typescript-eslint/parser": "^8", 42 | "aws-cdk": "^2", 43 | "aws-cdk-lib": "2.233.0", 44 | "commit-and-tag-version": "^12.4.4", 45 | "constructs": "10.4.4", 46 | "eslint": "^9", 47 | "eslint-config-prettier": "^10.0.0", 48 | "eslint-import-resolver-typescript": "^4.0.0", 49 | "eslint-plugin-import": "^2.32.0", 50 | "eslint-plugin-prettier": "^5.0.0", 51 | "jest": "^30.0.0", 52 | "jest-junit": "^16", 53 | "jsii": "5.9.20", 54 | "jsii-diff": "1.121.0", 55 | "jsii-docgen": "10.11.7", 56 | "jsii-pacmak": "1.121.0", 57 | "jsii-rosetta": "5.9.23", 58 | "json-schema-to-typescript": "^15.0.4", 59 | "prettier": "^3.0.0", 60 | "ts-jest": "^29.0.0", 61 | "ts-node": "^10.9.2", 62 | "typescript": "^5.0.0", 63 | "typescript-eslint": "^8" 64 | }, 65 | "publishConfig": { 66 | "access": "public" 67 | }, 68 | "jest": { 69 | "coverageProvider": "v8", 70 | "testMatch": [ 71 | "/@(src|test)/**/*(*.)@(spec|test).ts?(x)", 72 | "/@(src|test)/**/__tests__/**/*.ts?(x)" 73 | ], 74 | "clearMocks": true, 75 | "collectCoverage": true, 76 | "coverageReporters": [ 77 | "json", 78 | "lcov", 79 | "clover", 80 | "cobertura", 81 | "text" 82 | ], 83 | "coverageDirectory": "coverage", 84 | "coveragePathIgnorePatterns": [ 85 | "/node_modules/", 86 | "/lambda/" 87 | ], 88 | "testPathIgnorePatterns": [ 89 | "/node_modules/", 90 | "/lambda/" 91 | ], 92 | "watchPathIgnorePatterns": [ 93 | "/node_modules/" 94 | ], 95 | "reporters": [ 96 | "default", 97 | [ 98 | "jest-junit", 99 | { 100 | "outputDirectory": "test-reports" 101 | } 102 | ] 103 | ], 104 | "preset": "ts-jest", 105 | "transform": { 106 | "^.+\\.(ts|tsx)$": [ 107 | "ts-jest", 108 | { 109 | "tsconfig": "tsconfig.dev.json" 110 | } 111 | ] 112 | } 113 | }, 114 | "jsii": { 115 | "outdir": "dist", 116 | "targets": { 117 | "java": { 118 | "package": "de.db.systel.cdksopssecrets", 119 | "maven": { 120 | "groupId": "de.db.systel", 121 | "artifactId": "cdk-sops-secrets" 122 | } 123 | }, 124 | "python": { 125 | "distName": "cdk-sops-secrets", 126 | "module": "cdk_sops_secrets" 127 | }, 128 | "dotnet": { 129 | "namespace": "Db.De.Systel", 130 | "packageId": "Db.De.Systel.CdkSopsSecrets" 131 | } 132 | }, 133 | "tsc": { 134 | "outDir": "lib", 135 | "rootDir": "src" 136 | } 137 | }, 138 | "scripts": { 139 | "release:tag": "commit-and-tag-version" 140 | }, 141 | "bundleDependencies": [ 142 | "yaml" 143 | ], 144 | "commit-and-tag-version": { 145 | "scripts": { 146 | "postbump": "git restore package-lock.json && npm i --package-lock-only" 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | // Flat ESLint config migrated from legacy .eslintrc.json 2 | // Original file was generated by projen; adapted for ESLint v9 flat config. 3 | 4 | const js = require('@eslint/js'); 5 | const typescript = require('typescript-eslint'); 6 | const importPlugin = require('eslint-plugin-import'); 7 | const prettierPlugin = require('eslint-plugin-prettier'); 8 | const path = require('node:path'); 9 | 10 | const ignores = [ 11 | 'src/LambdaInterface.ts', 12 | '*.js', 13 | '*.d.ts', 14 | 'node_modules/', 15 | '*.generated.ts', 16 | 'coverage', 17 | ]; 18 | 19 | module.exports = [ 20 | js.configs.recommended, 21 | ...typescript.configs.recommended, 22 | { 23 | ignores, 24 | }, 25 | { 26 | plugins: { 27 | '@typescript-eslint': typescript.plugin, 28 | import: importPlugin, 29 | prettier: prettierPlugin, 30 | }, 31 | languageOptions: { 32 | parser: typescript.parser, 33 | parserOptions: { 34 | ecmaVersion: 2018, 35 | sourceType: 'module', 36 | project: path.join(process.cwd(), 'tsconfig.dev.json'), 37 | tsconfigRootDir: process.cwd(), 38 | }, 39 | globals: { 40 | afterAll: 'readonly', 41 | afterEach: 'readonly', 42 | beforeAll: 'readonly', 43 | beforeEach: 'readonly', 44 | describe: 'readonly', 45 | expect: 'readonly', 46 | it: 'readonly', 47 | jest: 'readonly', 48 | test: 'readonly', 49 | }, 50 | }, 51 | files: ['src/**/*.ts', 'test/**/*.ts'], 52 | ignores, 53 | settings: { 54 | 'import/parsers': { 55 | '@typescript-eslint/parser': ['.ts', '.tsx'], 56 | }, 57 | 'import/resolver': { 58 | node: {}, 59 | typescript: { 60 | project: './tsconfig.dev.json', 61 | alwaysTryTypes: true, 62 | }, 63 | }, 64 | }, 65 | rules: { 66 | curly: ['error', 'multi-line', 'consistent'], 67 | '@typescript-eslint/no-require-imports': 'error', 68 | 'import/no-extraneous-dependencies': [ 69 | 'error', 70 | { 71 | devDependencies: ['**/test/**', '**/build-tools/**'], 72 | optionalDependencies: false, 73 | peerDependencies: true, 74 | }, 75 | ], 76 | 'import/no-unresolved': ['error'], 77 | 'import/order': [ 78 | 'warn', 79 | { 80 | groups: ['builtin', 'external'], 81 | alphabetize: { order: 'asc', caseInsensitive: true }, 82 | }, 83 | ], 84 | 'import/no-duplicates': ['error'], 85 | 'no-shadow': ['off'], 86 | '@typescript-eslint/no-shadow': 'error', 87 | '@typescript-eslint/no-floating-promises': 'error', 88 | 'no-return-await': ['off'], 89 | '@typescript-eslint/return-await': 'error', 90 | 'dot-notation': ['error'], 91 | 'no-bitwise': ['error'], 92 | '@typescript-eslint/member-ordering': [ 93 | 'error', 94 | { 95 | default: [ 96 | 'public-static-field', 97 | 'public-static-method', 98 | 'protected-static-field', 99 | 'protected-static-method', 100 | 'private-static-field', 101 | 'private-static-method', 102 | 'field', 103 | 'constructor', 104 | 'method', 105 | ], 106 | }, 107 | ], 108 | // Allow namespaces for logical grouping (Permissions namespace) 109 | '@typescript-eslint/no-namespace': 'off', 110 | // Ignore unused vars/args when prefixed with underscore 111 | '@typescript-eslint/no-unused-vars': [ 112 | 'error', 113 | { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, 114 | ], 115 | 'prettier/prettier': 'error', 116 | }, 117 | }, 118 | { 119 | // Test file overrides 120 | files: ['test/**/*.ts'], 121 | rules: { 122 | '@typescript-eslint/no-explicit-any': 'off', 123 | }, 124 | }, 125 | { 126 | files: ['.projenrc.js'], 127 | rules: { 128 | '@typescript-eslint/no-require-imports': 'off', 129 | 'import/no-extraneous-dependencies': 'off', 130 | }, 131 | }, 132 | ]; 133 | -------------------------------------------------------------------------------- /lambda/main_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | 3 | package main 4 | 5 | import ( 6 | "encoding/json" 7 | "os" 8 | "testing" 9 | "time" 10 | 11 | "github.com/aws/aws-lambda-go/cfn" 12 | "github.com/aws/aws-sdk-go-v2/service/secretsmanager" 13 | "github.com/aws/aws-sdk-go-v2/service/ssm" 14 | "github.com/gkampitakis/go-snaps/snaps" 15 | "github.com/markussiebert/cdk-sops-secrets/internal/client" 16 | "github.com/stretchr/testify/assert" 17 | ) 18 | 19 | type putParameterCalls map[string]interface{} 20 | type putSecretValueCalls map[string]interface{} 21 | type getObjectEtagCalls map[string]client.SopsS3File 22 | type getObjectCalls map[string]client.SopsS3File 23 | type MockAwsClient struct { 24 | t *testing.T 25 | putParameter putParameterCalls 26 | putSecretValue putSecretValueCalls 27 | getObjectEtag getObjectEtagCalls 28 | getObject getObjectCalls 29 | } 30 | 31 | func (m *MockAwsClient) S3GetObject(file client.SopsS3File) ([]byte, error) { 32 | m.getObject[file.Key] = file 33 | localFile := "../" + file.Key 34 | content, err := os.ReadFile(localFile) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return content, nil 39 | } 40 | 41 | func (m *MockAwsClient) S3GetObjectETAG(file client.SopsS3File) (*string, error) { 42 | etag := "mock-etag" 43 | m.getObjectEtag[file.Key] = file 44 | return &etag, nil 45 | } 46 | 47 | func (m *MockAwsClient) SecretsManagerPutSecretValue(sopsHash string, secretArn string, secretContent *[]byte, binary *bool) (*secretsmanager.PutSecretValueOutput, error) { 48 | m.putSecretValue[secretArn] = map[string]interface{}{"sopsHash": sopsHash, "secretContent": string(*secretContent), "binary": *binary} 49 | arn := "mock-arn" 50 | return &secretsmanager.PutSecretValueOutput{ 51 | ARN: &arn, 52 | Name: &secretArn, 53 | VersionStages: []string{"mock-version-stage"}, 54 | VersionId: &sopsHash, 55 | }, nil 56 | } 57 | 58 | func (m *MockAwsClient) SsmPutParameter(parameterName string, parameterContent *[]byte, keyId string) (*ssm.PutParameterOutput, error) { 59 | m.putParameter[parameterName] = map[string]interface{}{"parameterContent": string(*parameterContent), "keyId": keyId} 60 | return &ssm.PutParameterOutput{Version: 1}, nil 61 | } 62 | 63 | func TestHandleRequestWithClients(t *testing.T) { 64 | t.Logf("Running at: %s", time.Now().String()) // Forces re-run 65 | os.Setenv("AWS_REGION", "eu-central-1") 66 | os.Setenv("SOPS_AGE_KEY", "AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3") 67 | files, err := os.ReadDir("events") 68 | if err != nil { 69 | t.Fatalf("failed to read events directory: %v", err) 70 | } 71 | 72 | var tests []struct { 73 | name string 74 | event cfn.Event 75 | } 76 | 77 | for _, file := range files { 78 | if file.IsDir() { 79 | continue 80 | } 81 | 82 | content, err := os.ReadFile("events/" + file.Name()) 83 | if err != nil { 84 | t.Fatalf("failed to read file %s: %v", file.Name(), err) 85 | } 86 | 87 | var resourceProperties map[string]interface{} 88 | if err := json.Unmarshal(content, &resourceProperties); err != nil { 89 | t.Fatalf("failed to unmarshal JSON from file %s: %v", file.Name(), err) 90 | } 91 | 92 | tests = append(tests, struct { 93 | name string 94 | event cfn.Event 95 | }{ 96 | name: file.Name(), 97 | event: cfn.Event{ 98 | RequestType: cfn.RequestCreate, 99 | ResourceProperties: resourceProperties, 100 | }, 101 | }) 102 | } 103 | 104 | for _, tt := range tests { 105 | t.Run(tt.name, func(t *testing.T) { 106 | clients := &MockAwsClient{ 107 | t: t, 108 | putParameter: putParameterCalls{}, 109 | putSecretValue: putSecretValueCalls{}, 110 | getObjectEtag: getObjectEtagCalls{}, 111 | getObject: getObjectCalls{}, 112 | } 113 | 114 | physicalResourceID, data, err := HandleRequestWithClients(clients, tt.event) 115 | snaps.WithConfig(snaps.Filename(t.Name()+"/"+tt.name)).MatchSnapshot(t, clients.getObject, clients.getObjectEtag, clients.putParameter, clients.putSecretValue) 116 | 117 | assert.NoError(t, err) 118 | assert.NotEmpty(t, physicalResourceID) 119 | assert.NotNil(t, data) 120 | }) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/41-PARAMETER_MULTI-json.json/41-PARAMETER_MULTI-json.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/41-PARAMETER_MULTI-json.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.json": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.json"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.json": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.json"}, 8 | } 9 | main.putParameterCalls{ 10 | "/41-PARAMETER_MULTI-json/apiKey": map[string]interface {}{ 11 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 12 | "parameterContent": "sk-1234567890abcdef", 13 | }, 14 | "/41-PARAMETER_MULTI-json/database/host": map[string]interface {}{ 15 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 16 | "parameterContent": "db.example.com", 17 | }, 18 | "/41-PARAMETER_MULTI-json/database/password": map[string]interface {}{ 19 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 20 | "parameterContent": "P@ssw0rd!", 21 | }, 22 | "/41-PARAMETER_MULTI-json/database/user": map[string]interface {}{ 23 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 24 | "parameterContent": "admin", 25 | }, 26 | "/41-PARAMETER_MULTI-json/someOtherKey": map[string]interface {}{ 27 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 28 | "parameterContent": "base64:aGFsbG8gd2VsdAo=", 29 | }, 30 | "/41-PARAMETER_MULTI-json/specific/HTLMEncodingTest3": map[string]interface {}{ 31 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 32 | "parameterContent": "@", 33 | }, 34 | "/41-PARAMETER_MULTI-json/specific/HTMLEncodingTest1": map[string]interface {}{ 35 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 36 | "parameterContent": "test ", 37 | }, 38 | "/41-PARAMETER_MULTI-json/specific/HTMLEncodingTest2": map[string]interface {}{ 39 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 40 | "parameterContent": "&", 41 | }, 42 | "/41-PARAMETER_MULTI-json/specific/SpecialCharacters": map[string]interface {}{ 43 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 44 | "parameterContent": "\"ajkscbuiuXA34%%&&=", 45 | }, 46 | "/41-PARAMETER_MULTI-json/specific/boolean": map[string]interface {}{ 47 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 48 | "parameterContent": "true", 49 | }, 50 | "/41-PARAMETER_MULTI-json/specific/null": map[string]interface {}{ 51 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 52 | "parameterContent": "", 53 | }, 54 | "/41-PARAMETER_MULTI-json/specific/number1": map[string]interface {}{ 55 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 56 | "parameterContent": "123", 57 | }, 58 | "/41-PARAMETER_MULTI-json/specific/number2": map[string]interface {}{ 59 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 60 | "parameterContent": "123.456", 61 | }, 62 | "/41-PARAMETER_MULTI-json/tokens/0/service": map[string]interface {}{ 63 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 64 | "parameterContent": "service1", 65 | }, 66 | "/41-PARAMETER_MULTI-json/tokens/0/token": map[string]interface {}{ 67 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 68 | "parameterContent": "token1", 69 | }, 70 | "/41-PARAMETER_MULTI-json/tokens/1/service": map[string]interface {}{ 71 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 72 | "parameterContent": "service2", 73 | }, 74 | "/41-PARAMETER_MULTI-json/tokens/1/token": map[string]interface {}{ 75 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 76 | "parameterContent": "token2", 77 | }, 78 | } 79 | main.putSecretValueCalls{ 80 | } 81 | --- 82 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/42-PARAMETER_MULTI-yaml.json/42-PARAMETER_MULTI-yaml.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/42-PARAMETER_MULTI-yaml.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.yaml": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.yaml"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.yaml": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.yaml"}, 8 | } 9 | main.putParameterCalls{ 10 | "/42-PARAMETER_MULTI-yaml/apiKey": map[string]interface {}{ 11 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 12 | "parameterContent": "sk-1234567890abcdef", 13 | }, 14 | "/42-PARAMETER_MULTI-yaml/database/host": map[string]interface {}{ 15 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 16 | "parameterContent": "db.example.com", 17 | }, 18 | "/42-PARAMETER_MULTI-yaml/database/password": map[string]interface {}{ 19 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 20 | "parameterContent": "P@ssw0rd!", 21 | }, 22 | "/42-PARAMETER_MULTI-yaml/database/user": map[string]interface {}{ 23 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 24 | "parameterContent": "admin", 25 | }, 26 | "/42-PARAMETER_MULTI-yaml/someOtherKey": map[string]interface {}{ 27 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 28 | "parameterContent": "base64:aGFsbG8gd2VsdAo=", 29 | }, 30 | "/42-PARAMETER_MULTI-yaml/specific/HTLMEncodingTest3": map[string]interface {}{ 31 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 32 | "parameterContent": "@", 33 | }, 34 | "/42-PARAMETER_MULTI-yaml/specific/HTMLEncodingTest1": map[string]interface {}{ 35 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 36 | "parameterContent": "test ", 37 | }, 38 | "/42-PARAMETER_MULTI-yaml/specific/HTMLEncodingTest2": map[string]interface {}{ 39 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 40 | "parameterContent": "&", 41 | }, 42 | "/42-PARAMETER_MULTI-yaml/specific/SpecialCharacters": map[string]interface {}{ 43 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 44 | "parameterContent": "\"ajkscbuiuXA34%%&&=", 45 | }, 46 | "/42-PARAMETER_MULTI-yaml/specific/boolean": map[string]interface {}{ 47 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 48 | "parameterContent": "true", 49 | }, 50 | "/42-PARAMETER_MULTI-yaml/specific/null": map[string]interface {}{ 51 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 52 | "parameterContent": "", 53 | }, 54 | "/42-PARAMETER_MULTI-yaml/specific/number1": map[string]interface {}{ 55 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 56 | "parameterContent": "123", 57 | }, 58 | "/42-PARAMETER_MULTI-yaml/specific/number2": map[string]interface {}{ 59 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 60 | "parameterContent": "123.456", 61 | }, 62 | "/42-PARAMETER_MULTI-yaml/tokens/0/service": map[string]interface {}{ 63 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 64 | "parameterContent": "service1", 65 | }, 66 | "/42-PARAMETER_MULTI-yaml/tokens/0/token": map[string]interface {}{ 67 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 68 | "parameterContent": "token1", 69 | }, 70 | "/42-PARAMETER_MULTI-yaml/tokens/1/service": map[string]interface {}{ 71 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 72 | "parameterContent": "service2", 73 | }, 74 | "/42-PARAMETER_MULTI-yaml/tokens/1/token": map[string]interface {}{ 75 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 76 | "parameterContent": "token2", 77 | }, 78 | } 79 | main.putSecretValueCalls{ 80 | } 81 | --- 82 | -------------------------------------------------------------------------------- /lambda/__snapshots__/TestHandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json/43-PARAMETER_MULTI-dotenv.json.snap: -------------------------------------------------------------------------------- 1 | 2 | [TestHandleRequestWithClients/43-PARAMETER_MULTI-dotenv.json - 1] 3 | main.getObjectCalls{ 4 | "test-secrets/testsecret.sops.env": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.env"}, 5 | } 6 | main.getObjectEtagCalls{ 7 | "test-secrets/testsecret.sops.env": {Bucket:"cdk-hnb659fds-assets-505755377845-eu-central-1", Key:"test-secrets/testsecret.sops.env"}, 8 | } 9 | main.putParameterCalls{ 10 | "/43-PARAMETER_MULTI-dotenv/apiKey": map[string]interface {}{ 11 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 12 | "parameterContent": "sk-1234567890abcdef", 13 | }, 14 | "/43-PARAMETER_MULTI-dotenv/database_host": map[string]interface {}{ 15 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 16 | "parameterContent": "db.example.com", 17 | }, 18 | "/43-PARAMETER_MULTI-dotenv/database_password": map[string]interface {}{ 19 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 20 | "parameterContent": "P@ssw0rd!", 21 | }, 22 | "/43-PARAMETER_MULTI-dotenv/database_user": map[string]interface {}{ 23 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 24 | "parameterContent": "admin", 25 | }, 26 | "/43-PARAMETER_MULTI-dotenv/someOtherKey": map[string]interface {}{ 27 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 28 | "parameterContent": "base64:aGFsbG8gd2VsdAo=", 29 | }, 30 | "/43-PARAMETER_MULTI-dotenv/specific_HTLMEncodingTest3": map[string]interface {}{ 31 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 32 | "parameterContent": "@", 33 | }, 34 | "/43-PARAMETER_MULTI-dotenv/specific_HTMLEncodingTest1": map[string]interface {}{ 35 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 36 | "parameterContent": "test ", 37 | }, 38 | "/43-PARAMETER_MULTI-dotenv/specific_HTMLEncodingTest2": map[string]interface {}{ 39 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 40 | "parameterContent": "&", 41 | }, 42 | "/43-PARAMETER_MULTI-dotenv/specific_SpecialCharacters": map[string]interface {}{ 43 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 44 | "parameterContent": "\"ajkscbuiuXA34%%&&=", 45 | }, 46 | "/43-PARAMETER_MULTI-dotenv/specific_boolean": map[string]interface {}{ 47 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 48 | "parameterContent": "True", 49 | }, 50 | "/43-PARAMETER_MULTI-dotenv/specific_null": map[string]interface {}{ 51 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 52 | "parameterContent": "", 53 | }, 54 | "/43-PARAMETER_MULTI-dotenv/specific_number1": map[string]interface {}{ 55 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 56 | "parameterContent": "123", 57 | }, 58 | "/43-PARAMETER_MULTI-dotenv/specific_number2": map[string]interface {}{ 59 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 60 | "parameterContent": "123.456", 61 | }, 62 | "/43-PARAMETER_MULTI-dotenv/tokens_0_service": map[string]interface {}{ 63 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 64 | "parameterContent": "service1", 65 | }, 66 | "/43-PARAMETER_MULTI-dotenv/tokens_0_token": map[string]interface {}{ 67 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 68 | "parameterContent": "token1", 69 | }, 70 | "/43-PARAMETER_MULTI-dotenv/tokens_1_service": map[string]interface {}{ 71 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 72 | "parameterContent": "service2", 73 | }, 74 | "/43-PARAMETER_MULTI-dotenv/tokens_1_token": map[string]interface {}{ 75 | "keyId": "arn:aws:kms:eu-central-1:505755377845:key/58b75958-883a-440c-842c-85af5d33a5bb", 76 | "parameterContent": "token2", 77 | }, 78 | } 79 | main.putSecretValueCalls{ 80 | } 81 | --- 82 | -------------------------------------------------------------------------------- /lambda/internal/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "log" 8 | "log/slog" 9 | 10 | "github.com/aws/aws-sdk-go-v2/aws" 11 | "github.com/aws/aws-sdk-go-v2/aws/retry" 12 | "github.com/aws/aws-sdk-go-v2/config" 13 | "github.com/aws/aws-sdk-go-v2/service/s3" 14 | s3Types "github.com/aws/aws-sdk-go-v2/service/s3/types" 15 | "github.com/aws/aws-sdk-go-v2/service/secretsmanager" 16 | "github.com/aws/aws-sdk-go-v2/service/ssm" 17 | ssmTypes "github.com/aws/aws-sdk-go-v2/service/ssm/types" 18 | ) 19 | 20 | type S3Client interface { 21 | GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) 22 | GetObjectAttributes(ctx context.Context, params *s3.GetObjectAttributesInput, optFns ...func(*s3.Options)) (*s3.GetObjectAttributesOutput, error) 23 | } 24 | 25 | type SecretsManagerClient interface { 26 | PutSecretValue(ctx context.Context, params *secretsmanager.PutSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.PutSecretValueOutput, error) 27 | } 28 | 29 | type SsmClient interface { 30 | PutParameter(ctx context.Context, params *ssm.PutParameterInput, optFns ...func(*ssm.Options)) (*ssm.PutParameterOutput, error) 31 | } 32 | 33 | type AwsClient interface { 34 | S3GetObject(file SopsS3File) (data []byte, err error) 35 | S3GetObjectETAG(file SopsS3File) (*string, error) 36 | SecretsManagerPutSecretValue(sopsHash string, secretArn string, secretContent *[]byte, binary *bool) (data *secretsmanager.PutSecretValueOutput, err error) 37 | SsmPutParameter(parameterName string, parameterContent *[]byte, keyId string) (response *ssm.PutParameterOutput, err error) 38 | } 39 | 40 | type Client struct { 41 | ctx context.Context 42 | s3 S3Client 43 | secretsManager SecretsManagerClient 44 | ssm SsmClient 45 | } 46 | 47 | func CreateAwsClients(context context.Context) AwsClient { 48 | cfg, err := config.LoadDefaultConfig(context, config.WithRetryer(func() aws.Retryer { 49 | return retry.AddWithMaxAttempts(retry.NewStandard(), 5) 50 | })) 51 | if err != nil { 52 | log.Fatalf("unable to load SDK config, %v", err) 53 | } 54 | return &Client{ 55 | ctx: context, 56 | s3: s3.NewFromConfig(cfg), 57 | secretsManager: secretsmanager.NewFromConfig(cfg), 58 | ssm: ssm.NewFromConfig(cfg), 59 | } 60 | } 61 | 62 | type SopsS3File struct { 63 | Bucket string `json:"Bucket,omitempty"` 64 | Key string `json:"Key,omitempty"` 65 | } 66 | 67 | func (c *Client) S3GetObject(file SopsS3File) (data []byte, err error) { 68 | logger := slog.With("Package", "client", "Function", "S3GetObject") 69 | logger.Info("Downloading file", "Bucket", file.Bucket, "Key", file.Key) 70 | 71 | resp, err := c.s3.GetObject(c.ctx, &s3.GetObjectInput{ 72 | Bucket: &file.Bucket, 73 | Key: &file.Key, 74 | }) 75 | if err != nil { 76 | return nil, fmt.Errorf("S3 get object error:\n%v", err) 77 | } 78 | defer resp.Body.Close() 79 | 80 | buf := new(bytes.Buffer) 81 | _, err = buf.ReadFrom(resp.Body) 82 | if err != nil { 83 | return nil, fmt.Errorf("read buffer error:\n%v", err) 84 | } 85 | logger.Info("Downloaded file", "Size", buf.Len()) 86 | return buf.Bytes(), nil 87 | } 88 | 89 | func (c *Client) S3GetObjectETAG(file SopsS3File) (*string, error) { 90 | attr, err := c.s3.GetObjectAttributes(c.ctx, &s3.GetObjectAttributesInput{ 91 | Bucket: &file.Bucket, 92 | Key: &file.Key, 93 | ObjectAttributes: []s3Types.ObjectAttributes{ 94 | "ETag", 95 | }, 96 | }) 97 | 98 | if err != nil { 99 | return nil, fmt.Errorf("error while getting S3 object attributes:\n%v", err) 100 | } 101 | 102 | if attr.ETag == nil { 103 | return nil, fmt.Errorf("no ETag checksum found in S3 object:\n%v", err) 104 | } 105 | 106 | return attr.ETag, nil 107 | } 108 | 109 | func (c *Client) SecretsManagerPutSecretValue(sopsHash string, secretArn string, secretStringData *[]byte, binary *bool) (data *secretsmanager.PutSecretValueOutput, err error) { 110 | if binary != nil && *binary { 111 | input := &secretsmanager.PutSecretValueInput{ 112 | SecretId: &secretArn, 113 | SecretBinary: *secretStringData, 114 | ClientRequestToken: &sopsHash, 115 | } 116 | return c.secretsManager.PutSecretValue(c.ctx, input) 117 | } 118 | input := &secretsmanager.PutSecretValueInput{ 119 | SecretId: &secretArn, 120 | SecretString: aws.String(string(*secretStringData)), 121 | ClientRequestToken: &sopsHash, 122 | } 123 | return c.secretsManager.PutSecretValue(c.ctx, input) 124 | } 125 | 126 | func (c *Client) SsmPutParameter(parameterName string, parameterContent *[]byte, keyId string) (response *ssm.PutParameterOutput, err error) { 127 | parameterContentString := string(*parameterContent) 128 | input := &ssm.PutParameterInput{ 129 | Name: ¶meterName, 130 | Value: ¶meterContentString, 131 | Type: ssmTypes.ParameterTypeSecureString, 132 | Overwrite: aws.Bool(true), 133 | KeyId: &keyId, 134 | } 135 | return c.ssm.PutParameter(c.ctx, input) 136 | } 137 | --------------------------------------------------------------------------------