DEV Community

Build a Prompt-Injection Regression Fixture for CodeQL 2.26.0

GitHub announced on July 10, 2026 that CodeQL 2.26.0 adds AI prompt-injection detection. Enabling a query is useful; owning a regression test is better. Primary source: GitHub Changelog, July 10, 2026. The examples below are implementation templates, not results from a repository I tested.

Model a path, not a phrase

A useful fixture contains an untrusted source, prompt construction, and a model sink:

security-fixtures/prompt-injection/
โ”œโ”€โ”€ positive/direct-flow.ts
โ”œโ”€โ”€ positive/helper-flow.ts
โ”œโ”€โ”€ negative/trusted-instruction.ts
โ””โ”€โ”€ expected-alerts.json

Do not test for the literal phrase ignore previous instructions. Static analysis needs a data-flow path. Preserve a supported SDK call from your production stack so CodeQL can recognize the sink.

// Intentionally vulnerable fixture. Never ship this path.
import { model } from "./supported-client";

declare function loadIssueBody(id: number): Promise<string>;

export async function summarize(id: number) {
  const untrusted = await loadIssueBody(id);
  return model.generate({
    system: "Summarize the issue",
    user: untrusted,
  });
}

Add a second positive case that passes the value through a helper. Then add a negative control where attacker input cannot select or alter the instruction. A function named sanitize() is not evidence of sanitization.

Assert SARIF evidence

Uploading SARIF alone does not create a regression gate. Commit the expected rule and fixture location:

{
  "required": [
    {
      "ruleId": "REPLACE_WITH_DOCUMENTED_RULE_ID",
      "pathSuffix": "positive/direct-flow.ts"
    }
  ],
  "forbiddenPathSuffixes": [
    "negative/trusted-instruction.ts"
  ]
}

Keep the rule ID as a placeholder until it is copied from the CodeQL 2.26.0 documentation or an observed SARIF result. Machine-facing identifiers should never be guessed.

A small assertion can compare runs[].results[].ruleId and each physical location against this file. Fail when a required alert disappears or a negative fixture starts alerting. Do not assert the total number of repository alerts; unrelated code changes make that brittle.

Treat upgrades as security changes

Result after upgrade Action
Required alert remains Continue normal review
Required alert disappears Block until query, model, build, or fixture changes are explained
New positive location appears Inspect and deliberately update expectations
Negative control alerts Revisit the mitigation assumption before suppressing

Pin the CodeQL CLI or action version, record it in CI, and run the fixture on a disposable database. Keep runtime adversarial tests too: static analysis cannot prove how a model will react, and custom wrappers or unsupported SDKs may not be modeled.

The goal is intentionally narrow: make one important untrusted-to-model path an executable repository contract instead of a changelog checkbox.

Comments

No comments yet. Start the discussion.