Skip to main content

Presidio security-hardened distribution of ScoutSuite — runs the multi-cloud auditor out of process with hardened defaults, report redaction, and a least-privilege deployment model.

Project description

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.


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 baselines for AWS, Azure, GCP, Alibaba Cloud, and Oracle Cloud 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 azure                        # Azure audit with the hardened Azure baseline
presidio-scout gcp                          # GCP audit with the hardened GCP baseline
presidio-scout aliyun                       # Alibaba Cloud audit with the hardened aliyun baseline
presidio-scout oci                          # Oracle Cloud audit with the hardened oci baseline

AWS, Azure, GCP, Alibaba Cloud (aliyun), and Oracle Cloud (oci) each ship a curated, manifest-verified baseline applied by default; the launcher falls back to ScoutSuite's default ruleset (with a warning) for any provider without one.

Anything after -- is forwarded to ScoutSuite only if it's on the pass-through allowlist (--profile, --region(s), --services, --skip, --max-rate, …). 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 (presidio-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

The signature is detached over the canonical JSON of every ref field except signature itself, so the checklist item, ledger reference, signer, source/version and timestamp are all tamper-evident. Each ref's content_hash covers the clean control's subject and its ledger_ref is bound to the report's integrity-manifest digest, tying the claim to one specific report. 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. The wire format matches the shared presidio-evidence golden vectors, so ikigov-assess verifies it byte-for-byte.

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:

  • 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.
  • Azuremanaged 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-gcp"
provider = "gcp"
env = { CLOUDSDK_CORE_PROJECT = "analytics-123456" }
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, and GCP under src/presidio_scoutsuite/policy/ (aws-cis.json, azure-cis.json, gcp-cis.json). 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.29.0) 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.29.0. The release pipeline has shipped v0.18.0 (PyPI + cosign-signed, attested image); 0.19–0.29 are on main awaiting a release tag. The rest of the fourth arc (0.30.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.29.0 are delivered; 0.30.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 consumerpresidio-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 (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

Developed under the Presidio hardened-family SDLC: https://github.com/presidio-v/presidio-hardened-docs/blob/main/sdlc/sdlc-report.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

presidio_hardened_scoutsuite-0.29.0.tar.gz (287.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

presidio_hardened_scoutsuite-0.29.0-py3-none-any.whl (151.7 kB view details)

Uploaded Python 3

File details

Details for the file presidio_hardened_scoutsuite-0.29.0.tar.gz.

File metadata

  • Download URL: presidio_hardened_scoutsuite-0.29.0.tar.gz
  • Upload date:
  • Size: 287.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","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}

File hashes

Hashes for presidio_hardened_scoutsuite-0.29.0.tar.gz
Algorithm Hash digest
SHA256 8df908eb8895e4792b0204ebf53c6bcf4dfc4e0336b27fa5a40bae357050796d
MD5 d592a1e33b243f2d35a0cea2ea42e9ed
BLAKE2b-256 48469ffd888f31c4370db9cc128e0d000f440d771d7ab67fc981bdd939c491db

See more details on using hashes here.

File details

Details for the file presidio_hardened_scoutsuite-0.29.0-py3-none-any.whl.

File metadata

  • Download URL: presidio_hardened_scoutsuite-0.29.0-py3-none-any.whl
  • Upload date:
  • Size: 151.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","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}

File hashes

Hashes for presidio_hardened_scoutsuite-0.29.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8f402c3b394e07e54d96d5dc5a0e9d5732cd63005095803ee032ad16aa74ab1e
MD5 3d7616fcd52ef15dccafc76fd11a6bb5
BLAKE2b-256 5490a4a70b044abdc1ad56157908687e4f3a87dc112672c50fc19c20678a751c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page