# security-scan

> Report-only security reviews of operator-configured repositories at immutable Git commits, dispatched through a read-only Harness policy.

| field | value |
|-------|-------|
| version | 0.1.12 |
| type | binary |
| license | Apache-2.0 |
| repo | https://github.com/iii-hq/workers |
| supported_targets | aarch64-apple-darwin, x86_64-pc-windows-msvc, aarch64-pc-windows-msvc, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-unknown-linux-musl, armv7-unknown-linux-gnueabihf |
| author | iii |

## installation

```sh
iii trigger compose::add worker=security-scan@0.1.12
```

## configuration

```yaml
- analysis:
    max_cost_usd: 2
    max_output_tokens: 8000
    max_total_tokens: 50000
    max_turns: 4
    model: 
  repositories:

```

## dependencies

- `configuration` @ `latest`
- `cron` @ `latest`
- `github` @ `latest`
- `harness` @ `latest`
- `iii-observability` @ `latest`
- `iii-stream` @ `latest`
- `queue` @ `latest`
- `state` @ `latest`
- `storage` @ `latest`
- `worktree` @ `latest`

## readme

# security-scan

`security-scan` accepts manual and operator-scheduled review requests for configured repositories and queues a report-only security analysis of an exact Git commit. It creates an isolated checkout of the **full tree at that commit**, constrains Harness to read-only code functions, validates the structured result, and never applies a suggested change. The SHA is not a commit-diff review and not a scan of git history. Omit `target_sha` (or leave the Console SHA field blank) to analyze the entire repository at HEAD.

## Install

```bash
iii trigger compose::add worker=security-scan
```

Analysis requires the Harness stack. GitHub issue and draft fix-PR actions also require `approval-gate` to be live; those mutations stay closed until a user approves each one.

```bash
iii trigger compose::add worker=harness worker=approval-gate
```

The worker composes existing iii infrastructure rather than implementing local substitutes: private compare-and-set records live in `state`, durable steps run through `queue`, exact checkouts come from `worktree`, configured schedules bind through the `cron` dependency, analysis runs through `harness`, and GitHub publication is held by `approval-gate`.

## Quickstart

Request a scan using a configured repository id and a full commit SHA:

```bash
iii trigger security-scan::request \
  repository=iii-hq/iii \
  target_sha="$(git -C /srv/repos/iii rev-parse HEAD)" \
  mode=scan \
  model=deepseek::deepseek-v4-flash
```

The request returns immediately:

```json
{
  "run_id": "sec_...",
  "status": "queued",
  "deduplicated": false
}
```

Submitting the same repository, commit, mode, and model again returns the same run id with `deduplicated: true`. Omit `target_sha` to resolve HEAD and analyze the entire repository. Omit `model` to use the operator `analysis.model` (and its provider). A Console scan from the sidebar runs on the model picked in its analysis-model control; that picker defaults to following the composer catalog id of the open chat, such as `deepseek::deepseek-v4-flash`. A retryable failed run is restarted as a new attempt under that same id. If the first queue wake fails, the durable queued checkpoint remains available to the recovery sweep. Use `mode=suggest` to include minimal patch suggestions in the report; suggestions remain text and are never applied.

Read the current status or completed report:

```bash
iii trigger security-scan::read run_id=sec_...
```

Read the persisted GitHub reconciliation snapshot, or explicitly refresh it:

```bash
iii trigger security-scan::reconciliation run_id=sec_...
iii trigger security-scan::reconciliation run_id=sec_... refresh=true limit=50
```

The Harness count and GitHub alert counts answer different questions and are never added together. Harness findings are validated against the requested exact commit. Dependabot is a repository default-branch snapshot, while code scanning is a repository snapshot whose latest instances may refer to commits other than the requested SHA. A Harness count of 3 and GitHub source counts totaling 221 therefore remain 3 exact-commit findings and 221 GitHub records, not 224 unique findings.

Each GitHub source reports its own scope and collection status: `complete`, `partial`, `unavailable`, `authentication_required`, `permission_denied`, `disabled`, `not_configured`, or `not_collected`. `complete` with `record_count: 0` is a successful empty collection. A null count means no usable count was collected and is not equivalent to zero. Records are deduplicated only by GitHub source and alert number; v1 does not claim semantic matches between model-authored Harness findings and typed GitHub alerts.

The default `refresh=false` reads the last sanitized snapshot without calling GitHub. Before the first collection it returns `not_collected`, or `not_configured` when no GitHub mapping exists. `refresh=true` queries Dependabot and code scanning, replaces the persisted snapshot, and then applies source, severity, lifecycle, cursor, and limit filters. One unavailable source does not fail the whole response; its status explains the missing count.

List recent runs, optionally filtered by repository or status:

```bash
iii trigger security-scan::list repository=iii-hq/iii status=completed limit=50
```

## Console page

