env-secrets 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/cicd.md +189 -0
- package/.claude/rules/docs.md +96 -0
- package/.claude/rules/git-hooks.md +43 -0
- package/.claude/rules/local-dev-badges.md +91 -0
- package/.claude/rules/local-dev-env.md +382 -0
- package/.claude/rules/local-dev-license.md +104 -0
- package/.claude/rules/local-dev-mcp.md +70 -0
- package/.claude/rules/observability.md +23 -0
- package/.claude/rules/publishing-api.md +158 -0
- package/.claude/rules/publishing-apps.md +204 -0
- package/.claude/rules/publishing-apt.md +146 -0
- package/.claude/rules/publishing-brew.md +110 -0
- package/.claude/rules/publishing-cli.md +238 -0
- package/.claude/rules/publishing-libraries.md +115 -0
- package/.claude/rules/publishing-sdks.md +109 -0
- package/.claude/rules/publishing-web.md +185 -0
- package/.claude/rules/typescript-linting.md +141 -0
- package/.claude/rules/typescript-logging.md +356 -0
- package/.claude/rules/typescript-testing.md +185 -0
- package/.claude/settings.json +18 -0
- package/.claude/skills/github-health-check.skill +0 -0
- package/.codex/rules/cicd.md +21 -0
- package/.codex/rules/docs.md +98 -0
- package/.codex/rules/git-hooks.md +43 -0
- package/.codex/rules/github-health-check.md +440 -0
- package/.codex/rules/local-dev-env.md +47 -0
- package/.codex/rules/local-dev-license.md +3 -1
- package/.codex/rules/publishing-api.md +160 -0
- package/.codex/rules/publishing-apps.md +206 -0
- package/.codex/rules/publishing-apt.md +148 -0
- package/.codex/rules/publishing-brew.md +112 -0
- package/.codex/rules/publishing-cli.md +240 -0
- package/.codex/rules/publishing-libraries.md +117 -0
- package/.codex/rules/publishing-sdks.md +111 -0
- package/.codex/rules/publishing-web.md +187 -0
- package/.codex/rules/typescript-linting.md +143 -0
- package/.codex/rules/typescript-logging.md +358 -0
- package/.codex/rules/typescript-testing.md +187 -0
- package/.github/workflows/deploy-docs.yml +2 -2
- package/.github/workflows/unittests.yaml +1 -1
- package/.rulesrc.json +20 -0
- package/AGENTS.md +34 -0
- package/CLAUDE.md +58 -0
- package/README.md +17 -3
- package/__e2e__/aws-exec-args.test.ts +97 -1
- package/__e2e__/aws-secret-value-args.test.ts +142 -0
- package/__e2e__/utils/test-utils.ts +78 -0
- package/__tests__/cli/helpers.test.ts +35 -0
- package/__tests__/index.test.ts +208 -58
- package/dist/cli/helpers.js +13 -1
- package/dist/index.js +94 -44
- package/docker-compose.yaml +1 -1
- package/docs/AWS.md +42 -13
- package/package.json +6 -6
- package/src/cli/helpers.ts +16 -0
- package/src/index.ts +117 -52
package/.rulesrc.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": ["claude", "codex"],
|
|
3
|
+
"agents": [
|
|
4
|
+
"local-dev",
|
|
5
|
+
"docs",
|
|
6
|
+
"cicd",
|
|
7
|
+
"observability",
|
|
8
|
+
"publishing",
|
|
9
|
+
"git-hooks",
|
|
10
|
+
"linting",
|
|
11
|
+
"logging",
|
|
12
|
+
"testing"
|
|
13
|
+
],
|
|
14
|
+
"skills": ["github-health-check"],
|
|
15
|
+
"ballastVersion": "5.9.2",
|
|
16
|
+
"languages": ["typescript"],
|
|
17
|
+
"paths": {
|
|
18
|
+
"typescript": ["."]
|
|
19
|
+
}
|
|
20
|
+
}
|
package/AGENTS.md
CHANGED
|
@@ -156,3 +156,37 @@ yarn install
|
|
|
156
156
|
brew install awscli-local
|
|
157
157
|
yarn build
|
|
158
158
|
```
|
|
159
|
+
|
|
160
|
+
## Installed agent rules
|
|
161
|
+
|
|
162
|
+
Created by [Ballast](https://github.com/everydaydevopsio/ballast) v5.9.2. Do not edit this section.
|
|
163
|
+
|
|
164
|
+
Read and follow these rule files in `.codex/rules/` when they apply:
|
|
165
|
+
|
|
166
|
+
- `.codex/rules/local-dev-badges.md` — Add standard badges (CI, Release, License, GitHub Release, npm) to the top of README.md
|
|
167
|
+
- `.codex/rules/local-dev-env.md` — Local development environment specialist - reproducible dev setup, DX, and documentation
|
|
168
|
+
- `.codex/rules/local-dev-license.md` — License setup - ensure LICENSE file, package.json license field, and README reference (default MIT; overridable in AGENTS.md/CLAUDE.md)
|
|
169
|
+
- `.codex/rules/local-dev-mcp.md` — Optional: use GitHub MCP and issues MCP (Jira/Linear/GitHub) for local-dev context
|
|
170
|
+
- `.codex/rules/docs.md` — Documentation specialist - GitHub Markdown docs by default, or maintain existing Docusaurus sites with publish-docs automation
|
|
171
|
+
- `.codex/rules/cicd.md` — CI/CD specialist - pipeline design, quality gates, and deployment
|
|
172
|
+
- `.codex/rules/observability.md` — Observability specialist - logging, tracing, metrics, and SLOs
|
|
173
|
+
- `.codex/rules/publishing-api.md` — REST API publishing specialist - Docker CD with Kubernetes health probes and Helm chart update
|
|
174
|
+
- `.codex/rules/publishing-apps.md` — App publishing specialist - npmjs for Node apps, PyPI for Python apps, GitHub Releases for Go apps
|
|
175
|
+
- `.codex/rules/publishing-apt.md` — APT/deb package publishing specialist - GoReleaser nfpms and GitHub Releases
|
|
176
|
+
- `.codex/rules/publishing-brew.md` — Homebrew tap publishing specialist - GoReleaser brews block and tap repo setup
|
|
177
|
+
- `.codex/rules/publishing-cli.md` — CLI publishing specialist - GoReleaser for Go, npmjs for Node, PyPI for Python
|
|
178
|
+
- `.codex/rules/publishing-libraries.md` — Library publishing specialist - npmjs for TypeScript, PyPI for Python, GitHub tags/releases for Go
|
|
179
|
+
- `.codex/rules/publishing-sdks.md` — SDK publishing specialist - npmjs for TypeScript SDKs, PyPI for Python SDKs, GitHub tags/releases for Go SDKs
|
|
180
|
+
- `.codex/rules/publishing-web.md` — Web app publishing specialist - Docker to GHCR/Docker Hub with Helm chart CD on push to main
|
|
181
|
+
- `.codex/rules/git-hooks.md` — Git hook specialist - configure pre-commit, pre-push, and Husky workflows that match the repository layout
|
|
182
|
+
- `.codex/rules/typescript-linting.md` — TypeScript linting specialist - implements comprehensive linting and code formatting for TypeScript/JavaScript projects
|
|
183
|
+
- `.codex/rules/typescript-logging.md` — Centralized logging specialist - configures Pino with Fluentd for Node/Next.js, and pino-browser to /api/logs
|
|
184
|
+
- `.codex/rules/typescript-testing.md` — Testing specialist - sets up Jest (default) or Vitest for Vite projects, 50% coverage, and test step in build GitHub Action
|
|
185
|
+
|
|
186
|
+
## Installed skills
|
|
187
|
+
|
|
188
|
+
Created by [Ballast](https://github.com/everydaydevopsio/ballast) v5.9.2. Do not edit this section.
|
|
189
|
+
|
|
190
|
+
Read and use these skill files in `.codex/rules/` when they are relevant:
|
|
191
|
+
|
|
192
|
+
- `.codex/rules/github-health-check.md` — Run a comprehensive GitHub repository health check. Use this skill whenever the user asks to: check GitHub health, audit the repo, check CI status, review open PRs, merge Dependabot PRs, check code coverage, check GitHub Code Quality, check GitHub security feature enablement, check security advisories, check Dependabot alerts, check code scanning alerts, check secret scanning alerts, check Snyk integration, keep GitHub in good shape, or any variation of "how is the repo doing". Also trigger for: "check dependabot PRs", "any PRs to merge", "check branch status", "repo health", "GitHub status check", "what needs attention in GitHub", "tidy up GitHub".
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code for working in this repository.
|
|
4
|
+
|
|
5
|
+
## Repository Facts
|
|
6
|
+
|
|
7
|
+
Use this section for durable repo-specific facts that agents repeatedly need. Prefer facts stored here over re-deriving them with shell commands on every task.
|
|
8
|
+
|
|
9
|
+
Keep only stable, reviewable metadata here. Do not store secrets, credentials, or ephemeral runtime state.
|
|
10
|
+
|
|
11
|
+
Suggested facts to record:
|
|
12
|
+
|
|
13
|
+
- Canonical GitHub repo: `<OWNER/REPO>`
|
|
14
|
+
- Default branch: `<main>`
|
|
15
|
+
- Primary package manager: `<pnpm | npm | yarn | uv | go>`
|
|
16
|
+
- Version-file locations agents should check first: `<.nvmrc, packageManager, pyproject.toml, go.mod, etc.>`
|
|
17
|
+
- Canonical config files: `<paths agents should read before falling back to discovery>`
|
|
18
|
+
- Primary CI workflows: `<workflow filenames>`
|
|
19
|
+
- Primary release/publish workflows: `<workflow filenames>`
|
|
20
|
+
- Preferred build/test/lint/format/coverage commands: `<commands>`
|
|
21
|
+
- Coverage threshold: `<value>`
|
|
22
|
+
- Generated or protected paths agents should avoid editing directly: `<paths>`
|
|
23
|
+
|
|
24
|
+
Update this section when those facts change. If live runtime state is required, discover it separately instead of treating it as a durable repo fact.
|
|
25
|
+
|
|
26
|
+
## Installed agent rules
|
|
27
|
+
|
|
28
|
+
Created by [Ballast](https://github.com/everydaydevopsio/ballast) v5.9.2. Do not edit this section.
|
|
29
|
+
|
|
30
|
+
Read and follow these rule files in `.claude/rules/` when they apply:
|
|
31
|
+
|
|
32
|
+
- `.claude/rules/local-dev-badges.md` — Add standard badges (CI, Release, License, GitHub Release, npm) to the top of README.md
|
|
33
|
+
- `.claude/rules/local-dev-env.md` — Local development environment specialist - reproducible dev setup, DX, and documentation
|
|
34
|
+
- `.claude/rules/local-dev-license.md` — License setup - ensure LICENSE file, package.json license field, and README reference (default MIT; overridable in AGENTS.md/CLAUDE.md)
|
|
35
|
+
- `.claude/rules/local-dev-mcp.md` — Optional: use GitHub MCP and issues MCP (Jira/Linear/GitHub) for local-dev context
|
|
36
|
+
- `.claude/rules/docs.md` — Documentation specialist - GitHub Markdown docs by default, or maintain existing Docusaurus sites with publish-docs automation
|
|
37
|
+
- `.claude/rules/cicd.md` — CI/CD specialist - pipeline design, quality gates, and deployment
|
|
38
|
+
- `.claude/rules/observability.md` — Observability specialist - logging, tracing, metrics, and SLOs
|
|
39
|
+
- `.claude/rules/publishing-api.md` — REST API publishing specialist - Docker CD with Kubernetes health probes and Helm chart update
|
|
40
|
+
- `.claude/rules/publishing-apps.md` — App publishing specialist - npmjs for Node apps, PyPI for Python apps, GitHub Releases for Go apps
|
|
41
|
+
- `.claude/rules/publishing-apt.md` — APT/deb package publishing specialist - GoReleaser nfpms and GitHub Releases
|
|
42
|
+
- `.claude/rules/publishing-brew.md` — Homebrew tap publishing specialist - GoReleaser brews block and tap repo setup
|
|
43
|
+
- `.claude/rules/publishing-cli.md` — CLI publishing specialist - GoReleaser for Go, npmjs for Node, PyPI for Python
|
|
44
|
+
- `.claude/rules/publishing-libraries.md` — Library publishing specialist - npmjs for TypeScript, PyPI for Python, GitHub tags/releases for Go
|
|
45
|
+
- `.claude/rules/publishing-sdks.md` — SDK publishing specialist - npmjs for TypeScript SDKs, PyPI for Python SDKs, GitHub tags/releases for Go SDKs
|
|
46
|
+
- `.claude/rules/publishing-web.md` — Web app publishing specialist - Docker to GHCR/Docker Hub with Helm chart CD on push to main
|
|
47
|
+
- `.claude/rules/git-hooks.md` — Git hook specialist - configure pre-commit, pre-push, and Husky workflows that match the repository layout
|
|
48
|
+
- `.claude/rules/typescript-linting.md` — TypeScript linting specialist - implements comprehensive linting and code formatting for TypeScript/JavaScript projects
|
|
49
|
+
- `.claude/rules/typescript-logging.md` — Centralized logging specialist - configures Pino with Fluentd for Node/Next.js, and pino-browser to /api/logs
|
|
50
|
+
- `.claude/rules/typescript-testing.md` — Testing specialist - sets up Jest (default) or Vitest for Vite projects, 50% coverage, and test step in build GitHub Action
|
|
51
|
+
|
|
52
|
+
## Installed skills
|
|
53
|
+
|
|
54
|
+
Created by [Ballast](https://github.com/everydaydevopsio/ballast) v5.9.2. Do not edit this section.
|
|
55
|
+
|
|
56
|
+
Read and use these skill files in `.claude/skills/` when they are relevant:
|
|
57
|
+
|
|
58
|
+
- `.claude/skills/github-health-check.skill` — Run a comprehensive GitHub repository health check. Use this skill whenever the user asks to: check GitHub health, audit the repo, check CI status, review open PRs, merge Dependabot PRs, check code coverage, check GitHub Code Quality, check GitHub security feature enablement, check security advisories, check Dependabot alerts, check code scanning alerts, check secret scanning alerts, check Snyk integration, keep GitHub in good shape, or any variation of "how is the repo doing". Also trigger for: "check dependabot PRs", "any PRs to merge", "check branch status", "repo health", "GitHub status check", "what needs attention in GitHub", "tidy up GitHub".
|
package/README.md
CHANGED
|
@@ -101,9 +101,10 @@ env-secrets aws -s my-app-secrets -r us-east-1 -- node app.js
|
|
|
101
101
|
- `-r, --region <region>` (optional): AWS region where the secret is stored. If not provided, uses `AWS_DEFAULT_REGION` environment variable
|
|
102
102
|
- `-p, --profile <profile>` (optional): Local AWS profile to use. If not provided, uses `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables
|
|
103
103
|
- `-o, --output <file>` (optional): Output secrets to a file instead of injecting into environment variables. File will be created with 0400 permissions and will not overwrite existing files
|
|
104
|
+
- `--no-shell` (optional): Run the program directly without a shell wrapper. Disables shell expansion — use when you do not need `$VAR` interpolation in arguments
|
|
104
105
|
- `-- <program-to-run>`: The program to run with the injected environment variables (only used when `-o` is not specified)
|
|
105
106
|
|
|
106
|
-
For `aws secret` management subcommands (`create`, `update`, `
|
|
107
|
+
For `aws secret` management subcommands (`create`, `update`, `upsert`/`import`, `append`, `remove`, `list`, `get`, `value`, `delete`), use:
|
|
107
108
|
|
|
108
109
|
- `-r, --region <region>` to target a specific region
|
|
109
110
|
- `-p, --profile <profile>` to select credentials profile
|
|
@@ -111,8 +112,8 @@ For `aws secret` management subcommands (`create`, `update`, `append`, `remove`,
|
|
|
111
112
|
|
|
112
113
|
These options are honored consistently on `aws secret` subcommands.
|
|
113
114
|
|
|
114
|
-
`env-secrets aws -s` is for fetching/injecting secret values into a child process.
|
|
115
|
-
`env-secrets aws secret ...` is for lifecycle management commands (`create`, `update`, `
|
|
115
|
+
`env-secrets aws -s` is for fetching/injecting secret values into a child process. Use `--no-shell` to run the program directly without a shell wrapper (disables shell expansion).
|
|
116
|
+
`env-secrets aws secret ...` is for lifecycle management commands (`create`, `update`, `upsert`/`import`, `append`, `remove`, `list`, `get`, `value`, `delete`).
|
|
116
117
|
|
|
117
118
|
#### Examples
|
|
118
119
|
|
|
@@ -262,6 +263,19 @@ env-secrets aws secret append -n app/dev --key JIRA_EMAIL_TOKEN -v blah --output
|
|
|
262
263
|
env-secrets aws secret remove -n app/dev --key API_KEY --key OLD_TOKEN --output json
|
|
263
264
|
```
|
|
264
265
|
|
|
266
|
+
13. **View the values of a secret:**
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Table output — values masked as **** by default
|
|
270
|
+
env-secrets aws secret value -n app/dev -r us-east-1
|
|
271
|
+
|
|
272
|
+
# Reveal actual values (warning printed to stderr)
|
|
273
|
+
env-secrets aws secret value -n app/dev -r us-east-1 --reveal
|
|
274
|
+
|
|
275
|
+
# JSON output — full values, suitable for scripting (warns if stdout is a terminal)
|
|
276
|
+
env-secrets aws secret value -n app/dev -r us-east-1 --output json
|
|
277
|
+
```
|
|
278
|
+
|
|
265
279
|
## Security Considerations
|
|
266
280
|
|
|
267
281
|
- 🔐 **Credential Management**: The tool respects AWS credential precedence (environment variables, IAM roles, profiles)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cliWithEnv } from './utils/test-utils';
|
|
1
|
+
import { cliWithEnv, cliWithRealSpawn } from './utils/test-utils';
|
|
2
2
|
import { registerAwsE2eContext } from './utils/aws-e2e-context';
|
|
3
3
|
|
|
4
4
|
describe('AWS Program Execution CLI Args', () => {
|
|
@@ -42,3 +42,99 @@ describe('AWS Program Execution CLI Args', () => {
|
|
|
42
42
|
expect(envVars.API_KEY).toBe('secret123');
|
|
43
43
|
});
|
|
44
44
|
});
|
|
45
|
+
|
|
46
|
+
describe('AWS Real Spawn Execution (no NODE_ENV=test)', () => {
|
|
47
|
+
const { createTestSecret, getLocalStackEnv } = registerAwsE2eContext();
|
|
48
|
+
|
|
49
|
+
test('injected env vars are visible to the spawned child process (shell mode)', async () => {
|
|
50
|
+
const secret = await createTestSecret({
|
|
51
|
+
name: `test-secret-realspawn-${Date.now()}`,
|
|
52
|
+
value: '{"INJECTED_KEY": "injected_value"}',
|
|
53
|
+
description: 'Real spawn env injection test'
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// printenv avoids any shell-quoting complexity in the test command string
|
|
57
|
+
const result = await cliWithRealSpawn(
|
|
58
|
+
['aws', '-s', secret.prefixedName, '--', 'printenv', 'INJECTED_KEY'],
|
|
59
|
+
getLocalStackEnv()
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
expect(result.code).toBe(0);
|
|
63
|
+
expect(result.stdout.trim()).toBe('injected_value');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('exit code of child process is propagated on success', async () => {
|
|
67
|
+
const secret = await createTestSecret({
|
|
68
|
+
name: `test-secret-exitcode-ok-${Date.now()}`,
|
|
69
|
+
value: '{"DUMMY": "1"}',
|
|
70
|
+
description: 'Exit code propagation test (success)'
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Use --no-shell so node -e args are passed directly without shell re-parsing
|
|
74
|
+
const result = await cliWithRealSpawn(
|
|
75
|
+
[
|
|
76
|
+
'aws',
|
|
77
|
+
'-s',
|
|
78
|
+
secret.prefixedName,
|
|
79
|
+
'--no-shell',
|
|
80
|
+
'--',
|
|
81
|
+
'node',
|
|
82
|
+
'-e',
|
|
83
|
+
'process.exit(0)'
|
|
84
|
+
],
|
|
85
|
+
getLocalStackEnv()
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(result.code).toBe(0);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('exit code of child process is propagated on failure', async () => {
|
|
92
|
+
const secret = await createTestSecret({
|
|
93
|
+
name: `test-secret-exitcode-fail-${Date.now()}`,
|
|
94
|
+
value: '{"DUMMY": "1"}',
|
|
95
|
+
description: 'Exit code propagation test (failure)'
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Use --no-shell so node -e args are passed directly without shell re-parsing
|
|
99
|
+
const result = await cliWithRealSpawn(
|
|
100
|
+
[
|
|
101
|
+
'aws',
|
|
102
|
+
'-s',
|
|
103
|
+
secret.prefixedName,
|
|
104
|
+
'--no-shell',
|
|
105
|
+
'--',
|
|
106
|
+
'node',
|
|
107
|
+
'-e',
|
|
108
|
+
'process.exit(42)'
|
|
109
|
+
],
|
|
110
|
+
getLocalStackEnv()
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(result.code).toBe(42);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('--no-shell passes args directly and env is injected', async () => {
|
|
117
|
+
const secret = await createTestSecret({
|
|
118
|
+
name: `test-secret-noshell-${Date.now()}`,
|
|
119
|
+
value: '{"INJECTED_KEY": "direct_value"}',
|
|
120
|
+
description: 'No-shell spawn test'
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// printenv avoids any shell-quoting complexity in the test command string
|
|
124
|
+
const result = await cliWithRealSpawn(
|
|
125
|
+
[
|
|
126
|
+
'aws',
|
|
127
|
+
'-s',
|
|
128
|
+
secret.prefixedName,
|
|
129
|
+
'--no-shell',
|
|
130
|
+
'--',
|
|
131
|
+
'printenv',
|
|
132
|
+
'INJECTED_KEY'
|
|
133
|
+
],
|
|
134
|
+
getLocalStackEnv()
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(result.code).toBe(0);
|
|
138
|
+
expect(result.stdout.trim()).toBe('direct_value');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
|
|
5
|
+
import { cliWithEnv, cleanupTempFile } from './utils/test-utils';
|
|
6
|
+
import { registerAwsE2eContext } from './utils/aws-e2e-context';
|
|
7
|
+
|
|
8
|
+
describe('AWS Secret Value CLI Args', () => {
|
|
9
|
+
const { getLocalStackEnv } = registerAwsE2eContext();
|
|
10
|
+
|
|
11
|
+
test('should show masked values by default (table output)', async () => {
|
|
12
|
+
const secretName = `e2e-value-masked-${Date.now()}`;
|
|
13
|
+
const tempFile = path.join(
|
|
14
|
+
os.tmpdir(),
|
|
15
|
+
`env-secrets-value-${Date.now()}.env`
|
|
16
|
+
);
|
|
17
|
+
fs.writeFileSync(tempFile, 'DB_PASSWORD=super-secret\nAPI_KEY=abc123');
|
|
18
|
+
|
|
19
|
+
const createResult = await cliWithEnv(
|
|
20
|
+
['aws', 'secret', 'upsert', '--file', tempFile, '--name', secretName],
|
|
21
|
+
getLocalStackEnv()
|
|
22
|
+
);
|
|
23
|
+
expect(createResult.code).toBe(0);
|
|
24
|
+
|
|
25
|
+
const valueResult = await cliWithEnv(
|
|
26
|
+
['aws', 'secret', 'value', '-n', secretName],
|
|
27
|
+
getLocalStackEnv()
|
|
28
|
+
);
|
|
29
|
+
expect(valueResult.code).toBe(0);
|
|
30
|
+
expect(valueResult.stdout).toContain('DB_PASSWORD');
|
|
31
|
+
expect(valueResult.stdout).toContain('API_KEY');
|
|
32
|
+
expect(valueResult.stdout).toContain('****');
|
|
33
|
+
expect(valueResult.stdout).not.toContain('super-secret');
|
|
34
|
+
expect(valueResult.stdout).not.toContain('abc123');
|
|
35
|
+
|
|
36
|
+
const deleteResult1 = await cliWithEnv(
|
|
37
|
+
[
|
|
38
|
+
'aws',
|
|
39
|
+
'secret',
|
|
40
|
+
'delete',
|
|
41
|
+
'-n',
|
|
42
|
+
secretName,
|
|
43
|
+
'--force-delete-without-recovery',
|
|
44
|
+
'--yes'
|
|
45
|
+
],
|
|
46
|
+
getLocalStackEnv()
|
|
47
|
+
);
|
|
48
|
+
expect(deleteResult1.code).toBe(0);
|
|
49
|
+
cleanupTempFile(tempFile);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('should reveal values with --reveal flag and warn on stderr', async () => {
|
|
53
|
+
const secretName = `e2e-value-reveal-${Date.now()}`;
|
|
54
|
+
const tempFile = path.join(
|
|
55
|
+
os.tmpdir(),
|
|
56
|
+
`env-secrets-reveal-${Date.now()}.env`
|
|
57
|
+
);
|
|
58
|
+
fs.writeFileSync(tempFile, 'DB_PASSWORD=super-secret\nAPI_KEY=abc123');
|
|
59
|
+
|
|
60
|
+
const createResult = await cliWithEnv(
|
|
61
|
+
['aws', 'secret', 'upsert', '--file', tempFile, '--name', secretName],
|
|
62
|
+
getLocalStackEnv()
|
|
63
|
+
);
|
|
64
|
+
expect(createResult.code).toBe(0);
|
|
65
|
+
|
|
66
|
+
const valueResult = await cliWithEnv(
|
|
67
|
+
['aws', 'secret', 'value', '-n', secretName, '--reveal'],
|
|
68
|
+
getLocalStackEnv()
|
|
69
|
+
);
|
|
70
|
+
expect(valueResult.code).toBe(0);
|
|
71
|
+
expect(valueResult.stdout).toContain('DB_PASSWORD');
|
|
72
|
+
expect(valueResult.stdout).toContain('super-secret');
|
|
73
|
+
expect(valueResult.stdout).toContain('API_KEY');
|
|
74
|
+
expect(valueResult.stdout).toContain('abc123');
|
|
75
|
+
expect(valueResult.stderr).toContain(
|
|
76
|
+
'Warning: displaying sensitive secret values.'
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const deleteResult2 = await cliWithEnv(
|
|
80
|
+
[
|
|
81
|
+
'aws',
|
|
82
|
+
'secret',
|
|
83
|
+
'delete',
|
|
84
|
+
'-n',
|
|
85
|
+
secretName,
|
|
86
|
+
'--force-delete-without-recovery',
|
|
87
|
+
'--yes'
|
|
88
|
+
],
|
|
89
|
+
getLocalStackEnv()
|
|
90
|
+
);
|
|
91
|
+
expect(deleteResult2.code).toBe(0);
|
|
92
|
+
cleanupTempFile(tempFile);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('should output full values as JSON without --reveal', async () => {
|
|
96
|
+
const secretName = `e2e-value-json-${Date.now()}`;
|
|
97
|
+
const tempFile = path.join(
|
|
98
|
+
os.tmpdir(),
|
|
99
|
+
`env-secrets-json-${Date.now()}.env`
|
|
100
|
+
);
|
|
101
|
+
fs.writeFileSync(tempFile, 'DB_PASSWORD=super-secret\nAPI_KEY=abc123');
|
|
102
|
+
|
|
103
|
+
const createResult = await cliWithEnv(
|
|
104
|
+
['aws', 'secret', 'upsert', '--file', tempFile, '--name', secretName],
|
|
105
|
+
getLocalStackEnv()
|
|
106
|
+
);
|
|
107
|
+
expect(createResult.code).toBe(0);
|
|
108
|
+
|
|
109
|
+
const valueResult = await cliWithEnv(
|
|
110
|
+
['aws', 'secret', 'value', '-n', secretName, '--output', 'json'],
|
|
111
|
+
getLocalStackEnv()
|
|
112
|
+
);
|
|
113
|
+
expect(valueResult.code).toBe(0);
|
|
114
|
+
const parsed = JSON.parse(valueResult.stdout) as Record<string, string>;
|
|
115
|
+
expect(parsed.DB_PASSWORD).toBe('super-secret');
|
|
116
|
+
expect(parsed.API_KEY).toBe('abc123');
|
|
117
|
+
|
|
118
|
+
const deleteResult3 = await cliWithEnv(
|
|
119
|
+
[
|
|
120
|
+
'aws',
|
|
121
|
+
'secret',
|
|
122
|
+
'delete',
|
|
123
|
+
'-n',
|
|
124
|
+
secretName,
|
|
125
|
+
'--force-delete-without-recovery',
|
|
126
|
+
'--yes'
|
|
127
|
+
],
|
|
128
|
+
getLocalStackEnv()
|
|
129
|
+
);
|
|
130
|
+
expect(deleteResult3.code).toBe(0);
|
|
131
|
+
cleanupTempFile(tempFile);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('should fail for a non-existent secret', async () => {
|
|
135
|
+
const valueResult = await cliWithEnv(
|
|
136
|
+
['aws', 'secret', 'value', '-n', 'does-not-exist-e2e'],
|
|
137
|
+
getLocalStackEnv()
|
|
138
|
+
);
|
|
139
|
+
expect(valueResult.code).not.toBe(0);
|
|
140
|
+
expect(valueResult.stderr).toContain('not found');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -654,6 +654,84 @@ export function restoreTestProfile(
|
|
|
654
654
|
}
|
|
655
655
|
}
|
|
656
656
|
|
|
657
|
+
/**
|
|
658
|
+
* Like cliWithEnv but does NOT set NODE_ENV=test, so the real spawn path in
|
|
659
|
+
* src/index.ts is exercised instead of the early-return test branch.
|
|
660
|
+
*/
|
|
661
|
+
export async function cliWithRealSpawn(
|
|
662
|
+
args: string[],
|
|
663
|
+
env: Record<string, string>,
|
|
664
|
+
cwd = '.'
|
|
665
|
+
): Promise<CliResult> {
|
|
666
|
+
return new Promise((resolve) => {
|
|
667
|
+
const cleanEnv = { ...process.env };
|
|
668
|
+
delete cleanEnv.AWS_PROFILE;
|
|
669
|
+
delete cleanEnv.AWS_DEFAULT_PROFILE;
|
|
670
|
+
delete cleanEnv.AWS_SESSION_TOKEN;
|
|
671
|
+
delete cleanEnv.AWS_SECURITY_TOKEN;
|
|
672
|
+
delete cleanEnv.AWS_ROLE_ARN;
|
|
673
|
+
delete cleanEnv.AWS_ROLE_SESSION_NAME;
|
|
674
|
+
delete cleanEnv.AWS_WEB_IDENTITY_TOKEN_FILE;
|
|
675
|
+
delete cleanEnv.AWS_WEB_IDENTITY_TOKEN;
|
|
676
|
+
// Deliberately omit NODE_ENV=test so real spawn is used
|
|
677
|
+
delete cleanEnv.NODE_ENV;
|
|
678
|
+
|
|
679
|
+
const defaultEnv = {
|
|
680
|
+
AWS_ENDPOINT_URL: process.env.LOCALSTACK_URL || 'http://localhost:4566',
|
|
681
|
+
AWS_ACCESS_KEY_ID: 'test',
|
|
682
|
+
AWS_SECRET_ACCESS_KEY: 'test',
|
|
683
|
+
AWS_DEFAULT_REGION: 'us-east-1',
|
|
684
|
+
AWS_REGION: 'us-east-1'
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
const envVars = { ...cleanEnv, ...defaultEnv, ...env };
|
|
688
|
+
const cliPath = path.resolve('./dist/index');
|
|
689
|
+
const spawnArgs = [cliPath, ...args];
|
|
690
|
+
const command = `node ${cliPath} ${args.join(' ')}`;
|
|
691
|
+
|
|
692
|
+
debugLog(`Running CLI command (real spawn): ${command}`);
|
|
693
|
+
|
|
694
|
+
const child = spawn('node', spawnArgs, { cwd, env: envVars });
|
|
695
|
+
|
|
696
|
+
let stdout = '';
|
|
697
|
+
let stderr = '';
|
|
698
|
+
child.stdout.on('data', (chunk: Buffer) => {
|
|
699
|
+
stdout += chunk.toString();
|
|
700
|
+
});
|
|
701
|
+
child.stderr.on('data', (chunk: Buffer) => {
|
|
702
|
+
stderr += chunk.toString();
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
child.on('close', (code: number | null, signal: NodeJS.Signals | null) => {
|
|
706
|
+
const exitCode = code ?? (signal ? 1 : 0);
|
|
707
|
+
const errorMessage = signal
|
|
708
|
+
? `Process terminated by signal ${signal}`
|
|
709
|
+
: exitCode !== 0
|
|
710
|
+
? `Process exited with code ${exitCode}`
|
|
711
|
+
: null;
|
|
712
|
+
const result = {
|
|
713
|
+
code: exitCode,
|
|
714
|
+
error: errorMessage ? new Error(errorMessage) : null,
|
|
715
|
+
stdout,
|
|
716
|
+
stderr
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
if (exitCode !== 0) {
|
|
720
|
+
debugError(
|
|
721
|
+
signal
|
|
722
|
+
? `CLI command failed: terminated by signal ${signal}`
|
|
723
|
+
: `CLI command failed with code ${exitCode}`
|
|
724
|
+
);
|
|
725
|
+
debugError(`Command: ${command}`);
|
|
726
|
+
debugError(`Stdout: ${result.stdout}`);
|
|
727
|
+
debugError(`Stderr: ${result.stderr}`);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
resolve(result);
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
|
|
657
735
|
export async function checkAwslocalInstalled(): Promise<void> {
|
|
658
736
|
try {
|
|
659
737
|
await execAwslocalCommand('awslocal --version', {});
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
readStdin,
|
|
12
12
|
renderTable,
|
|
13
13
|
resolveAwsScope,
|
|
14
|
+
resolveOutputFormat,
|
|
14
15
|
resolveSecretValue
|
|
15
16
|
} from '../../src/cli/helpers';
|
|
16
17
|
|
|
@@ -214,4 +215,38 @@ describe('cli/helpers', () => {
|
|
|
214
215
|
region: 'us-west-2'
|
|
215
216
|
});
|
|
216
217
|
});
|
|
218
|
+
|
|
219
|
+
describe('resolveOutputFormat', () => {
|
|
220
|
+
it('uses explicit local output option', () => {
|
|
221
|
+
expect(resolveOutputFormat({ output: 'json' })).toBe('json');
|
|
222
|
+
expect(resolveOutputFormat({ output: 'table' })).toBe('table');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('throws for invalid local output option', () => {
|
|
226
|
+
expect(() => resolveOutputFormat({ output: 'xml' })).toThrow(
|
|
227
|
+
'Invalid output format'
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('inherits json or table from global options', () => {
|
|
232
|
+
const jsonCmd = { optsWithGlobals: () => ({ output: 'json' }) };
|
|
233
|
+
const tableCmd = { optsWithGlobals: () => ({ output: 'table' }) };
|
|
234
|
+
expect(resolveOutputFormat({}, jsonCmd)).toBe('json');
|
|
235
|
+
expect(resolveOutputFormat({}, tableCmd)).toBe('table');
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('ignores a file path in global output and defaults to table', () => {
|
|
239
|
+
const command = { optsWithGlobals: () => ({ output: 'secrets.env' }) };
|
|
240
|
+
expect(resolveOutputFormat({}, command)).toBe('table');
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('defaults to table when no options are provided', () => {
|
|
244
|
+
expect(resolveOutputFormat({})).toBe('table');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('prefers local option over global option', () => {
|
|
248
|
+
const command = { optsWithGlobals: () => ({ output: 'json' }) };
|
|
249
|
+
expect(resolveOutputFormat({ output: 'table' }, command)).toBe('table');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
217
252
|
});
|