Skip to main content

presidio-hardened-scoutsuite

CI CodeQL License: MIT Python 3.10+

A Presidio security-hardened distribution of ScoutSuite, NCC Group's multi-cloud security auditing tool.

Wrapper, not a fork. ScoutSuite is GPL-2.0. Rather than fork or import it, this project drives the upstream scout CLI out of process and wraps it with hardened defaults, report redaction, supply-chain integrity, and a least-privilege deployment model. That keeps the wrapper a separate, non-derivative work — so this code is MIT — while you still run stock, trusted-upstream ScoutSuite underneath. See LICENSES/README.md.

AWS only. The hardened runtime audits AWS accounts. The Azure, GCP, Alibaba Cloud and Oracle Cloud SDKs that ScoutSuite pulls in carried advisories with no fix that ScoutSuite's exact pins allow, so the bundled runtime leaves them out and presidio-scout refuses those providers with exit code 2 (ADR-0002). To audit another cloud, run upstream ScoutSuite directly. The offline report tools (findings, compliance, evidence, SARIF/ASFF export) still read reports from any provider.


What "hardened" means here

Axis What you get
Runtime credential & data safety Env scrubbed to cloud creds only; 0700 report dir + umask 0077; secrets redacted out of the report and ScoutSuite's logs; --fail-on-secret gate
Report integrity & isolation Strict CSP + Subresource Integrity on local report assets; remote-reference detection (--fail-on-remote-ref); a signed-able SHA-256 integrity manifest verified offline with presidio-scout-verify
Secure-by-default policy Curated, CIS-aligned AWS baseline applied by default (high-impact IAM/logging/network/storage controls forced to danger)
Supply-chain & build integrity Hash-pinned requirements.lock, pinned build backend, CycloneDX SBOM, CodeQL, Dependabot, cosign-signed images + SLSA build provenance, reproducible wheel/sdist, a presidio-scout-verify-provenance policy gate for what you pull, a fail-closed preflight that the scout you run is the pinned, vetted ScoutSuite version, and a release vulnerability-scan gate (pip-audit + Trivy + presidio-scout-vuln-gate) with signed SBOM/provenance attestations
Hardened deployment Distroless, non-root, --read-only container; bundled least-privilege AWS audit role (read-only + explicit Deny, MFA + ExternalId trust); hardened Kubernetes Job/CronJob + Helm chart (workload identity, read-only rootfs, dropped caps, seccomp, default-deny NetworkPolicy)

How it works

presidio-scout aws ──▶ launcher ──▶ [ scout aws … ] ──▶ redact ──▶ report_guard ──▶ report/
                       (validate     (subprocess;       (scrub      (CSP + integrity
                        + harden)     env-scoped)        secrets)     manifest)
  1. launcher — validates the provider + pass-through flags (fail-closed allowlist), forces --no-browser and a locked-down --report-dir, wires in the curated ruleset, and scrubs the environment.
  2. preflight — before any credentials are handed over, a fail-closed gate confirms the scout on PATH is the pinned, vetted ScoutSuite version; an unexpected/modified ScoutSuite is refused (override: --allow-unverified-scout).
  3. subprocess — stock ScoutSuite runs with only the cloud credentials it needs.
  4. redact — credentials are scrubbed out of the report files and ScoutSuite's own output.
  5. report_guard — a strict CSP and per-asset Subresource Integrity hashes are injected into the HTML report, network-reaching references are flagged, and a SHA-256 integrity manifest (presidio-report-manifest.json, optionally HMAC-signed) is written so the report can be verified later with presidio-scout-verify.

Install

The wrapper has effectively no runtime dependencies (only the tiny tomli TOML parser, and only on Python < 3.11; 3.11+ uses the stdlib) and never imports ScoutSuite. You supply scout yourself (recommended: a pinned virtualenv or the container).

pip install presidio-hardened-scoutsuite          # wrapper only (MIT)

# Convenience extra that also installs ScoutSuite (GPL-2.0) into your env:
pip install 'presidio-hardened-scoutsuite[scoutsuite]'

# Optional extra for Ed25519-signed evidence (--evidence-out); HMAC needs no extra:
pip install 'presidio-hardened-scoutsuite[crypto]'

Or use the hardened container (bundles a pinned ScoutSuite):

docker run --rm --read-only --tmpfs /tmp \
  -e AWS_PROFILE=auditor \
  -v "$HOME/.aws:/tmp/.aws:ro" \
  -v "$PWD/scoutsuite-report:/report" \
  ghcr.io/presidio-v/presidio-hardened-scoutsuite:latest \
  aws --report-dir /report

CLI usage

presidio-scout aws                          # audit AWS with the hardened defaults
presidio-scout aws --report-dir ./out       # choose the (0700) report directory
presidio-scout aws -- --profile auditor     # pass-through flags after '--' (allowlisted)
presidio-scout aws --fail-on-secret         # non-zero exit if a secret survives redaction
presidio-scout aws --fail-on-remote-ref     # non-zero exit if the report references a remote resource
presidio-scout aws --fail-on-finding danger # exit 4 if any flagged finding is danger (gate a pipeline)
presidio-scout aws --sarif results.sarif    # also emit SARIF for GitHub code scanning
presidio-scout aws --waivers waivers.json --fail-on-finding danger  # suppress accepted findings
presidio-scout aws --attest run.intoto.json # emit a signed-able run attestation
presidio-scout aws --evidence-out evidence.json # emit signed evidence for clean controls (peer-verifiable)
presidio-scout aws --require-short-lived-creds  # refuse to run with long-lived static keys
presidio-scout --profile nightly            # apply org defaults from .presidio-scout.toml
presidio-scout aws --no-baseline            # use ScoutSuite's default ruleset instead
presidio-scout aws --allow-unverified-scout # run even if scout isn't the pinned version (warns)
presidio-scout aws --dry-run                # print the hardened command, run nothing
presidio-scout gcp                          # refused, exit 2: the runtime is AWS-only (ADR-0002)

The curated, manifest-verified AWS baseline is applied by default. azure, gcp, aliyun and oci are refused with exit code 2 and the reason; see ADR-0002.

Anything after -- is forwarded to ScoutSuite only if it's on the pass-through allowlist (--profile, --region(s), --services, --skip, --max-rate, …). --result-format accepts only json: ScoutSuite's sqlite format loads results through sqlitedict's pickle (PYSEC-2026-1939, unfixed). Flags the launcher owns (--report-dir, --ruleset, --no-browser) and unknown flags are rejected with exit code 2 — a new upstream flag can't silently weaken a run until it's vetted and added.

Exit codes: 0 ok · 2 invalid invocation / scout not found / unverified ScoutSuite · 3 report guard failure (e.g. --fail-on-secret) · 4 findings severity threshold exceeded (--fail-on-finding).


Library usage

from presidio_scoutsuite import build_plan, run, redact_report_dir, guard_report

plan = build_plan("aws", "scoutsuite-report", ruleset="src/presidio_scoutsuite/policy/aws-cis.json")
print(plan.redacted_command())  # scout aws --no-browser --report-dir … --ruleset …

result = run(plan)  # subprocess.CompletedProcess
redact_report_dir(plan.report_dir)  # scrub secrets out of the report
guard = guard_report(plan.report_dir)  # CSP + SRI + write integrity manifest
print(len(guard.manifest), "files;", len(guard.sri_hardened), "SRI-pinned")

Report integrity & verification

Every guarded report carries presidio-report-manifest.json: a SHA-256 over each file plus a self-digest of those hashes. Verify a report offline — no ScoutSuite, no network — at any later point:

presidio-scout-verify ./scoutsuite-report
# ok   verified 214 file(s) in scoutsuite-report

The verifier re-hashes the tree and reports any modified, missing, or added file, and detects edits to the manifest's own recorded hashes. Exit codes: 0 verified · 3 tampered/mismatch · 2 no usable manifest.

Signing. Two layers, both optional and independent of the always-on hashes:

  • HMAC (pipeline integrity). Set PRESIDIO_MANIFEST_HMAC_KEY and the manifest gains an HMAC-SHA256 signature; verification on a host with the same key confirms the manifest came from your pipeline. Symmetric — proves provenance within a trust boundary you control, not non-repudiation.
  • Detached cosign (distribution). For third-party verification, sign the manifest blob out of band exactly as the release pipeline signs images: cosign sign-blob scoutsuite-report/presidio-report-manifest.json.

Beyond the manifest, the guard makes the static report safe to open and fully offline: a strict CSP (default-src 'none', no remote/inline script), Subresource Integrity on every local <script>/stylesheet (the browser refuses a tampered local asset), and detection of any network-reaching reference (--fail-on-remote-ref turns one into a non-zero exit).

Signed run attestation

presidio-scout aws --attest run.intoto.json (or presidio-scout-attest generate) emits an in-toto statement about the run itself: its subject is the report's integrity manifest (by SHA-256), and its predicate records the inputs — provider, the curated ruleset's digest, the verified ScoutSuite version, this wrapper's version, the manifest's content digest, and finding counts. Sign it as a blob for a complete, portable record:

cosign sign-blob run.intoto.json --output-signature run.intoto.json.sig
presidio-scout-attest verify ./scoutsuite-report run.intoto.json  # binding check

This chains the layers: report files → manifest (presidio-scout-verify) → attestation subject (this) → signature. presidio-scout-attest verify confirms the statement still describes the report on disk (subject + manifest digest match); cosign confirms the signature. Together they prove this exact report was produced by this provider, with this ruleset, by this vetted ScoutSuite.


Gating a pipeline on findings

ScoutSuite writes machine-readable results next to the report; the wrapper reads that data (it's data, not ScoutSuite code) and turns the flagged findings (flagged_items > 0) into a severity-ranked model. Use it to fail a pipeline on the audit result — inline during a run, or after the fact:

presidio-scout aws --fail-on-finding danger      # exit 4 if any danger-level finding fired
presidio-scout-findings ./scoutsuite-report      # summarize an existing report
presidio-scout-findings ./scoutsuite-report --fail-on warning --format json
# findings [aws]: 7 flagged (danger=2, warning=5)

Levels rank danger > warning; --fail-on <level> trips on anything at or above it. The gate is fail-closed: if the results data is missing or unparseable it errors (exit 2) rather than passing a report it never evaluated.

Waiving accepted findings

Findings an org has reviewed and consciously accepted are checked in as data — each with a justification, an owner, and a mandatory expiry — instead of being hidden by weakening the ruleset:

{
  "waivers": [
    { "rule": "s3/s3-bucket-world-acl", "resource": "s3.buckets.public-assets",
      "justification": "Public static-site bucket, reviewed in TICKET-123",
      "owner": "web-platform@example.com", "expires": "2026-12-31" }
  ]
}

presidio-scout aws --waivers waivers.json (also on presidio-scout-findings and presidio-scout-export) suppresses matching findings before the gate and SARIF output. Omit resource (or use "*") to waive the whole finding; a resource pattern (fnmatch) waives only those resources and the finding survives with a reduced count if any flagged resource is left unwaived. Fail-closed: a malformed/missing waiver file errors, and an expired waiver stops suppressing — the finding resurfaces (and is reported) — so risk can't be hidden indefinitely.

GitHub code scanning (SARIF)

Export the flagged findings as SARIF 2.1.0 so they surface as code-scanning alerts (tracked and triageable in the Security tab) — inline during a run (--sarif PATH) or from an existing report (presidio-scout-export):

# .github/workflows/cloud-audit.yml (excerpt)
- run: presidio-scout aws --report-dir ./report --sarif results.sarif
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: results.sarif

danger maps to SARIF error (security-severity 8.0 / high), warning to warning (4.0 / medium); each flagged resource becomes a result with a stable fingerprint so GitHub tracks the same alert across runs.

Compliance mapping (CIS / NIST 800-53 / SOC 2)

Express the audit as control failures, not just raw findings. presidio-scout-compliance maps each flagged finding to the controls it breaks across CIS, NIST SP 800-53 Rev. 5, and the SOC 2 Trust Services Criteria, using curated, checked-in mappings (policy/<provider>.controls.json):

presidio-scout-compliance ./report                       # per-control summary
presidio-scout-compliance ./report --framework cis --format json
presidio-scout-compliance ./report --fail-on-unmapped    # exit 4 if a finding has no mapping

The mappings are validated fail-closed against the rule manifest (a mapping that names a rule the pinned ScoutSuite doesn't ship errors in CI), and a flagged finding with no mapping is reported as unmapped rather than silently dropped.

AWS Security Hub (ASFF)

Send findings to Security Hub as ASFF (BatchImportFindings input), enriched with the compliance controls as Compliance.RelatedRequirements — inline (--asff PATH) or from an existing report (presidio-scout-asff):

presidio-scout aws --report-dir ./report \
  --asff findings.asff.json --aws-account-id 123456789012 --aws-region us-east-1
aws securityhub batch-import-findings --findings file://findings.asff.json

danger maps to HIGH (Normalized 70), warning to MEDIUM (40); each flagged resource becomes one ASFF finding with a stable Id.

Emitting signed evidence (family evidence producer)

A clean control — one with no flagged finding for any rule mapped to it — is positive evidence that a security property held at audit time. --evidence-out (or the standalone presidio-scout-evidence) emits a signed evidence envelope (presidio-hardened/evidence-ref@2) that a peer presidio-hardened-* tool — e.g. presidio-hardened-ikigov-assess — verifies fail-closed and uses to affirm its governance checklist. A clean infrastructure audit evidences IKI-Gov item T5 ("a security review of the infrastructure and all dependencies has been conducted and findings addressed"); clean logging/audit-trail controls evidence O5 ("an audit log is maintained, stored immutably, and accessible to authorised auditors"). A claim is signed only when every rule mapped to its item is clean.

# Ed25519 (recommended; needs the optional [crypto] extra):
presidio-scout aws --report-dir ./report --evidence-key ed25519.key --evidence-out evidence.json

# …or HMAC-SHA256 (stdlib only, shared secret), from an existing report:
presidio-scout-evidence emit ./report --alg hmac-sha256 -o evidence.json

# the consumer verifies it against a trust store mapping signer -> key:
presidio-scout-evidence verify --evidence evidence.json --trust trust.json

# re-sign refs written by v0.29.x or earlier (legacy signing message):
presidio-scout-evidence resign --evidence old.json --trust trust.json \
  --key ed25519.key -o evidence.json

Since v0.30.0 the signature is detached over the family evidence contract's signing message: the canonical JSON of {"content_hash", "signer"} (sorted keys, (",", ":") separators, UTF-8, floats and NaN/Infinity rejected). The signer is bound directly; each ref's content_hash commits to the checklist item, the provider, the exact set of clean rules and the report's integrity-manifest digest, tying the claim to one specific report. item_id, ledger_ref and claimed_at are not covered by the signature itself; a verifier that holds the report can re-derive content_hash to bind them. The rule→item map (policy/<provider>.evidence.json) is validated fail-closed against the rule manifest, and the envelope carries only item ids, rule names, digests and signatures — never raw findings. Conformance is tested against the vendored family evidence contract test vectors (tests/vectors/), and the committed interop envelope (tests/interop/) is checked the way the family verifier checks it, so ikigov-assess verifies it byte-for-byte.

Legacy signatures (deprecated). v0.29.x and earlier signed every ref field except signature, which no other family verifier accepts. verify still accepts such a ref, but only after the contract message fails: the ref is marked [legacy signature], a WARNING is logged, and the Python API returns VerifyResult(ok=True, legacy_signature=True) from verify_ref_detailed. resign upgrades those refs. It re-signs only refs that already verify against the trust store, checks each new signature against the signer's trust entry, and never prints the key. Legacy acceptance will be removed in v0.32.0 or 90 days after the v0.30.0 release, whichever is later.

Tracking drift between runs

Gating on the absolute set of findings is noisy when there's a known, already-triaged backlog. presidio-scout-diff compares a baseline report to a current one and reports only what changed — so a pipeline can block regressions while ignoring pre-existing findings:

presidio-scout-diff ./baseline-report ./scoutsuite-report --fail-on-new-finding danger
# drift: +1 new finding(s), +2 new resource(s); -3 resolved finding(s), -0 resolved resource(s)

It diffs at resource granularity, distinguishing a brand-new finding from an existing finding that started flagging an additional resource, and from resolved findings/resources. --fail-on-new-finding {any,warning,danger} exits 4 when a newly flagged occurrence is at or above the chosen severity (--format json for the full structured delta).


Verifying what you pull

Release artifacts carry SLSA build provenance (the container image is cosign-signed with provenance: mode=max; the PyPI wheel ships attestations), and the wheel/sdist are a reproducible function of the source.

A signature only proves an attestation is authentic — you still have to check it says the right thing. presidio-scout-verify-provenance is that policy gate, run after cosign has cryptographically verified the attestation:

# 1. cosign verifies the signature + transparency-log entry (crypto + network)
cosign verify-attestation --type slsaprovenance \
  --certificate-identity-regexp '^https://github.com/presidio-v/.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  ghcr.io/presidio-v/presidio-hardened-scoutsuite@sha256:DIGEST \
  --output text > prov.jsonl

# 2. presidio enforces the policy: built by this repo's CI, from this source,
#    for this exact digest (exit 0 verified · 3 policy mismatch · 2 unparseable)
presidio-scout-verify-provenance prov.jsonl --digest sha256:DIGEST

It understands both SLSA provenance v0.2 (buildx) and v1 (slsa-github-generator) predicates — and reads a bare statement, a DSSE envelope, cosign's JSON-Lines, or the output of gh attestation verify --format json — and checks the builder identity, source repository, predicate type, and that the artifact digest is actually attested. Override the expected source/builder with --source-uri / --builder-id-prefix.

Container image. The released image is multi-arch (linux/amd64 + linux/arm64) with timestamps pinned to the tagged commit (reproducible digests), cosign-signed, and carries GitHub-signed SLSA build provenance. The release pipeline re-verifies the freshly published image end-to-end before the run is marked good; you can do the same:

cosign verify ghcr.io/presidio-v/presidio-hardened-scoutsuite@sha256:DIGEST \
  --certificate-identity-regexp '^https://github.com/presidio-v/.*release\.yml@refs/tags/v' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
gh attestation verify oci://ghcr.io/presidio-v/presidio-hardened-scoutsuite@sha256:DIGEST \
  --repo presidio-v/presidio-hardened-scoutsuite --format json \
  | presidio-scout-verify-provenance - --digest sha256:DIGEST
# a signed CycloneDX SBOM is attached too:
gh attestation verify oci://ghcr.io/presidio-v/presidio-hardened-scoutsuite@sha256:DIGEST \
  --repo presidio-v/presidio-hardened-scoutsuite --predicate-type https://cyclonedx.org/bom

Vulnerability gate. Before a release is accepted, the locked dependency tree is audited (pip-audit) and the published image is scanned (Trivy); the scan is gated by presidio-scout-vuln-gate, which fails closed on any fixable vulnerability at or above a chosen severity. It reads a Trivy or Grype JSON report, so you can run the same gate locally:

trivy image --format json ghcr.io/presidio-v/presidio-hardened-scoutsuite:vX > scan.json
presidio-scout-vuln-gate scan.json --fail-on critical --ignore-unfixed   # exit 4 if any remain

Reproducible build. Builds are pinned to the tagged commit's timestamp (SOURCE_DATE_EPOCH), so anyone can rebuild from the same commit and confirm the wheel is byte-identical to what was published — and CI fails the reproducible-build job if two builds diverge:

SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) python -m build
sha256sum dist/*.whl          # compare against the published wheel's digest

(The .tar.gz sdist is reproducible at the content level; its gzip container carries an mtime that isn't part of the archived files, so compare it with gzip -dc dist/*.tar.gz | sha256sum rather than the raw bytes — which is exactly what the CI gate does.)


Least-privilege audit identities

ScoutSuite needs broad read-only access. Bundled, ready-to-apply identities grant exactly that and nothing else, per cloud. The runtime is AWS-only (ADR-0002); the Azure and GCP identities stay for operators who run upstream ScoutSuite against those clouds:

  • AWS (iam/aws/) — the two managed read-only policies, a supplemental read policy with an explicit Deny on any non-read action, and a trust policy requiring MFA + a random ExternalId.
  • Azure (iam/azure/) — Reader + Security Reader (or a custom */read role with no dataActions, so secret/key values stay unreadable), plus minimal directory read for the Azure AD findings.
  • GCP (iam/gcp/) — roles/viewer + roles/iam.securityReviewer (or a custom role listing only *.list/*.get permissions), assumed via service-account impersonation over a downloaded key.

Keyless / short-lived credentials

The wrapper doesn't broker credentials itself — ScoutSuite's bundled cloud SDKs already resolve assumed roles, OIDC, impersonation, and managed identity. What it adds is a fail-closed preflight: presidio-scout … --require-short-lived-creds refuses to run when the (scrubbed) environment carries a long-lived static secret — an AWS access key with no session token, a downloaded GCP service-account key, or an Azure client secret — pushing you onto short-lived credentials that pair with the bundled audit roles. Without the flag, a static secret is allowed but warned about. (An "unknown" posture — e.g. an AWS_PROFILE that may itself assume a role — never blocks.)

Recommended keyless setups:

  • AWS — a profile that assumes the audit role (role_arn + source_profile
    • mfa_serial + external_id) so the SDK vends temporary creds; or, in CI, GitHub OIDC → AWS_ROLE_ARN + AWS_WEB_IDENTITY_TOKEN_FILE (no stored keys).
  • GCP — service-account impersonation (CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT) or Workload Identity Federation (external_account), never a downloaded key.
  • Azure — managed identity (IDENTITY_ENDPOINT/MSI_ENDPOINT, passed through to the child) or workload-identity federation (AZURE_FEDERATED_TOKEN_FILE), not a client secret.
# CI: GitHub OIDC → assume the AWS audit role, then run with no long-lived keys
permissions: { id-token: write, contents: read }
steps:
  - uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::<acct>:role/presidio-scoutsuite-auditor
      aws-region: us-east-1
  - run: presidio-scout aws --report-dir ./report --require-short-lived-creds

Org config & profiles

Check in your hardened defaults once instead of repeating flags in every pipeline. A .presidio-scout.toml in the repo root (auto-discovered, or pass --config) supplies defaults; a [profiles.<name>] table overlays them with --profile; and explicit CLI flags always win over both.

# .presidio-scout.toml
[defaults]
provider = "aws"
require-short-lived-creds = true
fail-on-secret = true
waivers = "security/scout-waivers.json"

[profiles.nightly]
fail-on-finding = "danger"
sarif = "scoutsuite-report/results.sarif"
presidio-scout --profile nightly      # provider + gates come from the config
presidio-scout-policy validate        # fail-closed check of the org config
presidio-scout-policy show --profile nightly   # print the resolved settings

presidio-scout-policy validate rejects unknown sections/keys, wrong types, and out-of-range values (an unknown provider or severity), so a typo in org policy fails loudly. See .presidio-scout.toml.example.

Extending redaction & composing a baseline (from config)

The same config can tailor two hardened behaviours without forking the distribution — both validated fail-closed by presidio-scout-policy:

# Scrub your own credential shapes in addition to the built-in redactors.
[redaction]
extra-patterns = [
  { name = "internal_token", pattern = "INT-[A-Z0-9]{20}" },
  "GHX-[a-z0-9]{16}",
]

# Compose the ruleset from a bundled baseline instead of hand-writing one.
[baseline]
base = "aws"
[baseline.set-level]            # raise/lower (or add) a rule's severity
"s3-bucket-no-versioning.json" = "danger"
[baseline.disable]              # drop rules from the base baseline
rules = ["ec2-default-security-group-with-rules.json"]

[redaction].extra-patterns are applied during report redaction (a custom-secret survives into nothing). [baseline] is used automatically as the ruleset (unless you pass --ruleset/--no-baseline); every rule it names must exist in the pinned ScoutSuite's manifest, and an uncompilable regex or unknown rule fails validation rather than silently doing nothing.


Keep ScoutSuite current (upgrade automation)

The pinned ScoutSuite version underpins every gate — the install-integrity preflight, the hash-pinned lockfile, and the rule inventory the baselines validate against. That version is declared in several files that must agree; presidio-scout-upgrade makes coherence a fail-closed gate and bumps reviewable.

presidio-scout-upgrade check          # all pin sites must agree (exit 4 on drift)
presidio-scout-upgrade current        # the authoritative pinned version
presidio-scout-upgrade plan --to 5.15.0   # ordered, reviewable bump steps
presidio-scout-upgrade apply --to 5.15.0  # apply the offline text pins only

apply does only the deterministic in-repo edits (the scoutsuite extra and the install-integrity constant); regenerating the hash-pinned requirements.lock (needs PyPI) and the rule manifests (needs the installed ScoutSuite) is done by the scheduled scout-upgrade workflow, which runs the whole sequence, all gates, and opens a PR — nothing merges without review. The pin-coherence CI job runs presidio-scout-upgrade check on every push so the pin sites can never silently drift.


Notify a sink

A gate result is only useful if someone sees it. presidio-scout-notify summarizes a finished report (provider, per-level counts, top findings) and pushes it to a sink — a local file, a generic JSON webhook, or a Slack incoming webhook:

presidio-scout-notify ./report --sink slack --url "$SLACK_WEBHOOK_URL"
presidio-scout-notify ./report --sink file --path summary.json
presidio-scout-notify ./report --sink webhook --url https://hooks/x --only-if danger
# or resolve a named sink from .presidio-scout.toml:
presidio-scout-notify ./report --sink-name prod-slack --config .presidio-scout.toml
# .presidio-scout.toml
[sinks.prod-slack]
type = "slack"
url = "https://hooks.slack.com/services/…"

Redaction-aware, fail-closed: the outgoing payload is scanned by the same secret detector the report redaction uses — if a secret survives into the message (e.g. echoed in a resource name), delivery is refused rather than leaked. Webhooks use stdlib urllib (no new dependency); a non-2xx response fails the command. --only-if danger|warning suppresses noise when nothing is at/above.


Audit a fleet of accounts

Most orgs have many accounts, not one. presidio-scout-orchestrate fans the hardened audit out across a declared matrix of targets — each pinned to its own read-only identity — producing one report per target and a single aggregated gate. Declare targets in .presidio-scout-targets.toml:

[[targets]]
name = "prod-aws"
provider = "aws"
env = { AWS_PROFILE = "prod-audit", AWS_DEFAULT_REGION = "us-east-1" }

[[targets]]
name = "analytics-aws"
provider = "aws"
env = { AWS_PROFILE = "analytics-audit", AWS_DEFAULT_REGION = "eu-central-1" }
presidio-scout-orchestrate --report-dir ./fleet --fail-on-finding danger
# pass extra flags through to every run after `--`:
presidio-scout-orchestrate -- --require-short-lived-creds --attest run.intoto

Each target is a separate presidio-scout subprocess with its own scrubbed environment, so ScoutSuite state never bleeds between accounts. The orchestrator only selects each target's identity via its env (profile / project / subscription) — it never brokers credentials (set up assume-role / impersonation / managed identity in your cloud config). Fail-closed: a target that can't be audited, or whose results can't be read for the gate, fails the fleet run (exit 2) rather than being silently skipped; the severity gate exits 4. See .presidio-scout-targets.toml.example.


Run in Kubernetes

Hardened in-cluster manifests and a Helm chart live under deploy/: a one-shot Job or scheduled CronJob that runs the signed image as a least-privilege workload-identity ServiceAccount (no long-lived keys), with readOnlyRootFilesystem, all capabilities dropped, seccompProfile: RuntimeDefault, automountServiceAccountToken: false, a default-deny NetworkPolicy (egress only DNS + 443), and --fail-on-finding danger so a finding fails the Job.

kubectl apply -f deploy/kubernetes/          # annotate serviceaccount.yaml for your cloud first
# or, with Helm:
helm install nightly deploy/helm/presidio-scout \
  --set provider=aws --set schedule="0 6 * * *" \
  --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::<acct>:role/presidio-scoutsuite-auditor

See deploy/kubernetes/README.md for per-cloud workload-identity annotations (IRSA / GKE WI / Azure WI) and NetworkPolicy tightening.


Curated rulesets

Curated baselines ship for AWS, Azure, GCP, Alibaba Cloud, and Oracle Cloud under src/presidio_scoutsuite/policy/ (aws-cis.json, azure-cis.json, gcp-cis.json, aliyun-cis.json, oci-cis.json). Only the AWS baseline is applied by presidio-scout, which runs AWS only (ADR-0002); the others stay manifest-verified for use with upstream ScoutSuite. Each enables a CIS-aligned subset of ScoutSuite's findings and elevates the high-impact ones to danger. Override with --ruleset PATH or opt out with --no-baseline.

A ruleset's keys are the filenames of finding rules that live inside ScoutSuite. If a baseline names a rule the pinned ScoutSuite doesn't ship (a typo or an upstream rename), ScoutSuite silently drops that control. To catch that, each provider ships a rule-name inventory (policy/<provider>.rules.txt) tracking the pinned upstream version, and a validator checks the baselines against it:

presidio-scout-validate                  # offline: baselines ⊆ checked-in manifests (runs in CI)
presidio-scout-validate --source installed   # release: baselines ⊆ the installed ScoutSuite

CI runs the offline check on every push; the release pipeline runs the installed check against the pinned ScoutSuite so the manifest can't drift from upstream unnoticed. Regenerate the manifests from an installed ScoutSuite with presidio-scout-validate --regenerate --source installed (the upgrade workflow does this automatically on a version bump).


Roadmap

Status: all versions below are delivered and merged to main (currently v0.30.1) across three complete arcs — the single-run hardened auditor (0.1–0.15), fleet tooling & integrations (0.16–0.21), and continuous assurance & remediation (0.22–0.27) — plus the fourth arc, consortium interop & the evidence substrate (0.28.0+), now through 0.30.0. Released to PyPI: v0.18.0, v0.29.0, v0.30.0 and the security patch v0.30.1 (the wrapper); 0.19–0.28 were not tagged separately and ship in v0.29.0. No container image has been published: the bundled ScoutSuite runtime is a separate release track, blocked while pip-audit reports inherited ScoutSuite dependency vulnerabilities in requirements.lock (see ADR-0001). The rest of the fourth arc (0.31.0+) is planned (see the tables at the end).

Version Highlights
0.1.0 Out-of-process hardened launcher, report redaction + guard, AWS-first curated ruleset + least-privilege IAM, hardened container, full supply-chain posture
0.2.0 Azure + GCP curated baselines & least-privilege IAM; ruleset rule-name validation against the pinned ScoutSuite (offline manifest in CI, installed-source drift check at release)
0.3.0 Deeper report guard — Subresource Integrity on local assets, offline-viewer (remote-reference) enforcement, and a signed-able, offline-verifiable report manifest (presidio-scout-verify)
0.4.0 SLSA build-provenance policy verification (presidio-scout-verify-provenance, v0.2 + v1) and a reproducible wheel/sdist with a reproducible-build CI gate
0.5.0 ScoutSuite install-integrity gate — fail-closed preflight that the scout you run is the pinned, vetted version (--allow-unverified-scout to override); real hash-pinned requirements.lock; pinned build backend
0.6.0 Findings model + severity gate — presidio-scout --fail-on-finding danger|warning and the standalone presidio-scout-findings, parsed from the report data (fail-closed; exit 4)
0.7.0 SARIF export + GitHub code-scanning — presidio-scout-export and presidio-scout --sarif PATH emit SARIF 2.1.0 (severity-mapped, per-resource, stable fingerprints)
0.8.0 Waivers / exceptions framework — checked-in JSON waivers (justification + owner + expiry; resource-level globs), applied to the gate/SARIF via --waivers; expired/malformed waivers fail closed
0.9.0 Signed run attestation — in-toto statement binding run inputs (provider, ruleset digest, ScoutSuite version) to the report-manifest digest; presidio-scout --attest + presidio-scout-attest generate/verify
0.10.0 Drift detection / run diff — presidio-scout-diff compares two reports at resource granularity (new vs resolved findings), with --fail-on-new-finding {any,warning,danger}
0.11.0 Reproducible, multi-arch (amd64+arm64) container; GitHub-signed image provenance; a release verify-image gate that re-checks the signature + provenance (cosign + presidio-scout-verify-provenance) end-to-end
0.12.0 Keyless / short-lived credentials — a fail-closed --require-short-lived-creds preflight that rejects long-lived static secrets, keyless/managed-identity env passthrough, and OIDC/assume-role/impersonation setup docs (chose configuration + preflight over in-wrapper brokering)
0.13.0 Kubernetes deployment — hardened Job/CronJob + Helm chart (workload identity, read-only rootfs, dropped caps, seccomp, default-deny NetworkPolicy) under deploy/
0.14.0 Vulnerability-scan gate (pip-audit + Trivy + presidio-scout-vuln-gate, fail-closed on fixable findings) + signed CycloneDX SBOM attestation verified alongside provenance at release
0.15.0 Org policy profiles / config — .presidio-scout.toml defaults + named profiles applied as CLI defaults, validated by presidio-scout-policy
0.16.0 ScoutSuite upgrade automation — presidio-scout-upgrade (fail-closed pin-coherence gate + reviewable bump planner/applier), --regenerate for the rule manifests, a pin-coherence CI gate, and a scheduled workflow that bumps the lockfile + manifests and opens a PR
0.17.0 Compliance mapping + ASFF export — presidio-scout-compliance maps findings to CIS / NIST 800-53 / SOC 2 controls (validated fail-closed against the manifest); presidio-scout-asff / --asff emit AWS Security Hub findings enriched with the mapped controls
0.18.0 Verified & extended provider baselines — every AWS/Azure/GCP baseline, manifest, and compliance-map rule name reconciled against the real ScoutSuite 5.14.0 source (correcting names that never existed upstream) and expanded (AWS 34 / Azure 26 / GCP 27 curated rules, incl. GKE)
0.19.0 Org-wide orchestration — presidio-scout-orchestrate fans the audit across a .presidio-scout-targets.toml matrix (one out-of-process run + report per account, no credential brokering) with an aggregated, fail-closed severity gate
0.20.0 Notification sinks — presidio-scout-notify pushes an audit summary to a file / webhook / Slack sink (config- or flag-driven), redaction-aware and fail-closed so a secret can't leak to an external sink
0.21.0 Config-driven redaction & baseline composition — [redaction].extra-patterns add org secret redactors; [baseline] composes a ruleset from a bundled baseline (set-level / disable), both validated fail-closed by presidio-scout-policy
0.22.0 Posture history & trend — presidio-scout-trend records each run to an append-only history and gates on regression (a new finding vs the previous run); persists across runs without keeping old reports
0.23.0 Remediation guidance — curated per-rule fix steps + doc references (AWS/Azure/GCP, validated against the manifest); presidio-scout-remediate emits them and they fill the AWS Security Hub ASFF Remediation field
0.24.0 Policy-as-code assertions — presidio-scout-assert evaluates a declarative [[assert]] policy (named rules over service / rule-glob / severity with a max count) richer than a single threshold; fail-closed, exit 4 on any violation
0.25.0 Alibaba Cloud (aliyun) & Oracle Cloud (oci) baselines — curated, manifest-verified baselines + compliance maps for ScoutSuite's remaining providers (rule names reconciled against the real 5.14.0 source); all five providers now ship a hardened default
0.26.0 Executive & multi-format reporting — presidio-scout-summary renders a report (or a --fleet rollup) as Markdown, self-contained HTML (escaped, no scripts), CSV, or JSON
0.27.0 Stable extension API — presidio-scout-ext discovers MIT-safe plugins (redactors / exporters / sinks) via entry points; installed redactor plugins feed the redaction step, fail-closed (a broken redactor errors rather than letting a secret through)

Fourth arc: consortium interop & the evidence substrate (0.28.0+)

0.28.0–0.30.0 are delivered; 0.31.0 is planned. This arc makes a ScoutSuite audit a first-class evidence producer in the presidio-hardened-* family. A clean curated control (no danger/warning finding for its mapped rules) becomes a signed EvidenceRef (presidio-hardened/evidence-ref@2) that a peer consumer — presidio-hardened-ikigov-assess — verifies fail-closed and uses to affirm its governance checklist items, upgrading them from self-attested to affirmed-by-evidence. ScoutSuite becomes the second producer in the consortium (after presidio-hardened-ai); ikigov-assess consumes both. The wire format (canonical JSON + SHA-256 + detached Ed25519/HMAC) is the one shared in the presidio-evidence library — vendored inline and cross-validated against its golden vectors until that library ships its implementation, exactly as ikigov-assess does today.

Version Planned
0.28.0 ✓ Signed finding evidence (presidio-evidence producer) — evidence.py + presidio-scout-evidence (emit/verify) + presidio-scout --evidence-out PATH: emit a signed EvidenceRef envelope per clean curated control (content hash over the canonical control subject, ledger_ref bound to the report-manifest digest), Ed25519 (optional [crypto]) + HMAC (stdlib), fail-closed; a curated rule→checklist-item map (T5/O5) validated against the rule manifest; golden-vector conformance against presidio-evidence/vectors/
0.29.0 ✓ Trust store, rotation & interop hardening — verify --trust against presidio-hardened/trust-store@1 (key rotation, signer-bound signatures, tamper cases fail closed; malformed Ed25519 keys rejected at load) and a committed cross-repo interop golden, verified byte-for-byte by ikigov-assess's own verify_ref
0.30.0 ✓ Evidence signing conforms to the family contract — refs are signed over canonical {content_hash, signer} (the message ikigov-assess and the family verifier check); the canonicaliser rejects floats and NaN/Infinity; legacy full-document signatures still verify with a warning until v0.32.0 (or 90 days after v0.30.0, if later), and presidio-scout-evidence resign upgrades them
0.31.0 (stretch) Library extraction & evidence consumption — migrate the vendored contract to presidio-evidence>=0.2.0 imports, and optionally ingest peer evidence to annotate findings with provenance (mirror of ikigov-assess's consumer side)

See PRESIDIO-REQ.md for the per-version rationale, dependencies, delivery status, and open design questions.


Running tests

pip install -e ".[dev]"
pytest --cov=presidio_scoutsuite --cov-report=term-missing
ruff check . && ruff format --check .

Tests run without ScoutSuite installed — the subprocess boundary is injected.


Project structure

presidio-hardened-scoutsuite/
├── src/presidio_scoutsuite/
│   ├── launcher.py        # build/run the hardened scout subprocess
│   ├── redact.py          # secret detection + in-place redaction
│   ├── report_guard.py    # CSP + SRI injection, remote-ref detection, manifest write
│   ├── manifest.py        # integrity-manifest shape, self-digest, HMAC signing
│   ├── verify.py          # offline report verification (presidio-scout-verify)
│   ├── provenance.py      # SLSA provenance policy gate (presidio-scout-verify-provenance)
│   ├── scout_integrity.py # pinned-ScoutSuite preflight gate
│   ├── findings.py        # findings model + severity gate (presidio-scout-findings)
│   ├── sarif.py           # SARIF 2.1.0 export for code scanning (presidio-scout-export)
│   ├── compliance.py      # CIS/NIST/SOC2 control mapping (presidio-scout-compliance)
│   ├── asff.py            # AWS Security Hub ASFF export (presidio-scout-asff)
│   ├── evidence.py        # signed evidence for clean controls (presidio-scout-evidence)
│   ├── waivers.py         # expiring findings waivers / exceptions (--waivers)
│   ├── attestation.py     # in-toto run attestation (presidio-scout-attest)
│   ├── diff.py            # drift detection between two runs (presidio-scout-diff)
│   ├── credentials.py     # short-lived-credential preflight (--require-short-lived-creds)
│   ├── vuln.py            # Trivy/Grype vulnerability gate (presidio-scout-vuln-gate)
│   ├── config.py          # .presidio-scout.toml org config (presidio-scout-policy)
│   ├── ruleset.py         # baseline rule-name validation (presidio-scout-validate)
│   ├── compose.py         # config-driven redaction patterns + baseline composition
│   ├── summary.py         # executive / multi-format reporting (presidio-scout-summary)
│   ├── extensions.py      # stable plugin API: redactors/exporters/sinks (presidio-scout-ext)
│   ├── upgrade.py         # ScoutSuite pin-coherence gate + bump tooling (presidio-scout-upgrade)
│   ├── orchestrate.py     # multi-account fleet fan-out + aggregated gate (presidio-scout-orchestrate)
│   ├── notify.py          # redaction-aware notification sinks (presidio-scout-notify)
│   ├── cli.py             # presidio-scout entrypoint
│   ├── errors.py          # exception hierarchy
│   └── policy/            # curated baselines + rule manifests + provenance-policy.json
├── iam/{aws,azure,gcp}/   # least-privilege audit identities per cloud
├── deploy/                # hardened Kubernetes manifests + Helm chart
├── .presidio-scout.toml.example   # org config / profiles example
├── tests/
├── Dockerfile             # distroless, non-root
├── requirements.lock      # hash-pinned runtime tree (incl. ScoutSuite)
├── .github/workflows/     # ci, codeql, sbom, release (cosign + provenance)
├── LICENSE                # MIT (this wrapper)
└── LICENSES/README.md     # GPL-2.0 notice for bundled ScoutSuite

License

MIT for this wrapper — see LICENSE. It bundles/installs ScoutSuite (GPL-2.0-only) separately; see LICENSES/README.md.

Security

See SECURITY.md.

SDLC

This repository is on the open-source baseline of the PRESIDIO hardened-family SDLC (report · PDF). The controls measured for it, and any open gaps, are its row in the applicability matrix.

Metadata

Release files for presidio-hardened-scoutsuite 0.30.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for presidio-hardened-scoutsuite 0.30.1
File Size Uploaded Provenance Actions
presidio_hardened_scoutsuite-0.30.1.tar.gz 251.3 kB Without provenance Details for presidio_hardened_scoutsuite-0.30.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for presidio-hardened-scoutsuite 0.30.1
File Interpreter ABI Platform Actions
presidio_hardened_scoutsuite-0.30.1-py3-none-any.whl Python 3 none any Details for presidio_hardened_scoutsuite-0.30.1-py3-none-any.whl

Total release size: 407.4 kB

Release files / presidio_hardened_scoutsuite-0.30.1.tar.gz

Download URL presidio_hardened_scoutsuite-0.30.1.tar.gz
Size 251.3 kB
Tags Source
SHA-256 checksum
How to use checksums
4b2a7fb2fdf67e6b73b73add06d2106d032f0baf04219adf2d28233e9c3c6918
BLAKE2b-256 checksum
How to use checksums
b434438ba48a993741ed63d7b85a2b3357aba5db3da3a61962625e768df90c1f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / presidio_hardened_scoutsuite-0.30.1-py3-none-any.whl

Download URL presidio_hardened_scoutsuite-0.30.1-py3-none-any.whl
Size 156.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
db1fcb2cb2216d86064779f3c6ac5836e2cb71338d831f463b16f5ae306c50a0
BLAKE2b-256 checksum
How to use checksums
7f94a8dcbdd422602e89e02357b6dfc98f5ce5921e765e7b6971660fd303ebff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for presidio-hardened-scoutsuite 0.30.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page