When `security-scan` and Console are connected, open the `security-scan` page to browse persisted run history and inspect a selected report. The page shows the exact repository and commit, current pipeline status, evidence and remediation for each finding, and suggested patches in `suggest` mode. Suggested patches remain read-only until you explicitly create a draft fix PR and approve the GitHub mutation. The sidebar form reviews the full tree at the pasted SHA. Its analysis-model picker lists the live router catalog and pins one model for the scan; left on `follow chat` it takes the model selected in the open chat composer, and falls back to the operator `analysis.model` when that chat has none. The catalog is re-read on the router's `router::models::changed` fan-out, so a credential added or a provider removed updates the list without a reload.

Each Harness finding can start an approval-gated GitHub issue. Completed `suggest` findings that include a patch can also start an isolated draft fix PR. GitHub reconciliation alerts are a separate snapshot and cannot start exact-commit Harness actions.

Run updates arrive through the `security-scan:runs` stream. The stream is a refresh doorbell rather than the source of truth: each frame makes the page refetch `security-scan::list` and `security-scan::read`. Nothing is polled. The page re-reads on three other events instead — the socket reconnecting, the tab becoming visible, and the refresh control — so a dropped frame delays convergence until the next event rather than stranding the view.

A completed report records coverage separately for vulnerabilities, dependencies, secrets, and supply-chain review. An area can be assessed, not assessed with a reason, or unknown for reports created before coverage tracking. Zero findings are never presented as proof that the code is vulnerability-free.

GitHub reconciliation and GitHub source links require the explicit operator-verified `github.full_name` mapping. The worker never infers a GitHub repository from the security-scan repository id.

For local UI development:

```bash
pnpm --dir security-scan/ui build
III_SECURITY_SCAN_UI_WATCH=security-scan/ui/dist cargo run --manifest-path security-scan/Cargo.toml
```

The page header's standard settings control opens this worker in the Console's global Settings modal: the analysis budgets (`max_turns` and the token and cost ceilings), the operator `analysis.model`, and the repository allowlist all live there.

## Configuration

Repositories are an operator-owned allowlist. Callers choose an id, not an arbitrary filesystem path or URL.

```yaml
repositories:
  - id: iii-hq/iii       # stable id accepted by security-scan::request
    path: /srv/repos/iii # local Git repository owned by the operator
    github:               # optional; required for GitHub reconciliation
      full_name: iii-hq/iii # exact owner/name for this checkout
    schedule:             # optional; omit to disable automation for this repository
      expression: "0 0 3 * * *" # second minute hour day month weekday [year], UTC
      target_ref: refs/heads/main # resolved locally when each fire occurs
      mode: scan           # scan or suggest
analysis:
  model: provider/model-id # required model from the live router catalog
  provider: provider-id    # optional explicit provider
  max_turns: 4             # maximum Harness generations
  max_output_tokens: 8000  # ceiling for one generation
  max_total_tokens: 50000  # ceiling for the complete review
  max_cost_usd: 2.0        # optional spend ceiling
archive:                   # optional; JSON copies of run records in `storage`
  bucket: security-scan    # worker-facing bucket name
  prefix: runs             # object key prefix, default runs/
```

The shipped configuration leaves `analysis.model` empty and `repositories: []` unchanged. Set a model and at least one repository before requesting a scan; the empty repository allowlist rejects every request.

`github.full_name` is optional so existing local-only repositories remain valid, but it must be configured explicitly as `owner/name` before refresh is enabled for that repository. The `github` worker needs an authenticated GitHub CLI session or `GH_TOKEN` with permission to read Dependabot and code-scanning alerts for the mapped repository. Authentication, permission, and disabled-feature failures are stored only as sanitized source statuses; credentials and raw dependency payloads are never persisted or returned.

Each repository has at most one schedule, so the repository id is also its unique schedule identity. The expression must use six fields starting with seconds, with an optional seventh year field. Cron evaluation is UTC. Fires missed while `cron` or `security-scan` is stopped are skipped and are not replayed.

At fire time the internal handler uses trigger metadata only to find this operator-owned configuration. It resolves `target_ref` with a bounded local `git rev-parse` call, does not fetch, requires one lowercase full 40-character commit SHA, and submits that SHA through the same `security-scan::request` path used manually. Repeated fires that resolve to the same repository, commit, and mode therefore return the existing run instead of creating duplicate work.

Configuration is loaded at worker startup in this MVP. Restart `security-scan` after changing repositories, GitHub mappings, schedules, analysis settings, or archive settings. The worker manifest starts `github` and `cron` as dependencies. If a manually assembled stack starts `security-scan` before a cron trigger owner is available, manual scans remain available and the recovery loop binds each configured schedule once `cron` appears.

## Persistence

The Console Scan runs list is served from `state`. Point that worker at a file-backed or Redis adapter so history survives engine restarts. `store_method: in_memory` drops every run on shutdown.

Optional JSON copies of each run record are written through `storage::putObject` when `archive.bucket` is set. On boot, missing runs are imported from that bucket into `state` using `storage::getObject` and `runs/manifest.json` (`storage` does not list objects). Configure the bucket on the `storage` worker first:

```yaml
providers:
  local:
    data_dir: ./data/storage
buckets:
  security-scan:
    provider: local
    bucket: security-scan
```

The local provider spawns a rustfs sidecar (`iii trigger compose::add worker=storage`). Set `$RUSTFS_BIN` or put `rustfs` on `PATH`. JSON copies are a backup; the Console Scan runs list still comes from `state`.

## Safety boundary

The worker accepts only 40-character commit SHAs, verifies the materialized checkout matches the requested commit, and disables ignored-file provisioning for scanner worktrees so local `.env`, dependency, and cache files are not copied into the review scope. The Harness scan turn can discover function contracts and call only `coder::info`, `coder::tree`, `coder::list-folder`, `coder::read-file`, and `coder::search`. It cannot run repository code, access the network, mutate files, update state, or start another agent.

Dependency sessions use private random identities rather than the public run id. Structured output is rejected if it exposes the internal checkout root or high-confidence credential material. Terminal scanner worktrees are removed through the existing `worktree` worker.

GitHub issue and draft fix-PR actions use a separate Harness session after an explicit user request. Issue sessions may call only `github::issue::create`. Fix sessions use an exact-SHA worktree with scoped file writes, explicit git commands, and `github::pr::create`. Both require `approval::gate` to be live; GitHub publication, branch push, and PR creation stay held until the user approves them. Fix PRs open as drafts and never merge automatically.

The public MVP exposes `security-scan::request`, `security-scan::read`, `security-scan::list`, `security-scan::reconciliation`, `security-scan::action`, and `security-scan::action-read`. `security-scan::execute`, `security-scan::action-execute`, `security-scan::on-turn-completed`, and `security-scan::on-schedule` are internal worker functions. Scan analysis still does not apply, commit, push, comment, review, merge, or dismiss alerts on its own.

This first phase is the bounded investigation layer. A later phase will feed it deterministic, pinned SAST, dependency, and secret-scanner candidates before Harness analysis, following the same candidate-discovery then evidence-review split used by DeepSec.

## api reference

```json
{
  "functions": [
    {
      "description": "Start an approval-gated GitHub issue or draft fix PR for one validated Harness finding. Duplicate run, finding, and action requests return the same action id.",
      "metadata": {},
      "name": "security-scan::action",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "SecurityActionKindV1": {
            "enum": [
              "issue",
              "fix_pr"
            ],
            "type": "string"
          }
        },
        "properties": {
          "action": {
            "$ref": "#/definitions/SecurityActionKindV1"
          },
          "finding_index": {
            "format": "uint32",
            "minimum": 0,
            "type": "integer"
          },
          "run_id": {
            "type": "string"
          }
        },
        "required": [
          "action",
          "finding_index",
          "run_id"
        ],
        "title": "SecurityScanActionRequestV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "SecurityActionKindV1": {
            "enum": [
              "issue",
              "fix_pr"
            ],
            "type": "string"
          },
          "SecurityActionStatusV1": {
            "enum": [
              "queued",
              "preparing",
              "awaiting_approval",
              "completed",
              "failed",
              "cancelled"
            ],
            "type": "string"
          }
        },
        "properties": {
          "action": {
            "$ref": "#/definitions/SecurityActionKindV1"
          },
          "action_id": {
            "type": "string"
          },
          "deduplicated": {
            "type": "boolean"
          },
          "finding_index": {
            "format": "uint32",
            "minimum": 0,
            "type": "integer"
          },
          "run_id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/definitions/SecurityActionStatusV1"
          }
        },
        "required": [
          "action",
          "action_id",
          "deduplicated",
          "finding_index",
          "run_id",
          "status"
        ],
        "title": "SecurityScanActionResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Commit the current fix action through its checkout-bound capability.",
      "metadata": {
        "internal": true
      },
      "name": "security-scan::action-commit",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "properties": {
          "action_id": {
            "type": "string"
          },
          "capability": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "action_id",
          "capability",
          "message"
        ],
        "title": "ActionCommitRequestV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "properties": {
          "commit_sha": {
            "type": "string"
          }
        },
        "required": [
          "commit_sha"
        ],
        "title": "ActionCommitResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Internal durable queue step for approval-gated GitHub issue and draft PR publication.",
      "metadata": {
        "internal": true,
        "trace_hidden": true
      },
      "name": "security-scan::action-execute",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "properties": {
          "action_id": {
            "type": "string"
          },
          "attempt": {
            "format": "uint32",
            "minimum": 0,
            "type": "integer"
          },
          "run_id": {
            "type": "string"
          },
          "step": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "action_id",
          "attempt",
          "run_id",
          "step"
        ],
        "title": "ActionEnqueueRequestV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "SecurityActionStatusV1": {
            "enum": [
              "queued",
              "preparing",
              "awaiting_approval",
              "completed",
              "failed",
              "cancelled"
            ],
            "type": "string"
          }
        },
        "properties": {
          "skipped": {
            "type": "boolean"
          },
          "status": {
            "$ref": "#/definitions/SecurityActionStatusV1"
          },
          "step": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "skipped",
          "status",
          "step"
        ],
        "title": "ActionExecuteResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Push the current fix action through its checkout-bound capability.",
      "metadata": {
        "internal": true
      },
      "name": "security-scan::action-push",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "properties": {
          "action_id": {
            "type": "string"
          },
          "capability": {
            "type": "string"
          }
        },
        "required": [
          "action_id",
          "capability"
        ],
        "title": "ActionPushRequestV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "properties": {
          "branch": {
            "type": "string"
          }
        },
        "required": [
          "branch"
        ],
        "title": "ActionPushResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Read a durable security-scan GitHub action without exposing internal checkout paths or Harness session identifiers.",
      "metadata": {},
      "name": "security-scan::action-read",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "properties": {
          "action_id": {
            "type": "string"
          }
        },
        "required": [
          "action_id"
        ],
        "title": "SecurityScanActionReadRequestV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "PublicActionV1": {
            "additionalProperties": false,
            "properties": {
              "action": {
                "$ref": "#/definitions/SecurityActionKindV1"
              },
              "action_id": {
                "type": "string"
              },
              "attempt": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "completed_at": {
                "format": "int64",
                "type": [
                  "integer",
                  "null"
                ]
              },
              "created_at": {
                "format": "int64",
                "type": "integer"
              },
              "error": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/RunErrorV1"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "finding_index": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "repository": {
                "type": "string"
              },
              "result": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/SecurityActionResultV1"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "run_id": {
                "type": "string"
              },
              "schema_version": {
                "type": "string"
              },
              "status": {
                "$ref": "#/definitions/SecurityActionStatusV1"
              },
              "target_sha": {
                "type": "string"
              },
              "updated_at": {
                "format": "int64",
                "type": "integer"
              }
            },
            "required": [
              "action",
              "action_id",
              "attempt",
              "created_at",
              "finding_index",
              "repository",
              "run_id",
              "schema_version",
              "status",
              "target_sha",
              "updated_at"
            ],
            "type": "object"
          },
          "RunErrorV1": {
            "additionalProperties": false,
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "retryable": {
                "type": "boolean"
              }
            },
            "required": [
              "code",
              "message",
              "retryable"
            ],
            "type": "object"
          },
          "SecurityActionKindV1": {
            "enum": [
              "issue",
              "fix_pr"
            ],
            "type": "string"
          },
          "SecurityActionResultV1": {
            "additionalProperties": false,
            "properties": {
              "branch": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "commit_sha": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "draft": {
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "kind": {
                "type": "string"
              },
              "url": {
                "type": "string"
              },
              "validation": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "kind",
              "url"
            ],
            "type": "object"
          },
          "SecurityActionStatusV1": {
            "enum": [
              "queued",
              "preparing",
              "awaiting_approval",
              "completed",
              "failed",
              "cancelled"
            ],
            "type": "string"
          }
        },
        "properties": {
          "action": {
            "anyOf": [
              {
                "$ref": "#/definitions/PublicActionV1"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "title": "SecurityScanActionReadResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Make a run's Harness review discoverable through session metadata and report whether it is available, without returning the private session identifier.",
      "metadata": {},
      "name": "security-scan::analysis-chat",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "properties": {
          "run_id": {
            "type": "string"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "SecurityScanAnalysisChatRequestV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "properties": {
          "available": {
            "type": "boolean"
          }
        },
        "required": [
          "available"
        ],
        "title": "SecurityScanAnalysisChatResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Stop an in-flight security-scan run. Queued and materializing runs are marked cancelled; analyzing runs stop the Harness turn and clean up the isolated checkout.",
      "metadata": {},
      "name": "security-scan::cancel",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "properties": {
          "run_id": {
            "type": "string"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "SecurityScanCancelRequestV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "RunStatusV1": {
            "enum": [
              "queued",
              "materializing",
              "materialized",
              "dispatching",
              "analyzing",
              "completed",
              "failed",
              "cancelling",
              "cancelled"
            ],
            "type": "string"
          }
        },
        "properties": {
          "deduplicated": {
            "type": "boolean"
          },
          "run_id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/definitions/RunStatusV1"
          }
        },
        "required": [
          "deduplicated",
          "run_id",
          "status"
        ],
        "title": "SecurityScanCancelResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Internal durable queue step for target materialization and read-only Harness dispatch.",
      "metadata": {
        "internal": true,
        "trace_hidden": true
      },
      "name": "security-scan::execute",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "properties": {
          "attempt": {
            "format": "uint32",
            "minimum": 0,
            "type": "integer"
          },
          "repository": {
            "type": "string"
          },
          "run_id": {
            "type": "string"
          },
          "step": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "attempt",
          "repository",
          "run_id",
          "step"
        ],
        "title": "EnqueueRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "RunStatusV1": {
            "enum": [
              "queued",
              "materializing",
              "materialized",
              "dispatching",
              "analyzing",
              "completed",
              "failed",
              "cancelling",
              "cancelled"
            ],
            "type": "string"
          }
        },
        "properties": {
          "skipped": {
            "type": "boolean"
          },
          "status": {
            "$ref": "#/definitions/RunStatusV1"
          },
          "step": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "skipped",
          "status",
          "step"
        ],
        "title": "ExecuteResponseV1",
        "type": "object"
      }
    },
    {
      "description": "List security-scan runs as sanitized lightweight summaries, newest update first. Optional repository and status filters are applied before the bounded result limit.",
      "metadata": {},
      "name": "security-scan::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "RunStatusV1": {
            "enum": [
              "queued",
              "materializing",
              "materialized",
              "dispatching",
              "analyzing",
              "completed",
              "failed",
              "cancelling",
              "cancelled"
            ],
            "type": "string"
          }
        },
        "properties": {
          "limit": {
            "format": "uint32",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "repository": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "anyOf": [
              {
                "$ref": "#/definitions/RunStatusV1"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "title": "SecurityScanListRequestV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "PublicRunSummaryV1": {
            "additionalProperties": false,
            "properties": {
              "attempt": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "completed_at": {
                "format": "int64",
                "type": [
                  "integer",
                  "null"
                ]
              },
              "created_at": {
                "format": "int64",
                "type": "integer"
              },
              "error": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/RunErrorV1"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "finding_count": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "mode": {
                "$ref": "#/definitions/ScanModeV1"
              },
              "model": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "repository": {
                "type": "string"
              },
              "resolved_from_head": {
                "type": "boolean"
              },
              "run_id": {
                "type": "string"
              },
              "status": {
                "$ref": "#/definitions/RunStatusV1"
              },
              "target_sha": {
                "type": "string"
              },
              "updated_at": {
                "format": "int64",
                "type": "integer"
              }
            },
            "required": [
              "attempt",
              "created_at",
              "finding_count",
              "mode",
              "repository",
              "run_id",
              "status",
              "target_sha",
              "updated_at"
            ],
            "type": "object"
          },
          "RunErrorV1": {
            "additionalProperties": false,
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "retryable": {
                "type": "boolean"
              }
            },
            "required": [
              "code",
              "message",
              "retryable"
            ],
            "type": "object"
          },
          "RunStatusV1": {
            "enum": [
              "queued",
              "materializing",
              "materialized",
              "dispatching",
              "analyzing",
              "completed",
              "failed",
              "cancelling",
              "cancelled"
            ],
            "type": "string"
          },
          "ScanModeV1": {
            "enum": [
              "scan",
              "suggest"
            ],
            "type": "string"
          }
        },
        "properties": {
          "runs": {
            "items": {
              "$ref": "#/definitions/PublicRunSummaryV1"
            },
            "type": "array"
          }
        },
        "required": [
          "runs"
        ],
        "title": "SecurityScanListResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Internal UTC cron target that uses invocation metadata only to look up an operator-configured repository schedule, resolves its local Git ref at fire time, and queues the exact commit through security-scan::request.",
      "metadata": {
        "internal": true,
        "trace_hidden": true
      },
      "name": "security-scan::on-schedule",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Payload emitted by the iii cron trigger. Values are observability data only; scan inputs come from operator configuration.",
        "properties": {
          "actual_time": {
            "type": "string"
          },
          "job_id": {
            "type": "string"
          },
          "scheduled_time": {
            "type": "string"
          },
          "trigger": {
            "type": "string"
          }
        },
        "required": [
          "actual_time",
          "job_id",
          "scheduled_time",
          "trigger"
        ],
        "title": "SecurityScanScheduleEventV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "RunStatusV1": {
            "enum": [
              "queued",
              "materializing",
              "materialized",
              "dispatching",
              "analyzing",
              "completed",
              "failed",
              "cancelling",
              "cancelled"
            ],
            "type": "string"
          },
          "ScanModeV1": {
            "enum": [
              "scan",
              "suggest"
            ],
            "type": "string"
          }
        },
        "properties": {
          "deduplicated": {
            "type": "boolean"
          },
          "mode": {
            "$ref": "#/definitions/ScanModeV1"
          },
          "repository": {
            "type": "string"
          },
          "run_id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/definitions/RunStatusV1"
          },
          "target_sha": {
            "type": "string"
          }
        },
        "required": [
          "deduplicated",
          "mode",
          "repository",
          "run_id",
          "status",
          "target_sha"
        ],
        "title": "SecurityScanScheduleResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Internal Harness completion doorbell that validates and checkpoints a structured report.",
      "metadata": {
        "internal": true,
        "trace_hidden": true
      },
      "name": "security-scan::on-turn-completed",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "result": {
            "default": null
          },
          "result_error": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "session_id": {
            "default": "",
            "type": "string"
          },
          "status": {
            "default": "",
            "type": "string"
          },
          "terminal": {
            "default": false,
            "type": "boolean"
          },
          "turn_id": {
            "default": "",
            "type": "string"
          }
        },
        "title": "TurnCompletedEventV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "RunStatusV1": {
            "enum": [
              "queued",
              "materializing",
              "materialized",
              "dispatching",
              "analyzing",
              "completed",
              "failed",
              "cancelling",
              "cancelled"
            ],
            "type": "string"
          }
        },
        "properties": {
          "status": {
            "anyOf": [
              {
                "$ref": "#/definitions/RunStatusV1"
              },
              {
                "type": "null"
              }
            ]
          },
          "woke": {
            "type": "boolean"
          }
        },
        "required": [
          "woke"
        ],
        "title": "TurnCompletedResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Read a security-scan run and its validated report without exposing internal checkout paths or Harness session identifiers.",
      "metadata": {},
      "name": "security-scan::read",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "properties": {
          "run_id": {
            "type": "string"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "SecurityScanReadRequestV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "AssessmentStatusV1": {
            "enum": [
              "assessed",
              "not_assessed",
              "unknown"
            ],
            "type": "string"
          },
          "FindingLocationV1": {
            "additionalProperties": false,
            "properties": {
              "line_end": {
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "line_start": {
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "path": {
                "type": "string"
              }
            },
            "required": [
              "path"
            ],
            "type": "object"
          },
          "PublicRunV1": {
            "additionalProperties": false,
            "properties": {
              "attempt": {
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "completed_at": {
                "format": "int64",
                "type": [
                  "integer",
                  "null"
                ]
              },
              "created_at": {
                "format": "int64",
                "type": "integer"
              },
              "error": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/RunErrorV1"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "mode": {
                "$ref": "#/definitions/ScanModeV1"
              },
              "model": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "report": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/SecurityReportV1"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "repository": {
                "type": "string"
              },
              "resolved_from_head": {
                "type": "boolean"
              },
              "run_id": {
                "type": "string"
              },
              "schema_version": {
                "type": "string"
              },
              "status": {
                "$ref": "#/definitions/RunStatusV1"
              },
              "target_sha": {
                "type": "string"
              },
              "updated_at": {
                "format": "int64",
                "type": "integer"
              }
            },
            "required": [
              "attempt",
              "created_at",
              "mode",
              "repository",
              "run_id",
              "schema_version",
              "status",
              "target_sha",
              "updated_at"
            ],
            "type": "object"
          },
          "RunErrorV1": {
            "additionalProperties": false,
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "retryable": {
                "type": "boolean"
              }
            },
            "required": [
              "code",
              "message",
              "retryable"
            ],
            "type": "object"
          },
          "RunStatusV1": {
            "enum": [
              "queued",
              "materializing",
              "materialized",
              "dispatching",
              "analyzing",
              "completed",
              "failed",
              "cancelling",
              "cancelled"
            ],
            "type": "string"
          },
          "ScanModeV1": {
            "enum": [
              "scan",
              "suggest"
            ],
            "type": "string"
          },
          "SecurityAreaAssessmentV1": {
            "additionalProperties": false,
            "properties": {
              "reason": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "$ref": "#/definitions/AssessmentStatusV1"
              }
            },
            "required": [
              "status"
            ],
            "type": "object"
          },
          "SecurityAssessmentsV1": {
            "additionalProperties": false,
            "properties": {
              "dependencies": {
                "$ref": "#/definitions/SecurityAreaAssessmentV1"
              },
              "secrets": {
                "$ref": "#/definitions/SecurityAreaAssessmentV1"
              },
              "supply_chain": {
                "$ref": "#/definitions/SecurityAreaAssessmentV1"
              },
              "vulnerabilities": {
                "$ref": "#/definitions/SecurityAreaAssessmentV1"
              }
            },
            "required": [
              "dependencies",
              "secrets",
              "supply_chain",
              "vulnerabilities"
            ],
            "type": "object"
          },
          "SecurityFindingV1": {
            "additionalProperties": false,
            "properties": {
              "description": {
                "type": "string"
              },
              "evidence": {
                "type": "string"
              },
              "location": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/FindingLocationV1"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "remediation": {
                "type": "string"
              },
              "rule_id": {
                "type": "string"
              },
              "severity": {
                "$ref": "#/definitions/SeverityV1"
              },
              "suggested_patch": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "description",
              "evidence",
              "remediation",
              "rule_id",
              "severity",
              "title"
            ],
            "type": "object"
          },
          "SecurityReportV1": {
            "additionalProperties": false,
            "properties": {
              "assessments": {
                "$ref": "#/definitions/SecurityAssessmentsV1"
              },
              "findings": {
                "items": {
                  "$ref": "#/definitions/SecurityFindingV1"
                },
                "type": "array"
              },
              "summary": {
                "type": "string"
              }
            },
            "required": [
              "assessments",
              "findings",
              "summary"
            ],
            "type": "object"
          },
          "SeverityV1": {
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "info"
            ],
            "type": "string"
          }
        },
        "properties": {
          "run": {
            "anyOf": [
              {
                "$ref": "#/definitions/PublicRunV1"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "title": "SecurityScanReadResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Read or refresh a persisted, sanitized comparison of one Harness report with separately counted Dependabot and code-scanning snapshots. Supports bounded source, severity, lifecycle, and cursor filters; never reports a combined unique total.",
      "metadata": {},
      "name": "security-scan::reconciliation",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "ReconciliationLifecycleV1": {
            "enum": [
              "open"
            ],
            "type": "string"
          },
          "ReconciliationSourceV1": {
            "enum": [
              "dependabot",
              "code_scanning"
            ],
            "type": "string"
          },
          "SeverityV1": {
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "info"
            ],
            "type": "string"
          }
        },
        "properties": {
          "cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "lifecycle": {
            "anyOf": [
              {
                "$ref": "#/definitions/ReconciliationLifecycleV1"
              },
              {
                "type": "null"
              }
            ]
          },
          "limit": {
            "format": "uint32",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "refresh": {
            "default": false,
            "type": "boolean"
          },
          "run_id": {
            "type": "string"
          },
          "severity": {
            "anyOf": [
              {
                "$ref": "#/definitions/SeverityV1"
              },
              {
                "type": "null"
              }
            ]
          },
          "source": {
            "anyOf": [
              {
                "$ref": "#/definitions/ReconciliationSourceV1"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "run_id"
        ],
        "title": "SecurityScanReconciliationRequestV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "HarnessReconciliationStatusV1": {
            "enum": [
              "verified",
              "not_available"
            ],
            "type": "string"
          },
          "HarnessReconciliationSummaryV1": {
            "additionalProperties": false,
            "properties": {
              "scope": {
                "$ref": "#/definitions/ReconciliationScopeV1"
              },
              "status": {
                "$ref": "#/definitions/HarnessReconciliationStatusV1"
              },
              "verified_at": {
                "format": "int64",
                "type": [
                  "integer",
                  "null"
                ]
              },
              "verified_count": {
                "description": "Validated Harness report findings. This is never added to GitHub source counts.",
                "format": "uint32",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              }
            },
            "required": [
              "scope",
              "status"
            ],
            "type": "object"
          },
          "ReconciliationAlertV1": {
            "additionalProperties": false,
            "properties": {
              "description": {
                "type": "string"
              },
              "end_line": {
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "lifecycle": {
                "$ref": "#/definitions/ReconciliationLifecycleV1"
              },
              "number": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "observed_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "path": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "public_url": {
                "description": "Reconstructed public github.com URL. Dependency-provided URLs are never persisted.",
                "type": "string"
              },
              "scope": {
                "$ref": "#/definitions/ReconciliationScopeV1"
              },
              "severity": {
                "$ref": "#/definitions/SeverityV1"
              },
              "source": {
                "$ref": "#/definitions/ReconciliationSourceV1"
              },
              "start_line": {
                "format": "uint64",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "structured_ids": {
                "default": [],
                "description": "Exact source identifiers only, such as GHSA, CVE, or scanner rule IDs.",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "description",
              "lifecycle",
              "number",
              "public_url",
              "scope",
              "severity",
              "source",
              "title"
            ],
            "type": "object"
          },
          "ReconciliationHealthStatusV1": {
            "enum": [
              "healthy",
              "warning",
              "error",
              "unknown"
            ],
            "type": "string"
          },
          "ReconciliationLifecycleV1": {
            "enum": [
              "open"
            ],
            "type": "string"
          },
          "ReconciliationMatchingStatusV1": {
            "enum": [
              "available",
              "unavailable"
            ],
            "type": "string"
          },
          "ReconciliationMatchingV1": {
            "additionalProperties": false,
            "properties": {
              "matched_records": {
                "description": "Present only when exact structured identifiers produced matches.",
                "format": "uint32",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "status": {
                "$ref": "#/definitions/ReconciliationMatchingStatusV1"
              }
            },
            "required": [
              "status"
            ],
            "type": "object"
          },
          "ReconciliationScopeV1": {
            "enum": [
              "exact_commit",
              "repository_default_branch",
              "repository_snapshot"
            ],
            "type": "string"
          },
          "ReconciliationSourceHealthV1": {
            "additionalProperties": false,
            "properties": {
              "commit_sha": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "observed_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "$ref": "#/definitions/ReconciliationHealthStatusV1"
              },
              "tool": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "status"
            ],
            "type": "object"
          },
          "ReconciliationSourceStatusV1": {
            "enum": [
              "complete",
              "partial",
              "unavailable",
              "authentication_required",
              "permission_denied",
              "disabled",
              "not_configured",
              "not_collected"
            ],
            "type": "string"
          },
          "ReconciliationSourceSummaryV1": {
            "additionalProperties": false,
            "properties": {
              "collected_at": {
                "description": "Collection time in Unix milliseconds. Null means the source was not queried.",
                "format": "int64",
                "type": [
                  "integer",
                  "null"
                ]
              },
              "health": {
                "$ref": "#/definitions/ReconciliationSourceHealthV1"
              },
              "record_count": {
                "description": "Number of normalized records when collection returned usable data. Null is unavailable/not-collected and is deliberately distinct from zero.",
                "format": "uint32",
                "minimum": 0,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "scope": {
                "$ref": "#/definitions/ReconciliationScopeV1"
              },
              "source": {
                "$ref": "#/definitions/ReconciliationSourceV1"
              },
              "status": {
                "$ref": "#/definitions/ReconciliationSourceStatusV1"
              }
            },
            "required": [
              "health",
              "scope",
              "source",
              "status"
            ],
            "type": "object"
          },
          "ReconciliationSourceV1": {
            "enum": [
              "dependabot",
              "code_scanning"
            ],
            "type": "string"
          },
          "SeverityV1": {
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "info"
            ],
            "type": "string"
          }
        },
        "properties": {
          "github_repository": {
            "type": [
              "string",
              "null"
            ]
          },
          "harness": {
            "$ref": "#/definitions/HarnessReconciliationSummaryV1"
          },
          "matching": {
            "$ref": "#/definitions/ReconciliationMatchingV1"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "records": {
            "items": {
              "$ref": "#/definitions/ReconciliationAlertV1"
            },
            "type": "array"
          },
          "repository": {
            "type": "string"
          },
          "run_id": {
            "type": "string"
          },
          "schema_version": {
            "type": "string"
          },
          "sources": {
            "items": {
              "$ref": "#/definitions/ReconciliationSourceSummaryV1"
            },
            "type": "array"
          },
          "target_sha": {
            "type": "string"
          }
        },
        "required": [
          "harness",
          "matching",
          "records",
          "repository",
          "run_id",
          "schema_version",
          "sources",
          "target_sha"
        ],
        "title": "SecurityScanReconciliationResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Scan a repository for security issues, report-only, at one commit. Pass an exact 40-character target_sha, or omit it to review HEAD. Duplicate repository, commit, mode and model requests return the same run id.",
      "metadata": {},
      "name": "security-scan::request",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "ScanModeV1": {
            "enum": [
              "scan",
              "suggest"
            ],
            "type": "string"
          }
        },
        "properties": {
          "mode": {
            "$ref": "#/definitions/ScanModeV1"
          },
          "model": {
            "description": "Catalog model id from the Console composer. Omitted requests use operator `analysis.model`.",
            "type": [
              "string",
              "null"
            ]
          },
          "provider": {
            "description": "Optional explicit provider. Omitted when `model` is a catalog id such as `deepseek::…`.",
            "type": [
              "string",
              "null"
            ]
          },
          "repository": {
            "type": "string"
          },
          "target_sha": {
            "default": "",
            "description": "Exact 40-character commit SHA. Omit or leave empty to analyze the entire repository at HEAD.",
            "type": "string"
          }
        },
        "required": [
          "mode",
          "repository"
        ],
        "title": "SecurityScanRequestV1",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "additionalProperties": false,
        "definitions": {
          "RunStatusV1": {
            "enum": [
              "queued",
              "materializing",
              "materialized",
              "dispatching",
              "analyzing",
              "completed",
              "failed",
              "cancelling",
              "cancelled"
            ],
            "type": "string"
          }
        },
        "properties": {
          "deduplicated": {
            "type": "boolean"
          },
          "run_id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/definitions/RunStatusV1"
          }
        },
        "required": [
          "deduplicated",
          "run_id",
          "status"
        ],
        "title": "SecurityScanResponseV1",
        "type": "object"
      }
    },
    {
      "description": "Serve the security-scan worker's injected console UI assets (content function for its console:script / console:style triggers).",
      "metadata": {
        "internal": true
      },
      "name": "security-scan::ui-content",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Input of the content function: the console asks for one asset by path.",
        "properties": {
          "path": {
            "description": "The asset path from the trigger config (e.g. `state/page.js`).",
            "type": "string"
          }
        },
        "required": [
          "path"
        ],
        "title": "UiContentInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Output of the content function.",
        "properties": {
          "content": {
            "description": "The asset source, verbatim.",
            "type": "string"
          },
          "content_type": {
            "description": "MIME type the console should serve the asset with.",
            "type": "string"
          }
        },
        "required": [
          "content",
          "content_type"
        ],
        "title": "UiContentResult",
        "type": "object"
      }
    }
  ],
  "triggers": []
}
```
