<- blog

Prompt Injection Scans Deserve Triage Rules

CodeQL prompt-injection detection turns AI app security into review work. Route findings by exploit path, not scanner severity alone.

#security#ai-agents#developer-tools

GitHub's 10 July changelog for CodeQL 2.26.0 is a useful signal for teams building AI-enabled products: prompt injection is moving from conference-talk risk into normal code-scanning workflow. The same release also expands Kotlin support, which matters for teams with Android, JVM, or service code that sits beside web funnels and internal tools.

Two nearby GitHub updates reinforce the operational pattern. Secret scanning is getting clearer detector type names, making it easier to understand how a credential was found. GitHub also added per-user states for multi-user budgets in the REST API, so owners can see who is approaching or exceeding a budget without stitching user state together manually.

Fresh evidence points in one direction: security, AI usage, and cost controls are becoming machine-readable review queues. The useful question is not whether a scanner can find an AI risk. It is how a small team decides which finding can actually harm customers, revenue, data, or operations.

The repeated angle to avoid

Recent posts here have covered agent audit trails, cost budgets, permission budgets, deploy dry runs, feature-flag targeting diffs, cache variants, accessibility-tree contracts, Copilot telemetry, PR risk dashboards, and public secret monitoring. The lazy version of this article would repeat the old X needs Y pattern: AI apps need better security scanning.

The sharper thesis is that prompt-injection scanning creates a new triage problem. Operators need a rule that connects the finding to what the model can read, what tools it can call, and what business state it can change.

Why prompt injection is not one severity level

A prompt-injection issue is dangerous when untrusted content can steer a model across a boundary the business cares about. That boundary might be obvious, such as an agent with a tool that sends email or edits customer records. It might also be quieter, such as an assistant that summarises a supplier PDF and writes recommendations into a quote.

The same class of bug can have very different blast radii:

AI surface Example untrusted input Possible consequence Triage posture
Marketing FAQ bot Public web page or chat message Wrong answer, brand risk, support escalation Fix prompts and retrieval boundaries; monitor
Lead qualification assistant Form submission, uploaded photo, email body Bad routing, missed urgent lead, CRM pollution High priority if it can write or suppress leads
Ecommerce support agent Customer message, order note, policy page Refund, discount, account, or shipping action High priority when tool calls affect money or orders
Internal analytics assistant Report labels, campaign names, notes Misleading recommendations or hidden instructions Medium to high if decisions are automated
Admin or deployment agent Issues, PR comments, logs, docs Code change, config change, release action Critical when write tools are reachable

Scanner output should start the conversation, not finish it. The triage decision depends on the route from untrusted text to model instruction to tool action.

Build the exploit-path record

When CodeQL or another scanner flags possible prompt-injection exposure, create a compact record that reviewers can reason about. Do not only paste the alert into a ticket.

prompt_injection_triage:
  surface: lead-routing assistant
  untrusted_sources:
    - quote form message
    - uploaded job description
    - inbound email body
  model_can_read:
    - CRM lead profile
    - service area rules
    - recent conversation history
  model_can_do:
    - classify urgency
    - assign sales queue
    - draft reply
  model_must_not_do:
    - delete or suppress a lead
    - change price or discount
    - send without approval
  boundary:
    - untrusted content is data, not instruction
  evidence_required:
    - test payload that tries to override system rules
    - tool-call allowlist
    - human approval point for customer-visible messages

This record turns a vague AI security concern into something a builder can fix. If the model only reads public documentation and drafts text for review, the response may be narrower. If the model can call a CRM, send a quote, edit a ticket, change a campaign, or open a pull request, the same finding deserves stronger controls.

Separate detection from containment

Prompt-injection scanning is useful because it tells you where to look. Containment is the product and architecture work that decides what happens when hostile instructions appear.

For practical web teams, containment usually means five layers:

  1. Input classification: mark customer text, scraped pages, emails, tickets, PDFs, and comments as untrusted data.
  2. Instruction separation: keep system and developer instructions outside retrievable or user-editable content.
  3. Tool allowlists: expose only the tools needed for the job, with narrower actions for untrusted sessions.
  4. Commit gates: require human approval before writes that affect customers, money, records, deployments, or public content.
  5. Regression payloads: keep a small suite of malicious examples that try to override the assistant, reveal hidden context, or trigger unsafe tool calls.

The failure mode is treating the scanner alert as the control. A green scan does not prove the workflow is safe if the tool permissions are too broad. A red scan does not always mean shutdown if the model cannot cross a meaningful boundary.

Secret findings and prompt findings belong in the same queue

GitHub's clearer secret-scanning detector names matter because triage improves when the alert says how the credential was identified. A precise detector name helps responders decide whether the secret is provider-issued, generic, verified, structured, or pattern-matched.

Prompt-injection findings need the same clarity. Reviewers should be able to see:

  • whether the source is user-controlled, public-web controlled, partner-controlled, or internal;
  • whether the model's output is advisory, customer-visible, or tool-executed;
  • whether the workflow has write permissions;
  • whether the affected code path is production, preview, demo, or internal;
  • whether a regression test proves the boundary now holds.

That lets security, engineering, and operations share one queue instead of juggling unrelated dashboards. A leaked active token that can deploy production and a prompt-injection path that can command a deployment agent are different alerts, but they both ask the same business question: what can this exposure touch?

Add budgets to the triage decision

The multi-user budgets API update may look unrelated, but it points at a useful operating habit. AI and automation controls are easier to manage when per-user or per-actor state is queryable. Budget state can become a risk signal.

A user or agent that is close to an AI budget limit, repeatedly triggering deep investigations, or consuming more high-capability model time than expected deserves context in the review queue. That does not mean every over-budget user is unsafe. It means the operator should ask why the work is expensive:

  • Is the agent looping on failed validation?
  • Is it reading broad logs because the task is poorly scoped?
  • Is a prompt-injection test suite generating expected extra spend?
  • Is one user running production-impacting AI jobs without a clear owner?
  • Is the work valuable enough to justify a higher budget and stronger review?

Cost is not a security control by itself. But unusual spend can reveal broad context access, unclear tasks, or runaway tool use. Those are exactly the conditions that make prompt injection more dangerous.

A triage rule small teams can use

Use this rule before arguing over severity labels:

Question If yes If no
Can untrusted text enter the model context? Keep the finding open until the boundary is tested Treat as lower priority unless context can change later
Can the model call tools or trigger writes? Require tool allowlist review and approval gates Focus on answer quality, disclosure, and monitoring
Can the action affect customers, money, records, releases, or analytics? Escalate to high-risk review Handle in normal security backlog
Is there a regression payload for the exploit path? Use it in CI or release checks Write one before closing the alert
Is usage or spend abnormal for the workflow? Check for loops, broad context, or misuse Do not use budget state as the only signal

This is deliberately simple. It gives a founder, developer, or operator a way to turn new scanner coverage into decisions without building an enterprise governance programme.

The implementation consequence

The next useful step is to add prompt-injection triage to the same place the team already reviews secrets, agent runs, and release risk. For a small web business, that might be a pull request template, a security issue label, or a lightweight runbook.

The important change is the evidence standard. Closing a prompt-injection alert should require more than "we changed the prompt." It should show what untrusted input can reach, which tools are exposed, what the model is forbidden to do, which approval gate catches risky writes, and which test proves the boundary.

That is the practical value of CodeQL adding AI prompt-injection detection. It makes the risk visible in the code workflow. The team still has to connect that visibility to the business system the model can affect.

Need technical help?

I'm a software engineer who builds web apps, APIs, and AI tooling. If you've got a project or a problem to talk through, book a free 30-minute call.

Book time with me ->