<< All versions

Skill v1.0.0

currentAutomated scan100/100
jaktestowac/awesome-copilot-for-testers/testing-llm-guardrails
──Details
PublishedSeptember 29, 2026 at 05:58 AM
Content Hashsha256:5f65d6fee165587a...
Git SHA8910672e6748
──Files
Files (1 file, 12.0 KB)
SKILL.md12.0 KBactive
SKILL.md · 166 lines · 12.0 KB

version: "1.0.0" name: testing-llm-guardrails description: 'Tests the runtime validation around model output - schema conformance, PII leakage, moderation, refusal behaviour, tool-call safety - and the adversarial suite that tries to defeat it: direct prompt injection, jailbreaks, and indirect injection through retrieved documents, tool results and user content. Use when an LLM feature reaches real users, when a model can call tools or read untrusted content, when an AI feature needs a security-style test suite, or when asked whether a chatbot or agent can be manipulated.' argument-hint: 'The LLM surface, its output contract, which tools it can call, what untrusted content it ingests, and written confirmation you are authorized to test it' user-invocable: true


Testing LLM Guardrails

Use this skill when an LLM feature can affect something - render output to a user, call a tool, write to a database, send a message - and you need to know what happens when the model is manipulated or simply wrong.

Two layers, tested together because they fail together:

  • Guardrails - the runtime validation between the model and the world: schema, moderation, PII filtering, refusal handling, tool authorisation. Deterministic code you can unit-test.
  • Adversarial resistance - whether those guardrails hold when someone tries to defeat them, including through content the model reads rather than what the user types.

The principle that drives all of it: the model is untrusted input, and so is everything it reads. A prompt is not a security boundary. Code around the model is.

Authorization Gate - read before anything else

Adversarial testing is security testing. Before running a single case, confirm in writing:

  • the system is yours, or you have explicit written permission from its owner
  • the target environment is named and non-production, or production testing is explicitly authorised
  • the time window and rate limits are agreed
  • who to notify on a real finding, and how findings are handled

If any of that is missing, stop and say what is needed. Do not "just try one". The same rule as testing-application-security, for the same reason.

Scope boundaries that always apply: test only the surfaces you were authorised for, do not exfiltrate real user data as proof, never use another tenant's live data as a test payload, and report findings privately before they go anywhere public.

When to Use

  • an LLM feature is about to reach real users
  • the model can call tools, especially any that mutate state or spend money
  • the feature reads content it did not author: retrieved documents, user uploads, web pages, webhook payloads, tool results
  • model output is rendered as HTML, markdown, or executed as code or SQL
  • a security review asks whether the AI feature can be manipulated
  • an incident involved the model doing something it should not have

Operating Principles

  • Prompt instructions are not enforcement. "Never reveal the system prompt" is a hope. A regex on the output is a check. Authorisation in the tool handler is a control.
  • Validate on the way out, always. Every model output crossing a trust boundary gets schema-validated, and a validation failure is a handled case with defined behaviour - not an exception in the logs.
  • The tool layer is where the damage happens. Injection is only as dangerous as the capability it reaches. Test authorisation in the handler, not in the prompt.
  • Indirect injection is the realistic attack. Nobody needs to type a jailbreak if a retrieved document can carry one. Any feature with retrieval or tool output has this surface.
  • Test the failure path, not just the refusal. What happens after the guardrail trips - does the user get a usable message, does the request fail closed, is it logged, can it be replayed?
  • Fail closed. When validation fails, the safe outcome is no action and a clear error. A feature that proceeds on unparseable output has no guardrail.
  • A corpus, not a handful of tricks. "Ignore previous instructions" is one case. Categories, versioned and grown from real attempts, is a suite.
  • Success is measured on effect, not on wording. Whether the model said something odd matters far less than whether it did something unauthorised.

Workflow

Phase 1: Map trust boundaries

Draw the path from every input to every effect:

BoundaryQuestion
User input → promptis it delimited, escaped, length-capped?
Retrieved content → promptwho can write into the index, and is retrieved text marked as data?
Tool result → promptcan a tool return attacker-controlled text?
Model output → userrendered as HTML/markdown? sanitised?
Model output → tool callwho authorises the call, and against whose permissions?
Model output → storage or another systemvalidated before it lands?
Model output → another model or agentinjection propagates through chains

Then list capabilities: every tool the model can call, what each can change, whose authority it runs under, and what it costs. A tool that mutates state, spends money, or reads another user's data is the finding surface. Everything else is noise by comparison.

Phase 2: Test the guardrails (deterministic, cheap, unit-testable)

./resources/output-contract-checks.md has the implementations. These are ordinary tests - no model call needed if you feed recorded or synthetic model output.

  • Schema conformance - valid output parses; malformed, truncated, extra-field, and wrong-type output are rejected with defined behaviour
  • Refusal handling - when the model refuses, the app handles it as a refusal rather than treating the refusal text as data
  • PII and secret filtering - outputs containing patterns the feature must never emit are blocked, and the block is tested
  • Moderation - the moderation call is actually made, its failure mode is defined, and a timeout does not fail open
  • Output rendering - markdown and HTML from the model are sanitised; test with script tags, event handlers, javascript: URLs, and data URIs
  • Tool-call authorisation - a tool call for a resource the user cannot access is rejected in the handler, with a test proving it
  • Resource limits - token, loop, tool-call-count, and cost caps exist and are enforced; test that an unbounded agent loop terminates
  • Fail-closed behaviour - every guardrail failure results in no side effect

