# Multi-Step Policy Architecture

## Workflow Steps

```
Step 1: pip-install
  Attestors: command-run (trace), pip-install, environment, 
             omnitrail, secretscan, sbom, slsa, material, product
  Output: install behavior (processes, files, network, packages)

Step 2: pip-import  
  Attestors: command-run (trace), environment, omnitrail
  AttestationsFrom: [pip-install]  ← can reference Step 1 data
  Output: import behavior (what happens on `import package`)
```

## Policy Structure

```json
{
  "steps": {
    "pip-install": {
      "name": "pip-install",
      "functionaries": [{"type": "publickey", "publickeyid": "..."}],
      "attestations": [
        {"type": "command-run/v0.1", "regopolicies": [
          "network_exfiltration", "credential_harvesting", "code_execution",
          "persistence", "attack_sequences", "pth_injection",
          "teampcp_ioc", "openclaw_ioc", "container_escape"
        ]},
        {"type": "pip-install/v0.1", "regopolicies": [
          "supply_chain", "package_signing", "dependency_confusion",
          "release_integrity"
        ]},
        {"type": "omnitrail/v0.1"},
        {"type": "secretscan/v0.1"},
        {"type": "sbom/v0.1"},
        {"type": "slsa/v1.0"},
        {"type": "environment/v0.1"},
        {"type": "material/v0.1"},
        {"type": "product/v0.1"}
      ]
    },
    "pip-import": {
      "name": "pip-import",
      "functionaries": [{"type": "publickey", "publickeyid": "..."}],
      "attestationsFrom": ["pip-install"],
      "attestations": [
        {"type": "command-run/v0.1", "regopolicies": [
          "import_behavior"
        ]},
        {"type": "environment/v0.1"},
        {"type": "omnitrail/v0.1"}
      ]
    }
  }
}
```

## Cross-Step Rego Policy: import_behavior.rego

```rego
package pip_witness.import_behavior

# This policy runs against the pip-import step's command-run attestation
# It can reference pip-install step data via input.steps["pip-install"]

import rego.v1

# CRITICAL: Import makes network connections not seen during install
deny[msg] if {
    # Get install-time connections
    install_conns := {sprintf("%s:%d", [c.address, c.port]) |
        some proc in input.steps["pip-install"]["command-run"].processes
        some c in proc.network.connections
        c.family == "AF_INET"}
    
    # Get import-time connections
    some proc in input.processes
    some c in proc.network.connections
    c.family == "AF_INET"
    c.port != 53
    conn_key := sprintf("%s:%d", [c.address, c.port])
    not conn_key in install_conns
    
    msg := sprintf("CRITICAL:IMPORT_NEW_CONNECTION: Import connected to %s:%d which was NOT contacted during install — import-time C2/exfil", [c.address, c.port])
}

# HIGH: Import opens files not opened during install
deny[msg] if {
    install_files := {f |
        some proc in input.steps["pip-install"]["command-run"].processes
        some f, _ in proc.openedfiles}
    
    some proc in input.processes
    some f, _ in proc.openedfiles
    not f in install_files
    is_sensitive(f)
    
    msg := sprintf("CRITICAL:IMPORT_CRED_ACCESS: Import accessed %s which was NOT accessed during install — import-time credential theft", [f])
}

is_sensitive(f) if { contains(f, ".ssh/") }
is_sensitive(f) if { contains(f, ".aws/") }
is_sensitive(f) if { contains(f, ".kube/config") }

# HIGH: Import spawns processes not seen during install
deny[msg] if {
    install_progs := {p.program |
        some p in input.steps["pip-install"]["command-run"].processes}
    
    some proc in input.processes
    proc.program != ""
    not proc.program in install_progs
    not contains(proc.program, "python")
    
    msg := sprintf("HIGH:IMPORT_NEW_PROCESS: Import spawned %s which was NOT seen during install", [proc.program])
}

# MEDIUM: Import has more syscall events than install
# (should be simpler — just loading modules, not downloading/building)
deny[msg] if {
    import_events := count([e |
        some proc in input.processes
        some e in proc.syscallEvents])
    import_events > 0
    
    msg := sprintf("MEDIUM:IMPORT_SYSCALL_EVENTS: Import triggered %d security-relevant syscall events", [import_events])
}

# HIGH: OmniTrail shows files changed between install and import
# (no files should change during import — it's read-only)
deny[msg] if {
    install_trail := input.steps["pip-install"]["omnitrail"]
    import_trail := input.omnitrail  # from this step
    # Compare file hashes — any difference means import modified files
    # (implementation depends on omnitrail data structure)
    msg := "HIGH:IMPORT_FILE_MUTATION: Files changed during import — imports should be read-only"
}
```

## What Each New Attestor Gives Us

### omnitrail (PreMaterialRunType)
Captures BEFORE the command runs:
- Full file tree with SHA1, SHA256, gitoid hashes
- File permissions, ownership, timestamps
- Used in BOTH steps → diff between install and import shows what changed

Cross-attestation value:
- Install omnitrail vs import omnitrail = what import modified
- Install omnitrail vs product = what install created

### secretscan (PostProductRunType)  
Scans installed files for secrets using Gitleaks patterns:
- API keys, tokens, passwords in source code
- AWS access keys, private keys
- Database connection strings

Policy value:
- Flag packages that ship hardcoded credentials
- Could indicate a compromised package leaking its own secrets

### sbom (PostProductRunType)
CycloneDX SBOM of everything installed:
- Every component with version, license, purl
- Dependency tree relationships
- Vulnerability references

Policy value:
- SSDF compliance (EO 14028)
- Cross-reference with VEX/OSV for known vulns
- License compliance checking

### slsa (PostProductRunType)
SLSA v1.0 provenance document:
- buildDefinition (what was built)
- runDetails (how it was built)
- Formal SLSA level claim

Policy value:
- Formal L1 provenance claim
- Interoperable with other SLSA tools (cosign, etc.)
```
