Skip to content

feat(secrets): add interactive masked prompt for secrets set#4861

Open
sbs44 wants to merge 3 commits intosupabase:developfrom
sbs44:feat/secrets-set-interactive-prompt
Open

feat(secrets): add interactive masked prompt for secrets set#4861
sbs44 wants to merge 3 commits intosupabase:developfrom
sbs44:feat/secrets-set-interactive-prompt

Conversation

@sbs44
Copy link

@sbs44 sbs44 commented Feb 16, 2026

Problem

supabase secrets set MY_SECRET=value exposes secret values in cleartext in shell history. There is no way to set secrets interactively without them being recorded.

Solution

Add an interactive masked prompt when a secret name is provided without a value:

$ supabase secrets set MY_SECRET
Paste your secret for MY_SECRET: ********
Finished supabase secrets set.
  • Asterisks echo per character so users can verify input length
  • Ctrl+C cleanly cancels the prompt
  • Backspace works as expected
  • Non-interactive terminals get a clear error with guidance to use KEY=VALUE format
  • SUPABASE_ prefixed names are skipped before prompting

Backwards compatibility is fully maintained — KEY=VALUE args and --env-file work as before. Mixed usage also works: supabase secrets set KEY1=val KEY2 sets KEY1 inline and prompts for KEY2.

Implementation

  • Inject a promptSecret callback into ListSecrets — callers like serve.go pass nil to preserve existing behavior
  • readMaskedInput uses term.MakeRaw for raw terminal mode with byte-by-byte reading
  • Test suite with 20+ test cases covering the prompt callback contract and byte-handling logic

Related

Summary by CodeRabbit

  • New Features

    • Interactive prompting for secret names provided without values; input is masked with asterisks.
    • Non-interactive guard: prompting is disallowed in non-TTY sessions with a clear error.
    • Reserved-name handling: names prefixed with SUPABASE_ are skipped with a warning and not prompted.
  • Documentation

    • CLI usage updated to show optional NAME and note interactive prompting; guidance for non-interactive usage added.
  • Tests

    • Expanded tests for prompting flow, masking, reserved-name handling, and prompt error propagation.

- Prompt for secret values when name provided without value
- Display asterisks per character for input length verification
- Handle Ctrl+C, backspace, and non-printable character filtering
- Maintain backwards compatibility with NAME=VALUE inline syntax
- Skip SUPABASE_ prefixed names before prompting
@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Adds interactive masked prompting to secrets set for bare secret names, updates ListSecrets to accept a prompt callback, skips SUPABASE_ names, errors in non-interactive contexts, adds per-byte masked input with asterisks, updates tests, and adjusts a call site to preserve non-interactive behavior. (45 words)

Changes

Cohort / File(s) Summary
CLI Command Updates
cmd/secrets.go
Usage/help text updated to show optional NAME and note interactive prompting when a name is provided without a value.
Secrets Set Core Logic
internal/secrets/set/set.go
ListSecrets signature extended to accept promptSecret func(string) (string, error); implements interactive prompting for bare names, skips SUPABASE_-prefixed names with a warning, guards non-interactive environments, and preserves inline/--env-file parsing.
Serve Function Integration
internal/functions/serve/serve.go
Call to ListSecrets updated to pass nil for the prompt callback to retain non-interactive serve behavior.
Masked Input Utility
internal/utils/credentials/input.go
Added PromptMaskedWithAsterisks(stdin *os.File) (string, error) and per-byte masked input handling (echo asterisks, backspace handling, Ctrl+C handling).
Secrets Set Tests
internal/secrets/set/set_test.go
Reworked tests: replaced malformed-pair test with non-interactive prompt error test; added TestListSecrets covering prompting flows, mixed inline/prompted secrets, prompt error propagation, SUPABASE_ prefix handling, and prompt counts.
Masked Input Tests
internal/utils/credentials/input_test.go
Added tests for masked-input behavior (Enter/newline, Ctrl+C, backspace, non-printables, EOF) and TTY error handling for PromptMaskedWithAsterisks.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant CLI as CLI (secrets set)
participant List as ListSecrets (builder)
participant Prompt as Prompt Utility
participant User as Terminal (stdin)
participant API as Secrets API

CLI->>List: pass args (NAME or NAME=VALUE, --env-file)
alt bare NAME without value
    List->>Prompt: request value for NAME
    Prompt->>User: prompt "Paste your secret for NAME:"
    User-->>Prompt: enter masked value (asterisks echoed)
    Prompt-->>List: return secret value
else inline NAME=VALUE or env-file
    List-->>List: parse inline or file values
end
List->>API: assemble CreateSecretBody and send request

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(secrets): add interactive masked prompt for secrets set' clearly and accurately summarizes the main change: adding an interactive masked prompt feature to the secrets set command.
Linked Issues check ✅ Passed All coding requirements from issue #4860 are met: interactive masked prompting for bare secret names, asterisk echoing per character, Ctrl+C cancellation, backspace support, backward compatibility with KEY=VALUE format, mixed usage support, SUPABASE_ prefix skipping, and non-interactive terminal error handling.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the PR objectives: secrets command help text updates, ListSecrets callback injection, masked input utilities, comprehensive test coverage, and no extraneous modifications unrelated to the feature requirements.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into develop

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@sbs44 sbs44 force-pushed the feat/secrets-set-interactive-prompt branch from 324a073 to d9c11cb Compare February 16, 2026 03:23
- Check read byte count before accessing buffer to fix G602 gosec warnings
- Explicitly discard term.Restore error to fix errcheck warning
@sbs44 sbs44 force-pushed the feat/secrets-set-interactive-prompt branch from d9c11cb to e6c8b8f Compare February 16, 2026 03:27
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@internal/utils/credentials/input.go`:
- Around line 24-53: The readMaskedInput function currently only accepts
printable ASCII bytes (32–126) and drops bytes ≥128, corrupting UTF‑8/binary
secrets; update the printable branch in readMaskedInput so that any non‑control
byte is treated as input: keep the existing special cases for Ctrl+C (ch==3),
Enter (ch==13||ch==10) and Backspace/Delete (ch==127||ch==8), and replace the
"ch >= 32 && ch < 127" condition with a broader check such as "ch >= 32 && ch !=
127" or "ch >= 32" (so bytes >=128 are accepted), appending the raw byte to buf
and echoing '*', ensuring secrets containing non‑ASCII bytes are preserved.

Bytes >= 128 were silently dropped, corrupting UTF-8 secrets.
@7ttp 7ttp added the enhancement New feature or request label Feb 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: interactive masked prompt for secrets set

2 participants

Comments