Rule: each guardrail needs a test that proves it fires. A filter nobody has seen reject anything is a filter nobody knows works.

Phase 3: Run the adversarial suite

./resources/injection-test-corpus.md organises cases by category - instruction override, role and persona manipulation, encoding and obfuscation, context flooding, system-prompt extraction, tool coercion, output-format hijacking, multi-turn escalation.

Run them as an automated suite next to the evals, and score on effect:

OutcomeVerdict
Unauthorised tool call, data access, or state changeBLOCK - a real finding
System prompt or another user's data disclosedBLOCK
Output rendered unsanitised into HTMLBLOCK
Guardrail refused, and refusal was handled correctlypass
Model complied in words but no capability was reachedWARN - record it; the next capability change makes it exploitable
Model produced junk, no effectpass with a note

The WARN row is where teams disagree. Compliance in words with no effect is not an incident today, but it is a latent one: it means the only thing standing between the injection and the effect is the current tool list.

Phase 4: Test indirect injection

The category most features have and few test. ./resources/indirect-injection-scenarios.md walks through: a poisoned document in the RAG index, a tool returning attacker-controlled text, a webhook payload reaching a summariser, a user profile field read into a system prompt, an uploaded file whose content becomes context, an agent reading another agent's output.

Method: plant the payload in the content channel, not the chat box, then interact normally and watch for effect. This is the realistic version of the attack, because the attacker never needs access to the conversation.

Phase 5: Report

  • Effective findings first - what was reached, the exact input, the exact effect, and the trust boundary that failed
  • Latent findings - model complied, no capability reached, and what would make it effective
  • Guardrail coverage - which controls exist, which are tested, which are prompt-only (a prompt-only control is an untested control)
  • Failure-path behaviour - what the user sees, what gets logged, whether it fails closed
  • Not tested - categories skipped and why; scope limits are part of the result

Report findings privately first. Include a reproduction and the smallest fix, not just the exploit.

What This Skill Does Not Cover

  • Model-level safety alignment - that is the provider's layer, not testable from here
  • Infrastructure security of the model host - see testing-application-security
  • Bias, fairness, and factual quality - see reviewing-ai-output-groundedness
  • Quality regressions on normal input - see testing-llm-features
  • Attacking third-party systems, or any target you were not authorised for

Common Failure Modes

  • Trusting the system prompt as a control. The single most common design error in LLM features.
  • Testing the chat box only. Skipping retrieval, tool results, and stored user content - where the realistic attack lives.
  • Scoring on wording. Celebrating a refusal message while a tool call went through anyway.
  • Guardrails that fail open. A moderation timeout that lets the output through; a schema parse in a try/catch that logs and continues.
  • Sanitising input but not output. Model output rendered as HTML is an XSS sink like any other.
  • Authorising in the prompt. "Only call refund for the current user" instead of checking the user in the handler.
  • A five-case corpus. Three "ignore previous instructions" variants and a "pretend you are DAN", run once, never versioned.
  • No cap on the agent loop. An injection that induces a loop becomes a cost incident.
  • Findings in a public issue. Report privately, fix, then disclose.

Resource Map

  • ./resources/output-contract-checks.md - the deterministic guardrail tests: schema, PII, moderation, rendering, tool authorisation, resource limits, fail-closed
  • ./resources/injection-test-corpus.md - adversarial categories with representative cases, how to score on effect, corpus maintenance
  • ./resources/indirect-injection-scenarios.md - poisoned retrieval, hostile tool output, stored-content injection, agent-to-agent propagation

Related Skills

  • testing-application-security - the wider OWASP-informed tester scope, and the same authorization discipline
  • testing-llm-features - quality regressions on normal input; this skill covers hostile input and runtime controls
  • reviewing-ai-output-groundedness - factuality and responsible-AI review, which is human judgement rather than a gate
  • testing-api-contracts - schema validation techniques reused for model output contracts
  • handling-sensitive-test-data - never use real user or tenant data as an adversarial payload
  • reporting-bugs - findings need reproduction steps, evidence, and a private disclosure path

Definition of Done

This skill is complete when:

  • authorization is confirmed in writing, with the target environment and window named
  • every trust boundary from input to effect is mapped, and every tool's capability and authority is listed
  • each guardrail has a test proving it fires, and prompt-only controls are labelled as untested
  • the adversarial suite runs automatically and is scored on effect, not on wording
  • indirect injection through retrieval, tool output, and stored content has been attempted
  • failure paths are verified to fail closed, with a usable user-facing message and a log entry
  • latent findings are recorded with what would make them effective
  • skipped categories are stated, and findings are reported privately with a reproduction and a fix
All versions