Skip to content

feat: redact secrets in job agent UI#864

Merged
jsbroks merged 1 commit intomainfrom
tfe-ui-redaction
Mar 25, 2026
Merged

feat: redact secrets in job agent UI#864
jsbroks merged 1 commit intomainfrom
tfe-ui-redaction

Conversation

@zacharyblasczyk
Copy link
Copy Markdown
Member

@zacharyblasczyk zacharyblasczyk commented Mar 24, 2026

Summary

  • Show [REDACTED] for TFE token and ArgoCD API key in job agent config cards
  • Redact sensitive keys (token, apiKey, secret) in the generic fallback config renderer
  • Persist failure status when TFE config parsing or workspace templating fails early (bug fix)

Replaces #858 — keeps token in the per-agent config rather than moving to env var.

Summary by CodeRabbit

  • Bug Fixes

    • Job dispatch now properly reports failures when configuration parsing or workspace templating fails.
  • New Features

    • Sensitive configuration fields (tokens, API keys, secrets) are now automatically redacted in configuration displays for improved security.
    • Configuration displays are now unified across different agent and deployment components for consistency.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 24, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 24, 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. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e1733f53-d90c-4065-b00a-061ed6dd3be7

📥 Commits

Reviewing files that changed from the base of the PR and between 331ab34 and 86c4672.

📒 Files selected for processing (5)
  • apps/web/app/components/config-entry.tsx
  • apps/web/app/routes/ws/deployments/settings/_components/DeploymentAgentCard.tsx
  • apps/web/app/routes/ws/runners/JobAgentCard.tsx
  • apps/web/app/routes/ws/runners/card-contents/TfeConfig.tsx
  • apps/workspace-engine/svc/controllers/jobdispatch/jobagents/terraformcloud/tfe.go
✅ Files skipped from review due to trivial changes (2)
  • apps/web/app/routes/ws/deployments/settings/_components/DeploymentAgentCard.tsx
  • apps/web/app/components/config-entry.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/workspace-engine/svc/controllers/jobdispatch/jobagents/terraformcloud/tfe.go
  • apps/web/app/routes/ws/runners/card-contents/TfeConfig.tsx
  • apps/web/app/routes/ws/runners/JobAgentCard.tsx

📝 Walkthrough

Walkthrough

This PR refactors configuration entry rendering across multiple web components by extracting repeated inline markup into a reusable ConfigEntry component that automatically redacts sensitive keys. Additionally, it adds job status failure tracking for early-exit errors in Terraform Cloud dispatch and displays a redacted token field in the TfeConfig UI.

Changes

Cohort / File(s) Summary
Configuration Entry Component
apps/web/app/components/config-entry.tsx
New reusable component that renders key-value pairs with automatic redaction of sensitive keys (token, apiKey, secret), displaying [REDACTED] for sensitive values or formatted JSON/string content otherwise.
Web Component Refactoring
apps/web/app/routes/ws/runners/JobAgentCard.tsx, apps/web/app/routes/ws/deployments/settings/_components/DeploymentAgentCard.tsx
Replaced inline config entry rendering with the new ConfigEntry component, removing duplicated layout and value-formatting logic across both components.
Config Display Enhancement
apps/web/app/routes/ws/runners/card-contents/TfeConfig.tsx
Added a new "Token" row in the UI displaying a static [REDACTED] placeholder alongside existing "Address" and "Organization" rows.
Backend Error Handling
apps/workspace-engine/svc/controllers/jobdispatch/jobagents/terraformcloud/tfe.go
Enhanced Dispatch function to persist JobStatusFailure updates on two additional early-exit error paths: after config parsing and workspace templating failures.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • jsbroks
  • adityachoudhari26

Poem

🐰 Config entries, once spread so wide,
Now bundled snug where secrets hide,
Redacted tokens, clean and bright,
One component sets things right! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: redact secrets in job agent UI' directly and accurately describes the main feature being implemented: adding secret redaction in the job agent UI components.

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

✨ 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 tfe-ui-redaction

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 and usage tips.

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (2)
apps/web/app/routes/ws/runners/JobAgentCard.tsx (2)

60-78: Move sensitiveKeys outside the .map() callback.

The new Set(...) is recreated on every iteration, which is wasteful. Define it once outside the callback.

♻️ Proposed refactor
+  const sensitiveKeys = new Set(["token", "apiKey", "secret"]);
   return (
     <Card className="scrollbar-thin scrollbar-thumb-muted-foreground/20 scrollbar-track-muted overflow-auto rounded-md bg-accent p-2">
       <CardContent className="space-y-0.5 p-0">
         {entries
           .sort((a, b) => a[0].localeCompare(b[0]))
           .map(([key, value]) => {
-            const sensitiveKeys = new Set(["token", "apiKey", "secret"]);
             const isSensitive = sensitiveKeys.has(key);
             return (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/routes/ws/runners/JobAgentCard.tsx` around lines 60 - 78, The
code recreates the Set of sensitive keys inside the .map() callback in
JobAgentCard (sensitiveKeys), which is wasteful; move the declaration const
sensitiveKeys = new Set(["token","apiKey","secret"]); above the .map() call
(outside the render mapping function) and then use that same sensitiveKeys
inside the map to compute isSensitive without reallocating each iteration.

61-62: Consider case-insensitive matching and additional sensitive keys.

The current check is case-sensitive and covers a limited set of keys. Keys like "Token", "API_KEY", "password", or "apiSecret" would not be redacted.

💡 Optional: Broader sensitive key detection
-            const sensitiveKeys = new Set(["token", "apiKey", "secret"]);
-            const isSensitive = sensitiveKeys.has(key);
+            const sensitivePatterns = ["token", "apikey", "secret", "password", "credential"];
+            const isSensitive = sensitivePatterns.some((pattern) =>
+              key.toLowerCase().includes(pattern)
+            );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/routes/ws/runners/JobAgentCard.tsx` around lines 61 - 62, The
redaction check in JobAgentCard.tsx currently uses a case-sensitive Set
(sensitiveKeys) and only a few names; update the logic in the
sensitiveKeys/isSensitive area to perform case-insensitive matching and cover
more variants by (1) normalizing the inspected key with key.toLowerCase() before
checking, (2) expanding the sensitive list to include common names like
"password", "passwd", "api_key", "apikey", "api-secret", "secret", "token",
"credential", "auth", and (3) applying substring/regex matching for
tokens/keys/secrets (e.g., if lowerKey.includes("token") ||
lowerKey.includes("key") || lowerKey.includes("secret") ||
lowerKey.includes("password")) so "API_KEY", "apiSecret", "Password" and similar
variants are redacted by isSensitive.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/app/routes/ws/runners/card-contents/ArgoCDConfig.tsx`:
- Around line 17-24: The anchor currently always prepends "https://" to
config.serverUrl, causing double-protocols; update the JSX in ArgoCDConfig (the
<a> element that uses config.serverUrl) to first normalize the URL by checking
if config.serverUrl already starts with a protocol (e.g., /^https?:\/\//i or use
the URL constructor) and only prepend "https://" when no protocol is present,
then use the normalized value for href while keeping the displayed text as
config.serverUrl (or a normalized/trimmed form if desired).

---

Nitpick comments:
In `@apps/web/app/routes/ws/runners/JobAgentCard.tsx`:
- Around line 60-78: The code recreates the Set of sensitive keys inside the
.map() callback in JobAgentCard (sensitiveKeys), which is wasteful; move the
declaration const sensitiveKeys = new Set(["token","apiKey","secret"]); above
the .map() call (outside the render mapping function) and then use that same
sensitiveKeys inside the map to compute isSensitive without reallocating each
iteration.
- Around line 61-62: The redaction check in JobAgentCard.tsx currently uses a
case-sensitive Set (sensitiveKeys) and only a few names; update the logic in the
sensitiveKeys/isSensitive area to perform case-insensitive matching and cover
more variants by (1) normalizing the inspected key with key.toLowerCase() before
checking, (2) expanding the sensitive list to include common names like
"password", "passwd", "api_key", "apikey", "api-secret", "secret", "token",
"credential", "auth", and (3) applying substring/regex matching for
tokens/keys/secrets (e.g., if lowerKey.includes("token") ||
lowerKey.includes("key") || lowerKey.includes("secret") ||
lowerKey.includes("password")) so "API_KEY", "apiSecret", "Password" and similar
variants are redacted by isSensitive.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8ea86414-ff91-475a-8e56-759a01114a1f

📥 Commits

Reviewing files that changed from the base of the PR and between d1f9848 and accf4c8.

📒 Files selected for processing (4)
  • apps/web/app/routes/ws/runners/JobAgentCard.tsx
  • apps/web/app/routes/ws/runners/card-contents/ArgoCDConfig.tsx
  • apps/web/app/routes/ws/runners/card-contents/TfeConfig.tsx
  • apps/workspace-engine/svc/controllers/jobdispatch/jobagents/terraformcloud/tfe.go

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (1)
apps/workspace-engine/svc/controllers/jobdispatch/jobagents/terraformcloud/tfe_test.go (1)

14-16: Consider adding test cleanup to restore global state.

The setTFEToken helper mutates config.Global.TFEToken directly. While tests appear to run sequentially, adding cleanup (e.g., via t.Cleanup) would make tests more robust and prevent subtle issues if parallelism is introduced later.

Optional: Add cleanup helper
 func setTFEToken(token string) {
 	config.Global.TFEToken = token
 }
+
+func setTFETokenWithCleanup(t *testing.T, token string) {
+	t.Helper()
+	old := config.Global.TFEToken
+	config.Global.TFEToken = token
+	t.Cleanup(func() { config.Global.TFEToken = old })
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/workspace-engine/svc/controllers/jobdispatch/jobagents/terraformcloud/tfe_test.go`
around lines 14 - 16, The helper setTFEToken mutates config.Global.TFEToken
without restoring it, which can leak state into other tests; update callers to
use a version that saves the old value and registers a t.Cleanup to restore it
(or change setTFEToken to accept testing.T and call t.Cleanup) so that
config.Global.TFEToken is reset after each test, referencing the setTFEToken
helper and the config.Global.TFEToken global and using t.Cleanup to perform the
restore.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.compose:
- Line 37: Remove the stray trailing single quote from the inline comment on the
TFE_WEBHOOK_SECRET line so the comment reads "# openssl rand -hex 32" instead of
"# openssl rand -hex 32'"; update the comment text adjacent to the
TFE_WEBHOOK_SECRET entry in the .env.compose file to eliminate the extraneous
quote.

---

Nitpick comments:
In
`@apps/workspace-engine/svc/controllers/jobdispatch/jobagents/terraformcloud/tfe_test.go`:
- Around line 14-16: The helper setTFEToken mutates config.Global.TFEToken
without restoring it, which can leak state into other tests; update callers to
use a version that saves the old value and registers a t.Cleanup to restore it
(or change setTFEToken to accept testing.T and call t.Cleanup) so that
config.Global.TFEToken is reset after each test, referencing the setTFEToken
helper and the config.Global.TFEToken global and using t.Cleanup to perform the
restore.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 642a7db3-358a-4977-9764-4fa484947177

📥 Commits

Reviewing files that changed from the base of the PR and between accf4c8 and 421db6a.

📒 Files selected for processing (9)
  • .env.compose
  • apps/web/app/routes/ws/runners/card-contents/TfeConfig.tsx
  • apps/workspace-engine/oapi/spec/schemas/jobs.jsonnet
  • apps/workspace-engine/pkg/config/env.go
  • apps/workspace-engine/svc/controllers/jobdispatch/jobagents/terraformcloud/config.go
  • apps/workspace-engine/svc/controllers/jobdispatch/jobagents/terraformcloud/tfe.go
  • apps/workspace-engine/svc/controllers/jobdispatch/jobagents/terraformcloud/tfe_test.go
  • docs/integrations/job-agents/terraform-cloud.mdx
  • packages/trpc/src/routes/job-agents.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/workspace-engine/svc/controllers/jobdispatch/jobagents/terraformcloud/tfe.go
  • apps/web/app/routes/ws/runners/card-contents/TfeConfig.tsx

Copy link
Copy Markdown
Contributor

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/web/app/routes/ws/deployments/settings/_components/DeploymentAgentCard.tsx`:
- Around line 97-101: The UI currently only checks top-level keys
(sensitiveKeys) and then JSON.stringify()s nested objects, leaking nested
secrets; add a shared renderer used in both DeploymentAgentCard render sites
that deep-redacts any object/array by recursively walking the value and
replacing any property whose key matches sensitiveKeys (or whose key name
contains common secret substrings like "token","apiKey","secret") with
"[REDACTED]" before stringifying; implement this as a helper function (e.g.,
redactValue(value, sensitiveKeys) or renderSecretValue) and use it in place of
the current ternary so both branches call the same redaction + render logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 40f39fcd-0971-4754-b7f6-c882fd065e95

📥 Commits

Reviewing files that changed from the base of the PR and between 421db6a and 8508d53.

📒 Files selected for processing (1)
  • apps/web/app/routes/ws/deployments/settings/_components/DeploymentAgentCard.tsx

Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (1)
apps/web/app/components/config-entry.tsx (1)

1-1: Case-sensitive key matching may miss sensitive fields.

The sensitiveKeys check uses exact case matching. Keys like "Token", "apikey", "API_KEY", or "Secret" from backend responses won't be redacted, potentially leaking credentials.

Consider normalizing to lowercase before matching:

♻️ Proposed fix for case-insensitive matching
-const sensitiveKeys = new Set(["token", "apiKey", "secret"]);
+const sensitiveKeys = new Set(["token", "apikey", "secret"]);

 export function ConfigEntry({
   entryKey,
   value,
   className,
 }: {
   entryKey: string;
   value: unknown;
   className?: string;
 }) {
-  const isSensitive = sensitiveKeys.has(entryKey);
+  const isSensitive = sensitiveKeys.has(entryKey.toLowerCase());

Also applies to: 12-12

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/components/config-entry.tsx` at line 1, The sensitiveKeys set
uses case-sensitive matching and will miss variants like "API_KEY" or "Token";
change the set to contain lowercase entries (e.g., ["token","apikey","secret"])
and ensure all lookups normalize the inspected key to lowercase (e.g., use
key.toLowerCase() when checking sensitiveKeys.has). Update any other uses of
sensitiveKeys (e.g., the second occurrence) to perform the same lowercase
normalization so redaction is case-insensitive.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/app/components/config-entry.tsx`:
- Line 1: The sensitiveKeys set uses case-sensitive matching and will miss
variants like "API_KEY" or "Token"; change the set to contain lowercase entries
(e.g., ["token","apikey","secret"]) and ensure all lookups normalize the
inspected key to lowercase (e.g., use key.toLowerCase() when checking
sensitiveKeys.has). Update any other uses of sensitiveKeys (e.g., the second
occurrence) to perform the same lowercase normalization so redaction is
case-insensitive.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 672582ed-fb70-4f59-91da-9e3774f2ea02

📥 Commits

Reviewing files that changed from the base of the PR and between 8508d53 and 331ab34.

📒 Files selected for processing (3)
  • apps/web/app/components/config-entry.tsx
  • apps/web/app/routes/ws/deployments/settings/_components/DeploymentAgentCard.tsx
  • apps/web/app/routes/ws/runners/JobAgentCard.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/routes/ws/runners/JobAgentCard.tsx

Show [REDACTED] for sensitive fields (token, apiKey, secret) in job
agent config displays. Extract shared ConfigEntry component used by
both the runners page and deployment settings page. Also persist
failure status when TFE config parsing or workspace templating fails.
@jsbroks jsbroks merged commit 879f77e into main Mar 25, 2026
10 checks passed
@jsbroks jsbroks deleted the tfe-ui-redaction branch March 25, 2026 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants