{"id":49695755,"url":"https://github.com/github/gh-aw-threat-detection","last_synced_at":"2026-06-12T02:00:33.583Z","repository":{"id":356360198,"uuid":"1224985525","full_name":"github/gh-aw-threat-detection","owner":"github","description":"GitHub Agentic Workflows Threat Detection","archived":false,"fork":false,"pushed_at":"2026-06-11T22:24:37.000Z","size":407,"stargazers_count":7,"open_issues_count":4,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T23:07:56.808Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/github.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-29T20:40:11.000Z","updated_at":"2026-06-11T21:22:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/github/gh-aw-threat-detection","commit_stats":null,"previous_names":["github/gh-aw-threat-detection"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/github/gh-aw-threat-detection","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/github%2Fgh-aw-threat-detection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/github%2Fgh-aw-threat-detection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/github%2Fgh-aw-threat-detection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/github%2Fgh-aw-threat-detection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/github","download_url":"https://codeload.github.com/github/gh-aw-threat-detection/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/github%2Fgh-aw-threat-detection/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34225350,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-08T00:02:36.902Z","updated_at":"2026-06-12T02:00:33.560Z","avatar_url":"https://github.com/github.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gh-aw-threat-detection\n\nThreat Detection component for [GitHub Agentic Workflows](https://github.com/github/gh-aw). Analyzes AI agent output for security threats including prompt injection, secret leaks, and malicious patches.\n\n## Contents\n\n- [Quick Start](#quick-start)\n- [Overview](#overview)\n- [Guardrails and Security Considerations](#guardrails-and-security-considerations)\n- [Usage](#usage)\n- [Stage Status and Decisions](#stage-status-and-decisions)\n- [Private Container Release Setup](#private-container-release-setup)\n- [Release Lifecycle and Emergency Yanks](#release-lifecycle-and-emergency-yanks)\n- [Development](#development)\n- [Architecture](#architecture)\n- [Integration with gh-aw](#integration-with-gh-aw)\n- [Specification](#specification)\n- [Contributing](#contributing)\n- [Support](#support)\n- [Code of Conduct](#code-of-conduct)\n- [Security](#security)\n- [License](#license)\n\n## Quick Start\n\nBuild the CLI and run it against an artifacts directory:\n\n```bash\nmake build\n./bin/threat-detect /path/to/artifacts\n```\n\nRun tests locally:\n\n```bash\nmake test\n```\n\n## Overview\n\nThis tool runs as a standalone binary or container that analyzes artifacts produced by AI agents before safe outputs are permitted. It supports multiple AI engines (Copilot, Claude, Codex) for detection analysis.\n\n## Guardrails and Security Considerations\n\nThis project is designed to help reduce risk when running AI agent workflows by inspecting generated artifacts before they are accepted as safe output. Detection is advisory and should be combined with defense-in-depth controls such as least-privilege permissions, human review, and repository protections.\n\nDo not treat a \"safe\" result as a security guarantee. Use the output as one signal in a broader security review process.\n\n## Usage\n\n### CLI\n\n```bash\nthreat-detect [flags] \u003cartifacts-dir\u003e\n```\n\n**Flags:**\n- `--engine` — AI engine to use (`copilot`, `claude`, `codex`). Default: `copilot`\n- `--model` — Model override for the engine\n- `--prompt-template` — Path to custom prompt template\n- `--output` — Path to write JSON result (defaults to stdout)\n- `--triage` — Enable fast Phase 1 structured-output triage. Default: `true`\n- `--reflect-url` — `api-proxy` `/reflect` base URL for structured-output calls. Default: `http://127.0.0.1:8080/reflect`\n- `--triage-model` — Model override for Phase 1 `/reflect` triage\n- `--triage-max-bytes` — Maximum bytes per artifact to inline during triage\n- `--triage-retries` — Retries for malformed structured-output responses\n- `--version` — Print version and exit\n\n`--reflect-url` can also be supplied with `THREAT_DETECTION_REFLECT_URL`,\n`API_PROXY_REFLECT_URL`, or `REFLECT_URL`. By default, `threat-detect` first\ntries a non-agentic `/reflect` call with a strict JSON schema matching the result\ncontract. An all-false valid triage result exits successfully without the full\ndetector. Threats, uncertainty, unsupported models, proxy errors, or malformed\nresponses fail safe into the full detector. The full detector preserves the\nexisting CLI engine behavior and prefers `/reflect` structured output when a\nschema-capable model is available.\n\nSet `THREAT_DETECTION_LOG_REFLECT_RESPONSE=true` to print raw `/reflect`\nresponses to stderr for controlled debugging. Responses may include reflected\nartifact content, so leave this disabled outside targeted diagnostic runs.\n\n#### In-session result reporting (`threat_detection_result`)\n\nOn the agentic CLI engine path (`copilot`, `claude`, `codex`), the detector\nprovisions a `threat_detection_result` command on the model's `PATH` and sets\n`THREAT_DETECTION_RESULT_FILE` to a private sink file before each engine\ninvocation. The model reports its verdict by running the command exactly once:\n\n```bash\nthreat_detection_result --prompt-injection \u003ctrue|false\u003e --secret-leak \u003ctrue|false\u003e --malicious-patch \u003ctrue|false\u003e --reason \"...\"\n```\n\nThe command validates the input synchronously: on bad input it prints\n`THREAT_DETECTION_RESULT_ERROR:` and exits non-zero without recording anything,\nso the model can correct it in-session; on valid input it atomically records the\ncanonical JSON verdict to the sink (first valid write wins, idempotent) and\nprints `THREAT_DETECTION_RESULT_RECORDED:`. As soon as a valid verdict is\nrecorded, the detector cancels the engine subprocess (early termination),\neliminating dead-spiral latency and cost. The detector prefers the sink result\nover scraping the transcript. The legacy single-line\n`THREAT_DETECTION_RESULT:{...}` transcript output remains supported as a\nfallback for engines without in-session shell access.\n\n**Exit codes:**\n- `0` — Safe (no threats detected)\n- `1` — Threat detected\n- `2` — Infrastructure/configuration error\n\n### Container\n\n```bash\ndocker run --rm \\\n  -v /path/to/artifacts:/workspace/artifacts \\\n  ghcr.io/github/gh-aw-threat-detection:v0.0.2 \\\n  /workspace/artifacts\n```\n\nThe current image is suitable for CLI packaging and smoke testing.\nProduction AI-backed detection requires the selected engine CLI and its authentication to be available in the runtime image or installed by a future image variant.\n\n### Input (Artifacts Directory)\n\n```\n\u003cartifacts-dir\u003e/\n├── aw-prompts/\n│   ├── prompt.txt          # Expanded workflow prompt file\n│   ├── prompt-template.txt # Pre-expansion prompt template (optional)\n│   └── prompt-import-tree.json # Runtime-import provenance (optional)\n├── agent_output.json       # Agent structured output\n├── aw_info.json            # Activation metadata (optional)\n├── aw-*.patch              # Git format-patch files (optional)\n├── aw-*.bundle             # Git bundle files (optional)\n├── experiments/            # Experiment assignment/state files (optional)\n└── comment-memory/         # Agent comment memory (optional)\n    └── *.md\n```\n\n### Output (JSON)\n\n```json\n{\n  \"prompt_injection\": false,\n  \"secret_leak\": false,\n  \"malicious_patch\": false,\n  \"reasons\": []\n}\n```\n\n### Replay workflow\n\nMaintainers can manually run **Replay Threat Detection** from the Actions tab to rerun detection against artifacts from a prior workflow run. Provide the source repository and run ID; the workflow downloads the `agent`, `activation`, optional experiment, and optional original `detection` artifacts, normalizes them into the CLI input contract above, runs `threat-detect`, and uploads a sanitized `replay-detection-\u003crun_id\u003e` artifact with the manifest, file inventory, logs, replay result, and original-result comparison when available.\n\nReplay uses the dispatching repository's `GITHUB_TOKEN`; no extra replay token is required. The selected source run must be accessible to that token.\n\nCommon dispatch examples:\n\n- Current checkout, direct CLI replay: set `run_id`, leave `detector_source=current`, `engine=copilot`, and `use_awf=false`.\n- Released detector replay: set `detector_source=release` and `detector_ref` to a release tag such as `v0.0.2`.\n- Image detector replay: set `detector_source=image` and optionally set `detector_ref` to an image tag. The workflow extracts the `threat-detect` binary from the image and runs it on the host so the selected engine CLI can be installed there.\n- Model comparison: set `model` to the engine-specific model name to pass through `--model`.\n- Additional detection instructions: set `custom_prompt`; it is passed as `CUSTOM_PROMPT` and appended to the default detector prompt.\n- AWF mode: set `use_awf=true` only on a runner image that already provides the `awf` CLI. Direct mode is the default.\n\nThe `run_attempt` input is only safe for the latest attempt of a source run because GitHub artifact downloads are not attempt-scoped. The workflow fails with a clear error if an older attempt is requested.\n\n## Stage Status and Decisions\n\nThe extraction staging model is:\n- Stage 1: standalone repository\n- Stage 2: containerization\n- Stage 3: `github/gh-aw` integration\n\nStage 1 is functionally represented in this repository.\nThe standalone Go CLI, artifact reader, prompt builder, two-phase `/reflect` triage, result parser, engine abstraction, W3C-style specification, unit tests, CI, Dockerfile, and release workflow are present.\nRemaining work involves integration with `github/gh-aw` and production hardening of the container runtime in Stage 2/3, not additional JavaScript porting in this repository.\n\nDecisions for the unresolved extraction questions:\n\n- **JavaScript scripts**: detection setup and result parsing are implemented in Go here; the old GitHub Actions JavaScript scripts should not be needed once `gh-aw` switches to the container contract.\n- **Engine CLIs and `/reflect`**: do not bundle Copilot, Claude, or Codex CLIs into the detector image. The detector invokes the selected engine CLI from `PATH` and forwards the `--model` value when full CLI analysis is needed. When `--reflect-url` is configured, the detector can call `api-proxy` directly for structured-output triage and schema-capable full analysis before falling back to CLI behavior. Production `gh-aw` integration should install or provide the selected engine CLI in the detection job, then run the pinned detector binary extracted from the detector image in that same runner/AWF environment. This keeps the image small, avoids runtime installation inside the image, and reuses the existing engine installation/authentication path.\n- **Custom steps**: custom `threat-detection.steps` remain orchestrator-owned. They should run before or after the container in the `gh-aw` job rather than being passed into this container as arbitrary scripts.\n- **Backward compatibility**: do not ship a long-lived dual-mode compatibility window. Stage 3 should switch `gh-aw` to the pinned detector image path after Stage 4 validation passes; users that need inline detection can pin an older `gh-aw` release. A temporary internal fallback is acceptable during implementation only, but should not become a documented public feature flag unless Stage 4 exposes a blocking compatibility issue.\n- **Ollama/LlamaGuard**: keep this as a custom-step pattern unless a dedicated image variant is explicitly required.\n- **Version coupling**: use strict, semver-compatible image tags and have `gh-aw` pin a specific `DefaultThreatDetectionVersion`, matching the firewall pattern.\n- **Isolation**: the detector should run in the standard detection job initially. Running the detector itself inside an additional firewall/isolation layer can be evaluated later.\n\n## Private Container Release Setup\n\nThe repository can move through Stage 2 while remaining private. GitHub Container Registry supports private packages, and the release workflow publishes `ghcr.io/github/gh-aw-threat-detection:\u003ctag\u003e` using the automatic `GITHUB_TOKEN` with `packages: write`.\n\nMaintainers need to configure the following before the image is consumed by `gh-aw`:\n\n1. Keep Actions enabled for this private repository.\n2. Ensure the package created under `ghcr.io/github/gh-aw-threat-detection` inherits repository visibility or is explicitly private.\n3. Grant the consuming `github/gh-aw` repository access to the private package, or configure the organization package settings so `GITHUB_TOKEN` from `gh-aw` can pull it with `packages: read`.\n4. Keep the `release-publish` and `release-promote` environments if manual approval is desired; otherwise update the environment protection rules in repository settings.\n5. Tag releases with semantic versions such as `v0.0.2`. The release workflow publishes the version tag; the promote workflow tags the verified digest as `latest`.\n\nNo additional secrets are required for unit tests, `make build`, `make test`, or the container smoke test. Engine authentication is only needed when running real AI-backed detection:\n\n| Variable | Required when | Notes |\n|----------|---------------|-------|\n| `COPILOT_GITHUB_TOKEN` | Running `--engine copilot` in an environment that needs explicit token-based Copilot authentication | Use a fine-grained PAT owned by a user account with **Account permissions → Copilot Requests: Read**. `GITHUB_TOKEN` is not sufficient for Copilot inference. |\n| `ANTHROPIC_API_KEY` | Running `--engine claude` with the Claude CLI | Not used by unit tests. |\n| `OPENAI_API_KEY` | Running `--engine codex` with the Codex CLI | Not used by unit tests. |\n| `WORKFLOW_NAME` | Optional local/container runs | Included in the generated prompt. |\n| `WORKFLOW_DESCRIPTION` | Optional local/container runs | Included in the generated prompt. |\n| `CUSTOM_PROMPT` | Optional local/container runs | Appended to the default detection prompt. |\n\n## Release Lifecycle and Emergency Yanks\n\nRelease lifecycle metadata is recorded in [`releases/threat-detection-lifecycle.json`](releases/threat-detection-lifecycle.json).\nThe registry supports `active`, `deprecated`, `obsolete`, and `yanked` states.\n`yanked` means a release is unsafe to run and is stronger than `obsolete`.\n\nThe parent orchestrator (`gh-aw`) must check this registry before pulling or\nrunning a detector image:\n\n- `latest` must not point at a yanked version. Maintainers use the manual\n  **Yank Release** workflow to retag `latest` to a safe stable replacement.\n- Explicit version or digest pins that match a yanked registry entry must fail\n  closed before detector execution.\n- Explicit pins must not be silently downgraded or upgraded; the failure message\n  should include the yank reason and replacement version.\n\nYanked release artifacts remain available for audit and forensics unless\nmaintainers choose separate package removal. See [DEVGUIDE.md](DEVGUIDE.md#emergency-yank-process)\nfor maintainer approval, communication, and workflow details.\n\n## Development\n\n### Prerequisites\n\n- Go 1.23+\n- Docker (for container builds)\n\n### AW Smoke Workflows\n\nThis repository includes three Agentic Workflows smoke tests:\n\n- `.github/workflows/smoke-copilot.md`\n- `.github/workflows/smoke-claude.md`\n- `.github/workflows/smoke-codex.md`\n\nEach runs daily and by `workflow_dispatch`. The top-level `Smoke` workflow can be dispatched manually to start all three compiled smoke workflows and their three containerized siblings. The matching `.lock.yml` files are the compiled AW workflows. The `*-container.lock.yml` siblings are generated from those lock files by `scripts/create-threat-detection-sibling-workflows.py`; they pull the `ghcr.io/github/gh-aw-threat-detection` container, extract its detector binary, and execute it under the same AWF wrapper used by the generated detection job. The script also copies matching `*-container.md` source sidecars from the original smoke markdown files so gh-aw's stale lock-file check can resolve and verify the inherited frontmatter hash.\n\n### Detection-only Workflow\n\n`.github/workflows/detection-only.yml` is a manual iteration workflow for the generated detection job. It keeps the copied detection job body aligned with the containerized Copilot smoke workflow, while replacing prior activation and agent jobs with stubs that upload local fixtures from `testdata/detection-only/` as the `agent` artifact.\n\nAfter recompiling the smoke workflows with `gh aw compile`, regenerate and verify the sibling workflows:\n\n```bash\nscripts/create-threat-detection-sibling-workflows.py\nscripts/create-threat-detection-sibling-workflows.py --check\n```\n\nConfigure these Actions secrets to enable all smoke workflows:\n\n| Secret | Required for | Notes |\n|--------|--------------|-------|\n| `COPILOT_GITHUB_TOKEN` | Copilot smoke workflow and Copilot detection | Use a fine-grained PAT owned by a user account with **Account permissions → Copilot Requests: Read**. |\n| `ANTHROPIC_API_KEY` | Claude smoke workflow and Claude detection | Used by the Claude CLI. |\n| `OPENAI_API_KEY` or `CODEX_API_KEY` | Codex smoke workflow and Codex detection | Configure whichever token your Codex CLI setup expects. |\n| `GH_AW_GITHUB_TOKEN` | Recommended for GitHub MCP access, safe outputs, and private GHCR pulls | The generated workflows fall back to `GITHUB_TOKEN` where possible. |\n| `GH_AW_GITHUB_MCP_SERVER_TOKEN` | Optional GitHub MCP override | Falls back to `GITHUB_TOKEN` in the compiled workflows. |\n\nOptional Actions variables:\n\n| Variable | Purpose |\n|----------|---------|\n| `GH_AW_MODEL_AGENT_COPILOT`, `GH_AW_MODEL_AGENT_CLAUDE`, `GH_AW_MODEL_AGENT_CODEX` | Override the agent model for each smoke workflow. |\n| `GH_AW_MODEL_DETECTION_COPILOT`, `GH_AW_MODEL_DETECTION_CLAUDE`, `GH_AW_MODEL_DETECTION_CODEX` | Override the detection model for each engine. |\n| `GH_AW_THREAT_DETECTION_IMAGE` | Override the detector image used by the `*-container.lock.yml` siblings. Defaults to `ghcr.io/github/gh-aw-threat-detection:latest`. |\n\n### Build\n\n```bash\nmake build\n```\n\n### Test\n\n```bash\nmake test\n```\n\n### Lint\n\n```bash\nmake lint\n```\n\n### Docker\n\n```bash\nmake docker-build\n```\n\nRun the container smoke test:\n\n```bash\nmake docker-smoke\n```\n\n## Architecture\n\n```\ncmd/threat-detect/     CLI entry point\npkg/detector/          Core detection logic (prompt building, result parsing)\npkg/engine/            AI engine abstraction (copilot, claude, codex)\npkg/artifacts/         Artifact reading and validation\npkg/detector/prompts/  Embedded AI prompt template\nspecs/                 W3C-style specification\n```\n\n## Integration with gh-aw\n\nAfter containerization, `gh-aw` references this component via:\n\n```go\nconst DefaultThreatDetectionRegistry = \"ghcr.io/github/gh-aw-threat-detection\"\nconst DefaultThreatDetectionVersion  = \"v0.0.2\"\n```\n\nThe detection job in compiled workflows uses this container instead of inline AI engine invocation.\n\n`gh-aw` should also fetch or vendor\n[releases/threat-detection-lifecycle.json](releases/threat-detection-lifecycle.json)\nand evaluate the pinned `DefaultThreatDetectionVersion` before pulling or\nrunning the detector container. Active versions run normally. Deprecated versions\nshould emit a GitHub Actions warning annotation and job summary text that include\nthe reason, replacement version, dates, advisory URL, urgency, and upgrade\ninstructions, then continue. Obsolete versions should fail closed before the\ndetector runs and print the same remediation guidance. Unknown versions should\nfollow the registry policy, currently `fail-closed`.\n\n## Specification\n\nSee [specs/threat-detection-spec.md](specs/threat-detection-spec.md) for the full W3C-style specification.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and contribution guidelines.\n\n## Maintainers\n\nSee [CODEOWNERS](CODEOWNERS) for maintainers.\n\n## Support\n\nSee [SUPPORT.md](SUPPORT.md) for help, issue reporting, and support scope.\n\n## Code of Conduct\n\nSee [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).\n\n## Security\n\nSee [SECURITY.md](SECURITY.md) for vulnerability reporting instructions.\n\n## License\n\nSee [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgithub%2Fgh-aw-threat-detection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgithub%2Fgh-aw-threat-detection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgithub%2Fgh-aw-threat-detection/lists"}