Skip to main content
Three features turn aspex scan from a scanner a developer runs into a control a security team owns.

Policy file: .aspex.yaml

aspex scan looks for ./.aspex.yaml, then ~/.config/aspex/config.yaml. Pass --config <path> to use a specific file. Commit the project-local file so the whole team and CI share one policy.
Rules:
  • reason is required. A policy file with an unexplained ignore is rejected.
  • server is optional. Omit it to ignore a rule everywhere; use a glob like internal-* for a group.
  • An expired ignore stops suppressing and prints a warning, so the finding comes back into view instead of being silently forgotten.
  • Severity overrides are applied before scoring and before the --fail-on gate. Your score and your CI result reflect your policy, not the defaults.
Suppressed findings are always listed at the end of the report and in --json output under suppressed, so a clean run is never mistaken for “nothing found”. Attack paths (AP001AP006, see Capabilities and attack paths) are governed the same way: - rule: AP003 with a reason accepts a path, optionally scoped with server: to one of the servers on it. Baselines record paths too, so --baseline hides pre-existing compositions and only new ones fail the gate.

Baseline: adopt on an existing estate

Turning on --fail-on high against 30 servers with 90 existing findings fails forever. A baseline snapshots what exists today so only new findings fail the gate, then you burn the baseline down at your own pace.
Commit aspex-baseline.json. Re-run --save-baseline after you fix things to shrink it. Findings are keyed by server and rule ID, so the same rule appearing on a new server is correctly treated as new.
Use --baseline for the transition and .aspex.yaml ignores for permanent, explained decisions. A baseline entry means “not yet”; an ignore entry means “we decided”.

Prioritize with --with-trace

aspex scan knows what a server can do. aspex trace knows what your agents did. --with-trace joins them:
A critical server that was never invoked is a latent risk: remove it or scope it down. The same server invoked 340 times last week, twelve of them tripping a trace rule, is the one to fix today. The Prioritize list is servers with a HIGH or CRITICAL static finding and observed calls in the window. --json output includes the per-server activity under activity.

CI example

Detection contract

Aspex ships a corpus of known-malicious and known-benign MCP servers under testdata/corpus/. Every malicious fixture declares the rule IDs that must fire; every benign fixture (real, popular servers such as the official filesystem, GitHub, Slack, and fetch servers) declares the highest severity Aspex may report. CI runs both on every change, so a rule cannot regress detection or start crying wolf on a server people actually use. Contributing a fixture is the highest-signal way to improve Aspex: see CONTRIBUTING.md.