Skip to content

Feat: Add Requirement for Final LLM Output Sanitization / Validation #71

Closed

Description

@rashim27us

Summary

APTS currently includes controls such as inter-model validation (APTS-MR-022), but does not explicitly require validation or sanitization of final LLM outputs before they are consumed by users or downstream components.

This creates a potential gap related to OWASP GenAI LLM02: Insecure Output Handling, where untrusted LLM output can contribute to vulnerabilities such as XSS, SSRF, command injection, or unsafe rendering.

Proposed Requirement

Add a new requirement under Manipulation Resistance, tentatively APTS-MR-024:

Applications MUST validate and appropriately sanitize or encode LLM-generated outputs before they are rendered, executed, or passed to downstream security-sensitive components.

The guidance should emphasize context-specific validation, rather than relying on generic sanitization.

Proposed Changes

  • Add APTS-MR-024 to standard/6_Manipulation_Resistance/README.md
  • Add corresponding Implementation Guide guidance
  • Provide an example of context-aware output validation/sanitization
  • Add malicious output test vectors (e.g., XSS/SSRF payloads) with expected behavior
  • Update relevant generated artifacts/CI checks

Reference

OWASP GenAI Security Project – LLM02: Insecure Output Handling

Activity

  1. jinsonvarghese commented on Aug 22, 2026

    @jinsonvarghese
    Member

    Hi @rashim27us, thanks for this, it's a real gap. Nothing in the standard currently covers sanitizing or encoding the platform's final output before it's consumed. MR-018 guards the input side and MR-022 validates output between models, but neither reaches the last hop where a finding or report lands in front of a user or a downstream system. LLM02 is the right frame.

    One scoping note: since APTS is about the pentest platform itself, I'd anchor the requirement on what the platform emits, its findings and reports, rather than "applications" in general. That's what makes it sharp here. The platform reads untrusted target content by design, so its own report can carry an injected payload into the customer's reporting UI or an integration if the output isn't encoded for where it lands.

    On next steps, since this would be a new normative requirement, here's how it flows:

    1. The current v0.1.0 requirement set is frozen, so new requirements don't merge into main directly. They get batched into the next version. So the first step is a written proposal here in the thread, not a PR yet.
    2. You've already got the requirement sentence. What's mainly missing is the verification part: a couple of concrete test cases (say a stored XSS string and an SSRF link planted on the target) with the expected safe behavior, plus a suggested tier and the LLM02 mapping.
    3. Once we agree on the shape, it goes in when the v0.2.0 cycle opens.

    If you'd like it on record sooner, there's a faster path: we can add it as an advisory practice now, which doesn't change any requirement counts and so can merge to main, then promote it to the full requirement in v0.2.0. A few recent contributors used exactly that route.

    Want to take it up? If so, please drop the fleshed-out version here and we'll shape it together.

  2. rashim27us commented on Aug 23, 2026

    @rashim27us
    ContributorAuthor

    Thanks @jinsonvarghese for the feedback! That scoping refinement makes total sense—anchoring the requirement specifically on the platform's emitted deliverables (findings, PoC scripts, evidence packages, exported telemetry, reporting UI, and integration webhooks) makes it sharp and directly applicable to APTS.

    I'd love to take the faster path and add this as an Advisory Requirement (APTS-MR-A04) for v0.1.0 so it can merge into main immediately, while setting up its promotion to normative APTS-MR-024 (MUST | Tier 2) in v0.2.0.

    Here is the fleshed-out draft:


    APTS-MR-A04: Final LLM Output Sanitization and Downstream Context Isolation (Advisory)

    Applicability: Platforms generating findings, proof-of-concept (PoC) code, evidence summaries, export bundles, web dashboard views, or integration payloads (e.g., Jira, Slack, SIEM webhooks) from LLM outputs.

    Rationale: APTS-MR-002 sanitizes untrusted target data entering the model, and APTS-MR-022 validates intermediate representations across internal model boundaries. However, an LLM subject to indirect prompt injection or jailbreaks may emit malicious payloads (OWASP Top 10 for LLM: LLM02 Insecure Output Handling). If downstream components—such as execution shells, database drivers, web dashboards, or export integrations—consume the LLM's final output without contextual encoding or structural validation, the platform itself becomes an infection vector against customer reporting infrastructure and operator consoles.

    Value: Ensures untrusted target content reflected through model outputs cannot compromise downstream reporting platforms, customer integration pipelines, or operator dashboards.

    Practice Description:

    The platform SHOULD validate, sanitize, and contextually encode all final outputs produced by AI/LLM components before dispatching them to execution environments, storage engines, customer integrations, or presentation layers:

    1. Context-Aware Output Encoding for Reporting & UI: Apply context-specific encoding (HTML entity encoding for web dashboards/reports, JSON escaping for API exports) to all model-generated strings. Prohibit unescaped HTML or raw JavaScript execution from LLM-generated finding narratives.
    2. AST & Parameterized Validation for Synthesized PoC Code: Validate model-generated commands or PoC scripts against strict data schemas or Abstract Syntax Trees (ASTs) before execution. Reject unescaped shell metacharacters (;&|$()`) unless encapsulated in a validated literal payload parameter.
    3. URL & Webhook Egress Sanitization: Validate all URLs and webhooks synthesized by the LLM before invoking external integrations. Block destination URIs targeting internal RFC 1918 addresses, loopback interfaces, or cloud metadata endpoints (169.254.169.254).
    4. Presentation Layer Isolation: Render model-generated findings, PoC descriptions, and evidence summaries within sandboxed presentation frames (e.g., iframe sandbox, strict Content Security Policy).
    5. Sanitization Logging: Log any output validation failures, metacharacter stripping events, or schema rejections with the originating model identifier and prompt hash in the audit trail.

    Recommendation: Combine structured JSON output schemas with deterministic, context-aware post-processing encoders before data reaches presentation or export sinks.

    Related normative requirements: APTS-MR-002, APTS-MR-018, APTS-MR-022, APTS-SC-020, APTS-RP-001.


    Verification / Test Cases

    1. Stored XSS in Reporting UI / Findings Export:

      • Setup: Target returns a malicious script/onload payload in an HTTP header/title.
      • Pass Criteria: When rendered in the operator dashboard or exported to HTML/PDF reports, all HTML tags are contextually escaped (<script>...). Fails if any unescaped script executes in the DOM.
    2. Command / Code Injection in Synthesized PoC Scripts:

      • Setup: Target returns a banner with shell metacharacters (e.g., Server: Apache; $(whoami)).
      • Pass Criteria: Output dispatcher parses commands into array-based argument vectors (e.g., ["curl", "-H", "Server: Apache; $(whoami)", "http://target"]) or rejects unescaped metacharacters. Fails if raw string concatenation is passed to sh -c or system().
    3. SSRF / Webhook Callback URL Poisoning in Integrations:

      • Setup: Target content contains prompt injection attempting to rewrite finding callback URLs to cloud metadata endpoints (http://169.254.169.254/latest/meta-data/).
      • Pass Criteria: Integration dispatcher validates destination URIs against egress policy, blocks the metadata endpoint request, and logs the rejection in the audit trail.

    Implementation Guide Excerpt (standard/6_Manipulation_Resistance/Implementation_Guide.md)

    import html
    from urllib.parse import urlparse
    from pydantic import BaseModel, field_validator
    
    class FindingExportPayload(BaseModel):
        title: str
        description: str
        target_url: str
    
        @field_validator("title", "description")
        @classmethod
        def escape_html_entities(cls, v: str) -> str:
            """Prevent Stored XSS in customer reporting dashboards."""
            return html.escape(v, quote=True)
    
        @field_validator("target_url")
        @classmethod
        def validate_egress_url(cls, v: str) -> str:
            """Prevent SSRF / Cloud Metadata endpoint poisoning in exports."""
            parsed = urlparse(v)
            if parsed.hostname in ["169.254.169.254", "localhost", "127.0.0.1"]:
                raise ValueError("Forbidden egress destination")
            return v
  3. ossumpossum commented on Aug 24, 2026

    @ossumpossum
    Contributor

    Strong addition, and the advisory-first path (MR-A04 now, promote to MR-024 in v0.2.0) is the right vehicle. Scoping it to what the platform emits keeps it tight.

    One thing from building a harness that executes LLM-written PoCs against the target: point 2 (synthesized PoC code) deserves the most weight, because that output is executed, not just rendered. A rendered narrative fails safe under context-aware encoding; a PoC script fails open the moment it's concatenated into a shell. The array-argv / AST framing is right; the part worth making explicit is that the check has to sit at the dispatch boundary regardless of how clean the string looked upstream, because a model asked for "runnable" output is being asked to defeat exactly the escaping you'd rely on. In practice, parameterized execution (argv vectors, never sh -c on a composed string) plus a schema on the payload is what holds; metacharacter stripping alone doesn't.

    One scope note: the emitting agent can also poison an internal sink (the scoring, evidence, or audit channel), not just the external ones. If MR-A04 names internal sinks alongside the reporting UI and integrations, it closes that path too.

    Disclosure: I contributed the evidence-replay reframe and the forged-submission-resistance check to the existing advisory RP-A01 (finding authenticity), so weight this accordingly. There's an adjacent failure it covers, where an emitted finding is authentic-looking but fabricated rather than injection-carrying; a "see also" pointer from MR-A04 might be worth it, but that's your issue and your call.

  4. rashim27us commented on Aug 25, 2026

    @rashim27us
    ContributorAuthor

    Hi @ossumpossum , thanks for the excellent feedback! These points make the advisory more robust. I’ll incorporate them into the draft:

    1. PoC execution boundary: Require validation at the final dispatch/execution boundary, with structured/parameterized execution (array-argv; never composed strings passed to sh -c/system()). Metacharacter stripping alone is not sufficient.

    2. Internal sinks: Expand the scope to include internal sinks such as scoring, evidence, and audit channels, alongside reporting UIs and external integrations.

    3. RP-A01 cross-reference: Add a “See also” reference to APTS-RP-A01, since it addresses finding/evidence authenticity while MR-A04 addresses malicious payload injection through LLM-generated output.

    I’ll also update the verification cases to reflect these additions.

  5. jinsonvarghese commented on Aug 25, 2026

    @jinsonvarghese
    Member

    @rashim27us Sounds good, advisory-first is the right call. MR-A04 as an advisory now so it can land on main, and we line up the promotion to MR-024 for v0.2.0. Looking forward to the revised draft, and the three refinements you're folding in are the right ones, especially validating at the execution boundary for the PoC path.

    Two things to fold in at the same time so the PR lands clean. First, the framework mapping needs a version bump: "LLM02: Insecure Output Handling" is the 2023 numbering. In the current OWASP LLM Top 10 (2025) it's LLM05:2025 Improper Output Handling, and LLM02 is now Sensitive Information Disclosure. Same concept, just need to fix the label and number in the rationale and reference, and the issue title.

    Second, a heads-up on the mechanics: adding an advisory moves the count from 19 to 20, which shows up in a handful of prose spots (root README and index, Introduction, Frontispiece, Getting Started, Glossary, Vendor Evaluation Guide, standard/README), plus the "Three advisory practices relevant to this domain" line in the Manipulation Resistance README becomes "Four" with MR-A04 listed. PR #65 (SC-A04) touched exactly that set if you want a diff to copy from.

    Once the revised draft is up, please open the PR against main and I'll review.

  6. rashim27us commented on Aug 29, 2026

    @rashim27us
    ContributorAuthor

    Thanks @jinsonvarghese. Agreed on both points.

    I’ll update the draft to use LLM05:2025 – Improper Output Handling and revise the issue title/rationale/reference accordingly. I’ll also update the advisory count and all affected documentation/index references, following the changes from PR #65.

    I’ll incorporate the execution-boundary, internal-sink, and RP-A01 refinements discussed above, then open the PR against main for review.

  7. ossumpossum commented on Aug 29, 2026

    @ossumpossum
    Contributor

    Thanks @rashim27us, glad the execution-boundary, internal-sink, and RP-A01 points are useful, and the LLM05:2025 remap reads right to me. When the updated draft is up I'm happy to review it, especially the dispatch-boundary wording (argv-vector execution + payload schemas at the emit point) and the internal-sink scope. Tag me and I'll turn it around quickly.

  8. jinsonvarghese commented on Aug 31, 2026

    @jinsonvarghese
    Member

    Thanks for closing the loop here; the thread came together well, and the PR looks close. Following up there now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions