Skip to content

feat: add config set-app-secret to rotate a profile's app secret#1605

Open
luozhixiong01 wants to merge 6 commits into
mainfrom
feat/app-secret-rotate
Open

feat: add config set-app-secret to rotate a profile's app secret#1605
luozhixiong01 wants to merge 6 commits into
mainfrom
feat/app-secret-rotate

Conversation

@luozhixiong01

@luozhixiong01 luozhixiong01 commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

After resetting a bot's app secret on the Lark/Feishu open platform, the local lark-cli keeps using the old secret and has no command to update just that profile's secret — users (and AI agents acting for them) were forced to hack the OS keychain, which fails because lark-cli's secure storage rejects externally created entries. This PR adds config set-app-secret to safely rotate one profile's app secret, and redirects the invalid_client auth-failure hint away from the destructive config init to this new command.

Changes

  • Add config set-app-secret in cmd/config/set_app_secret.go (registered in cmd/config/config.go): reads the new secret from stdin (--app-secret-stdin); without --yes it previews the target (profile + app_id) and exits 10 without reading stdin or writing; verifies the secret via credential.FetchTAT before writing; stores it via core.ForStorage (same primitive as config init/config bind); migrates only the target profile's reference for plaintext/file sources; never touches other profiles, the active selection, or stored logins
  • Split verify-failure exit codes: deterministic invalid_client returns exit 3 (replace the secret), transient/network failure returns a NetworkError exit 4 (retry); neither writes anything
  • Add an optional target{profile, app_id, is_active} field to the error envelope in errs/problem.go and errs/types.go (WithTarget builders), so confirmation and failure envelopes name which bot will change (the field never carries the secret)
  • Redirect ConfigHint(SubtypeInvalidClient) in internal/errclass/classify.go from config init to config set-app-secret (preview-first, no --yes)

Test Plan

  • make unit-test passed
  • validate passed (build / vet / unit / integration / convention guard / security)
  • local-eval passed (E2E 4/4 top-level; 1 subtest skipped — valid-credential success path; skillave N/A — no skill change)
  • acceptance-reviewer passed (9/9 scenarios; 1 skipped — valid-credential success path)
  • manual verification: lark-cli config set-app-secret --app-secret-stdin (preview → exit 10 + target, nothing written); printf bogus | lark-cli config set-app-secret --app-secret-stdin --yes (invalid_client → exit 3, config.json unchanged)

Related Issues

N/A

Summary by CodeRabbit

  • New Features
    • Added config set-app-secret to rotate a bot profile’s app secret via --app-secret-stdin with a preview/confirmation flow (--yes).
  • Bug Fixes
    • Updated recovery guidance for invalid client/secret to point to secret rotation (config set-app-secret) instead of reinitializing.
    • Error responses now include optional structured, non-secret target identity context (profile/app and active state) when applicable.
  • Tests
    • Added/expanded tests for command shape/flags, confirmation gating, stdin validation, verification-before-write behavior, migration/write-by-source behavior, and output/error serialization.

@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label Jun 26, 2026
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

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
📝 Walkthrough

Walkthrough

Adds config set-app-secret, target metadata on typed errors, updated invalid-client hints, and tests for confirmation gating, secret verification, config migration, and success output.

Changes

App secret rotation

Layer / File(s) Summary
Target metadata
errs/problem.go, errs/types.go, errs/problem_test.go
Problem can carry optional target data, typed errors can attach it, and JSON serialization is covered by a unit test.
Invalid-client guidance
internal/errclass/classify.go, internal/errclass/classify_internal_test.go, internal/errclass/classify_test.go
Invalid-client recovery hints now point to config set-app-secret --app-secret-stdin, and the hint/classification tests assert the new text.
Command preview gate
cmd/config/config.go, cmd/config/set_app_secret.go, cmd/config/set_app_secret_test.go
The config command exposes set-app-secret; the command resolves the target profile, computes active status, and returns a confirmation-required error when --yes is omitted.
Input validation and verification
cmd/config/set_app_secret.go, cmd/config/set_app_secret_test.go
With --yes, the command requires --app-secret-stdin, reads one trimmed secret line, verifies it through credential.FetchTAT, and maps invalid-client or transport failures to typed errors.
Storage and migration
cmd/config/set_app_secret.go, cmd/config/set_app_secret_test.go
On success, the command stores the secret, conditionally rewrites the target config entry, and the write tests cover keychain, plaintext, and file sources.
Success output
cmd/config/set_app_secret.go, cmd/config/set_app_secret_test.go
The command emits JSON in non-terminal mode or formatted terminal output, and the output tests cover migrated and non-migrated success cases.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant setAppSecretRun
  participant credential.FetchTAT
  participant core.ForStorage
  participant core.SaveMultiAppConfig
  participant stdout

  User->>setAppSecretRun: run config set-app-secret
  alt --yes not set
    setAppSecretRun-->>User: confirmation-required error with target
  else --yes set
    setAppSecretRun->>credential.FetchTAT: verify secret with 3-second timeout
    alt verification succeeds
      credential.FetchTAT-->>setAppSecretRun: verified
      setAppSecretRun->>core.ForStorage: store verified secret
      alt config migration is needed
        setAppSecretRun->>core.SaveMultiAppConfig: persist updated app secret ref
      end
      setAppSecretRun->>stdout: write JSON or terminal success output
    else verification fails
      credential.FetchTAT-->>setAppSecretRun: typed config or network error
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Suggested labels

domain/base

Suggested reviewers

  • liangshuo-1

Poem

A bunny hopped with secret care,
Rotating keys from here to there.
Preview first, then verify bright,
Save the change and print it right.
🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely states the main change: adding config set-app-secret to rotate a profile's app secret.
Description check ✅ Passed The description matches the template with Summary, Changes, Test Plan, and Related Issues sections and includes the key implementation and verification details.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/app-secret-rotate

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 62.03008% with 101 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.60%. Comparing base (39d60cb) to head (b0269d6).

Files with missing lines Patch % Lines
cmd/config/set_app_secret.go 63.49% 78 Missing and 14 partials ⚠️
errs/types.go 25.00% 9 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1605      +/-   ##
==========================================
- Coverage   74.64%   74.60%   -0.05%     
==========================================
  Files         806      807       +1     
  Lines       81386    81651     +265     
==========================================
+ Hits        60752    60916     +164     
- Misses      16101    16188      +87     
- Partials     4533     4547      +14     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@b0269d67edb37a3af4719a8a4ad80e9a02724bd5

🧩 Skill update

npx skills add larksuite/cli#feat/app-secret-rotate -y -g

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/config/set_app_secret.go`:
- Around line 157-175: The verification block in set_app_secret should preserve
the original error cause on every failure path so callers can unwrap and match
it later. Update the error returns in the HttpClient initialization failure and
both credential.FetchTAT branches to attach the underlying err via the typed
error’s cause-setting method, using the existing symbols f.HttpClient,
credential.FetchTAT, and the errs.NewNetworkError / errs.NewConfigError
constructors. Also consider reclassifying the HttpClient initialization failure
as an internal error instead of a network transport error, while keeping the
same target and hint behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 865b4459-529f-4ecf-ba2b-a1b257e63601

📥 Commits

Reviewing files that changed from the base of the PR and between d9330b7 and 65ed8ae.

📒 Files selected for processing (9)
  • cmd/config/config.go
  • cmd/config/set_app_secret.go
  • cmd/config/set_app_secret_test.go
  • errs/problem.go
  • errs/problem_test.go
  • errs/types.go
  • internal/errclass/classify.go
  • internal/errclass/classify_internal_test.go
  • internal/errclass/classify_test.go

Comment thread cmd/config/set_app_secret.go
@luozhixiong01 luozhixiong01 force-pushed the feat/app-secret-rotate branch from 65ed8ae to 949f643 Compare June 26, 2026 07:46

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
cmd/config/set_app_secret_test.go (2)

392-399: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pin the new structured target metadata on verify failures.

These checks only prove the app identity indirectly via hint substrings. Since this PR adds target metadata so failure envelopes can identify the affected profile/app, please assert cfgErr.Target / netErr.Target directly as well; otherwise the envelope contract can regress while the hint text still passes.

Also applies to: 438-443

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/config/set_app_secret_test.go` around lines 392 - 399, The verification
tests for set_app_secret are only checking hint substrings, but they should also
lock in the new structured target metadata on failure envelopes. Update the
relevant assertions around cfgErr and netErr in set_app_secret_test.go to
directly verify the Target field (for the app/profile identity) in addition to
the existing Hint checks, using the existing cfgErr and netErr references so the
envelope contract cannot regress while the hint text still passes.

762-767: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert stderr stays empty on successful runs.

