AID-H-006.004: Generated Media & Binary Artifact Output Safety Release Gate

Version 1.20261015 · Actionable sub-technique

Treat every model-generated image, audio, video, PDF, office document, archive, or other binary artifact as untrusted bytes. Strictly decode the representation, enforce byte and decompression bounds, identify the actual type from content, reject executable or polyglot content, and either canonicalize through an approved decoder and encoder or quarantine for malware scanning and content disarm. Release only transformed bytes after checking their type, digest, policy binding, and required security results against the exact output bytes.

Pillars: app

Lifecycle: building, operation

Scope boundary

Owns preventive decode, type validation, bounds, canonical re-encoding, malware or content-disarm evidence, quarantine, and final release for generated media and binary artifacts.

AID-H-006.002: AID-H-006.004 owns non-text media and binary artifact release; AID-H-006.002 owns text, markup, URL, structured-payload, and token-stream release. A document may require bounded text extraction, but its container and embedded objects still belong to the binary gate.

AID-D-003.008: AID-H-006.004 transforms, quarantines, blocks, or releases the generated artifact; AID-D-003.008 emits policy, malware, type, and coverage findings over it. The detector supplies evidence, while this control owns the release decision.

AID-H-025: AID-H-006.004 rejects executable or unsafe binary output from release; AID-H-025 governs whether code or commands may be executed in a controlled runtime. Artifact release and execution admission remain separate even when the same bytes are involved.

Implementation guidance

AID-H-006.004-G001

Decode generated media under policy-defined byte, dimension, frame, duration, and decompression limits; verify content-derived type and re-encode from decoded pixels or samples.
Load and verify the versioned artifact runtime profile

Limits and allowed formats are deployment inputs, not AIDEFEND defaults. Mount the verifier key from an approved, read-only trust source that the codec worker cannot modify. The profile may choose a file path, but it may not supply or replace its own verifier key. Missing files, a bad signature, duplicate JSON keys, an unexpected schema, or an unversioned profile stops worker startup.

# File: output_guards/verified_artifact_profile.py
from __future__ import annotations

import base64
import hashlib
import hmac
import json
import os
import re
from dataclasses import dataclass
from pathlib import Path
from typing import Any

from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey


TRUSTED_VERIFIER_KEY = Path(
    "/etc/aidefend/trust/generated-artifact-policy-ed25519.pem"
)
PROFILE_PATH_ENV = "AIDEFEND_GENERATED_ARTIFACT_PROFILE"
SIGNATURE_PATH_ENV = "AIDEFEND_GENERATED_ARTIFACT_PROFILE_SIGNATURE"
PROFILE_DIGEST_ENV = "AIDEFEND_GENERATED_ARTIFACT_PROFILE_SHA256"
PROFILE_SCHEMA = "aidefend.generated-artifact-runtime-profile.v1"


class ProfileVerificationError(RuntimeError):
    pass


@dataclass(frozen=True)
class VerifiedArtifactProfile:
    version: str
    sha256: str
    image: dict[str, Any]
    document: dict[str, Any]


def _unique_object(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
    result: dict[str, Any] = {}
    for key, value in pairs:
        if key in result:
            raise ProfileVerificationError(f"duplicate_profile_key:{key}")
        result[key] = value
    return result


def _reject_non_finite(value: str) -> None:
    raise ProfileVerificationError(f"non_finite_profile_number:{value}")


def load_verified_artifact_profile() -> VerifiedArtifactProfile:
    try:
        profile_path = Path(os.environ[PROFILE_PATH_ENV]).resolve(strict=True)
        signature_path = Path(os.environ[SIGNATURE_PATH_ENV]).resolve(strict=True)
        raw_profile = profile_path.read_bytes()
        expected_digest = os.environ[PROFILE_DIGEST_ENV].lower()
        actual_digest = hashlib.sha256(raw_profile).hexdigest()
        if (
            re.fullmatch(r"[0-9a-f]{64}", expected_digest) is None
            or not hmac.compare_digest(actual_digest, expected_digest)
        ):
            raise ProfileVerificationError("artifact_profile_digest_mismatch")
        signature = base64.b64decode(
            signature_path.read_text(encoding="ascii").strip(), validate=True
        )
        public_key = serialization.load_pem_public_key(
            TRUSTED_VERIFIER_KEY.read_bytes()
        )
        if not isinstance(public_key, Ed25519PublicKey):
            raise ProfileVerificationError("unexpected_profile_verifier_key_type")
        public_key.verify(signature, raw_profile)
        document = json.loads(
            raw_profile.decode("utf-8"),
            object_pairs_hook=_unique_object,
            parse_constant=_reject_non_finite,
        )
    except (
        KeyError,
        OSError,
        UnicodeError,
        ValueError,
        InvalidSignature,
    ) as exc:
        raise ProfileVerificationError("artifact_profile_verification_failed") from exc

    required = {"schema_version", "profile_version", "image", "document"}
    if not isinstance(document, dict) or set(document) != required:
        raise ProfileVerificationError("artifact_profile_schema_mismatch")
    if document["schema_version"] != PROFILE_SCHEMA:
        raise ProfileVerificationError("unsupported_artifact_profile_schema")
    version = document["profile_version"]
    if not isinstance(version, str) or not version.strip():
        raise ProfileVerificationError("artifact_profile_version_missing")
    if not isinstance(document["image"], dict) or not isinstance(document["document"], dict):
        raise ProfileVerificationError("artifact_profile_sections_invalid")
    return VerifiedArtifactProfile(
        version=version,
        sha256=actual_digest,
        image=dict(document["image"]),
        document=dict(document["document"]),
    )


PROFILE = load_verified_artifact_profile()
Canonical image adapter

The image section of that verified profile supplies the input allowlist and every byte, pixel, width, and height bound. The adapter supports a single canonical PNG output contract; any other requested adapter contract fails startup. Do not trust a filename, declared MIME type, or Base64 wrapper.

# File: output_guards/canonical_image.py
from __future__ import annotations

import hashlib
import io
import warnings
from dataclasses import dataclass

from PIL import Image, UnidentifiedImageError

from output_guards.verified_artifact_profile import PROFILE, ProfileVerificationError


@dataclass(frozen=True)
class ImagePolicy:
    allowed_input_formats: frozenset[str]
    max_input_bytes: int
    max_output_bytes: int
    max_pixels: int
    max_width: int
    max_height: int
    output_format: str
    output_media_type: str


def _positive_int(value: object, field: str) -> int:
    if isinstance(value, bool) or not isinstance(value, int) or value <= 0:
        raise ProfileVerificationError(f"invalid_image_policy_field:{field}")
    return value


def _load_image_policy() -> ImagePolicy:
    raw = PROFILE.image
    required = {
        "allowed_input_formats", "max_input_bytes", "max_output_bytes",
        "max_pixels", "max_width", "max_height", "output_format",
        "output_media_type",
    }
    if set(raw) != required:
        raise ProfileVerificationError("image_policy_schema_mismatch")
    formats = raw["allowed_input_formats"]
    if (
        not isinstance(formats, list)
        or not formats
        or any(not isinstance(item, str) or not item for item in formats)
    ):
        raise ProfileVerificationError("image_input_format_allowlist_invalid")
    if raw["output_format"] != "PNG" or raw["output_media_type"] != "image/png":
        raise ProfileVerificationError("unsupported_canonical_image_contract")
    return ImagePolicy(
        allowed_input_formats=frozenset(formats),
        max_input_bytes=_positive_int(raw["max_input_bytes"], "max_input_bytes"),
        max_output_bytes=_positive_int(raw["max_output_bytes"], "max_output_bytes"),
        max_pixels=_positive_int(raw["max_pixels"], "max_pixels"),
        max_width=_positive_int(raw["max_width"], "max_width"),
        max_height=_positive_int(raw["max_height"], "max_height"),
        output_format=raw["output_format"],
        output_media_type=raw["output_media_type"],
    )


POLICY = _load_image_policy()
Image.MAX_IMAGE_PIXELS = POLICY.max_pixels
warnings.simplefilter("error", Image.DecompressionBombWarning)


def _check_dimensions(width: int, height: int) -> None:
    if (
        width <= 0
        or height <= 0
        or width > POLICY.max_width
        or height > POLICY.max_height
        or width * height > POLICY.max_pixels
    ):
        raise PermissionError("image_dimensions_denied")


def canonicalize_image(raw: bytes) -> tuple[bytes, dict]:
    if not isinstance(raw, bytes) or not raw or len(raw) > POLICY.max_input_bytes:
        raise PermissionError("image_input_budget_denied")
    try:
        with Image.open(io.BytesIO(raw)) as probe:
            if probe.format not in POLICY.allowed_input_formats:
                raise PermissionError("image_format_denied")
            _check_dimensions(probe.width, probe.height)
            probe.verify()
        with Image.open(io.BytesIO(raw)) as decoded:
            decoded.load()
            if decoded.format not in POLICY.allowed_input_formats:
                raise PermissionError("decoded_image_format_denied")
            if getattr(decoded, "n_frames", 1) != 1:
                raise PermissionError("animated_image_denied")
            _check_dimensions(decoded.width, decoded.height)
            with decoded.convert("RGB") as pixels:
                with Image.frombytes("RGB", pixels.size, pixels.tobytes()) as clean:
                    target = io.BytesIO()
                    clean.save(target, format=POLICY.output_format, optimize=True)
    except (
        UnidentifiedImageError,
        OSError,
        Image.DecompressionBombError,
        Image.DecompressionBombWarning,
    ) as exc:
        raise PermissionError("image_decode_denied") from exc

    body = target.getvalue()
    if not body or len(body) > POLICY.max_output_bytes:
        raise PermissionError("canonical_image_output_budget_denied")
    with Image.open(io.BytesIO(body)) as readback:
        readback.load()
        if readback.format != POLICY.output_format:
            raise PermissionError("canonical_image_readback_type_mismatch")
        _check_dimensions(readback.width, readback.height)
        width, height = readback.size
    return body, {
        "status": "PASS",
        "policy_version": PROFILE.version,
        "policy_sha256": PROFILE.sha256,
        "input_sha256": hashlib.sha256(raw).hexdigest(),
        "output_sha256": hashlib.sha256(body).hexdigest(),
        "output_media_type": POLICY.output_media_type,
        "width": width,
        "height": height,
    }

Run codec workers without network, credentials, host mounts, or shared writable paths, and enforce CPU, memory, wall-clock, frame, and duration budgets from the same verified runtime profile outside the codec. Independently reopen the emitted artifact and reconcile its digest, content-derived type, dimensions, profile version, and profile digest with the stored release receipt. Signature verification, profile loading, codec failure, or receipt readback failure releases nothing.

AID-H-006.004-G002

Identify binary type from bytes, deny executables and archives by default, and require malware plus content-disarm receipts for approved document types.
Quarantine and document transformation

Use the signature-verified profile loaded by the preceding guidance. Its document section supplies the allowed content-derived media types, encoded/decoded/output byte bounds, scanner timeout, CDR timeout, and approved scanner and adapter identities plus executable digests. The caller supplies only the encoded artifact and expected release type; it cannot choose the policy version, limits, timeouts, scanner, or CDR adapter.

# File: output_guards/document_gate.py
from __future__ import annotations

import base64
import hashlib
import hmac
import io
import json
import subprocess
import tempfile
from dataclasses import dataclass
from pathlib import Path
from typing import Any

import magic

from output_guards.verified_artifact_profile import PROFILE, ProfileVerificationError


@dataclass(frozen=True)
class ExecutableBinding:
    identity: str
    path: Path
    sha256: str


@dataclass(frozen=True)
class DocumentPolicy:
    allowed_media_types: frozenset[str]
    max_base64_encoded_bytes: int
    max_decoded_bytes: int
    max_output_bytes: int
    scanner_timeout_seconds: float
    cdr_timeout_seconds: float
    scanner: ExecutableBinding
    cdr_adapter: ExecutableBinding


def _positive_number(value: object, field: str) -> float:
    if isinstance(value, bool) or not isinstance(value, (int, float)) or value <= 0:
        raise ProfileVerificationError(f"invalid_document_policy_field:{field}")
    return float(value)


def _positive_int(value: object, field: str) -> int:
    if isinstance(value, bool) or not isinstance(value, int) or value <= 0:
        raise ProfileVerificationError(f"invalid_document_policy_field:{field}")
    return value


def _sha256_file(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as stream:
        for chunk in iter(lambda: stream.read(io.DEFAULT_BUFFER_SIZE), b""):
            digest.update(chunk)
    return digest.hexdigest()


def _load_executable(raw: object, field: str) -> ExecutableBinding:
    if not isinstance(raw, dict) or set(raw) != {"identity", "path", "sha256"}:
        raise ProfileVerificationError(f"invalid_executable_binding:{field}")
    identity, path_text, expected_digest = raw["identity"], raw["path"], raw["sha256"]
    if not all(isinstance(value, str) and value for value in (identity, path_text, expected_digest)):
        raise ProfileVerificationError(f"incomplete_executable_binding:{field}")
    path = Path(path_text).resolve(strict=True)
    actual_digest = _sha256_file(path)
    if not hmac.compare_digest(actual_digest, expected_digest.lower()):
        raise ProfileVerificationError(f"executable_digest_mismatch:{field}")
    return ExecutableBinding(identity=identity, path=path, sha256=actual_digest)


def _load_document_policy() -> DocumentPolicy:
    raw = PROFILE.document
    required = {
        "allowed_media_types", "max_base64_encoded_bytes", "max_decoded_bytes",
        "max_output_bytes", "scanner_timeout_seconds", "cdr_timeout_seconds",
        "scanner", "cdr_adapter",
    }
    if set(raw) != required:
        raise ProfileVerificationError("document_policy_schema_mismatch")
    allowed = raw["allowed_media_types"]
    if (
        not isinstance(allowed, list)
        or not allowed
        or any(not isinstance(item, str) or not item for item in allowed)
    ):
        raise ProfileVerificationError("document_media_type_allowlist_invalid")
    return DocumentPolicy(
        allowed_media_types=frozenset(allowed),
        max_base64_encoded_bytes=_positive_int(
            raw["max_base64_encoded_bytes"], "max_base64_encoded_bytes"
        ),
        max_decoded_bytes=_positive_int(raw["max_decoded_bytes"], "max_decoded_bytes"),
        max_output_bytes=_positive_int(raw["max_output_bytes"], "max_output_bytes"),
        scanner_timeout_seconds=_positive_number(
            raw["scanner_timeout_seconds"], "scanner_timeout_seconds"
        ),
        cdr_timeout_seconds=_positive_number(raw["cdr_timeout_seconds"], "cdr_timeout_seconds"),
        scanner=_load_executable(raw["scanner"], "scanner"),
        cdr_adapter=_load_executable(raw["cdr_adapter"], "cdr_adapter"),
    )


POLICY = _load_document_policy()


def _unique_receipt_object(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
    result: dict[str, Any] = {}
    for key, value in pairs:
        if key in result:
            raise PermissionError("cdr_receipt_duplicate_key")
        result[key] = value
    return result


def _reject_receipt_constant(value: str) -> None:
    raise PermissionError(f"cdr_receipt_non_finite_number:{value}")


def _load_json(path: Path) -> dict[str, Any]:
    value = json.loads(
        path.read_text(encoding="utf-8"),
        object_pairs_hook=_unique_receipt_object,
        parse_constant=_reject_receipt_constant,
    )
    if not isinstance(value, dict):
        raise PermissionError("cdr_receipt_not_an_object")
    return value


def _assert_executable_unchanged(binding: ExecutableBinding) -> None:
    try:
        current_digest = _sha256_file(binding.path)
    except OSError as exc:
        raise PermissionError("bound_executable_unavailable") from exc
    if not hmac.compare_digest(current_digest, binding.sha256):
        raise PermissionError("bound_executable_changed_after_profile_load")


def admit_document(encoded: str, expected_type: str) -> tuple[bytes, dict]:
    if not isinstance(encoded, str) or expected_type not in POLICY.allowed_media_types:
        raise PermissionError("document_policy_denied")
    try:
        encoded_bytes = encoded.encode("ascii", errors="strict")
    except UnicodeEncodeError as exc:
        raise PermissionError("document_base64_not_ascii") from exc
    if not encoded_bytes or len(encoded_bytes) > POLICY.max_base64_encoded_bytes:
        raise PermissionError("encoded_document_budget_denied")
    try:
        raw = base64.b64decode(encoded_bytes, validate=True)
    except (ValueError, base64.binascii.Error) as exc:
        raise PermissionError("invalid_document_base64") from exc
    if not raw or len(raw) > POLICY.max_decoded_bytes:
        raise PermissionError("decoded_document_budget_denied")
    detected_type = magic.from_buffer(raw, mime=True)
    if detected_type != expected_type:
        raise PermissionError("document_type_mismatch")

    input_digest = hashlib.sha256(raw).hexdigest()
    with tempfile.TemporaryDirectory(prefix="artifact-quarantine-") as directory:
        quarantine = Path(directory)
        candidate = quarantine / "candidate.bin"
        transformed_path = quarantine / "transformed.bin"
        cdr_receipt_path = quarantine / "cdr-receipt.json"
        candidate.write_bytes(raw)
        _assert_executable_unchanged(POLICY.scanner)
        try:
            scanner_version = subprocess.run(
                [str(POLICY.scanner.path), "--version"],
                capture_output=True,
                text=True,
                timeout=POLICY.scanner_timeout_seconds,
                check=True,
                close_fds=True,
            ).stdout.strip()
            scan = subprocess.run(
                [
                    str(POLICY.scanner.path), "--no-summary", "--infected",
                    "--stdout", str(candidate),
                ],
                capture_output=True,
                text=True,
                timeout=POLICY.scanner_timeout_seconds,
                check=False,
                close_fds=True,
            )
        except (OSError, subprocess.SubprocessError) as exc:
            raise PermissionError("malware_scanner_unavailable_or_timed_out") from exc
        if not scanner_version or scan.returncode != 0:
            raise PermissionError("malware_scan_not_clean_or_signatures_unavailable")

        _assert_executable_unchanged(POLICY.cdr_adapter)
        try:
            subprocess.run(
                [
                    str(POLICY.cdr_adapter.path),
                    "--input", str(candidate),
                    "--output", str(transformed_path),
                    "--receipt", str(cdr_receipt_path),
                    "--expected-media-type", expected_type,
                    "--policy-version", PROFILE.version,
                    "--policy-sha256", PROFILE.sha256,
                ],
                capture_output=True,
                text=True,
                timeout=POLICY.cdr_timeout_seconds,
                check=True,
                close_fds=True,
            )
            output_size = transformed_path.stat().st_size
            if output_size <= 0 or output_size > POLICY.max_output_bytes:
                raise PermissionError("transformed_document_budget_denied")
            transformed = transformed_path.read_bytes()
            cdr_receipt = _load_json(cdr_receipt_path)
        except (OSError, subprocess.SubprocessError, UnicodeError, ValueError) as exc:
            raise PermissionError("cdr_adapter_unavailable_or_failed") from exc

    if not transformed or len(transformed) > POLICY.max_output_bytes:
        raise PermissionError("transformed_document_budget_denied")
    output_type = magic.from_buffer(transformed, mime=True)
    output_digest = hashlib.sha256(transformed).hexdigest()
    required_receipt = {
        "status": "PASS",
        "policy_version": PROFILE.version,
        "policy_sha256": PROFILE.sha256,
        "adapter_identity": POLICY.cdr_adapter.identity,
        "adapter_sha256": POLICY.cdr_adapter.sha256,
        "input_sha256": input_digest,
        "input_media_type": detected_type,
        "output_sha256": output_digest,
        "output_media_type": output_type,
    }
    if output_type != expected_type or any(
        cdr_receipt.get(key) != value for key, value in required_receipt.items()
    ):
        raise PermissionError("cdr_receipt_binding_failed")
    return transformed, {
        **required_receipt,
        "scanner_identity": POLICY.scanner.identity,
        "scanner_sha256": POLICY.scanner.sha256,
        "scanner_version": scanner_version,
    }

Executables, scripts, disk images, archives, encrypted containers, unknown types, and polyglots remain denied unless a separately owned executable-artifact control admits them. Scanner timeout, missing signatures, non-clean or indeterminate return codes, CDR timeout, adapter digest drift, type or digest mismatch, malformed receipt, and evidence-write or readback failure release nothing. Test oversized encoded input before decode, oversized decoded input, malformed Base64, decompression bombs, appended executables, nested archives, encrypted documents, scanner outages, changed executables, and altered CDR receipts.

Open-source tools

Commercial / hosted tools

Threat mappings

MITRE ATLAS

MAESTRO

OWASP LLM Top 10 2026

OWASP ML Top 10 2023

OWASP Top 10 for Agentic Applications 2026

NIST Adversarial Machine Learning 2025

Cisco Integrated AI Security and Safety Framework

Google Secure AI Framework 2.0 - Risks

Databricks AI Security Framework 3.0

AID-H-006.004: Generated Media & Binary Artifact Output Safety Release Gate | AIDEFEND

Loading interactive view…

A Practical Knowledge Base for AI Security Defenses

Version