diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 0b69c6d7..ff8bc862 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -141,6 +141,17 @@ jobs: shell: pwsh run: ./scripts/sbom-validate-check.ps1 -SelfCheck + # Blocking C06 L33 Socket.dev posture evidence — hermetic SelfCheck only. + # Does not call Socket API or claim live org GitHub App install. + socket-posture: + name: Socket posture SelfCheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + - name: Socket.dev posture + in-repo dependency scan anchors + shell: pwsh + run: ./scripts/socket-posture-check.ps1 -SelfCheck + # Blocking C06 L53 protected-environment checklist — hermetic SelfCheck only. # Does not claim live GitHub Environment wiring or full SLSA Build L3 attestation. slsa-protected-env: diff --git a/CHANGELOG.md b/CHANGELOG.md index c0923c68..5a958201 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/); versioning is [SemVer]( ### Added +- Socket.dev supply-chain posture (C06 L33): `docs/ops/socket-posture.md`, `socket-posture.json`, `scripts/socket-posture-check.ps1 -SelfCheck`, blocking `security.yml` job, `tests/socket_posture.rs`. + - Wave-43 scope (396/402): consolidated `WAVE43_SCOPE.md` + `docs/ops/WAVE43_PERT.md` — five parallel carry-forward lanes (`w43-daemon-graph-hard`, `w43-jemalloc-default-on`, `w43-load-macro-gate`, `w43-sl-viewer-help`, `w43-socket-posture`) from Wave-42 deferred gaps. - Blocking alloc-profile / dhat PR gate (C00 L8): `.github/workflows/alloc-profile-hard.yml`, expanded `alloc-profile-check.ps1 -SelfCheck` anchors, `tests/alloc_profile_hard.rs` (soft `ops-load` job retained). diff --git a/SECURITY.md b/SECURITY.md index c490f4c3..fd76a048 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -71,6 +71,7 @@ Critical supply-chain or remote-code issues may be accelerated at maintainer dis - Advisory scanning: `cargo audit` job in .github/workflows/security.yml. - SBOM upload: qgate uploads `sbom-cyclonedx` artifact from `target/sbom.cdx.json`. - CVE feed subscription (GHSA + OSV + NVD): maintainer process and evidence links in [`docs/ops/cve-feed-subscription.md`](docs/ops/cve-feed-subscription.md). Hermetic proof: `pwsh ./scripts/cve-feed-check.ps1 -SelfCheck`. +- Socket.dev supply-chain posture (optional org GitHub App complement): policy SSOT in [`docs/ops/socket-posture.md`](docs/ops/socket-posture.md). Hermetic proof: `pwsh ./scripts/socket-posture-check.ps1 -SelfCheck`. Live org install is **not** attestable from this repository. ## Cryptography inventory diff --git a/docs/ops/cve-feed-subscription.md b/docs/ops/cve-feed-subscription.md index f4576e64..20462ceb 100644 --- a/docs/ops/cve-feed-subscription.md +++ b/docs/ops/cve-feed-subscription.md @@ -9,6 +9,7 @@ This document does **not** claim maintainer 2FA enforcement (L36) — org-level 2FA is not verifiable from checkout (see [`branch-protection.md`](branch-protection.md)). Related: [`SECURITY.md`](../../SECURITY.md) (reporting + supply-chain policy), +[`socket-posture.md`](socket-posture.md) (optional Socket.dev complement), [`deny.toml`](../../deny.toml) (cargo-deny advisories), [`.github/workflows/security.yml`](../../.github/workflows/security.yml) (cargo-deny + cargo-audit), [`.github/dependabot.yml`](../../.github/dependabot.yml), diff --git a/docs/ops/socket-posture.json b/docs/ops/socket-posture.json new file mode 100644 index 00000000..39c93c05 --- /dev/null +++ b/docs/ops/socket-posture.json @@ -0,0 +1,21 @@ +{ + "schema_version": 1, + "purpose": "Socket.dev supply-chain telemetry posture (C06 L33 complement to cargo-deny/audit)", + "github_app": "Socket Security for GitHub", + "expected_pr_checks": [ + "Socket Security: Pull Request Alerts", + "Socket Security: Project Report" + ], + "in_repo_blocking": [ + "cargo deny check", + "cargo audit", + "gitleaks", + "trufflehog" + ], + "verify_command": "./scripts/socket-posture-check.ps1 -SelfCheck", + "not_verifiable_in_repo": [ + "Socket.dev org API token", + "GitHub App install state", + "Live dashboard policy thresholds" + ] +} diff --git a/docs/ops/socket-posture.md b/docs/ops/socket-posture.md new file mode 100644 index 00000000..a150b220 --- /dev/null +++ b/docs/ops/socket-posture.md @@ -0,0 +1,72 @@ +# Socket.dev supply-chain posture + +Status: **C06 L33** complement — documents how SessionLedger aligns with +[Socket.dev](https://socket.dev) dependency-risk telemetry **alongside** in-repo +blocking scans (`cargo deny`, `cargo audit`, gitleaks, TruffleHog). Machine +proof: `pwsh ./scripts/socket-posture-check.ps1 -SelfCheck`. + +Policy manifest: [`socket-posture.json`](socket-posture.json). + +Related: [`SECURITY.md`](../../SECURITY.md), +[`cve-feed-subscription.md`](cve-feed-subscription.md), +[`.github/workflows/security.yml`](../../.github/workflows/security.yml), +[`deny.toml`](../../deny.toml). + +## Layered controls + +| Layer | Role | Evidence in this repo | +|-------|------|------------------------| +| **In-repo blocking** | Primary PR gate for Rust advisories + secrets | `cargo-deny`, `cargo-audit`, gitleaks, TruffleHog in `security.yml` | +| **GHSA/OSV/NVD feeds** | Maintainer triage beyond a single bot | [`cve-feed-subscription.md`](cve-feed-subscription.md) | +| **Socket.dev (optional org)** | Supply-chain behavior scoring on PRs when the GitHub App is installed | Expected check names in `socket-posture.json`; **not** enforced by this SelfCheck | + +SessionLedger does **not** embed a Socket API token or run Socket CLI in CI. +When the org installs the Socket GitHub App, PRs may show **Socket Security: Pull Request Alerts** and **Project Report** checks (third-party; informational unless branch protection requires them). + +## Maintainer process + +### 1. Keep in-repo scans green (required) + +Every PR / push to `main` runs [`.github/workflows/security.yml`](../../.github/workflows/security.yml): + +- `cargo deny check` — advisory policy from [`deny.toml`](../../deny.toml) +- `cargo audit` — RustSec / GHSA-backed advisories +- gitleaks + TruffleHog — secret scanning + +Triage failures before merging; do not rely on Socket alone to waive deny/audit hits. + +### 2. Optional Socket.dev GitHub App (org) + +1. Org admin installs **Socket Security for GitHub** on `KooshaPari/SessionLedger`. +2. Confirm PR checks include the names listed in [`socket-posture.json`](socket-posture.json). +3. Review Socket alerts as **supplementary** signal (typosquatting, install scripts, + risky postinstall) — cross-check against `cargo deny` / `cargo audit` before merge. +4. Record org install date in a maintainer note if branch protection later requires Socket checks. + +### 3. When Socket is absent + +In-repo `cargo-deny`, `cargo-audit`, Renovate/Dependabot, and +[`cve-feed-subscription.md`](cve-feed-subscription.md) remain the authoritative +dependency-risk surface. Absence of Socket checks is **not** a merge blocker +unless org policy adds them to required checks. + +## SelfCheck gate + +| Check | Status | +|-------|--------| +| `socket-posture-check.ps1 -SelfCheck` | **done** — docs + `security.yml` anchor | +| Live Socket API / org install | **NOT_VERIFIABLE_IN_REPO** | + +Hermetic SelfCheck validates policy doc anchors, `SECURITY.md` cross-link, +`security.yml` job wiring, and complementary scan evidence paths. No network, +no Socket API token, no false claim of live org install. + +## Done / unpaid + +| Item | Status | +|------|--------| +| Policy SSOT + JSON manifest | **done** | +| Blocking `security.yml` SelfCheck job | **done** | +| `tests/socket_posture.rs` cargo wrapper | **done** | +| Live Socket org API automation | **unpaid** — creds / org | +| Branch-protection required Socket checks | **unpaid** — human Settings | diff --git a/scripts/socket-posture-check.ps1 b/scripts/socket-posture-check.ps1 new file mode 100644 index 00000000..4e6c6ca3 --- /dev/null +++ b/scripts/socket-posture-check.ps1 @@ -0,0 +1,166 @@ +<# +.SYNOPSIS + Machine-check Socket.dev supply-chain posture doc anchors (C06 L33). + +.DESCRIPTION + Verifies docs/ops/socket-posture.md + socket-posture.json document Socket.dev + as an optional org GitHub App complement to in-repo cargo-deny / cargo-audit / + gitleaks / TruffleHog. Cross-checks SECURITY.md, security.yml job wiring, and + CVE feed subscription links. + Hermetic: no network, no Socket API token, no org install attestation. + +.PARAMETER SelfCheck + Explicit docs/path smoke (CI unit proof). Same checks as the default path. + +.EXAMPLE + pwsh ./scripts/socket-posture-check.ps1 -SelfCheck +#> +[CmdletBinding()] +param( + [switch]$SelfCheck +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +$repoRoot = Split-Path -Parent $PSScriptRoot +$docPath = Join-Path $repoRoot "docs/ops/socket-posture.md" +$policyPath = Join-Path $repoRoot "docs/ops/socket-posture.json" +$cveFeedPath = Join-Path $repoRoot "docs/ops/cve-feed-subscription.md" +$securityPath = Join-Path $repoRoot "SECURITY.md" +$denyPath = Join-Path $repoRoot "deny.toml" +$securityWorkflow = Join-Path $repoRoot ".github/workflows/security.yml" +$selfPath = Join-Path $repoRoot "scripts/socket-posture-check.ps1" +$wrapperTest = Join-Path $repoRoot "tests/socket_posture.rs" + +function Assert-File { + param( + [Parameter(Mandatory = $true)][string]$Path, + [Parameter(Mandatory = $true)][string]$Label + ) + if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { + throw "Missing $Label at '$Path'." + } +} + +function Write-Check { + param([string]$Label, [bool]$Ok) + $mark = if ($Ok) { "PASS" } else { "FAIL" } + Write-Host " [$mark] $Label" + return $Ok +} + +function Test-DocContains { + param( + [Parameter(Mandatory = $true)][string]$Doc, + [Parameter(Mandatory = $true)][string]$Needle, + [Parameter(Mandatory = $true)][string]$Label, + [string]$Context = "docs/ops/socket-posture.md" + ) + $ok = $Doc.Contains($Needle) + [void](Write-Check -Label $Label -Ok $ok) + if (-not $ok) { + throw "$Context missing required anchor: '$Needle'" + } +} + +Write-Host "Socket.dev posture check (C06 L33)" +if ($SelfCheck) { + Write-Host "Mode: SelfCheck (docs + evidence paths; no Socket API / no network)" +} + +Assert-File -Path $docPath -Label "socket posture doc" +Assert-File -Path $policyPath -Label "socket posture policy JSON" +Assert-File -Path $cveFeedPath -Label "CVE feed subscription doc" +Assert-File -Path $securityPath -Label "SECURITY.md" +Assert-File -Path $denyPath -Label "deny.toml" +Assert-File -Path $securityWorkflow -Label "security workflow" +Assert-File -Path $selfPath -Label "socket posture check script" +Assert-File -Path $wrapperTest -Label "socket_posture.rs test wrapper" + +$doc = Get-Content -LiteralPath $docPath -Raw +$policy = Get-Content -LiteralPath $policyPath -Raw +$cveFeed = Get-Content -LiteralPath $cveFeedPath -Raw +$security = Get-Content -LiteralPath $securityPath -Raw +$securityWf = Get-Content -LiteralPath $securityWorkflow -Raw + +Write-Host "Socket posture doc anchors:" +Test-DocContains -Doc $doc -Needle "Socket.dev supply-chain posture" ` + -Label "doc heading" +Test-DocContains -Doc $doc -Needle "**C06 L33**" ` + -Label "C06 L33 status" +Test-DocContains -Doc $doc -Needle "## Layered controls" ` + -Label "layered controls heading" +Test-DocContains -Doc $doc -Needle "Socket Security: Pull Request Alerts" ` + -Label "expected PR check name" +Test-DocContains -Doc $doc -Needle "NOT_VERIFIABLE_IN_REPO" ` + -Label "org install disclaimer" +Test-DocContains -Doc $doc -Needle "socket-posture-check.ps1" ` + -Label "SelfCheck script reference" +Test-DocContains -Doc $doc -Needle "socket-posture-check.ps1 -SelfCheck" ` + -Label "SelfCheck gate marked done" +Test-DocContains -Doc $doc -Needle "cargo deny" ` + -Label "cargo-deny named" +Test-DocContains -Doc $doc -Needle "cargo audit" ` + -Label "cargo-audit named" +Test-DocContains -Doc $doc -Needle "cve-feed-subscription.md" ` + -Label "CVE feed cross-link" + +Write-Host "Policy JSON anchors:" +Test-DocContains -Doc $policy -Needle '"schema_version"' ` + -Label "schema_version" -Context "socket-posture.json" +Test-DocContains -Doc $policy -Needle "Socket Security: Pull Request Alerts" ` + -Label "expected PR checks" -Context "socket-posture.json" +Test-DocContains -Doc $policy -Needle "socket-posture-check.ps1" ` + -Label "verify_command" -Context "socket-posture.json" + +Write-Host "Evidence path anchors:" +Test-DocContains -Doc $security -Needle "docs/ops/socket-posture.md" ` + -Label "SECURITY.md -> socket-posture" -Context "SECURITY.md" +Test-DocContains -Doc $security -Needle "socket-posture-check.ps1" ` + -Label "SECURITY.md SelfCheck reference" -Context "SECURITY.md" + +if ($securityWf -notmatch 'socket-posture-check\.ps1') { + throw "security.yml missing socket-posture-check.ps1 SelfCheck job." +} +[void](Write-Check -Label "security.yml socket-posture SelfCheck" -Ok $true) + +if ($securityWf -notmatch 'cargo-deny') { + throw "security.yml missing cargo-deny job/reference." +} +[void](Write-Check -Label "security.yml cargo-deny" -Ok $true) + +if ($securityWf -notmatch 'cargo.?audit') { + throw "security.yml missing cargo-audit job/reference." +} +[void](Write-Check -Label "security.yml cargo-audit" -Ok $true) + +if ($securityWf -notmatch 'gitleaks') { + throw "security.yml missing gitleaks job/reference." +} +[void](Write-Check -Label "security.yml gitleaks" -Ok $true) + +if ($securityWf -notmatch 'trufflehog') { + throw "security.yml missing trufflehog job/reference." +} +[void](Write-Check -Label "security.yml trufflehog" -Ok $true) + +if ($cveFeed -notmatch 'socket-posture') { + throw "cve-feed-subscription.md should cross-link socket-posture (complementary feeds)." +} +[void](Write-Check -Label "cve-feed socket cross-link" -Ok $true) + +$summary = @" +## Socket posture SelfCheck + +SelfCheck passed: ``docs/ops/socket-posture.md`` anchors, ``socket-posture.json`` +manifest, SECURITY.md cross-link, and complementary cargo-deny / cargo-audit / +gitleaks / TruffleHog evidence. Does not claim live Socket.org install. +"@ + +if ($env:GITHUB_STEP_SUMMARY) { + $summary | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append -Encoding utf8 +} + +Write-Host "Socket posture SelfCheck passed (C06 L33; no Socket API / no org install claim)." +exit 0 diff --git a/tests/socket_posture.rs b/tests/socket_posture.rs new file mode 100644 index 00000000..c5fe5b66 --- /dev/null +++ b/tests/socket_posture.rs @@ -0,0 +1,32 @@ +//! Hermetic `SelfCheck` for Socket.dev supply-chain posture (C06 L33). +//! +//! Local: `pwsh ./scripts/socket-posture-check.ps1 -SelfCheck` + +use std::path::PathBuf; +use std::process::Command; + +fn repo_root() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) +} + +#[test] +fn socket_posture_self_check_validates_policy_and_anchors() { + let script = repo_root().join("scripts/socket-posture-check.ps1"); + assert!(script.is_file(), "expected socket posture script at {}", script.display()); + + let output = Command::new("pwsh") + .args(["-NoProfile", "-File", script.to_str().expect("utf-8 script path"), "-SelfCheck"]) + .output() + .unwrap_or_else(|error| panic!("failed to spawn pwsh for SelfCheck: {error}")); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + output.status.success(), + "socket-posture-check.ps1 -SelfCheck failed\nstdout:\n{stdout}\nstderr:\n{stderr}" + ); + assert!( + stdout.contains("Socket posture SelfCheck passed"), + "expected SelfCheck success line, got:\n{stdout}" + ); +}