The success-path tests only inspect stdout. For commands under cmd/**, stdout is the program-output channel and stderr must stay reserved for warnings/progress, so an accidental mixed-channel success would currently slip through. Capture f.IOStreams.ErrOut and assert it remains empty in both JSON and pretty-mode cases. As per coding guidelines, "stdout is for program output (JSON envelopes), stderr is for progress, warnings, and hints; never mix them."

Also applies to: 776-928

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/config/set_app_secret_test.go` around lines 762 - 767, The success-path
tests are only checking stdout, so mixed-channel output on cmd/** commands could
go unnoticed. Update the relevant test setup around the shared helper and
success cases to capture f.IOStreams.ErrOut and assert it stays empty for both
JSON and pretty-mode runs. Use the existing helper that builds f, kc, and
outBuf, and add stderr assertions alongside the current stdout checks.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/config/set_app_secret_test.go`:
- Around line 122-141: The error-path tests in set_app_secret_test should assert
typed problem metadata instead of only checking concrete type, exit code, or
strings. Update these cases to use errs.ProblemOf(err) to verify
category/subtype/cause preservation, and keep using errors.As only when you need
the Param from *errs.ValidationError because errs.ProblemOf does not expose it.
Apply the same metadata assertions consistently in the other related test blocks
that currently only check type, exit code, or message.
- Around line 450-476: The transport failure test in setAppSecretRun only checks
the wrapped NetworkError and exit code, so it can miss loss of the underlying
cause. Introduce a sentinel transport error in
TestSetAppSecret_VerifyBeforeWrite_NetworkTransport, use it in the fakeRT
handler, and assert with errors.Is after errors.As that the returned error
preserves that cause. Keep the existing setAppSecretFactory,
SetAppSecretOptions, and output.ExitCodeOf checks, and add typed metadata
assertions via errs.ProblemOf for the NetworkError shape if applicable.

---

Nitpick comments:
In `@cmd/config/set_app_secret_test.go`:
- Around line 392-399: The verification tests for set_app_secret are only
checking hint substrings, but they should also lock in the new structured target
metadata on failure envelopes. Update the relevant assertions around cfgErr and
netErr in set_app_secret_test.go to directly verify the Target field (for the
app/profile identity) in addition to the existing Hint checks, using the
existing cfgErr and netErr references so the envelope contract cannot regress
while the hint text still passes.
- Around line 762-767: The success-path tests are only checking stdout, so
mixed-channel output on cmd/** commands could go unnoticed. Update the relevant
test setup around the shared helper and success cases to capture
f.IOStreams.ErrOut and assert it stays empty for both JSON and pretty-mode runs.
Use the existing helper that builds f, kc, and outBuf, and add stderr assertions
alongside the current stdout checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 567d1ba6-39b0-4bc0-a9ae-c7bcaefaec0f

📥 Commits

Reviewing files that changed from the base of the PR and between 65ed8ae and 949f643.

📒 Files selected for processing (9)
  • cmd/config/config.go
  • cmd/config/set_app_secret.go
  • cmd/config/set_app_secret_test.go
  • errs/problem.go
  • errs/problem_test.go
  • errs/types.go
  • internal/errclass/classify.go
  • internal/errclass/classify_internal_test.go
  • internal/errclass/classify_test.go
✅ Files skipped from review due to trivial changes (2)
  • internal/errclass/classify.go
  • internal/errclass/classify_internal_test.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • cmd/config/config.go
  • errs/problem.go
  • errs/problem_test.go
  • cmd/config/set_app_secret.go
  • errs/types.go
  • internal/errclass/classify_test.go

Comment thread cmd/config/set_app_secret_test.go
Comment thread cmd/config/set_app_secret_test.go
Add a config set-app-secret command so users and agents can rotate a
profile's app secret after resetting it on the open platform, instead of
hacking the OS keychain (which fails because the stored value lives in a
closed secure store). The command reads the new value from stdin, previews
the target and exits 10 without --yes, verifies the value via FetchTAT
before writing, stores it through core.ForStorage (the same primitive as
config init and config bind), and leaves other profiles untouched. It also
redirects the invalid_client hint to the new command and adds an optional
target field to the error envelope so the affected bot is named without
exposing the secret.
@luozhixiong01 luozhixiong01 force-pushed the feat/app-secret-rotate branch from e3ced8b to dd0e1e9 Compare June 26, 2026 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant