From 11d55d889bba7096e27c692f4696ea9778e5bc0b Mon Sep 17 00:00:00 2001 From: Nathan Randall Date: Wed, 8 Apr 2026 15:53:17 -0600 Subject: [PATCH] actions: experimental queries to detect CWE-1427 Add detection for prompt injection vulnerabilities (CWE-1427) in GitHub Actions workflows that use AI inference actions. New queries: - PromptInjectionCritical.ql: Detects user-controlled data flowing into AI prompts in privileged contexts (severity 9.0) - PromptInjectionMedium.ql: Detects prompt injection on non-privileged but externally triggerable events like pull_request (severity 5.0) New library: - PromptInjectionQuery.qll: Taint tracking from remote flow sources to MaD-defined prompt-injection sinks MaD model (prompt_injection_sinks.model.yml): - 30+ AI actions including actions/ai-inference, anthropics/claude-code-action, google-github-actions/run-gemini-cli, warpdotdev/oz-agent-action, and others ControlChecks.qll: Add 'prompt-injection' to control check categories --- .../codeql/actions/security/ControlChecks.qll | 3 +- .../actions/security/PromptInjectionQuery.qll | 130 ++++++++++++++++++ .../manual/prompt_injection_sinks.model.yml | 47 +++++++ .../CWE-1427/PromptInjectionCritical.md | 80 +++++++++++ .../CWE-1427/PromptInjectionCritical.ql | 24 ++++ .../CWE-1427/PromptInjectionMedium.md | 72 ++++++++++ .../CWE-1427/PromptInjectionMedium.ql | 25 ++++ .../CWE-1427/.github/workflows/safe1.yml | 14 ++ .../CWE-1427/.github/workflows/safe2.yml | 19 +++ .../CWE-1427/.github/workflows/safe3.yml | 14 ++ .../CWE-1427/.github/workflows/safe4.yml | 16 +++ .../.github/workflows/vulnerable1.yml | 20 +++ .../.github/workflows/vulnerable10.yml | 16 +++ .../.github/workflows/vulnerable2.yml | 16 +++ .../.github/workflows/vulnerable3.yml | 18 +++ .../.github/workflows/vulnerable4.yml | 15 ++ .../.github/workflows/vulnerable5.yml | 18 +++ .../.github/workflows/vulnerable6.yml | 18 +++ .../.github/workflows/vulnerable7.yml | 20 +++ .../.github/workflows/vulnerable8.yml | 24 ++++ .../.github/workflows/vulnerable9.yml | 18 +++ .../CWE-1427/PromptInjectionCritical.expected | 35 +++++ .../CWE-1427/PromptInjectionCritical.qlref | 1 + .../CWE-1427/PromptInjectionMedium.expected | 25 ++++ .../CWE-1427/PromptInjectionMedium.qlref | 1 + 25 files changed, 688 insertions(+), 1 deletion(-) create mode 100644 actions/ql/lib/codeql/actions/security/PromptInjectionQuery.qll create mode 100644 actions/ql/lib/ext/manual/prompt_injection_sinks.model.yml create mode 100644 actions/ql/src/experimental/Security/CWE-1427/PromptInjectionCritical.md create mode 100644 actions/ql/src/experimental/Security/CWE-1427/PromptInjectionCritical.ql create mode 100644 actions/ql/src/experimental/Security/CWE-1427/PromptInjectionMedium.md create mode 100644 actions/ql/src/experimental/Security/CWE-1427/PromptInjectionMedium.ql create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe1.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe2.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe3.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe4.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable1.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable10.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable2.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable3.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable4.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable5.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable6.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable7.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable8.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable9.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionCritical.expected create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionCritical.qlref create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionMedium.expected create mode 100644 actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionMedium.qlref diff --git a/actions/ql/lib/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll index 41f512abbc34..92a9147b4e00 100644 --- a/actions/ql/lib/codeql/actions/security/ControlChecks.qll +++ b/actions/ql/lib/codeql/actions/security/ControlChecks.qll @@ -5,7 +5,8 @@ string any_category() { [ "untrusted-checkout", "output-clobbering", "envpath-injection", "envvar-injection", "command-injection", "argument-injection", "code-injection", "cache-poisoning", - "untrusted-checkout-toctou", "artifact-poisoning", "artifact-poisoning-toctou" + "untrusted-checkout-toctou", "artifact-poisoning", "artifact-poisoning-toctou", + "prompt-injection" ] } diff --git a/actions/ql/lib/codeql/actions/security/PromptInjectionQuery.qll b/actions/ql/lib/codeql/actions/security/PromptInjectionQuery.qll new file mode 100644 index 000000000000..6c51b6bd215e --- /dev/null +++ b/actions/ql/lib/codeql/actions/security/PromptInjectionQuery.qll @@ -0,0 +1,130 @@ +/** + * Provides classes and predicates for detecting prompt injection vulnerabilities + * in GitHub Actions workflows that use AI inference actions. + * + * This library identifies: + * - CWE-1427: User-controlled data flowing into AI model prompts without sanitization + */ + +private import actions +private import codeql.actions.TaintTracking +private import codeql.actions.dataflow.ExternalFlow +import codeql.actions.dataflow.FlowSources +import codeql.actions.DataFlow +import codeql.actions.security.ControlChecks + +/** + * A sink for prompt injection vulnerabilities (CWE-1427). + * Defined entirely through MaD extensible `actionsSinkModel` with kind `prompt-injection`. + */ +class PromptInjectionSink extends DataFlow::Node { + PromptInjectionSink() { madSink(this, "prompt-injection") } +} + +/** + * A source representing user-controlled data from repository_dispatch client_payload. + * The client_payload can be set by anyone with write access to the repository + * or via the GitHub API, making it a potential vector for injection attacks. + */ +class RepositoryDispatchClientPayloadSource extends RemoteFlowSource { + string event; + + RepositoryDispatchClientPayloadSource() { + exists(Expression e | + this.asExpr() = e and + e.getExpression().matches("github.event.client_payload%") and + event = e.getATriggerEvent().getName() and + event = "repository_dispatch" + ) + } + + override string getSourceType() { result = "client_payload" } + + override string getEventName() { result = event } +} + +/** + * Gets the relevant event for a sink in a privileged context, + * excluding sinks protected by control checks for the prompt-injection category. + */ +Event getRelevantEventForSink(DataFlow::Node sink) { + inPrivilegedContext(sink.asExpr(), result) and + not exists(ControlCheck check | check.protects(sink.asExpr(), result, "prompt-injection")) +} + +/** + * Gets the relevant event for a prompt injection sink, including + * repository_dispatch events which are externally triggerable via the GitHub API. + */ +Event getRelevantEventForPromptInjection(DataFlow::Node sink) { + result = getRelevantEventForSink(sink) + or + exists(LocalJob job | + job = sink.asExpr().getEnclosingJob() and + job.getATriggerEvent() = result and + result.getName() = "repository_dispatch" + ) +} + +/** + * Holds when a critical-severity prompt injection path exists from source to sink. + */ +predicate criticalSeverityPromptInjection( + PromptInjectionFlow::PathNode source, PromptInjectionFlow::PathNode sink, Event event +) { + PromptInjectionFlow::flowPath(source, sink) and + event = getRelevantEventForPromptInjection(sink.getNode()) and + source.getNode().(RemoteFlowSource).getEventName() = event.getName() +} + +/** + * Gets the relevant event for a sink in any externally triggerable context, + * excluding sinks protected by control checks for the prompt-injection category. + * This is broader than `getRelevantEventForSink` — it includes non-privileged + * events like `pull_request` where an attacker can still control event properties + * (PR title, body, branch name) that flow into AI prompts. + */ +Event getRelevantEventForMediumSeverity(DataFlow::Node sink) { + exists(LocalJob job | + job = sink.asExpr().getEnclosingJob() and + job.getATriggerEvent() = result and + result.isExternallyTriggerable() and + not inPrivilegedContext(sink.asExpr(), result) and + not result.getName() = "repository_dispatch" and + not exists(ControlCheck check | check.protects(sink.asExpr(), result, "prompt-injection")) + ) +} + +/** + * Holds when a medium-severity prompt injection path exists from source to sink. + * Covers non-privileged but externally triggerable events (e.g. pull_request) + * where an attacker can control event properties that flow into AI prompts. + */ +predicate mediumSeverityPromptInjection( + PromptInjectionFlow::PathNode source, PromptInjectionFlow::PathNode sink, Event event +) { + PromptInjectionFlow::flowPath(source, sink) and + event = getRelevantEventForMediumSeverity(sink.getNode()) and + source.getNode().(RemoteFlowSource).getEventName() = event.getName() +} + +/** + * A taint-tracking configuration for unsafe user input + * that is used to construct AI prompts (CWE-1427). + */ +private module PromptInjectionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + predicate isSink(DataFlow::Node sink) { sink instanceof PromptInjectionSink } + + predicate observeDiffInformedIncrementalMode() { any() } + + Location getASelectedSinkLocation(DataFlow::Node sink) { + result = sink.getLocation() + or + result = getRelevantEventForPromptInjection(sink).getLocation() + } +} + +/** Tracks flow of unsafe user input that is used to construct AI prompts. */ +module PromptInjectionFlow = TaintTracking::Global; diff --git a/actions/ql/lib/ext/manual/prompt_injection_sinks.model.yml b/actions/ql/lib/ext/manual/prompt_injection_sinks.model.yml new file mode 100644 index 000000000000..88bf0730abc7 --- /dev/null +++ b/actions/ql/lib/ext/manual/prompt_injection_sinks.model.yml @@ -0,0 +1,47 @@ +extensions: + - addsTo: + pack: codeql/actions-all + extensible: actionsSinkModel + # AI actions whose prompt/input parameters accept user-controllable data. + # source: https://boostsecurityio.github.io/lotp/ + # source: https://github.com/marketplace?type=actions&category=ai-assisted + data: + # === GitHub official AI actions === + - ["actions/ai-inference", "*", "input.prompt", "prompt-injection", "manual"] + - ["actions/ai-inference", "*", "input.system-prompt", "prompt-injection", "manual"] + - ["github/ai-moderator", "*", "input.prompt", "prompt-injection", "manual"] + - ["github/ai-moderator", "*", "input.custom-instructions", "prompt-injection", "manual"] + # === Anthropic === + - ["anthropics/claude-code-action", "*", "input.prompt", "prompt-injection", "manual"] + - ["anthropics/claude-code-action", "*", "input.direct_prompt", "prompt-injection", "manual"] + - ["anthropics/claude-code-action", "*", "input.custom_instructions", "prompt-injection", "manual"] + # === Google === + - ["google/gemini-code-assist-action", "*", "input.prompt", "prompt-injection", "manual"] + - ["google-gemini/code-assist-action", "*", "input.prompt", "prompt-injection", "manual"] + # === OpenAI / GPT === + - ["openai/chat-completion-action", "*", "input.prompt", "prompt-injection", "manual"] + - ["openai/chat-completion-action", "*", "input.messages", "prompt-injection", "manual"] + - ["di-sukharev/opencommit", "*", "input.prompt", "prompt-injection", "manual"] + # === Community AI actions (marketplace) === + - ["quixio/quix-streams-ci-ai-review", "*", "input.prompt", "prompt-injection", "manual"] + - ["rubberduck-ai/rubberduck-review-action", "*", "input.prompt", "prompt-injection", "manual"] + - ["coderabbitai/ai-pr-reviewer", "*", "input.prompt", "prompt-injection", "manual"] + - ["coderabbitai/ai-pr-reviewer", "*", "input.system_message", "prompt-injection", "manual"] + - ["platisd/openai-pr-description", "*", "input.prompt", "prompt-injection", "manual"] + - ["CodiumAI/pr-agent", "*", "input.prompt", "prompt-injection", "manual"] + - ["arcee-ai/agent-action", "*", "input.prompt", "prompt-injection", "manual"] + - ["langchain-ai/langsmith-action", "*", "input.prompt", "prompt-injection", "manual"] + - ["abirismyname/create-discussion-with-ai", "*", "input.prompt", "prompt-injection", "manual"] + - ["yousefed/ai-action", "*", "input.prompt", "prompt-injection", "manual"] + - ["nickscamara/openai-github-action", "*", "input.prompt", "prompt-injection", "manual"] + - ["austenstone/openai-completion-action", "*", "input.prompt", "prompt-injection", "manual"] + - ["joshspicer/gpt-review", "*", "input.prompt", "prompt-injection", "manual"] + - ["github/copilot-text-inference", "*", "input.prompt", "prompt-injection", "manual"] + # === Google (GitHub Actions org) === + - ["google-github-actions/run-gemini-cli", "*", "input.prompt", "prompt-injection", "manual"] + # === Warp === + - ["warpdotdev/oz-agent-action", "*", "input.prompt", "prompt-injection", "manual"] + # === Generic AI action patterns (common parameter names) === + - ["togethercomputer/together-action", "*", "input.prompt", "prompt-injection", "manual"] + - ["huggingface/inference-action", "*", "input.prompt", "prompt-injection", "manual"] + - ["replicate/action", "*", "input.prompt", "prompt-injection", "manual"] diff --git a/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionCritical.md b/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionCritical.md new file mode 100644 index 000000000000..77f9597963c6 --- /dev/null +++ b/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionCritical.md @@ -0,0 +1,80 @@ +## Overview + +Passing user-controlled data into the prompt of an AI inference action allows an attacker to hijack the AI's behavior through **prompt injection**. Any workflow that feeds external input — issue titles, PR bodies, comments, or `repository_dispatch` payloads — directly into an AI prompt without sanitization is vulnerable to this class of attack. + +When the AI action runs with access to secrets, write permissions, or code execution capabilities, a successful prompt injection can lead to secret exfiltration, unauthorized repository modifications, malicious package publication, or arbitrary command execution within the CI/CD environment. + +## Recommendation + +Never pass user-controlled data directly into AI prompt parameters. Instead: + +- **Sanitize and truncate** user input before including it in prompts. Strip control characters and limit length. +- **Use environment variables** with shell-native interpolation (e.g. `$TITLE` not `${{ ... }}`) to prevent expression injection. +- **Restrict workflow permissions** to the minimum required (e.g. `issues: write`, `models: read` only). +- **Use deployment environments** with required reviewers for workflows that invoke AI actions on external input. +- **Validate AI output** before using it in subsequent steps — treat AI responses as untrusted data. + +## Example + +### Incorrect Usage + +The following example passes unsanitized issue data directly into an AI prompt. An attacker can craft an issue title containing hidden instructions that cause the AI to ignore its system prompt, exfiltrate secrets via its response, or produce output that compromises downstream steps: + +```yaml +on: + issues: + types: [opened] + +jobs: + summary: + runs-on: ubuntu-latest + permissions: + issues: write + models: read + steps: + - name: Run AI inference + uses: actions/ai-inference@v1 + with: + prompt: | + Summarize the following GitHub issue: + Title: ${{ github.event.issue.title }} + Body: ${{ github.event.issue.body }} +``` + +### Correct Usage + +The following example sanitizes and truncates user input before passing it to the AI, and uses environment variables to prevent expression injection: + +```yaml +on: + issues: + types: [opened] + +jobs: + summary: + runs-on: ubuntu-latest + permissions: + issues: write + models: read + steps: + - name: Sanitize input + id: sanitize + run: | + SAFE_TITLE=$(echo "$TITLE" | head -c 200 | tr -dc '[:print:]') + echo "title=$SAFE_TITLE" >> $GITHUB_OUTPUT + env: + TITLE: ${{ github.event.issue.title }} + + - name: Run AI inference + uses: actions/ai-inference@v1 + with: + prompt: | + Summarize the following GitHub issue title (user input has been sanitized): + Title: ${{ steps.sanitize.outputs.title }} +``` + +## References + +- Common Weakness Enumeration: [CWE-1427](https://cwe.mitre.org/data/definitions/1427.html). +- [OWASP LLM01: Prompt Injection](https://genai.owasp.org/llmrisk/llm01-prompt-injection/). +- GitHub Docs: [Security hardening for GitHub Actions](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions). diff --git a/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionCritical.ql b/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionCritical.ql new file mode 100644 index 000000000000..e2ce3430d0f8 --- /dev/null +++ b/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionCritical.ql @@ -0,0 +1,24 @@ +/** + * @name Prompt injection from user-controlled Actions input + * @description User-controlled data flowing into AI prompts in a privileged context + * may allow attackers to manipulate AI behavior through prompt injection. + * @kind path-problem + * @problem.severity error + * @security-severity 9.0 + * @precision high + * @id actions/prompt-injection/critical + * @tags actions + * security + * experimental + * external/cwe/cwe-1427 + */ + +import actions +import codeql.actions.security.PromptInjectionQuery +import PromptInjectionFlow::PathGraph + +from PromptInjectionFlow::PathNode source, PromptInjectionFlow::PathNode sink, Event event +where criticalSeverityPromptInjection(source, sink, event) +select sink.getNode(), source, sink, + "Potential prompt injection in $@, which may be controlled by an external user ($@).", sink, + sink.getNode().asExpr().(Expression).getRawExpression(), event, event.getName() diff --git a/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionMedium.md b/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionMedium.md new file mode 100644 index 000000000000..6817f5a5d8a2 --- /dev/null +++ b/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionMedium.md @@ -0,0 +1,72 @@ +## Overview + +Passing user-controlled data into the prompt of an AI inference action on non-privileged but externally triggerable events such as `pull_request` allows an attacker to manipulate AI behavior through **prompt injection**. While the `pull_request` event does not grant write access to the base repository by default, the AI action may still reveal sensitive information, produce misleading output, or influence downstream processes that trust the AI's response. + +This is a lower-severity variant of prompt injection (compared to privileged contexts like `issues`, `issue_comment`, or `pull_request_target`) because the attacker's ability to exploit the injection is limited by the reduced permissions of the triggering event. + +## Recommendation + +Apply the same mitigations as for critical-severity prompt injection: + +- **Sanitize and truncate** user input before including it in prompts. +- **Use environment variables** with shell-native interpolation instead of `${{ }}` expression syntax. +- **Restrict workflow permissions** to the minimum required. +- **Validate AI output** before using it in subsequent steps. + +## Example + +### Incorrect Usage + +The following example passes the pull request title directly into an AI prompt on the `pull_request` event: + +```yaml +on: + pull_request: + types: [opened] + +jobs: + analyze: + runs-on: ubuntu-latest + steps: + - name: AI analysis + uses: actions/ai-inference@v1 + with: + prompt: | + Analyze this PR title: + ${{ github.event.pull_request.title }} +``` + +### Correct Usage + +The following example sanitizes the PR title before passing it to the AI: + +```yaml +on: + pull_request: + types: [opened] + +jobs: + analyze: + runs-on: ubuntu-latest + steps: + - name: Sanitize input + id: sanitize + run: | + SAFE_TITLE=$(echo "$TITLE" | head -c 200 | tr -dc '[:print:]') + echo "title=$SAFE_TITLE" >> $GITHUB_OUTPUT + env: + TITLE: ${{ github.event.pull_request.title }} + + - name: AI analysis + uses: actions/ai-inference@v1 + with: + prompt: | + Analyze this PR title (sanitized): + ${{ steps.sanitize.outputs.title }} +``` + +## References + +- Common Weakness Enumeration: [CWE-1427](https://cwe.mitre.org/data/definitions/1427.html). +- [OWASP LLM01: Prompt Injection](https://genai.owasp.org/llmrisk/llm01-prompt-injection/). +- GitHub Docs: [Security hardening for GitHub Actions](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions). diff --git a/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionMedium.ql b/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionMedium.ql new file mode 100644 index 000000000000..47d0c705b93e --- /dev/null +++ b/actions/ql/src/experimental/Security/CWE-1427/PromptInjectionMedium.ql @@ -0,0 +1,25 @@ +/** + * @name Prompt injection from user-controlled Actions input (medium severity) + * @description User-controlled data flowing into AI prompts on non-privileged + * but externally triggerable events (e.g. pull_request) may allow + * attackers to manipulate AI behavior through prompt injection. + * @kind path-problem + * @problem.severity warning + * @security-severity 5.0 + * @precision medium + * @id actions/prompt-injection/medium + * @tags actions + * security + * experimental + * external/cwe/cwe-1427 + */ + +import actions +import codeql.actions.security.PromptInjectionQuery +import PromptInjectionFlow::PathGraph + +from PromptInjectionFlow::PathNode source, PromptInjectionFlow::PathNode sink, Event event +where mediumSeverityPromptInjection(source, sink, event) +select sink.getNode(), source, sink, + "Potential prompt injection in $@, which may be controlled by an external user ($@).", sink, + sink.getNode().asExpr().(Expression).getRawExpression(), event, event.getName() diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe1.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe1.yml new file mode 100644 index 000000000000..2be45ebfb315 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe1.yml @@ -0,0 +1,14 @@ +name: Safe AI Usage +on: + push: + branches: [main] + +jobs: + analyze: + runs-on: ubuntu-latest + steps: + - name: Run AI with hardcoded prompt + uses: actions/ai-inference@v1 + with: + prompt: | + Analyze the repository structure and suggest improvements. diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe2.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe2.yml new file mode 100644 index 000000000000..af5515c6a39a --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe2.yml @@ -0,0 +1,19 @@ +name: Safe AI with Author Association Check +on: + issue_comment: + types: [created] + +jobs: + respond: + if: >- + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: AI response + uses: actions/ai-inference@v1 + with: + prompt: | + Respond to this comment: + ${{ github.event.comment.body }} diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe3.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe3.yml new file mode 100644 index 000000000000..d1392a8a508a --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe3.yml @@ -0,0 +1,14 @@ +name: Safe AI on Push Event +on: + push: + branches: [main] + +jobs: + review: + runs-on: ubuntu-latest + steps: + - name: Run Gemini on push + uses: google-github-actions/run-gemini-cli@v1 + with: + prompt: | + Review the latest changes on the main branch and generate a summary. diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe4.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe4.yml new file mode 100644 index 000000000000..82b3532dd6d2 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/safe4.yml @@ -0,0 +1,16 @@ +name: Safe AI with Non-Bot Actor Check +on: + issues: + types: [opened] + +jobs: + triage: + if: github.actor == 'admin-user' + runs-on: ubuntu-latest + steps: + - name: AI triage for admin only + uses: warpdotdev/oz-agent-action@v1 + with: + prompt: | + Analyze this issue: + ${{ github.event.issue.title }} diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable1.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable1.yml new file mode 100644 index 000000000000..78de905cfe2e --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable1.yml @@ -0,0 +1,20 @@ +name: Summarize New Issues +on: + issues: + types: [opened] + +jobs: + summary: + runs-on: ubuntu-latest + permissions: + issues: write + models: read + steps: + - name: Run AI inference + id: inference + uses: actions/ai-inference@v1 + with: + prompt: | + Summarize the following GitHub issue: + Title: ${{ github.event.issue.title }} + Body: ${{ github.event.issue.body }} diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable10.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable10.yml new file mode 100644 index 000000000000..f8827e74927d --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable10.yml @@ -0,0 +1,16 @@ +name: Bot Actor Check Does Not Prevent Injection +on: + issues: + types: [opened] + +jobs: + triage: + if: github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + steps: + - name: AI triage for bot issues only + uses: warpdotdev/oz-agent-action@v1 + with: + prompt: | + Analyze this dependency update: + ${{ github.event.issue.title }} diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable2.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable2.yml new file mode 100644 index 000000000000..4fa966a8f9d3 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable2.yml @@ -0,0 +1,16 @@ +name: AI Command Handler +on: + repository_dispatch: + types: [ai-command] + +jobs: + ai-task: + runs-on: ubuntu-latest + steps: + - name: Call AI model + uses: actions/ai-inference@v1 + with: + prompt: | + Process the following request: + Command: ${{ github.event.client_payload.command }} + Args: ${{ github.event.client_payload.args }} diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable3.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable3.yml new file mode 100644 index 000000000000..b645f7b95a22 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable3.yml @@ -0,0 +1,18 @@ +name: Claude Triage Bot +on: + issues: + types: [opened] + +jobs: + triage: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: AI triage + uses: anthropics/claude-code-action@v1 + with: + prompt: | + Triage the following issue: + ${{ github.event.issue.title }} + ${{ github.event.issue.body }} diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable4.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable4.yml new file mode 100644 index 000000000000..5cb0f13bb2b1 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable4.yml @@ -0,0 +1,15 @@ +name: PR Comment Triage +on: + issue_comment: + types: [created] + +jobs: + triage: + runs-on: ubuntu-latest + steps: + - name: AI inference on comment + uses: actions/ai-inference@v1 + with: + prompt: | + Analyze the following comment: + ${{ github.event.comment.body }} diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable5.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable5.yml new file mode 100644 index 000000000000..357e2da6df3a --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable5.yml @@ -0,0 +1,18 @@ +name: AI PR Title Analysis +on: + pull_request: + types: [opened, synchronize] + +jobs: + analyze: + runs-on: ubuntu-latest + permissions: + models: read + steps: + - name: AI analysis + uses: actions/ai-inference@v2 + with: + model: openai/gpt-4o + prompt: | + Analyze this PR title: + ${{ github.event.pull_request.title }} diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable6.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable6.yml new file mode 100644 index 000000000000..319e88dbd115 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable6.yml @@ -0,0 +1,18 @@ +name: Oz Agent Issue Handler +on: + issues: + types: [opened] + +jobs: + handle: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Run Oz agent + uses: warpdotdev/oz-agent-action@v1 + with: + prompt: | + Investigate and respond to this issue: + Title: ${{ github.event.issue.title }} + Body: ${{ github.event.issue.body }} diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable7.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable7.yml new file mode 100644 index 000000000000..2a18092b5429 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable7.yml @@ -0,0 +1,20 @@ +name: Gemini PR Review +on: + pull_request_review: + types: [submitted] + +jobs: + review: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - name: Run Gemini review + id: gemini_pr_review + uses: google-github-actions/run-gemini-cli@v1 + with: + prompt: | + Review this pull request: + Title: ${{ github.event.pull_request.title }} + Body: ${{ github.event.pull_request.body }} diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable8.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable8.yml new file mode 100644 index 000000000000..05ddd2255bf8 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable8.yml @@ -0,0 +1,24 @@ +name: Gemini Fix from Branch Name +on: + workflow_run: + workflows: [Tests] + types: [completed] + +jobs: + fix: + if: github.event.workflow_run.conclusion == 'failure' + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run Gemini fix + id: gemini_fix + uses: google-github-actions/run-gemini-cli@v1 + with: + prompt: | + The CI failed on branch ${{ github.event.workflow_run.head_branch }}. + Analyze the failure and suggest a fix. diff --git a/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable9.yml b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable9.yml new file mode 100644 index 000000000000..2696aba118ca --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/.github/workflows/vulnerable9.yml @@ -0,0 +1,18 @@ +name: Gemini PR Analysis on pull_request +on: + pull_request: + types: [opened] + +jobs: + analyze: + runs-on: ubuntu-latest + permissions: + pull-requests: read + steps: + - name: Gemini analysis + uses: google-github-actions/run-gemini-cli@v1 + with: + prompt: | + Review this PR: + Title: ${{ github.event.pull_request.title }} + Body: ${{ github.event.pull_request.body }} diff --git a/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionCritical.expected b/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionCritical.expected new file mode 100644 index 000000000000..373ab05ec830 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionCritical.expected @@ -0,0 +1,35 @@ +edges +nodes +| .github/workflows/safe2.yml:19:13:19:44 | github.event.comment.body | semmle.label | github.event.comment.body | +| .github/workflows/safe4.yml:16:13:16:43 | github.event.issue.title | semmle.label | github.event.issue.title | +| .github/workflows/vulnerable1.yml:19:20:19:50 | github.event.issue.title | semmle.label | github.event.issue.title | +| .github/workflows/vulnerable1.yml:20:19:20:48 | github.event.issue.body | semmle.label | github.event.issue.body | +| .github/workflows/vulnerable2.yml:15:22:15:63 | github.event.client_payload.command | semmle.label | github.event.client_payload.command | +| .github/workflows/vulnerable2.yml:16:19:16:57 | github.event.client_payload.args | semmle.label | github.event.client_payload.args | +| .github/workflows/vulnerable3.yml:17:13:17:43 | github.event.issue.title | semmle.label | github.event.issue.title | +| .github/workflows/vulnerable3.yml:18:13:18:42 | github.event.issue.body | semmle.label | github.event.issue.body | +| .github/workflows/vulnerable4.yml:15:13:15:44 | github.event.comment.body | semmle.label | github.event.comment.body | +| .github/workflows/vulnerable5.yml:18:13:18:50 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/vulnerable6.yml:17:20:17:50 | github.event.issue.title | semmle.label | github.event.issue.title | +| .github/workflows/vulnerable6.yml:18:19:18:48 | github.event.issue.body | semmle.label | github.event.issue.body | +| .github/workflows/vulnerable7.yml:19:20:19:57 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/vulnerable7.yml:20:19:20:55 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | +| .github/workflows/vulnerable8.yml:23:38:23:81 | github.event.workflow_run.head_branch | semmle.label | github.event.workflow_run.head_branch | +| .github/workflows/vulnerable9.yml:17:20:17:57 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/vulnerable9.yml:18:19:18:55 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | +| .github/workflows/vulnerable10.yml:16:13:16:43 | github.event.issue.title | semmle.label | github.event.issue.title | +subpaths +#select +| .github/workflows/vulnerable1.yml:19:20:19:50 | github.event.issue.title | .github/workflows/vulnerable1.yml:19:20:19:50 | github.event.issue.title | .github/workflows/vulnerable1.yml:19:20:19:50 | github.event.issue.title | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable1.yml:19:20:19:50 | github.event.issue.title | ${{ github.event.issue.title }} | .github/workflows/vulnerable1.yml:3:3:3:8 | issues | issues | +| .github/workflows/vulnerable1.yml:20:19:20:48 | github.event.issue.body | .github/workflows/vulnerable1.yml:20:19:20:48 | github.event.issue.body | .github/workflows/vulnerable1.yml:20:19:20:48 | github.event.issue.body | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable1.yml:20:19:20:48 | github.event.issue.body | ${{ github.event.issue.body }} | .github/workflows/vulnerable1.yml:3:3:3:8 | issues | issues | +| .github/workflows/vulnerable2.yml:15:22:15:63 | github.event.client_payload.command | .github/workflows/vulnerable2.yml:15:22:15:63 | github.event.client_payload.command | .github/workflows/vulnerable2.yml:15:22:15:63 | github.event.client_payload.command | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable2.yml:15:22:15:63 | github.event.client_payload.command | ${{ github.event.client_payload.command }} | .github/workflows/vulnerable2.yml:3:3:3:21 | repository_dispatch | repository_dispatch | +| .github/workflows/vulnerable2.yml:16:19:16:57 | github.event.client_payload.args | .github/workflows/vulnerable2.yml:16:19:16:57 | github.event.client_payload.args | .github/workflows/vulnerable2.yml:16:19:16:57 | github.event.client_payload.args | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable2.yml:16:19:16:57 | github.event.client_payload.args | ${{ github.event.client_payload.args }} | .github/workflows/vulnerable2.yml:3:3:3:21 | repository_dispatch | repository_dispatch | +| .github/workflows/vulnerable3.yml:17:13:17:43 | github.event.issue.title | .github/workflows/vulnerable3.yml:17:13:17:43 | github.event.issue.title | .github/workflows/vulnerable3.yml:17:13:17:43 | github.event.issue.title | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable3.yml:17:13:17:43 | github.event.issue.title | ${{ github.event.issue.title }} | .github/workflows/vulnerable3.yml:3:3:3:8 | issues | issues | +| .github/workflows/vulnerable3.yml:18:13:18:42 | github.event.issue.body | .github/workflows/vulnerable3.yml:18:13:18:42 | github.event.issue.body | .github/workflows/vulnerable3.yml:18:13:18:42 | github.event.issue.body | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable3.yml:18:13:18:42 | github.event.issue.body | ${{ github.event.issue.body }} | .github/workflows/vulnerable3.yml:3:3:3:8 | issues | issues | +| .github/workflows/vulnerable4.yml:15:13:15:44 | github.event.comment.body | .github/workflows/vulnerable4.yml:15:13:15:44 | github.event.comment.body | .github/workflows/vulnerable4.yml:15:13:15:44 | github.event.comment.body | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable4.yml:15:13:15:44 | github.event.comment.body | ${{ github.event.comment.body }} | .github/workflows/vulnerable4.yml:3:3:3:15 | issue_comment | issue_comment | +| .github/workflows/vulnerable6.yml:17:20:17:50 | github.event.issue.title | .github/workflows/vulnerable6.yml:17:20:17:50 | github.event.issue.title | .github/workflows/vulnerable6.yml:17:20:17:50 | github.event.issue.title | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable6.yml:17:20:17:50 | github.event.issue.title | ${{ github.event.issue.title }} | .github/workflows/vulnerable6.yml:3:3:3:8 | issues | issues | +| .github/workflows/vulnerable6.yml:18:19:18:48 | github.event.issue.body | .github/workflows/vulnerable6.yml:18:19:18:48 | github.event.issue.body | .github/workflows/vulnerable6.yml:18:19:18:48 | github.event.issue.body | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable6.yml:18:19:18:48 | github.event.issue.body | ${{ github.event.issue.body }} | .github/workflows/vulnerable6.yml:3:3:3:8 | issues | issues | +| .github/workflows/vulnerable7.yml:19:20:19:57 | github.event.pull_request.title | .github/workflows/vulnerable7.yml:19:20:19:57 | github.event.pull_request.title | .github/workflows/vulnerable7.yml:19:20:19:57 | github.event.pull_request.title | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable7.yml:19:20:19:57 | github.event.pull_request.title | ${{ github.event.pull_request.title }} | .github/workflows/vulnerable7.yml:3:3:3:21 | pull_request_review | pull_request_review | +| .github/workflows/vulnerable7.yml:20:19:20:55 | github.event.pull_request.body | .github/workflows/vulnerable7.yml:20:19:20:55 | github.event.pull_request.body | .github/workflows/vulnerable7.yml:20:19:20:55 | github.event.pull_request.body | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable7.yml:20:19:20:55 | github.event.pull_request.body | ${{ github.event.pull_request.body }} | .github/workflows/vulnerable7.yml:3:3:3:21 | pull_request_review | pull_request_review | +| .github/workflows/vulnerable8.yml:23:38:23:81 | github.event.workflow_run.head_branch | .github/workflows/vulnerable8.yml:23:38:23:81 | github.event.workflow_run.head_branch | .github/workflows/vulnerable8.yml:23:38:23:81 | github.event.workflow_run.head_branch | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable8.yml:23:38:23:81 | github.event.workflow_run.head_branch | ${{ github.event.workflow_run.head_branch }} | .github/workflows/vulnerable8.yml:3:3:3:14 | workflow_run | workflow_run | +| .github/workflows/vulnerable10.yml:16:13:16:43 | github.event.issue.title | .github/workflows/vulnerable10.yml:16:13:16:43 | github.event.issue.title | .github/workflows/vulnerable10.yml:16:13:16:43 | github.event.issue.title | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable10.yml:16:13:16:43 | github.event.issue.title | ${{ github.event.issue.title }} | .github/workflows/vulnerable10.yml:3:3:3:8 | issues | issues | diff --git a/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionCritical.qlref b/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionCritical.qlref new file mode 100644 index 000000000000..cdb67487eefc --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionCritical.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-1427/PromptInjectionCritical.ql diff --git a/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionMedium.expected b/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionMedium.expected new file mode 100644 index 000000000000..bf0fa1e0a6b3 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionMedium.expected @@ -0,0 +1,25 @@ +edges +nodes +| .github/workflows/safe2.yml:19:13:19:44 | github.event.comment.body | semmle.label | github.event.comment.body | +| .github/workflows/safe4.yml:16:13:16:43 | github.event.issue.title | semmle.label | github.event.issue.title | +| .github/workflows/vulnerable1.yml:19:20:19:50 | github.event.issue.title | semmle.label | github.event.issue.title | +| .github/workflows/vulnerable1.yml:20:19:20:48 | github.event.issue.body | semmle.label | github.event.issue.body | +| .github/workflows/vulnerable2.yml:15:22:15:63 | github.event.client_payload.command | semmle.label | github.event.client_payload.command | +| .github/workflows/vulnerable2.yml:16:19:16:57 | github.event.client_payload.args | semmle.label | github.event.client_payload.args | +| .github/workflows/vulnerable3.yml:17:13:17:43 | github.event.issue.title | semmle.label | github.event.issue.title | +| .github/workflows/vulnerable3.yml:18:13:18:42 | github.event.issue.body | semmle.label | github.event.issue.body | +| .github/workflows/vulnerable4.yml:15:13:15:44 | github.event.comment.body | semmle.label | github.event.comment.body | +| .github/workflows/vulnerable5.yml:18:13:18:50 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/vulnerable6.yml:17:20:17:50 | github.event.issue.title | semmle.label | github.event.issue.title | +| .github/workflows/vulnerable6.yml:18:19:18:48 | github.event.issue.body | semmle.label | github.event.issue.body | +| .github/workflows/vulnerable7.yml:19:20:19:57 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/vulnerable7.yml:20:19:20:55 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | +| .github/workflows/vulnerable8.yml:23:38:23:81 | github.event.workflow_run.head_branch | semmle.label | github.event.workflow_run.head_branch | +| .github/workflows/vulnerable9.yml:17:20:17:57 | github.event.pull_request.title | semmle.label | github.event.pull_request.title | +| .github/workflows/vulnerable9.yml:18:19:18:55 | github.event.pull_request.body | semmle.label | github.event.pull_request.body | +| .github/workflows/vulnerable10.yml:16:13:16:43 | github.event.issue.title | semmle.label | github.event.issue.title | +subpaths +#select +| .github/workflows/vulnerable5.yml:18:13:18:50 | github.event.pull_request.title | .github/workflows/vulnerable5.yml:18:13:18:50 | github.event.pull_request.title | .github/workflows/vulnerable5.yml:18:13:18:50 | github.event.pull_request.title | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable5.yml:18:13:18:50 | github.event.pull_request.title | ${{ github.event.pull_request.title }} | .github/workflows/vulnerable5.yml:3:3:3:14 | pull_request | pull_request | +| .github/workflows/vulnerable9.yml:17:20:17:57 | github.event.pull_request.title | .github/workflows/vulnerable9.yml:17:20:17:57 | github.event.pull_request.title | .github/workflows/vulnerable9.yml:17:20:17:57 | github.event.pull_request.title | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable9.yml:17:20:17:57 | github.event.pull_request.title | ${{ github.event.pull_request.title }} | .github/workflows/vulnerable9.yml:3:3:3:14 | pull_request | pull_request | +| .github/workflows/vulnerable9.yml:18:19:18:55 | github.event.pull_request.body | .github/workflows/vulnerable9.yml:18:19:18:55 | github.event.pull_request.body | .github/workflows/vulnerable9.yml:18:19:18:55 | github.event.pull_request.body | Potential prompt injection in $@, which may be controlled by an external user ($@). | .github/workflows/vulnerable9.yml:18:19:18:55 | github.event.pull_request.body | ${{ github.event.pull_request.body }} | .github/workflows/vulnerable9.yml:3:3:3:14 | pull_request | pull_request | diff --git a/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionMedium.qlref b/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionMedium.qlref new file mode 100644 index 000000000000..b0954a1e2022 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-1427/PromptInjectionMedium.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-1427/PromptInjectionMedium.ql