Skip to content

feat(secret): add inject command to substitute secret references in template files#5747

Open
Andarius wants to merge 9 commits into
scaleway:masterfrom
Andarius:feat/secret-inject-fork
Open

feat(secret): add inject command to substitute secret references in template files#5747
Andarius wants to merge 9 commits into
scaleway:masterfrom
Andarius:feat/secret-inject-fork

Conversation

@Andarius

@Andarius Andarius commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Community Note

  • Please vote on this pull request by adding a 👍 reaction to the original pull request comment to help the community and maintainers prioritize this request.
  • Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for pull request followers and do not help prioritize the request

Summary

Adds a scw secret inject command that substitutes {{ scw://REFERENCE }} placeholders in template files or stdin with live secret values fetched from the Scaleway Secret Manager API. Multiple distinct secrets are fetched concurrently.
Similar to op inject and pass-cli inject

Usage

# Render a template to stdout
scw secret inject in-file=config.tmpl

# Write output to a file (default mode 0600)
scw secret inject in-file=config.tmpl out-file=/etc/app/config

# Pipe from stdin
echo "password={{ scw://my-app/db-password }}" | scw secret inject

Reference syntax

Pattern Meaning
UUID secret by ID, latest revision
UUID@REVISION secret by ID, specific revision
UUID:FIELD secret by ID, extract JSON field
NAME or PATH/NAME secret by name/path, latest revision
PATH/NAME@REVISION:FIELD name/path with revision and JSON field

REVISION accepts an integer, latest, or latest_enabled.
FIELD extracts a top-level string key from a JSON-encoded secret value.

Changes

  • internal/namespaces/secret/v1beta1/custom_inject.go — new secret inject command; parses all {{ scw://... }} references, fetches them concurrently via errgroup, then renders the output
  • internal/namespaces/secret/v1beta1/custom_inject_test.go — unit tests for parsing, resolution, and rendering
  • docs/commands/secret.md — generated docs for the new command
feat(secret): add `inject` command to substitute secret references in template files

Andarius added 8 commits June 23, 2026 20:44
Adds `scw secret inject` which substitutes `{{ scw://REFERENCE }}` placeholders
in a template file with the actual secret values fetched from Scaleway Secret Manager.

- Reads from --in-file or stdin, writes to --out-file or stdout
- Output files default to 0600 permissions (configurable via --file-mode)
- Supports lookup by secret ID (UUID) or by path/name
- Supports optional @revision and :FIELD (JSON key extraction) suffixes
- Deduplicates identical references — one API call per unique reference

Claude-Session: https://claude.ai/code/session_013hva3ABc7raJdEDkrW33N1
- Use errors.New for static error messages (perfsprint)
- Add blank lines before return statements (nlreturn)
- Extract revisionLatest constant (goconst)
- Move test to package secret_test via export_test.go (testpackage)
- Add constants for repeated strings in tests (goconst)
- Fix long line in secretInjectRun (golines)
- Run scw-doc-gen to update docs/commands/secret.md

Claude-Session: https://claude.ai/code/session_013hva3ABc7raJdEDkrW33N1
- Reorder test imports alphabetically in single external block (gci)
- Split long fmt.Errorf call across lines (golines)

Claude-Session: https://claude.ai/code/session_013hva3ABc7raJdEDkrW33N1
- Fix golden file: help outputs to STDERR in fork (no --list-sub-commands flag)
- Fix autocomplete.md: use /bin/bash as shell default to match CI env

Claude-Session: https://claude.ai/code/session_013hva3ABc7raJdEDkrW33N1
Unique references are now resolved in parallel using errgroup,
reducing latency from O(N) sequential API calls to O(1) wall-clock
time for N independent secrets.

Claude-Session: https://claude.ai/code/session_013hva3ABc7raJdEDkrW33N1
- Move 'sync' into stdlib import block (no blank line separator)
- Remove p := p loop copy (unnecessary since Go 1.22)

Claude-Session: https://claude.ai/code/session_013hva3ABc7raJdEDkrW33N1
@Andarius Andarius requested review from a team and remyleone as code owners June 24, 2026 05:48
@github-actions github-actions Bot added the secret Managed Secret Manager issues, bugs and feature requests label Jun 24, 2026
)
}

if err := os.WriteFile(args.OutFile, []byte(rendered), os.FileMode(mode)); err != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

os.WriteFile only applies the 0600 mode when it creates the file. If out-file already exists (e.g. 0644), it's overwritten but keeps its old permissions, so the plaintext secret can leak into a world-readable file despite the requested 0600.

Enforce it explicitly:

f, err := os.OpenFile(args.OutFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(mode))
if err != nil {
    return nil, fmt.Errorf("writing output file: %w", err)
}
defer f.Close()
if err := f.Chmod(os.FileMode(mode)); err != nil { // O_CREATE doesn't re-chmod existing files
    return nil, fmt.Errorf("setting output file permissions: %w", err)
}
if _, err := f.Write([]byte(rendered)); err != nil {
    return nil, fmt.Errorf("writing output file: %w", err)
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @jremy42, should be ok in latest commit.

`os.WriteFile` only applies the mode when it creates the file, so writing
to a pre-existing `out-file` (e.g. `0644`) kept its old, possibly
world-readable permissions and could leak the rendered secret.

- replace `os.WriteFile` with a `writeOutputFile` helper that opens with
  `O_CREATE|O_TRUNC` and `Chmod`s before writing, enforcing the requested
  mode even when the file already exists
- add `Test_WriteOutputFile_EnforcesModeOnExistingFile` regression test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

secret Managed Secret Manager issues, bugs and feature requests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants