Skip to main content

AppSec Rules Pack

CI Security CI/CD OpenSSF Scorecard PyPI Python License: Apache 2.0

A versioned AppSec rules pack and the validator that keeps it honest. Each rule states what to review, what evidence proves it, how to fix it, and how long an exception may last, with mappings to OWASP ASVS 5.0, the OWASP API Security Top 10, OWASP Top 10:2025, CWE, and NIST SSDF. The appsec-rules CLI validates packs against a JSON Schema contract and semantic checks, and checks review records, the per-service outcome of a review, against the pack's own exception policy. A CI gate reads both reports.

Recorded CLI demo: successful baseline validation followed by a missing-title error

The demo is real CLI output. Recreate it from a source checkout with python docs/assets/record-cli-demo.py.

Contents

Scope

It does It does not
Define a JSON Schema contract for AppSec review rules Scan application code or execute rules
Ship a generic baseline of 19 rules, each with a compliant and a violating example Claim compliance: mappings are review aids, not conformance
Validate packs: schema, duplicate IDs, exception windows and policy, mapping formats, rule lifecycle, sensitive values Embed enforcement: CI consumes the JSON report and decides (ADR-0004)
Derive a rule index, a Semgrep metadata scaffold, a SARIF rule catalog, and a mapping coverage report Turn the derived scaffold into detections: its patterns are placeholders (ADR-0001)
Check a review record against the pack: every rule accounted for, evidence for met, exceptions allowed, complete, unexpired, and inside the window Confirm the evidence is true, or decide what may stay open: the record is self-declared and the gate decides (ADR-0006)

The baseline covers authentication, authorization, input validation, injection and XSS, SSRF, secrets, file handling, logging, dependency risk, configuration, session hardening, CSRF, webhook integrity, excessive data exposure, mass assignment, open redirect, and rate limiting. It contains no product names, tenant identifiers, customer data, secrets, or environment-specific configuration.

Mapping coverage of the baseline: ASVS 5.0, API Top 10 2023, CWE, and NIST SSDF on 19 of 19 rules; the optional OWASP Top 10:2025 field on 18 of 19.

Quick start

pip install "appsec-rules-pack==0.5.0"
curl -LO https://github.com/lucashgrifoni/AppSec-Rules-Pack/releases/download/v0.5.0/appsec-baseline.yaml
appsec-rules validate appsec-baseline.yaml --require-examples --fail-on-warnings

Expected output: Validation passed: 1 file, 19 rules, 0 errors, 0 warnings.

Installation

pip install appsec-rules-pack

This installs the appsec-rules console script and requires Python 3.12 or newer. Pin a reviewed version, such as appsec-rules-pack==0.5.0, when the CLI runs as a quality gate.

The distribution contains the validator, the CLI, and the JSON Schema. It does not contain a rules pack. Take the baseline from the assets of the release you pin, copy rules/appsec-baseline.yaml, or write your own pack.

To work from source:

python -m venv .venv
.venv/bin/python -m pip install -e ".[dev]"     # Windows: .venv\Scripts\python

Verify a release

Releases from v0.2.0 on are built and published by publish-pypi.yml through PyPI Trusted Publishing, with no long-lived token. From v0.3.1, each GitHub Release carries the wheel, the sdist, a CycloneDX SBOM (sbom.cdx.json), and the baseline pack, all four with a SLSA build-provenance attestation; from v0.4.0 it also carries the signed provenance bundle (appsec-rules-pack-<tag>.intoto.jsonl). From v0.5.0, a release is published only from a tag on main that matches the package version and passes the tests, and the files are built with hash-pinned tools in a job that cannot publish. v0.1.0 was published by hand and has no attestation. All release tags are signed.

Verify a downloaded asset with a current GitHub CLI:

gh attestation verify appsec-baseline.yaml \
  --repo lucashgrifoni/AppSec-Rules-Pack \
  --signer-workflow lucashgrifoni/AppSec-Rules-Pack/.github/workflows/publish-pypi.yml \
  --source-ref refs/tags/v0.5.0

Or offline, against the bundle attached to the release:

gh attestation verify appsec-baseline.yaml \
  --repo lucashgrifoni/AppSec-Rules-Pack \
  --bundle appsec-rules-pack-v0.5.0.intoto.jsonl

The same commands work for the wheel, the sdist, and sbom.cdx.json. A passing check proves the artifact came from this repository's release workflow at that tag. It does not prove the rule guidance is complete or that an application is secure. Treat a failed or unavailable check as unverified provenance. ADR-0003 explains the release design.

CLI reference

Command Purpose
appsec-rules init <file> Write a starter pack that passes the strict gate
appsec-rules validate <file-or-dir> Validate one pack or every .yaml/.yml pack in a directory
appsec-rules review <pack> <record> Check a review record against a pack; --as-of, --fail-on-warnings, --format json
appsec-rules export index <pack> [-o file] Derive a machine-readable rule index (JSON)
appsec-rules export semgrep <pack> [-o file] Derive a Semgrep scaffold with rule metadata and placeholder patterns
appsec-rules export sarif <pack> [-o file] Derive a SARIF 2.1.0 rule catalog with an empty results array
appsec-rules report coverage <pack> [-f json] [-o file] Count framework mappings per rule and AppSec category
appsec-rules --version Print the installed version

validate options:

Option Effect
--fail-on-warnings Exit non-zero on warnings as well as errors
--require-examples Warn when an enabled rule has no compliant/violating example
--format json Emit a JSON report for CI

The JSON report is identified by "schema": "appsec-rules-validation/v1" and described by validation-report.schema.json. It has a summary object (files, rules, errors, warnings, ok) and a files array of per-file issues. Each issue has a level, a stable code, the rule_id it belongs to (or null), a path inside the pack, and a message. Match on code, not on the message text, which may be reworded. Validation exits 0 on a passing pack, 1 on a failing one, and 2 on a usage error such as a missing path. VERSIONING.md lists the codes and what counts as a breaking change.

The export and coverage commands only derive metadata; they do not validate first. Run validate --require-examples --fail-on-warnings as the gate. From a source checkout, python -m appsec_rules_pack works in place of appsec-rules.

Writing a rules pack

A pack is a pack block plus one or more rules. This minimal pack is complete and passes the strict gate. appsec-rules init my-pack.yaml writes it for you; then validate it and grow it. Every field shown is required except schema_version and examples, which the strict gate (--require-examples) expects anyway. The full contract is appsec-rule.schema.json, and the 19 rules in rules/appsec-baseline.yaml are worked examples.

pack:
  id: my-pack
  name: My Rules Pack
  version: 0.1.0
  schema_version: "0.5"
  mode: advisory
  owner: appsec
  description: A minimal rules pack to start from.

rules:
  - id: MYPACK-ERRORS-001
    title: Require controlled error handling
    description: Verify that invalid user input returns controlled errors without stack traces.
    severity: medium
    category: configuration
    status: enabled
    enforcement: advisory
    targets:
      - api
    mappings:
      owasp_asvs:
        - V16.5.1
      cwe:
        - CWE-209
      nist_ssdf:
        - PW.7
    evidence:
      required:
        - Error handling path returns a documented response shape.
      signals:
        - Negative tests assert controlled error messages for invalid input.
    match:
      type: review
      includes:
        - API handlers that process untrusted input.
      excludes:
        - Local developer-only scripts that are not shipped.
    remediation:
      guidance: Replace raw exception output with a controlled error contract.
      validation:
        - Run negative tests for invalid input and malformed requests.
    exceptions:
      allowed: true
      max_days: 30
      required_fields:
        - owner
        - justification
        - expires_at
    examples:
      compliant:
        language: python
        snippet: |
          except ValueError:
              log.exception("order parse failed")
              return {"error": "invalid order"}, 400
        explanation: The client gets a fixed message; the detail stays in server logs.
      violating:
        language: python
        snippet: |
          except ValueError as error:
              return {"error": repr(error)}, 500
        explanation: The raw exception reaches the client and can expose internal details.

Rules files must be UTF-8 YAML without aliases or duplicate keys, and no larger than 10 MiB; the validator rejects anything else before checking the schema.

docs/rule-fields.md lists every field, its limits and allowed values, and how to adapt the baseline: keep it as published next to your own pack, or fork it. Shapes that are easy to get wrong: evidence and match are objects, not lists; remediation needs guidance; required_fields accepts only owner, justification, expires_at, compensating_control, and validation_plan.

Rule ids follow PREFIX-AREA-NNN, so an organisation can use its own prefix (ACME-AUTH-001) next to the baseline's APPSEC- rules. The owasp_top_10_2025 and owasp_api_top_10_2023 mappings are optional; ASVS, CWE, and NIST SSDF are required. Keys starting with x- are allowed on the pack, on each rule, and inside mappings for your own metadata. A pack may declare the schema it targets with pack.schema_version: "0.5".

Rules are advisory by default. Each one carries a stable ID and severity, a target surface and category, framework mappings, expected evidence and review signals, match guidance, remediation and validation steps, exception requirements, a lifecycle status (deprecated rules carry a deprecation block), and a compliant and a violating example. pack.mode and each rule's enforcement (advisory, audit, blocking) are policy metadata for whoever consumes the pack. They never change the exit code of validate, which only judges whether the pack itself is well formed. CONTRIBUTING.md describes the severity model and the topic-based mapping convention.

Recording a review

A pack states a policy. A review record states what one review found against it: for one subject (a service, a repository, a release), each rule is met, not-met, not-applicable, or excepted.

review:
  pack: appsec-baseline
  pack_version: 0.4.0
  subject: payments-api
  reviewer: appsec-team
  date: 2026-10-01

results:
  - rule: APPSEC-AUTHZ-001
    status: met
    evidence:
      - tests/test_authz.py::test_other_tenant_payment_returns_404
  - rule: APPSEC-LOG-001
    status: not-met
    notes: Refund failures log the card holder name.
  - rule: APPSEC-RATELIMIT-001
    status: excepted
    exception:
      owner: payments-team
      justification: Per-client quotas need a new store; planned for 3.3.
      expires_at: 2026-12-15
      compensating_control: Gateway limit and an alert on 429 spikes.
appsec-rules review appsec-baseline.yaml payments-api-review.yaml --format json

review checks the record against the pack: the record names the right pack, each rule exists and appears once, met cites evidence, and each exception is allowed by the rule, has the fields the rule requires, has not expired, and fits in the rule's max_days. Enabled rules with no result are reported as unreviewed.

The exit code says whether the record is valid, nothing more. A valid record can still have open rules. The JSON report ("schema": "appsec-rules-review/v1", described by review-report.schema.json) lists every rule with its severity, enforcement, and status, and counts open rules by both, so the gate can apply your policy. The record format is review-record.schema.json. examples/review/ has a complete record against the baseline.

Use it in CI

  • examples/README.md has a GitHub Actions job that installs a pinned release, downloads and verifies the baseline, validates your packs, checks your review records, and gates on open rules, plus a PowerShell version.
  • examples/validation_gate.py is a stdlib-only gate for any other CI system. It passes only when the CLI exits 0 and the report says ok: true.
  • .github/workflows/policy-gate.yml is this repository's reference gate that consumes the JSON report.

Optional executable Semgrep rules

exports/semgrep-rules/ holds two hand-maintained, tested Semgrep rules, kept separate from the validator (ADR-0005):

Baseline rule Detection
APPSEC-INJECT-001 Flask query/form values reaching SQL arguments on locally created sqlite3 connections and cursors
APPSEC-SSRF-001 Flask query/form values reaching the URL argument of module-level Requests calls

The other 17 baseline rules have no executable detection, and these two cover only their documented source and sink combinations. The layer README lists the known false positives and false negatives. Run the fixture suite with a separately installed engine:

python -m pip install "semgrep==1.179.0"
semgrep --test --strict --metrics=off --config exports/semgrep-rules/rules exports/semgrep-rules/tests

CI runs this on Linux. Use Linux, macOS, or WSL: on native Windows, semgrep --test did not run these fixtures in a check on 2026-10-05.

The appsec-rules CLI never invokes Semgrep, and export semgrep still emits only the metadata scaffold.

Project layout

.github/            CI, security, policy-gate, Scorecard, Pages, and release workflows
docs/adr/           Architecture decision records
docs/assets/        CLI demo and social preview
examples/           Starter pack, worked review, and CI integration examples
exports/            Derived artifacts and the optional executable Semgrep rules
rules/              The baseline rules pack
site/               Source of the project landing page
src/appsec_rules_pack/
                    Validator, review checker, CLI, exporters, schemas, starter pack
tests/              Test suite and pass, fail, and warning fixtures

Documentation

Document Contents
CHANGELOG.md Changes per release
VERSIONING.md Version numbers, compatibility rules, public surface, issue codes
STATUS.md Current state, dated verification results, risks and limits
ROADMAP.md What shipped and what comes next
TECHNICAL_SPEC.md Rule contract and validation design
docs/rule-fields.md Every pack and rule field, and how to adapt the baseline
docs/adr/ Architecture decisions

Contributing, security, and license

Contributions are welcome. Read CONTRIBUTING.md for rule authoring principles, the severity and exception models, and the required checks:

python -m ruff check .
python -m pytest --cov=appsec_rules_pack --cov-report=term-missing
python -m appsec_rules_pack validate rules --require-examples --fail-on-warnings
python -m build

Participation follows the Code of Conduct. Report vulnerabilities privately as described in SECURITY.md; do not open a public issue.

Licensed under the Apache License 2.0.

Metadata

Release files for appsec-rules-pack 0.5.0

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

Source distribution (sdist)

Source distribution for appsec-rules-pack 0.5.0
File Size Uploaded
appsec_rules_pack-0.5.0.tar.gz 45.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for appsec-rules-pack 0.5.0
File Interpreter ABI Platform
appsec_rules_pack-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 89.9 kB

Release files / appsec_rules_pack-0.5.0.tar.gz

Download URL appsec_rules_pack-0.5.0.tar.gz
Size 45.4 kB
Tags Source
SHA-256 checksum
How to use checksums
93ad388a610f385c39410414e0f1ee3735a67e1c10b1ef2f0dd1035e7cfd75c7
BLAKE2b-256 checksum
How to use checksums
19240109dfa07dd57a641433a4140b3a30be8e53b8ed7690d9f8566bab325ba7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release files / appsec_rules_pack-0.5.0-py3-none-any.whl

Download URL appsec_rules_pack-0.5.0-py3-none-any.whl
Size 44.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
909eaa23eafdb23d489640efbd637608a1ad31c6e9251debc09465bc70ed7961
BLAKE2b-256 checksum
How to use checksums
804cd99244acc7f9884eda1da7edd1cc1523e1e120f15f501bedd4fdd51708d4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

This release

0.5.0 This release

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

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