# io.atcr.hold.scan

> Published by [did:web:lexicon.atcr.io](https://lexicon.garden/identity/did:web:lexicon.atcr.io)

✓ This is the authoritative definition for this NSID.

## Links

- [View on Lexicon Garden](https://lexicon.garden/lexicon/did:web:lexicon.atcr.io/io.atcr.hold.scan)
- [Documentation](https://lexicon.garden/lexicon/did:web:lexicon.atcr.io/io.atcr.hold.scan/docs)
- [Examples](https://lexicon.garden/lexicon/did:web:lexicon.atcr.io/io.atcr.hold.scan/examples)

## Definitions

### `io.atcr.hold.scan`

**Type**: `record`

Vulnerability scan results for a container manifest. Stored in the hold's embedded PDS. Record key is deterministic: the manifest digest hex without the 'sha256:' prefix, so re-scans upsert the existing record.

**Key**: `any`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `critical` | `integer` | Yes | Count of critical severity vulnerabilities |
| `high` | `integer` | Yes | Count of high severity vulnerabilities |
| `low` | `integer` | Yes | Count of low severity vulnerabilities |
| `manifest` | `string` (at-uri) | Yes | AT-URI of the scanned manifest (e.g., at://did:plc:xyz/io.atcr.manifest/abc123...) |
| `medium` | `integer` | Yes | Count of medium severity vulnerabilities |
| `reason` | `string` | No | Optional human-readable explanation for non-ok status (e.g. 'unscannable artifact type application/vnd.cncf.helm.config.v1+json'). |
| `repository` | `string` | Yes | Repository name (e.g., myapp) |
| `sbomBlob` | `blob` | No | SBOM blob (SPDX JSON format) uploaded to the hold's blob storage |
| `scannedAt` | `string` (datetime) | Yes | RFC3339 timestamp of when the scan completed |
| `scannerVersion` | `string` | Yes | Version of the scanner that produced this result (e.g., atcr-scanner-v1.0.0) |
| `status` | `string` | No | Outcome of the scan attempt. 'ok' (or omitted, for back-compat) means the scanner produced an SBOM. 'failed' means the scanner ran but errored. 'skipped' means the scanner intentionally bypassed this artifact type (e.g. helm charts). |
| `total` | `integer` | Yes | Total vulnerability count |
| `userDid` | `string` (did) | Yes | DID of the image owner |
| `vulnReportBlob` | `blob` | No | Grype vulnerability report blob (JSON) with full CVE details |

## Raw Schema

```json
{
  "$type": "com.atproto.lexicon.schema",
  "defs": {
    "main": {
      "description": "Vulnerability scan results for a container manifest. Stored in the hold's embedded PDS. Record key is deterministic: the manifest digest hex without the 'sha256:' prefix, so re-scans upsert the existing record.",
      "key": "any",
      "record": {
        "properties": {
          "critical": {
            "description": "Count of critical severity vulnerabilities",
            "minimum": 0,
            "type": "integer"
          },
          "high": {
            "description": "Count of high severity vulnerabilities",
            "minimum": 0,
            "type": "integer"
          },
          "low": {
            "description": "Count of low severity vulnerabilities",
            "minimum": 0,
            "type": "integer"
          },
          "manifest": {
            "description": "AT-URI of the scanned manifest (e.g., at://did:plc:xyz/io.atcr.manifest/abc123...)",
            "format": "at-uri",
            "type": "string"
          },
          "medium": {
            "description": "Count of medium severity vulnerabilities",
            "minimum": 0,
            "type": "integer"
          },
          "reason": {
            "description": "Optional human-readable explanation for non-ok status (e.g. 'unscannable artifact type application/vnd.cncf.helm.config.v1+json').",
            "maxLength": 256,
            "type": "string"
          },
          "repository": {
            "description": "Repository name (e.g., myapp)",
            "maxLength": 256,
            "type": "string"
          },
          "sbomBlob": {
            "accept": [
              "application/spdx+json"
            ],
            "description": "SBOM blob (SPDX JSON format) uploaded to the hold's blob storage",
            "type": "blob"
          },
          "scannedAt": {
            "description": "RFC3339 timestamp of when the scan completed",
            "format": "datetime",
            "type": "string"
          },
          "scannerVersion": {
            "description": "Version of the scanner that produced this result (e.g., atcr-scanner-v1.0.0)",
            "maxLength": 64,
            "type": "string"
          },
          "status": {
            "description": "Outcome of the scan attempt. 'ok' (or omitted, for back-compat) means the scanner produced an SBOM. 'failed' means the scanner ran but errored. 'skipped' means the scanner intentionally bypassed this artifact type (e.g. helm charts).",
            "knownValues": [
              "ok",
              "failed",
              "skipped"
            ],
            "maxLength": 32,
            "type": "string"
          },
          "total": {
            "description": "Total vulnerability count",
            "minimum": 0,
            "type": "integer"
          },
          "userDid": {
            "description": "DID of the image owner",
            "format": "did",
            "type": "string"
          },
          "vulnReportBlob": {
            "accept": [
              "application/vnd.atcr.vulnerabilities+json"
            ],
            "description": "Grype vulnerability report blob (JSON) with full CVE details",
            "type": "blob"
          }
        },
        "required": [
          "manifest",
          "repository",
          "userDid",
          "critical",
          "high",
          "medium",
          "low",
          "total",
          "scannerVersion",
          "scannedAt"
        ],
        "type": "object"
      },
      "type": "record"
    }
  },
  "id": "io.atcr.hold.scan",
  "lexicon": 1
}
```
