Skip to content

feat(cli): scope secret command for provider credentials - #1473

Merged
Cedric Vidal (cedricvidal) merged 8 commits into
mainfrom
cedricvidal-cli-secret-commands
Oct 10, 2026
Merged

Cedric Vidal (cedricvidal) merged 8 commits into
mainfrom
cedricvidal-cli-secret-commands

Conversation

@cedricvidal

@cedricvidal Cedric Vidal (cedricvidal) commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds a scope secret command group that manages provider credentials through the existing Secrets/Token Manager API (/api/v1/keys), closing a CLI ↔ Portal parity gap. Subcommands: list, get, create, preview, update, delete, validate.

  • Secret values are never printed. Input is via --value-stdin / --api-key-stdin (or flags).
  • New ApiFetchInit.sensitiveBody redacts credential request/response bodies in the API log sink.

Extracted from #1470 as an independent PR (based on main, no dependency on named environments).

Demos

CLI walkthrough recorded against a local Scope stack (API + Token Manager from main, CLI from this branch).

Before

N/A (new command)

After

Create a key from stdin, see server-side validation reject a dummy key, inspect metadata (value never printed), disable, delete.

scope-secret-walkthrough.mp4

Testing

  • cd apps/cli && npx tsc --noEmit: passed
  • pnpm exec vitest run apps/cli/src: passed (155 tests; 18 in secret.test.ts, including no-leak checks on stdout and logs)

Documentation and compatibility

  • apps/cli/README.md: new "Provider secrets" section
  • .agents/skills/scope-cli/SKILL.md: new "Provider Secrets" table
  • Website: new guide "Manage provider credentials" (guides/managing-credentials.md, under Guides → Admin). It covers credential types and capabilities, which services use which credential, every scope secret command, and the safety behaviour. getting-started/access.md now links to it.
  • Additive only. The CLI command snapshot gains the secret group.

Checklist

  • If Portal features changed, keep CLI capabilities in sync. (This adds CLI parity for Portal secrets.)
  • If Portal components changed, update their Storybook stories. N/A
  • If database changes require a migration, include up() / down() and keep it CosmosDB-compatible. N/A
  • If dependencies changed, update the lockfile and regenerate NOTICE / NOTICE-REVIEW.txt with pnpm notice as needed. N/A
  • Video showing the behavior before the suggested change. N/A
  • Video showing the behavior after the suggested change.

Closes a CLI↔Portal parity gap: provider credentials (GitHub, Anthropic,
Azure AI Foundry) could only be managed from the Portal secrets page.

- `secret list|get|create|preview|update|delete|validate` over the existing
  Secrets/Token Manager API (`/api/v1/keys`).
- Secret values are never fetched or printed in any output format; a
  regression test pins this even when the API echoes a value back.
- `ApiFetchInit.sensitiveBody` fully redacts request/response bodies in the
  API log sink for credential registration and validation.
- `--value-stdin`/`--api-key-stdin` inputs; argv secrets print a warning.
- Metadata reads retry transient failures; writes are never replayed.

Extracted from #1470.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added type: documentation Documentation additions, corrections, and improvements. area: cli Scope command-line interface and terminal workflows. language: javascript Work involving JavaScript code, tooling, or dependencies. area: skills Skill registration, installation, management, resolution, and delivery. topic: testing Test coverage, test infrastructure, and validation quality. labels Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Test Results (Node.js 22)

test: Run #211

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
3217 3217 0 0 0 0 0 1m14s

🎉 All tests passed!

Github Test Reporter

Import secret helpers from new shared/token-manager and shared/retry
subpaths instead of the shared root, which pulled ioredis into the
bundle and crashed it outside the monorepo.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the type: dependencies Dependency additions, removals, and version updates. label Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Test Results (Node.js 22)

test: Run #213

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
3217 3217 0 0 0 0 0 1m29s

🎉 All tests passed!

Github Test Reporter

Documents scope secret (create/preview/list/get/validate/update/delete),
credential types and derived capabilities, which services acquire which
capability, and the stdin/redaction safety behaviour.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the topic: ui Portal, visual presentation, layout, and interface components. label Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Test Results (Node.js 22)

test: Run #215

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
3217 3217 0 0 0 0 0 1m25s

🎉 All tests passed!

Github Test Reporter

Define KEY_TYPES once in shared/token-manager and derive KeyType from it.
Token Manager validation and the scope secret CLI now use it instead of
their own copies. Internal types (github-oauth-cookie-state) are marked
INTERNAL_KEY_TYPES and left out of CLI help and errors.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the topic: security Security protections, vulnerabilities, and risk reduction. label Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Test Results (Node.js 22)

test: Run #218

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
3220 3220 0 0 0 0 0 1m28s

🎉 All tests passed!

Github Test Reporter

Drop the internal/user key type split. scope secret help and errors now
list all KEY_TYPES, including github-oauth-cookie-state, which is also
added to the website credentials guide.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Test Results (Node.js 22)

test: Run #219

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
3219 3219 0 0 0 0 0 1m27s

🎉 All tests passed!

Github Test Reporter

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Test Results (Node.js 22)

test: Run #220

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
3219 3219 0 0 0 0 0 1m23s

🎉 All tests passed!

Github Test Reporter

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Test Results (Node.js 22)

test: Run #221

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
3219 3219 0 0 0 0 0 1m11s

🎉 All tests passed!

Github Test Reporter

];
const validationFields: DisplayField<KeyValidationResult>[] = [
{ key: "status", label: "Status" }, { key: "capabilities", label: "Capabilities", formatter: (result) => result.capabilities?.join(", ") ?? "" },
{ key: "error", label: "Error" },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P1: Validation errors can expose the credential. These error fields are printed verbatim, but the existing Anthropic validator can include the complete credential in an error message. I reproduced this with the real validator and built CLI using a fake multiline API key: secret preview --type anthropic-api-key --value-stdin -o json printed Anthropic key validation failed: Headers.append: "fixture-secret\nbad" is an invalid header value. The same error is persisted as lastValidationError and printed by secret get. The output allowlist and sensitiveBody log redaction therefore do not uphold the guarantee that secret values are never printed. Please sanitize validation errors at their source and add regression coverage for credentials echoed inside error/lastValidationError, not just a returned value property.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in 99cb3cb2. Provider exception details and response bodies are no longer included in validation errors, preventing echoed credentials from being persisted as lastValidationError. The CLI also omits stored validation-error details from metadata output, which protects against errors persisted before this change. Regression tests cover credentials echoed by network exceptions and provider responses, and verify validation-error text is excluded from CLI output.

Auto-resolved by the GitHub Copilot app.

Comment thread apps/cli/src/commands/secret.ts Outdated
import { getDefaultApiUrl, withOutputOption } from "../utils/shared.js";
import type { DisplayField, OutputFormat } from "../utils/types.js";

type KeyMetadata = Pick<KeyDocument, "_id" | "type" | "enabled" | "capabilities" | "lastValidationStatus" | "lastValidationError" | "comment">;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P2: Include expiration in the metadata output. create and update let operators set or clear expiresAt, but this type and the keyFields allowlist omit it from every output format. Even when the API returns an expiration, secret get -o json drops it, so operators cannot inspect credential expiration or confirm an expiration update without switching to the Portal. Please include expiresAt in the permitted metadata fields and cover its output after setting/clearing expiration.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in 99cb3cb2. Added expiresAt to the CLI metadata output allowlist. Regression coverage checks create/get, setting expiration, and clearing it in table, JSON, YAML, and TSV output formats.

Auto-resolved by the GitHub Copilot app.

Sanitize provider validation failures before they are returned or stored,
and omit persisted validation error details from CLI metadata output.
Include expiration in credential metadata output and cover set/clear flows.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

Test Results (Node.js 22)

test: Run #227

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
3224 3224 0 0 0 0 0 1m27s

🎉 All tests passed!

Github Test Reporter

@cedricvidal
Cedric Vidal (cedricvidal) merged commit e6ba8c3 into main Oct 10, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Scope command-line interface and terminal workflows. area: skills Skill registration, installation, management, resolution, and delivery. language: javascript Work involving JavaScript code, tooling, or dependencies. topic: security Security protections, vulnerabilities, and risk reduction. topic: testing Test coverage, test infrastructure, and validation quality. topic: ui Portal, visual presentation, layout, and interface components. type: dependencies Dependency additions, removals, and version updates. type: documentation Documentation additions, corrections, and improvements.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants