feat(core): Guard Instance AI prompts against asking for plaintext secrets#28876
feat(core): Guard Instance AI prompts against asking for plaintext secrets#28876
Conversation
There was a problem hiding this comment.
2 issues found across 35 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/cli/src/modules/instance-ai/eval/request-sanitizer.ts">
<violation number="1" location="packages/cli/src/modules/instance-ai/eval/request-sanitizer.ts:62">
P2: Delegating `isSecretKey()` to `isSensitiveInputKey()` drops `ssn` key matching, so fields like `ssn` are no longer redacted in eval request sanitization.</violation>
</file>
<file name="packages/@n8n/instance-ai/src/tracing/langsmith-tracing.ts">
<violation number="1" location="packages/@n8n/instance-ai/src/tracing/langsmith-tracing.ts:367">
P1: Redaction is now executed with unbounded recursion on arbitrary trace values, which can crash on cyclic inputs before depth-limited sanitization runs.</violation>
</file>
Architecture diagram
sequenceDiagram
participant User as User (Browser)
participant UI as Instance AI UI
participant API as AI Controller (CLI)
participant Redact as Redaction Engine
participant Agent as Instance AI Agent
participant Storage as Persistence (TypeORM)
participant Tracing as LangSmith Tracing
Note over User,Tracing: Message Submission Flow
User->>UI: Enter chat message / confirmation text
UI->>Redact: NEW: scanConversationTextForPreflight()
alt NEW: Secret shape detected in UI
UI-->>User: Show SensitiveInputWarning (Block Request)
UI->>UI: Track "Sensitive input blocked" Telemetry
else No secrets detected (Preflight)
UI->>API: POST /chat or /confirm
API->>Redact: CHANGED: assertNoSensitiveConversationInput()
alt NEW: Server-side validation fails
Redact-->>API: matches found
API-->>UI: 422 SensitiveInputBlockedError
UI-->>User: Show Blocked Message
else Validation Success
API->>Agent: Process message
Note over Agent,Redact: Agent Execution Guardrails
Agent->>Agent: CHANGED: Check system prompt (SECRET_ASK_GUARDRAIL)
opt NEW: ask-user tool called
Agent->>Redact: validateAskUserInput()
alt Question attempts to elicit secrets
Redact-->>Agent: Throw Error
end
end
Agent->>Storage: NEW: Save tool-call / resource data
Storage->>Redact: CHANGED: scanAndRedactSensitiveInput()
Redact-->>Storage: return <redacted> values
Storage->>Storage: Persist sanitized JSON to DB
Agent->>Tracing: Log Execution
Tracing->>Redact: NEW: sanitizeTracePayload()
Redact-->>Tracing: Redacted trace data
Tracing->>LangSmith: POST Trace (Sanitized)
Agent-->>API: Agent Response
API-->>UI: Return message (Streaming/Final)
UI-->>User: Display Response
end
end
Note over Storage,Redact: Background/Event Persistence
API->>Storage: publishEvent(threadId, event)
Storage->>Redact: NEW: Redact non-streaming events
Redact-->>Storage: Redacted payload
Storage->>Storage: Save to StoredEvent table
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
| } | ||
|
|
||
| function sanitizeTraceValue(value: unknown, depth = 0): unknown { | ||
| return sanitizeTraceStructureValue(scanAndRedactSensitiveInput(value).sanitizedValue, depth); |
There was a problem hiding this comment.
P1: Redaction is now executed with unbounded recursion on arbitrary trace values, which can crash on cyclic inputs before depth-limited sanitization runs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/@n8n/instance-ai/src/tracing/langsmith-tracing.ts, line 367:
<comment>Redaction is now executed with unbounded recursion on arbitrary trace values, which can crash on cyclic inputs before depth-limited sanitization runs.</comment>
<file context>
@@ -362,20 +363,26 @@ function sanitizeTraceValue(value: unknown, depth = 0): unknown {
}
+function sanitizeTraceValue(value: unknown, depth = 0): unknown {
+ return sanitizeTraceStructureValue(scanAndRedactSensitiveInput(value).sanitizedValue, depth);
+}
+
</file context>
| if (SAFE_KEY_PATTERN.test(key)) return false; | ||
| if (SECRET_KEY_PATTERN.test(key)) return true; | ||
| return false; | ||
| return isSensitiveInputKey(key); |
There was a problem hiding this comment.
P2: Delegating isSecretKey() to isSensitiveInputKey() drops ssn key matching, so fields like ssn are no longer redacted in eval request sanitization.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/modules/instance-ai/eval/request-sanitizer.ts, line 62:
<comment>Delegating `isSecretKey()` to `isSensitiveInputKey()` drops `ssn` key matching, so fields like `ssn` are no longer redacted in eval request sanitization.</comment>
<file context>
@@ -96,8 +59,5 @@ export function truncateForLlm(serialized: string, maxLength = MAX_BODY_LENGTH):
- if (SAFE_KEY_PATTERN.test(key)) return false;
- if (SECRET_KEY_PATTERN.test(key)) return true;
- return false;
+ return isSensitiveInputKey(key);
}
</file context>
Adds explicit guardrails so the orchestrator, sub-agents, builder, and browser-credential-setup prompts all instruct the agent to route secret entry through credential setup, browser handoff, or existing credential selection instead of chat or ask-user answers. Extracts the shared "never ask for plaintext secrets" wording into a single `SECRET_ASK_GUARDRAIL` constant so the orchestrator and sub-agent prompts stay in sync, and lifts the browser-credential-setup prompt into its own module so its wording can be asserted directly.
244adac to
751fd06
Compare
# Conflicts: # packages/@n8n/instance-ai/src/agent/sub-agent-factory.ts
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Instance AI Workflow Eval Results8/8 built | 5 run(s) | pass@5: 70% | pass^5: 28%
Failure detailsCreate a workflow that handles contact form submissions via a webhook. / partial-action-failure — 0/5 passed
Create a workflow that handles contact form submissions via a webhook. / invalid-email — 0/5 passed
Get all the Linear issues created in the last 2 weeks. Filter them for / happy-path — 1/5 passed
Get all the Linear issues created in the last 2 weeks. Filter them for / multi-team-creator — 0/5 passed
Get all the Linear issues created in the last 2 weeks. Filter them for / no-cross-team-issues — 4/5 passed
Get all the Linear issues created in the last 2 weeks. Filter them for / unknown-creator — 3/5 passed
Get all the Linear issues created in the last 2 weeks. Filter them for / api-error — 1/5 passed
Every day, get the posts made in the past day on 3 different Slack cha / high-volume — 2/5 passed
Every day, get the posts made in the past day on 3 different Slack cha / channel-not-found — 0/5 passed
Every day, get the posts made in the past day on 3 different Slack cha / insufficient-permissions — 0/5 passed
Every day, fetch all open GitHub issues from repository 'acme-corp/bac / happy-path — 0/5 passed
Every day, fetch all open GitHub issues from repository 'acme-corp/bac / no-bugs — 1/5 passed
Create a workflow that receives webhook notifications with a JSON body / high-priority — 2/5 passed
Create a workflow that receives webhook notifications with a JSON body / medium-priority — 2/5 passed
Create a workflow that receives webhook notifications with a JSON body / low-priority — 2/5 passed
Fetch the latest posts from the JSONPlaceholder API (GET https://jsonp / happy-path — 2/5 passed
Fetch the latest posts from the JSONPlaceholder API (GET https://jsonp / empty-response — 4/5 passed
Fetch the latest posts from the JSONPlaceholder API (GET https://jsonp / all-filtered — 0/5 passed
Every hour, check the current weather for London, New York, and Tokyo / happy-path — 0/5 passed
Every hour, check the current weather for London, New York, and Tokyo / no-alerts — 1/5 passed
|
Performance ComparisonComparing current → latest master → 14-day baseline Memory consumption baseline with starter plan resources
Idle baseline with Instance AI module loaded
docker-stats
How to read this table
|
Summary
Tightens Instance AI prompts so the orchestrator, every sub-agent, the builder, and the browser-credential-setup agent instruct themselves not to ask the user to paste plaintext secrets into chat or ask-user answers. Secret entry is routed through credential setup, browser handoff, or existing credential selection instead.
SECRET_ASK_GUARDRAILconstant extracted so the main agent prompt (system-prompt.ts) and sub-agent protocol (sub-agent-factory.ts) stay in sync.browser-credential-setup.prompt.ts) so its wording is testable, and its "copy values into chat" phrasing replaced with "enter values privately in the n8n credential form".build-workflow-agent.prompt.tsdrops "API key" from its ask-user examples so the builder doesn't frame secrets as acceptable ask-user answers.ask-usertool description now explicitly says never to ask for passwords, API keys, tokens, cookies, connection strings, or private keys.Prompt-only change — no runtime redaction, blocking, or FE preflight. Those may follow in a separate PR.
Related Linear tickets, Github issues, and Community forum posts
Review / Merge checklist