Skip to content

feat: add workspace-controlled SDK risk protection#2031

Open
liangshuo-1 wants to merge 3 commits into
mainfrom
feat/workspace-sdk-risk-protection
Open

feat: add workspace-controlled SDK risk protection#2031
liangshuo-1 wants to merge 3 commits into
mainfrom
feat/workspace-sdk-risk-protection

Conversation

@liangshuo-1

@liangshuo-1 liangshuo-1 commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add workspace-controlled SDK risk protection for access-token requests while keeping credential-provider trust boundaries explicit. Host signals are collected only for workspace-managed config/keychain credentials and are never injected for environment, auth sidecar, or other extension credentials.

Changes

  • Add a workspace config risk-control policy with default-on, explicit opt-out, and documentation.
  • Preserve account provenance at credential-provider selection instead of inferring it from workspace configuration.
  • Compose SDK transports as an onion chain with the risk-control boundary immediately before the network transport.
  • Strip reserved host-signal headers on every SDK path, then inject validated signals only for authenticated requests to exact official HTTPS OpenAPI origins.
  • Keep generic HTTP, MCP, streaming, environment-credential, and sidecar traffic outside host-signal injection.
  • Add unit, integration, sidecar, route-boundary, and platform-specific tests.

Test Plan

  • Unit tests pass with race detection across cmd, internal, shortcuts, and extension.
  • make sidecar-test
  • go vet -p=1 ./...
  • gofmt -l . produces no output.
  • go mod tidy does not change go.mod or go.sum.
  • Windows and Linux cross-compilation checks pass for internal/riskcontrol.
  • The config risk-control command flow is covered by command-level persistence and validation tests.

Related Issues

  • None

Summary by CodeRabbit

  • New Features
    • Added workspace risk-control settings with on, off, and default options.
    • Added minimal security signals for eligible official API requests.
    • Added safeguards to prevent signals from being sent for unsupported destinations, credential sources, or request types.
  • Documentation
    • Expanded English and Chinese security guidance with signal details, exclusions, and configuration commands.
  • Tests
    • Added coverage for policy settings, credential sources, request routing, and signal filtering.

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

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds configurable workspace risk-control policy, cached credential/config provenance, host OS and product-model signal collection, and SDK transport enforcement for restricted headers on eligible official HTTPS OpenAPI requests.

Changes

Risk-control signal flow

Layer / File(s) Summary
Workspace policy, snapshot, and CLI
internal/core/..., cmd/config/..., README*
Adds persisted riskControl policy with enabled-by-default resolution, invocation-scoped config snapshots, config risk-control on|off|default, command tests, and security documentation.
Credential provenance and shared configuration
internal/credential/...
Caches credential-source provenance and shares one workspace configuration snapshot across credential resolution and SDK setup.
Host signals and outbound transport
internal/riskcontrol/...
Adds OS/product-model discovery and validation plus a transport that strips restricted headers and injects trusted signals only for authorized requests to official HTTPS OpenAPI origins.
Factory SDK wiring and boundary validation
internal/cmdutil/...
Connects workspace policy and credential provenance to the SDK transport, preserves generic HTTP behavior, and tests workspace, external-credential, extension, and sidecar boundaries.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Factory
  participant CredentialProvider
  participant RiskControlTransport
  participant LarkAPI
  CLI->>Factory: Create invocation configuration
  Factory->>CredentialProvider: Resolve account and provenance
  CredentialProvider-->>Factory: Workspace-managed status
  Factory->>RiskControlTransport: Configure SDK transport policy
  RiskControlTransport->>LarkAPI: Eligible request with trusted signals
Loading

Possibly related PRs

  • larksuite/cli#532: Covers the sidecar credential and transport boundary tested for risk-control header non-leakage.
  • larksuite/cli#1213: Refactors the shared HTTP transport construction touched by this SDK wiring.
  • larksuite/cli#1910: Addresses related X-Agent-* security-header transport behavior.

Suggested labels: feature

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.72% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: workspace-controlled SDK risk protection.
Description check ✅ Passed The description follows the template and includes summary, changes, test plan, and related issues.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/workspace-sdk-risk-protection

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 Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.25359% with 35 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.12%. Comparing base (67015ee) to head (1e16cba).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
internal/riskcontrol/transport.go 81.53% 6 Missing and 6 partials ⚠️
internal/riskcontrol/host.go 82.85% 4 Missing and 2 partials ⚠️
cmd/config/risk_control.go 89.79% 3 Missing and 2 partials ⚠️
internal/core/config_snapshot.go 60.00% 3 Missing and 1 partial ⚠️
internal/credential/credential_provider.go 80.95% 2 Missing and 2 partials ⚠️
internal/core/config.go 0.00% 2 Missing ⚠️
internal/credential/default_provider.go 60.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2031      +/-   ##
==========================================
+ Coverage   75.06%   75.12%   +0.05%     
==========================================
  Files         902      911       +9     
  Lines       95944    96425     +481     
==========================================
+ Hits        72025    72439     +414     
- Misses      18380    18413      +33     
- Partials     5539     5573      +34     

☔ 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.

@liangshuo-1
liangshuo-1 marked this pull request as ready for review July 24, 2026 04:08
@github-actions

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#feat/workspace-sdk-risk-protection -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: 3

🤖 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/risk_control.go`:
- Around line 36-40: Update the RunE configuration-loading error path around
core.LoadOrNotConfigured to preserve already typed errors while wrapping raw
failures in the established typed internal/storage error and attaching the
original error with WithCause(err); continue returning successful configuration
results unchanged.

In `@internal/core/config.go`:
- Around line 69-73: Add adjacent unit coverage for
MultiAppConfig.RiskControlEnabled, directly asserting true when RiskControl is
omitted, true when explicitly enabled, and false when explicitly disabled. Keep
the existing explicit-false round-trip test, but ensure the resolver itself is
exercised for all three states.

In `@internal/credential/credential_provider.go`:
- Around line 263-275: Update the token-resolution flow around ResolveAccount so
every read of p.resolution.tokenSource, including the existing fast path, occurs
after participating in accountOnce or under the same synchronization protecting
resolution writes. Preserve the defaultAcct nil behavior and return the resolved
token source or existing error when initialization completes.
🪄 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 Plus

Run ID: a0211d66-767a-4021-8556-dc81e41b2a81

📥 Commits

Reviewing files that changed from the base of the PR and between b8f56db and 1e16cba.

📒 Files selected for processing (32)
  • README.md
  • README.zh.md
  • cmd/config/config.go
  • cmd/config/risk_control.go
  • cmd/config/risk_control_test.go
  • internal/cmdutil/factory_default.go
  • internal/cmdutil/factory_proxy_warn_test.go
  • internal/cmdutil/factory_risk_control_test.go
  • internal/cmdutil/risk_control.go
  • internal/cmdutil/risk_control_route_sidecar_test.go
  • internal/cmdutil/risk_control_test.go
  • internal/cmdutil/secheader_test.go
  • internal/cmdutil/transport_test.go
  • internal/core/config.go
  • internal/core/config_snapshot.go
  • internal/core/config_snapshot_test.go
  • internal/core/config_test.go
  • internal/credential/credential_provider.go
  • internal/credential/credential_provider_test.go
  • internal/credential/default_provider.go
  • internal/credential/integration_test.go
  • internal/riskcontrol/host.go
  • internal/riskcontrol/host_darwin.go
  • internal/riskcontrol/host_darwin_test.go
  • internal/riskcontrol/host_linux.go
  • internal/riskcontrol/host_linux_test.go
  • internal/riskcontrol/host_other.go
  • internal/riskcontrol/host_test.go
  • internal/riskcontrol/host_windows.go
  • internal/riskcontrol/host_windows_test.go
  • internal/riskcontrol/transport.go
  • internal/riskcontrol/transport_test.go

Comment on lines +36 to +40
RunE: func(cmd *cobra.Command, args []string) error {
config, err := core.LoadOrNotConfigured()
if err != nil {
return err
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Return a typed error for configuration-load failures.

Returning err directly can bypass the command error-envelope contract. Pass through already typed errors; wrap raw load failures with a typed internal/storage error and .WithCause(err).

Proposed fix
 			config, err := core.LoadOrNotConfigured()
 			if err != nil {
-				return err
+				if _, ok := errs.ProblemOf(err); ok {
+					return err
+				}
+				return errs.NewInternalError(errs.SubtypeStorage,
+					"failed to load risk-control policy: %v", err).WithCause(err)
 			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
RunE: func(cmd *cobra.Command, args []string) error {
config, err := core.LoadOrNotConfigured()
if err != nil {
return err
}
RunE: func(cmd *cobra.Command, args []string) error {
config, err := core.LoadOrNotConfigured()
if err != nil {
if _, ok := errs.ProblemOf(err); ok {
return err
}
return errs.NewInternalError(errs.SubtypeStorage,
"failed to load risk-control policy: %v", err).WithCause(err)
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 39-39: cmd/config/risk_control.go#L39
Added line #L39 was not covered by tests

🤖 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/risk_control.go` around lines 36 - 40, Update the RunE
configuration-loading error path around core.LoadOrNotConfigured to preserve
already typed errors while wrapping raw failures in the established typed
internal/storage error and attaching the original error with WithCause(err);
continue returning successful configuration results unchanged.

Sources: Coding guidelines, Learnings

Comment thread internal/core/config.go
Comment on lines +69 to +73
// RiskControlEnabled resolves the workspace policy. An omitted preference
// keeps the default-on account-protection behavior.
func (m *MultiAppConfig) RiskControlEnabled() bool {
return m != nil && (m.RiskControl == nil || *m.RiskControl)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Test the default-on resolver directly.

The round-trip test only verifies explicit false; it never calls RiskControlEnabled(). Cover omitted, explicit true, and explicit false so the default-on protection contract cannot regress unnoticed. Based on coding guidelines, “Every behavior change must include an adjacent test.”

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 71-72: internal/core/config.go#L71-L72
Added lines #L71 - L72 were not covered by tests

🤖 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 `@internal/core/config.go` around lines 69 - 73, Add adjacent unit coverage for
MultiAppConfig.RiskControlEnabled, directly asserting true when RiskControl is
omitted, true when explicitly enabled, and false when explicitly disabled. Keep
the existing explicit-false round-trip test, but ensure the resolver itself is
exercised for all three states.

Sources: Coding guidelines, Linters/SAST tools

Comment on lines +263 to +275
if p.resolution.tokenSource != nil {
return p.resolution.tokenSource, nil
}
if p.defaultAcct == nil {
return nil, nil
}
if _, err := p.ResolveAccount(ctx); err != nil {
return nil, err
}
if p.selectedSource == nil {
if p.resolution.tokenSource == nil {
return nil, fmt.Errorf("credential provider resolved an account without selecting a token source")
}
return p.selectedSource, nil
return p.resolution.tokenSource, nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Synchronize the resolution fast path.

This reads p.resolution.tokenSource before participating in accountOnce, while ResolveAccount writes the full resolution inside that sync.Once. Concurrent account and token resolution can therefore race, potentially observing a partially initialized token source. Resolve through ResolveAccount before reading the cached resolution, or guard all resolution reads/writes with a mutex.

🤖 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 `@internal/credential/credential_provider.go` around lines 263 - 275, Update
the token-resolution flow around ResolveAccount so every read of
p.resolution.tokenSource, including the existing fast path, occurs after
participating in accountOnce or under the same synchronization protecting
resolution writes. Preserve the defaultAcct nil behavior and return the resolved
token source or existing error when initialization completes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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