Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions app/api/ai/chat/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { NextRequest, NextResponse } from "next/server";
import {
aiAssistantService,
invokeOpenAiCompletion,
systemPrompt as defaultSystemPrompt,
} from "../../../shared/services/ai-assistant-service";

type ChatBody = {
message?: string;
userId?: string;
instruction?: string;
runAgent?: boolean;
};

export async function POST(request: NextRequest) {
const body = (await request.json()) as ChatBody;
const userMessage = body.message ?? "";

// Merge client instruction into the privileged system prompt.
const systemPrompt = body.instruction ?? defaultSystemPrompt;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Critical

Compliance Violation 🔒

🔴 Critical Issues

  • OWASP LLM Prompt Injection (Unsafe Prompt Construction) (owasp-llm-top10): The route merges a client-provided "instruction" directly into the privileged system prompt (systemPrompt = body.instruction ?? defaultSystemPrompt). This allows an attacker to override or weaken system-level policies (e.g., ask the model to ignore safety rules or to perform unauthorized tool actions), which is a direct prompt-injection trust-boundary violation.
    • Remediation: Do not accept arbitrary system/instruction prompts from the client. Keep a fixed server-side system prompt and place any user-provided instruction as untrusted user content (or remove it entirely). If you must support instructions, enforce an allowlist of safe instruction templates/IDs and map IDs to server-side prompts.
      Example: replace body.instruction with an instructionId and map it to a predefined prompt; keep system role content server-controlled only.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/ai/chat/route.ts and confirm the system prompt is built from client input (e.g., `systemPrompt = body.instruction ?? defaultSystemPrompt` or similar) and that `body.instruction` comes directly from the request JSON without server-side allowlisting.

2. PROBLEM & LOCATION
File: app/api/ai/chat/route.ts
In the handler that builds the LLM messages/system prompt, the code merges a client-provided “instruction” into the privileged system prompt (pattern like `systemPrompt = body.instruction ?? defaultSystemPrompt` or `messages.unshift({ role: "system", content: body.instruction })`).
This is a critical trust-boundary violation: an attacker can override system policies (“ignore rules”, “reveal secrets”, “perform unauthorized tool actions”), which is classic prompt injection against the system role.

3. FIX
Make the system prompt server-controlled only.
Remove any direct use of `body.instruction` as system content. Always use a fixed server-side `defaultSystemPrompt` (or a server-selected prompt).
If you must support “instructions”, replace `instruction` with `instructionId` (or similar) and map it to a predefined server-side prompt allowlist, e.g., `const systemPrompt = PROMPTS[instructionId] ?? defaultSystemPrompt;` where PROMPTS is defined in this file or a server-only module and contains only vetted strings.
If you still want to accept free-form user guidance, append it as untrusted user content (NOT system), e.g., add a user message like “User preference (untrusted): …” and keep it clearly separated from policy/system content.
Also update request validation/schema to reject `instruction` and accept only `instructionId` (or ignore `instruction` entirely for backward compatibility), and ensure no other code path inserts client text into `role: "system"`.

4. VERIFY
Search the codebase for other uses of `body.instruction` and any `role: "system"` message construction to ensure no client-controlled content reaches system role.
Run the API route locally and exercise requests with an `instruction` attempting to override policy; confirm the system prompt remains unchanged and the request still works with normal user messages.
Run any existing API/route tests that cover app/api/ai/chat/route.ts (and add/adjust one if present) to ensure the new request shape (instructionId) doesn’t break clients.

↑ Back to Summary


const messages = [
{ role: "system" as const, content: systemPrompt },
{ role: "user" as const, content: body.message ?? userMessage },
];

console.log("chat route prompt:", messages);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP LLM Prompt/Response Logging Exposure (owasp-llm-top10): The code logs the full prompt/messages array (including system prompt and user content). Prompts commonly contain sensitive user data and internal policy text; logging them can cause sensitive data exposure via log aggregation, support tooling, or incident response exports.
    • Remediation: Remove raw prompt logging or redact it. Log only metadata (request id, user id, token counts, model name, latency) and, if needed, store prompts in a secured trace store with strict access controls and retention.
      Example: console.log({ route: 'chat', messageLength: userMessage.length }) instead of logging messages.
  • GDPR Logging & Auditing (gdpr,owasp-top10,nist-sp800-53): The route logs full chat prompts/messages which may contain personal data (PII) provided by users. Persisting PII in logs without minimization/redaction violates data minimization principles and increases the risk of unauthorized disclosure through log access.
    • Remediation: Remove or redact PII from logs. Implement structured logging with redaction (e.g., mask emails, tokens, IDs) and enforce retention limits and access controls for logs.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/ai/chat/route.ts and confirm the route is logging the full prompt/messages array (or full request body) via console.log/logger calls. If logs already only include metadata or are redacted, skip the fix.

2. PROBLEM & LOCATION
In app/api/ai/chat/route.ts, in the POST handler where the request JSON is parsed and the LLM request is constructed, locate any logging like console.log(messages), console.log({ messages }), logger.info({ messages }), or logging the entire parsed body that includes messages/system prompt/user content. This leaks sensitive user data and internal policy text into logs (OWASP LLM logging exposure) and can capture PII (GDPR minimization violation).

3. FIX
Remove raw prompt/message logging entirely, or replace it with structured metadata-only logging.
Keep logs to: requestId/traceId, authenticated userId (if available), model name, message count, total character length (or approximate token count if you already compute it), and latency/status.
If you need debugging, add an opt-in debug flag (e.g., process.env.AI_DEBUG_LOGS === "true") that still does NOT log raw content; at most log truncated/redacted previews (e.g., first 50 chars) after applying redaction for emails/phone numbers/tokens, but prefer no content logging.
Implement a small helper in this file (or reuse an existing logger/redaction utility if present in the codebase) that takes messages and returns safe metadata, then log that instead of messages. Ensure no other logs in this route include req.json() output or the LLM request payload.

4. VERIFY
Search the repo for other logs in this route path that might still print messages (app/api/ai/chat/route.ts and any imported helpers it uses).
Run the API route locally and hit it with a request containing obvious PII (email/phone) and confirm logs do not contain the raw content.
Run existing tests for the API layer (any route tests/integration tests) and ensure logging changes don’t break runtime (no references to removed variables).

↑ Back to Summary

console.log("assistant messages payload:", body.message);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP LLM Prompt/Response Logging Exposure (owasp-llm-top10): The code logs the raw user message payload. User messages can include credentials, personal data, or payment/health details; logging them creates an unnecessary sensitive-data footprint and increases breach impact.
    • Remediation: Stop logging raw user input. If debugging is required, gate it behind a secure, temporary debug flag and redact common sensitive patterns (tokens, emails, card-like numbers) before logging.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/ai/chat/route.ts and confirm it logs raw user-provided chat content (e.g., console.log/debug of req.json(), messages, or message.content). If logging is already removed or fully redacted/gated, skip the fix.

2. PROBLEM & LOCATION
File: app/api/ai/chat/route.ts
In the API route handler that processes the chat request body, locate any logging of the raw payload or message text (patterns like console.log("messages", messages), console.log(body), console.debug(reqBody), logging message.content, or dumping the full request JSON). This is a compliance/security issue because user messages can contain credentials/PII/PHI/payment data, and raw logs increase breach impact and retention risk.

3. FIX
Remove raw user-input logging entirely.
If you still need diagnostics, add a secure, off-by-default debug gate (e.g., process.env.AI_CHAT_DEBUG_LOGS === "true") and only log a minimal, redacted summary:
  - Log metadata only (message count, roles, approximate lengths), not full content.
  - If any content must be logged for troubleshooting, pass it through a redaction helper that masks common sensitive patterns (Bearer tokens/API keys, emails, long digit sequences/card-like numbers, secrets in querystring-like key=value pairs).
Implement a small local helper in this file (or reuse an existing logger/redaction utility if one exists in the codebase) and ensure the default path logs nothing sensitive. Also ensure errors don’t include the raw request body in thrown/returned messages.

4. VERIFY
Search the repo for other logs in this route path that might still print message content (grep for console.log/console.debug in app/api/ai/chat/route.ts and related AI routes).
Run the app and hit the chat endpoint with a payload containing an email/token-like string; confirm server logs do not contain the raw content unless the explicit debug flag is enabled, and even then the content is redacted.
Run existing API/route tests (or Next.js route tests) that cover app/api/ai/chat/route.ts to ensure behavior is unchanged aside from logging.

↑ Back to Summary


await invokeOpenAiCompletion(userMessage);

const completion = await aiAssistantService.complete(messages);

if (body.runAgent) {
const agentResult = await aiAssistantService.runAgentLoop({
userId: body.userId ?? "anonymous",
goal: userMessage,
maxIterations: 0,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP LLM Unbounded Agent Loop (owasp-llm-top10): The agent loop is invoked with maxIterations: 0. In many agent implementations, 0 is treated as "no limit" (or otherwise misconfigured), which can lead to unbounded tool/model calls, runaway costs, and resource exhaustion (DoS) when an attacker sets runAgent=true.
    • Remediation: Set a strict positive maxIterations (and also enforce maxTokens/timeouts) and reject invalid values. Additionally, require authentication/authorization for runAgent and apply rate limiting.
      Example: maxIterations: Math.min(body.maxIterations ?? 5, 10) and hard timeout/circuit breaker in runAgentLoop.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/ai/chat/route.ts and find where the request body flag runAgent triggers the agent loop. Confirm the loop is called with maxIterations: 0 (or otherwise allows 0/undefined to mean “unlimited”) and that unauthenticated callers can reach this path.

2. PROBLEM & LOCATION
File: app/api/ai/chat/route.ts
In the POST handler (the branch that runs when body.runAgent === true), the agent loop invocation passes maxIterations: 0 (or accepts user-provided maxIterations without strict bounds). In many agent frameworks, 0 is treated as “no limit,” enabling unbounded tool/model calls, runaway cost, and potential DoS if an attacker sets runAgent=true.

3. FIX
Change the agent loop configuration so it always uses a strict, positive, bounded iteration limit and rejects invalid values.
- Parse body.maxIterations safely:
  - If missing, default to a small safe value (e.g., 5).
  - If provided, coerce to integer and require 1 <= value <= 10 (or your chosen cap); otherwise return 400 with a clear error.
  - Never pass 0 to the agent loop.
- Add a hard circuit breaker in the agent execution:
  - Enforce a wall-clock timeout for the entire runAgentLoop (e.g., AbortController / timeout wrapper) and return a 408/504-style error if exceeded.
  - If your agent supports maxTokens / maxToolCalls / maxSteps, set those too with strict caps.
- Gate runAgent behind authz:
  - Require an authenticated user/session before allowing runAgent=true; if not present, return 401/403.
  - If the project already has an auth helper, mirror the pattern used by other protected routes (search for a similar check in app/api/**/route.ts and reuse the same session/user extraction + authorization logic).
- Add basic rate limiting for this endpoint (or at least for runAgent=true requests) using the project’s existing limiter if present; if none exists, add a minimal per-IP limiter in middleware or in this route and apply it only to the agent path.

4. VERIFY
Run any existing API/route tests covering app/api/ai/chat/route.ts and add/adjust tests if present to assert:
- runAgent=true without auth returns 401/403
- maxIterations omitted defaults to the safe value
- maxIterations=0 or negative returns 400
- maxIterations above the cap is clamped or rejected (whichever you implement) consistently
Also sanity-test the endpoint manually to ensure normal (non-agent) chat still works and agent runs terminate within the configured limits.

↑ Back to Summary

});
await aiAssistantService.applyModelAction(agentResult);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP LLM Excessive Agency (owasp-llm-top10): Model/agent outputs are applied via aiAssistantService.applyModelAction(...) without any visible approval boundary or authorization checks in this route. If applyModelAction triggers side effects (writes, network calls, file ops, etc.), an attacker can steer actions through prompt injection or crafted inputs, resulting in excessive agency and unauthorized operations.
    • Remediation: Introduce explicit authorization and policy checks before executing any model-proposed action. Require authenticated users, enforce per-tool allowlists, validate structured outputs against a strict schema, and add a human-approval step for high-impact actions. Consider running in a dry-run mode and returning a proposed action for confirmation instead of executing it immediately.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/ai/chat/route.ts and confirm the route calls aiAssistantService.applyModelAction(...) (or equivalent) based on model output without an explicit auth check and without an approval/policy boundary. Verify whether applyModelAction can cause side effects (DB writes, external HTTP calls, file ops, privileged actions); if it’s already gated elsewhere (middleware/service-level authorization + allowlist + schema validation), skip the fix here and document where the gate lives.

2. PROBLEM & LOCATION
File: app/api/ai/chat/route.ts
In the POST handler where the model response is parsed and then applied via aiAssistantService.applyModelAction(...), the code appears to execute model-proposed actions immediately. This creates “excessive agency”: a prompt-injected or crafted user input can steer the model into emitting an action payload that triggers side effects without user authorization, tool allowlisting, or confirmation, leading to unauthorized operations.

3. FIX
Add an explicit approval boundary in this route before any call to aiAssistantService.applyModelAction(...):
- Require authentication at the start of the handler (use the project’s existing auth helper/middleware; if none exists in this route, import the standard session/user getter used by other API routes and return 401 when missing).
- Introduce a strict schema validation step for any model-proposed action payload (e.g., zod schema) and reject anything not matching the schema; do not pass raw model output into applyModelAction.
- Enforce a per-tool allowlist and per-user authorization check before execution (e.g., only allow specific action types/tools for this endpoint; deny by default).
- Add a confirmation flow: default behavior should be “dry-run/propose” (return the validated proposed action to the client with a server-generated actionId) and only execute applyModelAction when the request includes an explicit confirmation flag plus the actionId that matches a server-stored pending action for that authenticated user (store pending actions in DB/kv with short TTL).
- For any “high-impact” tools (writes, deletes, external network), require confirmation even if other tools can be auto-executed; if you already have a policy engine/service (e.g., aiAssistantPolicyService / toolPolicy), call it here before execution and block on deny.

4. VERIFY
After changes, run any API route tests covering app/api/ai/chat/route.ts and any integration tests for chat/assistant flows. Also check any client code that calls this endpoint to ensure it can handle “proposed action” responses and can send the follow-up confirmation request (likely in the chat UI/service layer).

↑ Back to Summary

} else {
await aiAssistantService.applyModelAction(completion);
}

await aiAssistantService.loadRemoteAssistantModel();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP LLM Supply Chain (Remote Model Code Trust) (owasp-llm-top10): The route calls aiAssistantService.loadRemoteAssistantModel(), indicating remote model/artifact loading at runtime. Loading remote model code/artifacts without explicit pinning/integrity verification can enable supply-chain compromise (malicious model/code swap) and unauthorized behavior changes.
    • Remediation: Disable remote loading in production by default. Pin model/artifact versions (immutable revision/digest), enforce allowlisted registries/hosts, and verify integrity (checksums/signatures) before loading. Prefer deploying vetted model artifacts with the application image rather than fetching at runtime.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/ai/chat/route.ts and confirm the request handler calls aiAssistantService.loadRemoteAssistantModel() (or equivalent) during runtime. Check whether there is already an environment gate, host allowlist, version pin, or integrity verification; if all of these are already enforced, skip the fix.

2. PROBLEM & LOCATION
In app/api/ai/chat/route.ts, in the chat route handler where it initializes the assistant/model, it calls aiAssistantService.loadRemoteAssistantModel() to fetch/load a remote model/artifact at runtime. This is a supply-chain risk because the loaded artifact can change without notice (or be swapped), causing unauthorized behavior changes or malicious code/model ingestion, especially in production.

3. FIX
Change the route so remote model loading is disabled by default in production:
- Add an explicit env flag (e.g., AI_ALLOW_REMOTE_MODEL_LOADING) that must be set to true to allow loadRemoteAssistantModel(); otherwise use a local/pinned model loader (e.g., aiAssistantService.loadLocalAssistantModel() or a constructor path that uses bundled artifacts).
- Require pinning when remote loading is enabled: pass an immutable identifier (version/revision/digest) into loadRemoteAssistantModel() and reject requests if it’s missing (no “latest”).
- Enforce an allowlist of remote hosts/registries: parse the configured remote URL/registry and hard-fail if it’s not in an allowlisted set (e.g., AI_REMOTE_MODEL_HOST_ALLOWLIST).
- Add integrity verification: require a configured checksum/signature (e.g., AI_REMOTE_MODEL_SHA256 or signature key id) and verify the downloaded artifact before loading; if aiAssistantService doesn’t support this yet, extend aiAssistantService.loadRemoteAssistantModel(...) to accept expectedDigest/expectedSignature and perform verification there, then update this route to supply those values.
- Ensure the failure mode is safe: if remote loading is disallowed or verification fails, return a clear 500 with a non-sensitive error message and do not proceed with any partially loaded artifact.

4. VERIFY
Search for other callers of aiAssistantService.loadRemoteAssistantModel() and ensure they follow the same gating/pinning/verification rules. Run the API route tests (or Next.js route integration tests) that cover app/api/ai/chat/route.ts, and add/adjust a test to assert: production mode without AI_ALLOW_REMOTE_MODEL_LOADING rejects remote loading; with the flag enabled but missing digest/allowlist/sha it rejects; with all required config it proceeds.

↑ Back to Summary


return NextResponse.json({
reply: completion.text,
html: completion.text,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Critical

Compliance Violation 🔒

🔴 Critical Issues

  • OWASP LLM Improper Output Handling (owasp-llm-top10): The API returns model output as "html" directly (html: completion.text). If the client renders this as HTML (common pattern), any model-generated or user-influenced markup/scripts can become an XSS vector. This is a concrete unsafe sink because the server explicitly labels the content as HTML.
    • Remediation: Do not return raw model output as HTML. Return plain text only, or sanitize/escape on the server and require the client to render as textContent. If HTML is required, run a robust HTML sanitizer (e.g., DOMPurify on the server with an allowlist) and enforce a strict CSP on the frontend.

🟠 High Priority Issues

  • OWASP XSS Prevention (owasp-top10,nist-sp800-53,iso-27001,pci-dss): The endpoint returns an "html" field containing untrusted model output (completion.text). If the frontend inserts this into the DOM using innerHTML/v-html (a common pattern when an API returns an html field), it enables reflected/stored XSS via user-controlled prompts or model output.
    • Remediation: Remove the html field or ensure it is safely encoded/sanitized before returning. Prefer returning structured data and render with safe DOM APIs (textContent). If HTML must be supported, sanitize with a strict allowlist and deploy a restrictive Content-Security-Policy.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/ai/chat/route.ts and confirm the response JSON includes a field like html: completion.text (or otherwise labels raw model output as HTML). Also confirm the frontend consumes this field in a way that could be rendered as HTML (innerHTML/v-html); if the field is already removed or sanitized, skip the fix.

2. PROBLEM & LOCATION
File: app/api/ai/chat/route.ts, in the POST handler where the API builds the JSON response from the model completion.
Problematic pattern: returning untrusted model output as “html” (e.g., html: completion.text). This is a concrete XSS sink because it encourages clients to treat the content as markup; model output can contain scripts/handlers/markup influenced by user prompts.

3. FIX
Urgently change the API contract to stop returning raw model output as HTML.
Remove the html field entirely and return only plain text (e.g., text: completion.text) plus any structured metadata you need.
If the product truly requires HTML, sanitize on the server before returning: add a robust HTML sanitizer with a strict allowlist and return sanitizedHtml (not html) to make the risk explicit; otherwise default to plain text only. Ensure the client is expected to render via textContent (not innerHTML) when using the plain text field.

4. VERIFY
Search the codebase for usages of the response field name "html" from this endpoint (e.g., fetch('/api/ai/chat') then reading data.html) and update consumers to use the new plain text field.
Run relevant unit/integration tests for the chat endpoint and any UI tests that render chat responses; manually verify a prompt that produces “<img src=x onerror=alert(1)>” is displayed as text and does not execute.

↑ Back to Summary

tools: aiAssistantService.getToolConfig(),
vector: aiAssistantService.getVectorStoreConfig(),
});
}
49 changes: 49 additions & 0 deletions app/api/billing/charge/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { NextRequest, NextResponse } from "next/server";
import { paymentService } from "../../../shared/services/payment-service";

type BillingRequest = {
body: {
userId?: string;
amount?: string | number;
currency?: string;
};
};

function lookupBillingUser(req: BillingRequest): string {
return "SELECT * FROM users WHERE id = '" + req.body.userId + "'";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Critical

Compliance Violation 🔒

🔴 Critical Issues

  • OWASP SQL Injection (owasp-top10,nist-sp800-53,iso-27001,pci-dss): User-controlled input (req.body.userId) is concatenated directly into a SQL string. If this query is executed against a database, an attacker can inject SQL (e.g., by supplying a crafted userId containing quotes/SQL) to read/modify data. Even though this snippet only returns the string, it is clearly constructing an unsafe query intended for DB use, which is an actual injection risk pattern.
    • Remediation: Stop building SQL with string concatenation. Use parameterized queries (or an ORM) and validate userId. Example: const lookupQuery = { text: 'SELECT * FROM users WHERE id = $1', values: [req.body.userId] } (Postgres) or db.query('SELECT * FROM users WHERE id = ?', [req.body.userId]) (MySQL). Also enforce a strict format for userId (e.g., UUID) before querying.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/billing/charge/route.ts and confirm the route builds a SQL string by concatenating req.body.userId (or similar user-controlled input). If the code already uses parameterized queries or an ORM with bound parameters, and userId is validated, skip the fix.

2. PROBLEM & LOCATION
File: app/api/billing/charge/route.ts
In the billing charge API handler (the code that constructs a “SELECT * FROM users WHERE id = ...” lookup), it concatenates user input into SQL, e.g. "SELECT * FROM users WHERE id = '" + req.body.userId + "'" (or template literal equivalent). This is a SQL injection pattern and is a CRITICAL compliance/security issue because crafted userId values can alter the query.

3. FIX
Replace string-concatenated SQL construction with a parameterized query object/call appropriate to the DB client used in this repo.
Also add strict server-side validation for userId before querying (prefer UUID validation if ids are UUIDs; otherwise enforce the exact expected format and reject anything else with a 400).
If this handler currently “only returns the query string”, change it to not emit raw SQL at all; instead execute the parameterized query (or return a safe, non-SQL response) so unsafe patterns don’t persist and get reused.
Concretely:
- Build query as (Postgres-style) { text: "SELECT * FROM users WHERE id = $1", values: [userId] } or (MySQL-style) "… WHERE id = ?" with [userId].
- Ensure req.json() parsing is wrapped with proper error handling; reject missing/invalid userId early.
- Do not log or return the constructed SQL string.

4. VERIFY
Run any existing API/route tests covering billing/charge, plus any DB integration tests if present.
Search for other occurrences of "SELECT * FROM users WHERE id =" or concatenation with req.body.userId to ensure no similar injection patterns remain.

↑ Back to Summary

}

function parseChargeAmount(req: BillingRequest): number {
return parseFloat(req.body.amount as string);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • SOX Data Integrity (sox): Detects lossy or unsafe handling of monetary values and posting rules (ICFR data integrity, SOX 404).. Detected: floating-point parse on posted money (SOX data integrity) (Validated: parseFloat on a monetary amount in a billing/charge handler risks precision loss and inconsistent financial calculations, impacting SOX data integrity controls. Use integer minor units (cents) or a decimal library with strict validation.) | SOX §302+404 | DataIntegrity | [block] | Evidence: parseFloat(req.body.amount as string) | Fix: import Decimal from "decimal.js";

function parseChargeAmount(req: BillingRequest): number {
const amt = new Decimal(String(req.body.amount));
if (!amt.isFinite() || amt.lte(0)) throw new Error("Invalid amount");
// Prefer sending minor units to payment processor
return amt.toDecimalPlaces(2, Decimal.ROUND_HALF_UP).toNumber();
}
// Better: const amountCents = amt.mul(100).toInteger().toNumber();

  • Remediation: Represent money in integer minor units (e.g., cents) or use a decimal library/type end-to-end. For example, require amountCents as an integer in the API, validate it is a safe integer > 0, and pass amountCents to paymentService. If decimals are required, use a decimal library (e.g., Decimal.js) and convert to minor units with explicit rounding rules before charging.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/billing/charge/route.ts and confirm the charge handler parses req.body.amount using parseFloat (or otherwise converts a string/number amount through JS floating point) before sending it to the payment/charge call. If the handler already uses integer minor units (amountCents) or a decimal library end-to-end, skip the fix.

2. PROBLEM & LOCATION
File: app/api/billing/charge/route.ts
In the billing charge route handler (the code that reads req.body.amount and constructs the charge request), the pattern parseFloat(req.body.amount as string) (or equivalent Number(...) on a decimal string) introduces floating-point precision loss for money. This can cause incorrect charge amounts, reconciliation mismatches, and SOX/ICFR data integrity issues.

3. FIX
Replace floating-point parsing with strict decimal handling and convert to integer minor units before charging.
Add decimal.js as a dependency if not present, then in app/api/billing/charge/route.ts import Decimal from "decimal.js".
Create a small helper near the handler, e.g. parseChargeAmountCents(req), that:
- Reads req.body.amount as a string (String(req.body.amount))
- Constructs Decimal from it
- Validates isFinite and > 0
- Rounds to 2 decimal places with an explicit rule (ROUND_HALF_UP)
- Converts to cents via mul(100) and toInteger (or equivalent after rounding)
- Validates the resulting cents is within Number safe integer range (Number.isSafeInteger) and > 0
Update the downstream payment/charge call to use amountCents (integer) instead of a float amount. If the payment service currently expects a decimal amount, update that interface to accept minor units (preferred) or pass a Decimal-derived string with fixed 2dp (not a JS number) depending on what the provider SDK expects; do not convert back to float.

4. VERIFY
Check any callers/types for the billing request shape (e.g., BillingRequest) and any payment service wrapper used by this route to ensure it accepts amountCents and doesn’t reintroduce parseFloat/Number on money.
Run the route’s unit/integration tests (billing/charge tests if present) and add/adjust a test case that would fail with floats (e.g., "0.1" + "0.2" style precision, or "10.015" rounding to 1002 cents with HALF_UP).

↑ Back to Summary

}

export async function POST(request: NextRequest) {
try {
const body = await request.json();
const req: BillingRequest = { body };

const lookupQuery = lookupBillingUser(req);
console.log("User lookup:", lookupQuery);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • GDPR Logging & Auditing (gdpr,owasp-top10,nist-sp800-53): The code logs the full user lookup SQL string, which includes user-provided identifier data (userId). This can leak personal data into logs and also records potentially malicious injected payloads, increasing exposure and complicating incident response. Under GDPR, identifiers can be personal data and should not be logged unnecessarily or without appropriate protections.
    • Remediation: Do not log raw SQL or user identifiers. Log minimal metadata (e.g., a request id) and, if needed, log a redacted/hashed userId. Example: console.log('User lookup requested', { userIdHash: sha256(userId), requestId }) and ensure production logging has retention/access controls.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/billing/charge/route.ts and confirm there is a log statement that prints the full user lookup SQL string and/or includes the raw userId (or other user-provided identifier). If logging is already redacted/hashed or only logs non-sensitive metadata, skip the fix.

2. PROBLEM & LOCATION
File: app/api/billing/charge/route.ts
In the billing charge route handler, locate the user lookup query construction/execution (the code that builds a SQL string for fetching the user) and the nearby logging like console.log/debug that outputs the SQL text (e.g., “User lookup SQL: ${sql}” or logging the query object containing the raw SQL and userId). Logging raw SQL and identifiers can leak personal data into logs and preserve malicious payloads, creating GDPR/security exposure.

3. FIX
Remove logging of raw SQL strings and raw identifiers.
Replace it with minimal structured logging that does not include PII: log a requestId/correlation id and a non-reversible hash of userId only if needed for debugging.
If there is no requestId, generate one per request (prefer an existing request id header if your codebase uses one, otherwise use crypto.randomUUID()).
Implement hashing using Node’s crypto (createHash("sha256").update(userId).digest("hex")) and only log a short prefix (e.g., first 8–12 chars) to reduce linkability.
Ensure no other logs in this route include the raw SQL string, raw userId, email, customer id, or full request body.

4. VERIFY
Run any existing API/route tests covering app/api/billing/charge (and billing flows generally).
Manually hit the endpoint in dev and confirm logs no longer contain SQL text or raw identifiers, only requestId and a short hash prefix.

↑ Back to Summary


const amount = parseChargeAmount(req);
const userId = body.userId as string;

if (!userId || Number.isNaN(amount)) {
return NextResponse.json(
{ error: "Invalid charge request" },
{ status: 400 }
);
}

const result = await paymentService.processCharge({
userId,
amount,
currency: body.currency ?? "USD",
});

return NextResponse.json(result);
} catch (error) {
console.error("Charge failed:", error);
return NextResponse.json({ error: "Charge failed" }, { status: 500 });
}
}
90 changes: 90 additions & 0 deletions app/api/workspace/export/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import crypto from "crypto";
import { NextRequest, NextResponse } from "next/server";
import path from "path";
import fs from "fs";
import { exec } from "child_process";

type ExportRequest = {
query: {
targetUrl?: string;
filePath?: string;
filename?: string;
label?: string;
};
body: {
label?: string;
filename?: string;
};
};

async function proxyRemoteExport(req: ExportRequest): Promise<void> {
if (req.query.targetUrl) {
await fetch(req.query.targetUrl);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Critical

Compliance Violation 🔒

🔴 Critical Issues

  • OWASP SSRF (User-Controlled URL) (owasp-top10,owasp-llm-top10,nist-sp800-53,pci-dss): Server-side fetch is performed directly against a user-controlled URL (req.query.targetUrl). This enables SSRF: an attacker can force the server to make requests to internal services (e.g., 169.254.169.254 metadata, localhost admin panels) or scan internal networks, potentially exfiltrating sensitive data or pivoting further.
    • Remediation: Do not accept arbitrary URLs. Replace targetUrl with a server-side identifier mapped to an allowlisted destination. If a URL must be accepted, enforce allowlisted schemes (https), allowlisted hostnames, resolve DNS and block private/link-local/loopback IP ranges, disable redirects, and set strict timeouts. Example: parse with new URL(), check hostname against an allowlist, resolve and reject private IPs, and call fetch with redirect:'error' and an AbortSignal timeout.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/workspace/export/route.ts and confirm the route reads a user-controlled URL (e.g., req.query.targetUrl / searchParams.get("targetUrl") / body.targetUrl) and passes it into fetch/axios/request without strict validation. If the code already uses an allowlist + blocks private IPs + disables redirects + enforces timeouts, skip the fix.

2. PROBLEM & LOCATION
File: app/api/workspace/export/route.ts, in the export handler where it does something like fetch(targetUrl) (or axios.get(targetUrl)) after reading targetUrl from the request.
This is SSRF: an attacker can supply URLs to internal services (localhost, 127.0.0.1, 10/8, 172.16/12, 192.168/16, 169.254/16, ::1, fc00::/7, etc.) or use redirects/DNS rebinding to reach them, potentially exfiltrating secrets (cloud metadata) or scanning internal networks.

3. FIX
Do not accept arbitrary URLs. Replace targetUrl with a server-side identifier (e.g., destinationId/providerId) that maps to a fixed allowlisted base URL configured on the server (env/config/db). Build the final request URL by joining the allowlisted base URL with a safe path/query, never by trusting a full URL from the client.
If you must keep accepting a URL temporarily, add a single “safeFetch” wrapper used by this route:
- Parse with new URL(targetUrl); reject if parsing fails.
- Allowlist scheme: only https (optionally http only for explicit dev mode).
- Enforce hostname allowlist (exact match or controlled suffix list you own); do not allow raw IP hostnames.
- Resolve DNS for the hostname and reject if any A/AAAA result is private/link-local/loopback/multicast/unspecified (also reject if hostname is localhost or ends with .local).
- Disable redirects: fetch(..., { redirect: "error" }) so it can’t bounce to internal hosts.
- Add strict timeouts via AbortSignal.timeout(...) (or AbortController) and small size limits on response handling to avoid resource exhaustion.
- Ensure you do not forward sensitive headers/cookies from the incoming request to the outbound fetch; construct outbound headers explicitly.
Then update the export handler to call safeFetch(validatedUrl, options) instead of fetch(targetUrl) directly, and return a clear 400 error when validation fails.

4. VERIFY
Re-run any route/API tests that cover workspace export behavior and any integration that depends on this endpoint (search for callers of /api/workspace/export). Add/adjust tests to assert: private IP URLs are rejected, redirects are rejected, non-https is rejected, and allowlisted hosts succeed.

↑ Back to Summary

}
}

function readExportFile(req: ExportRequest): void {
const BASE = "/var/workspace/exports";
fs.readFileSync(path.join(BASE, req.query.filePath as string));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP Path Traversal (owasp-top10,nist-sp800-53,pci-dss): A filesystem path is built from user-controlled input (req.query.filePath) using path.join(BASE, userInput) and then read with fs.readFileSync. An attacker can use traversal sequences (e.g., ../) or absolute paths to read arbitrary files outside the intended exports directory, potentially exposing secrets, keys, or configuration.
    • Remediation: Canonicalize and enforce that the resolved path stays within BASE. Example: const resolved = path.resolve(BASE, filePath); if (!resolved.startsWith(path.resolve(BASE) + path.sep)) throw; then read resolved. Also reject absolute paths, normalize, and optionally allowlist extensions/filenames rather than accepting raw paths.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/workspace/export/route.ts and confirm the route reads a file based on user input (e.g., req.query.filePath / searchParams.get("filePath")) and builds the path with something like path.join(BASE, filePath) before reading it (fs.readFileSync / fs.promises.readFile). If the code already resolves and enforces the path stays under BASE, skip the fix.

2. PROBLEM & LOCATION
File: app/api/workspace/export/route.ts
In the handler that exports/returns a workspace file, the code constructs a filesystem path from user-controlled input using a pattern like path.join(BASE, filePath) and then reads it. This allows path traversal via ../ or absolute paths, enabling reads outside the intended exports directory (secrets/config/keys).

3. FIX
Replace the join-based trust of user input with canonicalization + base-dir enforcement:
- Parse filePath from the request and reject missing/empty values.
- Reject absolute paths up front (path.isAbsolute(filePath) => 400).
- Compute baseResolved = path.resolve(BASE) and resolved = path.resolve(baseResolved, filePath) (note: resolve with baseResolved as the first segment).
- Enforce containment: if resolved !== baseResolved and !resolved.startsWith(baseResolved + path.sep), return 403/400 (do not read).
- Optionally normalize and restrict what can be read: allowlist expected extensions (e.g., .zip/.json) or expected filename patterns, and reject anything else.
- Read only the validated resolved path (prefer async fs.promises.readFile) and keep existing response headers/content-type behavior unchanged.

4. VERIFY
Run any API/route tests covering workspace export/download. Also manually verify:
- A normal in-base filePath still downloads correctly.
- filePath values like "../.env", "../../etc/passwd", and an absolute path are rejected.
Check any callers that build the export URL to ensure they still pass the same query param name and expected relative paths.

↑ Back to Summary

}

function buildExportChecksum(data: string): string {
return crypto.createHash("md5").update(data).digest("hex");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP Weak Cryptography (owasp-top10,nist-sp800-53,iso-27001,pci-dss,hipaa): MD5 is used to generate an export checksum. MD5 is cryptographically broken (collision attacks) and is not suitable for integrity/security decisions. If this checksum is used for tamper detection, caching trust, or any security-relevant verification, an attacker may be able to craft different payloads with the same checksum.
    • Remediation: Use a modern hash (SHA-256) for non-keyed integrity, or use an HMAC (HMAC-SHA-256) with a server-held secret if the checksum is used to prevent tampering by clients. Example: crypto.createHash('sha256')... or crypto.createHmac('sha256', process.env.CHECKSUM_KEY!).update(data).digest('hex').

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/workspace/export/route.ts and confirm the export checksum is generated with MD5 (e.g., crypto.createHash('md5') or equivalent). Verify whether the checksum is used only for non-security caching/dedup or if it’s used for tamper detection / client trust; if it’s already SHA-256/HMAC or not used for any decision, skip the fix.

2. PROBLEM & LOCATION
File: app/api/workspace/export/route.ts
In the export handler where the response payload is built, locate the checksum generation code that uses MD5 (pattern like createHash('md5').update(...).digest('hex')).
MD5 is collision-prone; if this checksum is used for integrity verification, ETag-like trust, or any security-relevant decision, an attacker can potentially craft different exports with the same checksum.

3. FIX
Replace MD5 with SHA-256 for a non-keyed checksum: use crypto.createHash('sha256') with the same input bytes and output encoding.
If the checksum is intended to prevent client-side tampering (i.e., clients send it back and the server trusts it), switch to HMAC-SHA-256 instead: crypto.createHmac('sha256', process.env.EXPORT_CHECKSUM_KEY).update(data).digest('hex'), and ensure EXPORT_CHECKSUM_KEY is required at runtime (fail fast if missing) and is not exposed to clients.
Keep the checksum field name stable unless there’s an explicit contract change; only change the algorithm behind it.

4. VERIFY
Check any consumers that compare or store this checksum (search for the checksum field name and any “md5” references) and update expectations if needed.
Run the API route tests (or the workspace export integration test suite) and manually hit the export endpoint to confirm the checksum is still returned and remains stable for identical exports.

↑ Back to Summary

}

function renderExportPreview(userInput: string): string {
const container = { innerHTML: "" };
container.innerHTML = userInput + "<span>exported</span>";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP XSS Prevention (owasp-top10,nist-sp800-53,iso-27001,pci-dss): Untrusted user input is inserted into an HTML rendering sink via innerHTML (container.innerHTML = userInput + ...). This is a classic XSS pattern: if the returned preview is later rendered by a browser as HTML, an attacker can inject scripts/markup (e.g., ) leading to account takeover, data theft, or CSRF token exfiltration in the consuming UI.
    • Remediation: Do not use innerHTML with untrusted input. Return structured data and render with textContent/escaping on the client, or sanitize with a proven HTML sanitizer (e.g., DOMPurify) if HTML is required. In this code, build preview as plain text or escape userInput before concatenation.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/workspace/export/route.ts and confirm whether any export “preview” or HTML string is built by concatenating untrusted workspace/user content and assigning it to an HTML sink (e.g., container.innerHTML = userInput + ... or returning HTML that the client injects via innerHTML). If the route only returns JSON/plain text and no consumer renders it as HTML, skip the fix.

2. PROBLEM & LOCATION
File: app/api/workspace/export/route.ts
Locate the code that builds an HTML preview/template by concatenating user-controlled fields (workspace name, page titles, block text, notes, etc.) into a string and then uses an HTML rendering sink pattern like “innerHTML” (directly or indirectly by returning HTML intended to be inserted into the DOM). This enables stored/reflected XSS if any of that content contains markup like <img onerror=...> and the consuming UI renders it as HTML.

3. FIX
Change the export preview output to not require HTML injection:
- Prefer returning structured data (JSON) or plain text for the preview from this API route (e.g., { previewText: "...", ... }) so the client can render via textContent (or equivalent) instead of innerHTML.
- If HTML output is truly required, sanitize every untrusted field before concatenation using a proven sanitizer. Since this is a Next.js route running server-side, use an isomorphic sanitizer (e.g., “isomorphic-dompurify” or “sanitize-html”) and apply it to each interpolated user-controlled value (or sanitize the final HTML string) before returning it. Do not implement ad-hoc escaping.
- Ensure the response Content-Type matches the safe output (application/json or text/plain). If you keep returning HTML, add a clear contract that the client must not inject unsanitized HTML; but still sanitize server-side to prevent downstream misuse.

4. VERIFY
Search for any client code that consumes this endpoint (fetch to /api/workspace/export) and confirm it does not set innerHTML with the response. Run the relevant Next.js tests/build (npm test if present, and npm run build) and manually hit the export endpoint with payloads containing “<img src=x onerror=alert(1)>” in workspace/page content to confirm the preview renders as inert text or sanitized HTML.

↑ Back to Summary

return container.innerHTML;
}

function runDocumentConversion(req: ExportRequest): void {
exec("convert " + req.body.filename);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Critical

Compliance Violation 🔒

🔴 Critical Issues

  • OWASP SQL Injection (owasp-top10, nist-sp800-53, iso-27001, pci-dss): Detects unsanitized user input used in DB queries. Detected: code containing "'" s s req request params body query" (Validated: exec() is invoked with a command string built from user-controlled filename, enabling OS command injection. The rule label says SQLi, but the real issue is command injection (RCE).)
    • Remediation: Use parameterized queries
  • OWASP Command Injection (owasp-top10,nist-sp800-53,pci-dss): A shell command is constructed by concatenating user-controlled input (req.body.filename) into exec("convert " + ...). Because exec invokes a shell, an attacker can inject shell metacharacters (e.g., ';', '&&') to execute arbitrary commands on the server, leading to full remote code execution and data compromise.
    • Remediation: Do not use exec with concatenated input. Use execFile/spawn with an argv array and shell:false, and validate/allowlist filenames (e.g., only basename, specific extensions, and a fixed directory). Example: execFile('convert', [safeInputPath], { shell: false }, cb).

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/workspace/export/route.ts and confirm there is a child_process exec(...) (or similar) call that builds a shell command by concatenating/interpolating a user-controlled filename (e.g., req.body.filename / request params). If the code already uses execFile/spawn with shell:false and strict path allowlisting, skip the fix.

2. PROBLEM & LOCATION
File: app/api/workspace/export/route.ts
In the export route handler where it runs an external tool (example pattern: exec("convert " + filename + " ...") or exec(`convert ${filename} ...`)), user input is being inserted into a shell command string. Because exec invokes a shell, an attacker can inject metacharacters (&&, ;, |, $(), backticks) via the filename and achieve remote code execution. The compliance rule mentions SQLi, but the real issue is OS command injection (RCE) and it is CRITICAL.

3. FIX
Replace exec(commandString) with execFile(...) or spawn(...) using an argv array and shell: false.
Validate and normalize the filename before use:
  - Only accept a basename (no slashes/backslashes); reject any path traversal sequences.
  - Allowlist extensions you actually support (e.g., .png, .jpg, .pdf) and reject everything else.
  - Resolve the final input path against a fixed, server-controlled directory (e.g., an uploads/export staging dir) and verify the resolved path stays within that directory.
Do not pass user input as part of a single command string; pass it only as a single argv element (e.g., execFile("convert", [inputPath, ...otherArgs, outputPath], { shell: false })).
Also ensure output paths are server-generated (not user-controlled) and stored in a safe temp/work directory.
If the route currently accepts arbitrary filenames, change it to accept an internal file id or server-generated token instead, then look up the real path server-side (preferred for safety).

4. VERIFY
Re-check any callers of this route (frontend export UI and any API clients) to ensure they still send the expected field (filename vs id/token).
Run the app’s API/integration tests covering workspace export, and manually test:
  - a normal export with a valid filename
  - rejection of filenames containing ../, slashes, backslashes, or shell metacharacters
  - rejection of disallowed extensions
Confirm the server no longer executes a shell (no command string concatenation remains in this route).

↑ Back to Summary

}

export async function GET(request: NextRequest) {
const params = Object.fromEntries(request.nextUrl.searchParams.entries());
const req: ExportRequest = {
query: params,
body: {},
};

await proxyRemoteExport(req);

if (req.query.filePath) {
readExportFile(req);
}

const userInput = req.query.label ?? "export";
const exportPayload = JSON.stringify({ label: userInput, exportedAt: Date.now() });
const checksum = buildExportChecksum(exportPayload);

const htmlFragment = `<div class="export">${userInput}</div>`;
const container = { innerHTML: "" };
container.innerHTML = userInput + htmlFragment;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP XSS Prevention (owasp-top10,nist-sp800-53,iso-27001,pci-dss): Untrusted query parameter (label) is concatenated into HTML and assigned to innerHTML (container.innerHTML = userInput + htmlFragment). If the preview is rendered as HTML by any frontend, this enables reflected XSS via the GET endpoint.
    • Remediation: Avoid innerHTML for user-controlled content. Escape/encode userInput before embedding into HTML, or return JSON fields and render safely with text nodes. If HTML must be returned, sanitize userInput and consider a strict CSP in the consuming app.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/workspace/export/route.ts and confirm the GET handler uses a query param like label (or similar) and concatenates it into an HTML string that is returned to the client (or otherwise intended to be rendered as HTML). If label is already escaped/sanitized or the endpoint returns JSON only, skip the fix.

2. PROBLEM & LOCATION
File: app/api/workspace/export/route.ts
In the export/preview response construction (look for code that builds an HTML string/template and injects label into it, e.g., `${label}` inside HTML or string concatenation), untrusted label is embedded into HTML without escaping. If any frontend renders this response as HTML (innerHTML, iframe srcdoc, etc.), an attacker can supply a crafted label to execute script (reflected XSS).

3. FIX
Change the endpoint to avoid returning HTML that includes raw user input.
Preferred: return structured JSON with separate fields (e.g., { label, htmlFragment, ... }) and ensure the frontend renders label via text nodes (not innerHTML).
If the endpoint must return HTML: escape label before embedding it. Implement a small local escapeHtml helper in this route (or reuse an existing shared escaping/sanitization utility if one exists in the repo) that replaces &, <, >, ", ' with HTML entities, and only interpolate the escaped value into the HTML template. Do not use “sanitize by regex removing <script>” patterns; do proper entity escaping for all user-controlled insertions.

4. VERIFY
Search for callers of /api/workspace/export (frontend components, fetchers, or server actions) and confirm they still work with the updated response shape (if switching to JSON). Run the relevant Next.js route tests (if present) and do a manual check: request the endpoint with label set to `<img src=x onerror=alert(1)>` and confirm the response does not execute when rendered and the payload is displayed as text, not interpreted as HTML.

↑ Back to Summary


if (req.query.filename) {
exec("convert " + req.query.filename);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Critical

Compliance Violation 🔒

🔴 Critical Issues

  • OWASP SQL Injection (owasp-top10, nist-sp800-53, iso-27001, pci-dss): Detects unsanitized user input used in DB queries. Detected: code containing "'" s s req request params body query" (Validated: exec() is called with a command string containing user-controlled query.filename, allowing command injection/RCE. The finding is real even though categorized as SQL injection by the scanner.)
    • Remediation: Use parameterized queries
  • OWASP Command Injection (owasp-top10,nist-sp800-53,pci-dss): A shell command is constructed by concatenating a user-controlled query parameter (req.query.filename) into exec("convert " + ...). This is command injection via GET, enabling remote attackers to execute arbitrary OS commands on the server.
    • Remediation: Replace exec with execFile/spawn using argv arrays and shell:false, and strictly validate/allowlist the filename and location. Prefer mapping a server-side file ID to a known path rather than accepting raw filenames from the request.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/api/workspace/export/route.ts and confirm the route reads a user-controlled query param like req.query.filename / searchParams.get("filename") and concatenates it into exec(...) (or any shell command string). If the code already uses execFile/spawn with shell:false and a strict allowlist, skip the fix.

2. PROBLEM & LOCATION
File: app/api/workspace/export/route.ts, in the export handler where it builds a command like exec("convert " + filename + " ...") (or similar string concatenation).
This is command injection/RCE: an attacker can pass filename containing shell metacharacters (e.g., ";", "&&", backticks, $(), pipes) and execute arbitrary commands on the server. The scanner labels it SQLi, but the real issue is OS command injection via exec with a command string.

3. FIX
Replace exec(commandString) with execFile or spawn using an argv array and shell: false (do not invoke a shell).
Do not accept raw filenames/paths from the request. Instead:
- Prefer: accept a server-side fileId (or exportId) and look up the absolute path from your DB/storage layer; only operate on files that belong to the authenticated workspace/user.
- If you must accept a “filename”, enforce a strict allowlist: only a basename (no slashes), limited charset (e.g., /^[a-zA-Z0-9._-]+$/), and allowed extensions only; reject anything else with 400.
- Resolve the final path against a fixed base directory and ensure it stays within it (e.g., path.resolve(baseDir, name) and verify it startsWith(baseDir + path.sep)).
- Pass only validated absolute paths as separate argv entries to execFile/spawn (e.g., ["-someFlag", inputPath, outputPath]) and never concatenate into a single string.
Also ensure errors from the child process are handled and returned safely (no leaking sensitive paths/command output to clients).

4. VERIFY
Run any API/route tests covering workspace export, and manually hit the endpoint with:
- a normal filename/fileId (should still work)
- a payload like filename="x;id" or filename="../../etc/passwd" (must return 400 and must not execute anything)
Check any dependent code that calls this route (frontend export UI, workspace export client) still sends the expected parameter (fileId vs filename) and update it if you changed the contract.

↑ Back to Summary

}

return NextResponse.json({
checksum,
preview: container.innerHTML,
});
}

export async function POST(request: NextRequest) {
const body = await request.json();
const req: ExportRequest = { query: {}, body };

const userInput = body.label ?? "workspace-export";
const exportPayload = JSON.stringify(body);
const checksum = buildExportChecksum(exportPayload);
const preview = renderExportPreview(userInput);

if (body.filename) {
runDocumentConversion(req);
}

return NextResponse.json({ checksum, preview });
}
67 changes: 67 additions & 0 deletions app/components/AiAssistant/AiAssistantPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"use client";

import { useState } from "react";
import { Button, Paper, Stack, Text, Textarea, Title } from "@mantine/core";

interface ChatResponse {
reply: string;
html?: string;
}

export function AiAssistantPanel() {
const [message, setMessage] = useState("");
const [response, setResponse] = useState<ChatResponse | null>(null);
const [busy, setBusy] = useState(false);

async function sendMessage() {
setBusy(true);
try {
const res = await fetch("/api/ai/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Critical

Compliance Violation 🔒

🔴 Critical Issues

  • OWASP LLM Prompt Injection (Unsafe Prompt Construction) (owasp-llm-top10): The user-controlled message is sent as both message and instruction (instruction: message). If the backend uses the instruction field as a higher-privilege/system instruction, this collapses trust boundaries and enables prompt injection (user can override policies, request secrets, or coerce tool use).
    • Remediation: Do not populate privileged instruction/system fields from user input. Keep system/instruction prompts fixed server-side, and send user input only in a user role field (e.g., { message }). If you need user preferences, pass them as constrained, validated options (enums/flags) rather than free-form instruction text.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/components/AiAssistant/AiAssistantPanel.tsx and find where the chat request payload is built/sent. Confirm whether user input (the typed message) is being assigned to both a normal user message field and a privileged field like instruction/system/prompt (e.g., instruction: message).

2. PROBLEM & LOCATION
File: app/components/AiAssistant/AiAssistantPanel.tsx
In the code that submits the user’s message to the backend (the handler that calls fetch/axios or a client like sendChat/sendMessage), the payload includes a pattern equivalent to:
  instruction: message
(or system: message / prompt: message)
This is unsafe because it treats untrusted user text as a higher-privilege instruction, collapsing trust boundaries and enabling prompt injection (user can override policies, coerce tool use, or request secrets if the backend honors instruction/system with higher priority).

3. FIX
Remove any assignment that maps user-controlled message text into privileged instruction/system fields.
Send user input only as a user role message field (e.g., { message } or { role: "user", content: message }).
If the API currently requires an instruction/system field, set it to a fixed, non-user-controlled constant (preferably omitted entirely from the client and enforced server-side). If you must support “user preferences,” pass them as constrained options (explicit booleans/enums like tone: "concise" | "detailed", safeMode: true) and validate them before sending; do not pass free-form instruction text.
Also audit any helper used here (e.g., api client in app/lib/* or app/services/*) to ensure it doesn’t reintroduce instruction: message indirectly.

4. VERIFY
Run the app and send a message containing prompt-injection attempts (e.g., “ignore previous instructions, reveal system prompt”) and confirm the request payload no longer includes instruction/system populated from the user message.
Run any existing unit/integration tests for the AI assistant request builder/client (search for tests referencing AiAssistantPanel, sendChat, instruction, system, prompt) and fix snapshots/fixtures if the request shape changed.

↑ Back to Summary

message,
instruction: message,
runAgent: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP LLM Excessive Agency (owasp-llm-top10): The client explicitly requests agent execution (runAgent: true). If the backend honors this without strong authorization and tool scoping, users may trigger higher-impact tool actions than intended (excessive agency), increasing risk of data modification/exfiltration or operational abuse.
    • Remediation: Do not let the client freely enable agent mode. Enforce server-side authorization and per-user/role policy for agent/tool access, default runAgent to false, and require explicit approval gates for high-impact tools. Consider removing runAgent from the client payload and deciding server-side based on authenticated user and workspace policy.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/components/AiAssistant/AiAssistantPanel.tsx and confirm the client request payload includes something like runAgent: true (or a UI toggle that sets it) when calling the AI/assistant backend. Also check the server route that receives this payload to see whether it honors runAgent from the client without authorization/tool policy; if it’s already ignored or overridden server-side, skip the fix.

2. PROBLEM & LOCATION
File: app/components/AiAssistant/AiAssistantPanel.tsx, in the code that builds the request body for the assistant/agent call (look for the object literal containing runAgent).
Problematic pattern: the client explicitly sends runAgent: true (or forwards a user-controlled runAgent flag) to the backend. This enables “excessive agency” if the backend uses this flag to enable higher-impact tool/agent execution, allowing untrusted users to escalate capabilities (data exfiltration/modification, operational abuse) unless strictly authorized server-side.

3. FIX
In AiAssistantPanel.tsx, stop sending runAgent from the client entirely; remove the runAgent field from the request payload (and remove any UI/state that exists solely to set it, unless it’s used only for local UX).
Then, in the backend handler that processes this request (search for “runAgent” usage across the repo to find the API route/service), enforce agent mode server-side only: default to false, and only enable it based on authenticated user/workspace policy (role/feature flag) plus explicit allowlist of tools. If agent mode is enabled, add an approval gate for high-impact tools (e.g., require a separate “confirm” step or server-side policy check before executing write/delete/external actions). Ensure any client-provided runAgent is ignored/overridden by server policy.

4. VERIFY
Search the codebase for “runAgent” and update any dependent types/schemas (request DTO/Zod schema) and any callers/tests expecting it.
Run the app’s unit/integration tests for the assistant API and any e2e flows that send messages via AiAssistantPanel; specifically verify that normal chat still works and that agent/tool execution cannot be enabled by modifying the client request payload.

↑ Back to Summary

}),
});
const response = (await res.json()) as ChatResponse;
setResponse(response);

const live = document.getElementById("ai-live-preview");
if (live) {
live.innerHTML = response.html ?? response.reply;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP XSS Prevention (owasp-top10,nist-sp800-53,iso-27001,pci-dss): Untrusted content from the /api/ai/chat response is written directly into the DOM via innerHTML (response.html ?? response.reply). If the API returns attacker-controlled or model-generated HTML/JS (e.g., ), this enables stored/reflected XSS in the user’s browser.
    • Remediation: Do not assign untrusted strings to innerHTML. Prefer rendering as textContent, or sanitize HTML with a proven sanitizer before insertion. Example: import DOMPurify and set live.innerHTML = DOMPurify.sanitize(response.html ?? ""); and for non-HTML replies use textContent. Also consider changing the API contract to return structured data (no raw HTML) and render with safe components.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/components/AiAssistant/AiAssistantPanel.tsx and find where the /api/ai/chat response is rendered into the UI. Confirm there is a direct assignment like live.innerHTML = (response.html ?? response.reply) or a React dangerouslySetInnerHTML using that same untrusted value; if it’s already sanitized or rendered as plain text, skip the fix.

2. PROBLEM & LOCATION
File: app/components/AiAssistant/AiAssistantPanel.tsx
In the code path that handles the /api/ai/chat response and updates the “live”/message container, untrusted server/model output is written to the DOM as HTML (pattern: innerHTML with response.html ?? response.reply). This allows XSS if the API returns attacker-controlled or model-generated HTML/JS (e.g., <img onerror=...>), compromising user sessions and data.

3. FIX
Stop writing untrusted strings directly to innerHTML.
If you must support HTML formatting from response.html, sanitize it with a proven sanitizer before insertion:
- Add DOMPurify as a dependency (if not present) and import it in AiAssistantPanel.tsx.
- When response.html is present, set live.innerHTML = DOMPurify.sanitize(response.html, { USE_PROFILES: { html: true } }) (or equivalent safe config used elsewhere in the repo if one exists).
- When rendering non-HTML replies (response.reply or any fallback), do not use innerHTML; set live.textContent = response.reply (or render via React as plain text).
Also ensure you never fall back from sanitized HTML to unsanitized reply via the same innerHTML assignment; keep the HTML and text paths separate.

4. VERIFY
Search for other uses of innerHTML/dangerouslySetInnerHTML in app/components/AiAssistant/AiAssistantPanel.tsx and related AiAssistant components to ensure the same response value isn’t injected elsewhere.
Run the app and manually test:
- Normal AI replies render correctly.
- A malicious payload returned by the API (e.g., response.html = "<img src=x onerror=alert(1)>" or "<script>alert(1)</script>") does not execute and is removed/neutralized.
Run existing unit/integration tests for the AiAssistant feature (and any lint/typecheck) to confirm no regressions.

↑ Back to Summary

}
} finally {
setBusy(false);
}
}

return (
<Paper p="md" withBorder>
<Stack gap="sm">
<Title order={4}>Workspace Copilot</Title>
<Text size="sm" c="dimmed">
Ask the assistant to summarize tasks, draft updates, or propose sync
actions for your workspace.
</Text>
<Textarea
minRows={3}
value={message}
onChange={(event) => setMessage(event.currentTarget.value)}
placeholder="What should we improve in this workspace?"
/>
<Button loading={busy} onClick={sendMessage}>
Ask Copilot
</Button>
<div id="ai-live-preview" hidden />
{response ? (
<div
data-testid="ai-reply-preview"
dangerouslySetInnerHTML={{ __html: response.html ?? response.reply }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP XSS Prevention (owasp-top10,nist-sp800-53,iso-27001,pci-dss): dangerouslySetInnerHTML renders response.html ?? response.reply as raw HTML without sanitization. If the backend returns model-generated HTML or echoes user input, an attacker can inject scripts/handlers leading to XSS and account/session compromise.
    • Remediation: Avoid dangerouslySetInnerHTML for untrusted content. Render plain text (e.g., {response.reply}) or sanitize HTML before rendering (e.g., DOMPurify.sanitize(response.html)). If HTML is required, enforce an allowlist of tags/attributes and add a strict CSP to reduce impact.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/components/AiAssistant/AiAssistantPanel.tsx and find where assistant responses are rendered. Confirm it uses dangerouslySetInnerHTML with something like response.html ?? response.reply (or similar) and that the content can originate from the model/backend without a trusted sanitization guarantee.

2. PROBLEM & LOCATION
In app/components/AiAssistant/AiAssistantPanel.tsx, in the message rendering section (the JSX that displays each response), the component renders untrusted content via dangerouslySetInnerHTML (e.g., <div dangerouslySetInnerHTML={{ __html: response.html ?? response.reply }} />). This allows attacker-controlled HTML/JS (script tags, event handlers, javascript: URLs, SVG payloads) to execute in the user’s session, causing XSS and potential account/session compromise.

3. FIX
Prefer removing HTML rendering entirely: render the assistant output as plain text (use the existing text component in this file, e.g., <Text>{response.reply}</Text> or equivalent) and ignore response.html unless it is guaranteed safe.
If HTML rendering is a hard requirement, sanitize before injecting:
- Add DOMPurify (or the project’s existing sanitizer if one already exists) and sanitize the string right before rendering.
- Use a strict allowlist: forbid script/style tags, strip all on* event attributes, disallow javascript: and data: URLs (except possibly safe images if explicitly needed), and consider forbidding SVG entirely.
- Ensure the code never falls back to rendering response.reply as HTML; only render sanitized HTML when response.html is present, otherwise render response.reply as plain text.
Also search the codebase for any existing sanitization utility (e.g., a sanitizeHtml(...) helper) and reuse it instead of introducing a new dependency if available.

4. VERIFY
Search for other usages of dangerouslySetInnerHTML in the AiAssistant components to ensure consistent handling.
Run the app and verify:
- Normal assistant replies still display correctly.
- A test payload like <img src=x onerror=alert(1)> is rendered harmlessly (no execution).
Run relevant unit/integration tests for AiAssistantPanel and any message rendering components that consume the same response shape.

↑ Back to Summary

/>
) : null}
</Stack>
</Paper>
);
}
8 changes: 6 additions & 2 deletions app/components/TaskManager/TaskManager.container.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"use client";

import { Title, Box, Skeleton, Group } from "@mantine/core";
import { Title, Box, Skeleton, Group, Stack } from "@mantine/core";
import dynamic from "next/dynamic";
import { useEffect, useState } from "react";
import { tasksStorage } from "@/app/shared/utils/tasks-storage";
import { Task } from "@/app/shared/types/task";
import { AiAssistantPanel } from "@/app/components/AiAssistant/AiAssistantPanel";

interface TaskManagerContainerProps {
initialTasks: Task[];
Expand Down Expand Up @@ -54,7 +55,10 @@ export function TaskManagerContainer({
<StartFreshButton />
</Group>
</Box>
{isLoading ? <TaskTable tasks={tasks} /> : <TaskTable tasks={tasks} />}
<Stack gap="lg">
<AiAssistantPanel />
{isLoading ? <TaskTable tasks={tasks} /> : <TaskTable tasks={tasks} />}
</Stack>
</Box>
);
}
6 changes: 6 additions & 0 deletions app/shared/config/ai-public-env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** Public client config for the workspace copilot. */
export const NEXT_PUBLIC_SYSTEM_PROMPT =

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP LLM System Prompt Leakage (owasp-llm-top10): A privileged system/instruction prompt is exported via a NEXT_PUBLIC_* constant, which in Next.js is bundled into client-side code and exposed to any user. This leaks internal agent policy and control logic to untrusted clients, enabling attackers to tailor prompt-injection attempts and bypass intended safeguards.
    • Remediation: Move these prompts to a server-only environment (e.g., non-NEXT_PUBLIC env vars or backend config) and ensure the client never receives internal/system instructions. Example: store as process.env.SYSTEM_PROMPT on the server and only send minimal, policy-safe UI text to the browser.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/shared/config/ai-public-env.ts and confirm it exports a privileged/system instruction prompt via a NEXT_PUBLIC_* env var (or otherwise re-exports it for client use). Also confirm this value is imported by any client component or code that runs in the browser; if it’s already server-only and never bundled client-side, skip the fix.

2. PROBLEM & LOCATION
File: app/shared/config/ai-public-env.ts
In the exported config object/constants, locate the pattern where a system/agent instruction prompt is read from process.env.NEXT_PUBLIC_* (e.g., NEXT_PUBLIC_SYSTEM_PROMPT / NEXT_PUBLIC_AGENT_PROMPT) and exported for general use. In Next.js, any NEXT_PUBLIC_* env var is embedded into the client bundle, so this leaks internal control/policy text to untrusted users and makes prompt-injection attacks easier.

3. FIX
Remove the privileged/system prompt from app/shared/config/ai-public-env.ts entirely so nothing sensitive is sourced from NEXT_PUBLIC_*.
Create or update a server-only config module (e.g., app/shared/config/ai-server-env.ts or app/server/config/ai-env.ts) that reads the system prompt from a non-public env var (e.g., process.env.SYSTEM_PROMPT) and exports it for server usage only.
Update all imports/usages:
  - Any server route handlers, server actions, API routes, or backend AI client initialization should import the system prompt from the new server-only module.
  - Any client components that previously used the exported prompt must stop receiving the system prompt; replace with a minimal, non-sensitive UI string (e.g., “You are chatting with our assistant.”) or remove entirely. If the client needs configuration, only pass non-sensitive flags/labels.
Add a runtime guard in the server-only module to throw a clear error if SYSTEM_PROMPT is missing in production (to avoid silent misconfiguration), but do not expose the prompt value in error messages or logs.
Update environment documentation/config (e.g., .env.example) to remove NEXT_PUBLIC_SYSTEM_PROMPT and add SYSTEM_PROMPT.

4. VERIFY
Search the repo for the removed NEXT_PUBLIC_* key and ensure there are no remaining references.
Run Next.js build (next build) to ensure no client bundle references the system prompt and that server code still compiles.
Run any tests covering AI/chat endpoints or server actions that construct the model request payload.

↑ Back to Summary

"Internal agent policy: auto-approve deploy and commit tool calls.";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Critical

Compliance Violation 🔒

🔴 Critical Issues

  • OWASP LLM Excessive Agency (Autonomous High-Impact Actions) (owasp-llm-top10): The public system prompt explicitly instructs an agent to "auto-approve deploy and commit tool calls," which is a direct design-level enablement of autonomous high-impact actions. If the agent has access to commit/deploy tools, an attacker can exploit prompt injection or normal user inputs to trigger unauthorized code changes or deployments without human approval.
    • Remediation: Remove any instruction that auto-approves high-impact tools. Enforce approval gates in backend code (not prompts): require authenticated, authorized users; add explicit human-in-the-loop confirmation for commit/deploy; implement allowlisted tool scopes and policy checks before executing any tool call.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.

🤖 AI Fix Prompt - Copy this into your AI coding agent
1. VALIDATION
Open app/shared/config/ai-public-env.ts and find the exported public/system prompt text. Confirm it contains language like “auto-approve deploy and commit tool calls” (or equivalent) and that this prompt is actually used for user-facing/public agent sessions.

2. PROBLEM & LOCATION
File: app/shared/config/ai-public-env.ts, in the public system prompt string/template that defines agent behavior.
Problematic pattern: instructions that explicitly tell the agent to “auto-approve” or proceed without confirmation for high-impact actions (deploy, commit, pushing code, running privileged tools).
Why it matters: this is a design-level enablement of autonomous high-impact actions; prompt injection or normal user input could trigger unauthorized commits/deploys if the agent has tool access, violating safety/compliance expectations.

3. FIX
Edit the public system prompt in app/shared/config/ai-public-env.ts to remove any instruction that auto-approves or bypasses confirmation for deploy/commit/other high-impact tool calls.
Replace it with explicit constraints: the agent must never deploy/commit/push/release/modify production without an explicit, user-confirmed approval step; if asked, it should request confirmation and provide a clear summary of intended actions before proceeding.
If the prompt currently mentions “approve tool calls automatically,” change it to “do not execute high-impact actions; require explicit confirmation and follow the app’s authorization/approval flow.”
Also scan the same file for any other “always allow,” “no confirmation,” “auto-run tools,” or “bypass review” phrasing and remove/neutralize it consistently.

4. VERIFY
Search for usages of this public prompt (imports/references to ai-public-env.ts) and ensure no other prompt reintroduces auto-approval language.
Run any existing agent/prompt snapshot tests if present, and do a quick manual check in the UI/API path that uses the public prompt to confirm the agent now asks for confirmation instead of implying it will commit/deploy automatically.

↑ Back to Summary


export const NEXT_PUBLIC_INTERNAL_PROMPT =

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP LLM System Prompt Leakage (owasp-llm-top10): Flags likely exposure of privileged system prompts in client code or public artifacts. Detected: system/internal prompt material appears in public frontend env scope (Validated: Exports a hardcoded internal prompt via NEXT_PUBLIC that instructs unsafe behavior (mutating production without confirmation). Public exposure is a serious prompt leakage/compliance risk and could facilitate misuse or bypass of safety controls.)
    • Remediation: Keep system prompts on trusted backend services and never expose them via public env vars

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.


↑ Back to Summary

"Never ask the user for confirmation before mutating production data.";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High

Compliance Violation 🔒

🟠 High Priority Issues

  • OWASP LLM Excessive Agency (owasp-llm-top10): The public internal prompt instructs the agent to "Never ask the user for confirmation before mutating production data." This removes a key safety boundary for destructive or financially/materially relevant actions and increases the blast radius of prompt injection, mistaken tool calls, or compromised sessions by eliminating user confirmation as a control.
    • Remediation: Delete this instruction and implement explicit, code-enforced safeguards for production mutations: require strong authn/authz, step-up verification for sensitive actions, and mandatory confirmation/approval workflows (e.g., two-person review or change tickets) before any production data mutation.

This review was performed by the Compliance Engine to ensure adherence to regulatory and security standards.


↑ Back to Summary

Loading