SCP Linter
A command-line tool for validating AWS Service Control Policies (SCPs) to ensure correctness, security, and best practices.
Installation
pip install scp-lint
Or install from source:
git clone https://github.com/OnticX/scp-lint.git
cd scp-lint
pip install -e .
Usage
Lint a single SCP file:
scp-lint policy.json
Lint all JSON files in a directory:
scp-lint ./policies/
Example Output
============================================================
File: ./my-scp.json
Status: PASSED with warnings
============================================================
WARNINGS (2):
[W042] Unknown action: 's3:GetObjet'
Location: Statement[0].Action[0]
Suggestion: Did you mean: s3:GetObject?
[W070] Invalid IP address or CIDR format: '999.999.999.999'
Location: Statement[1].Condition.IpAddress.aws:SourceIp
Suggestion: Use valid IPv4 (192.0.2.0/24) or IPv6 (2001:db8::/32) format
INFO (1):
[I031] Statement has no Sid (identifier)
Location: Statement[0]
Suggestion: Add a Sid for easier debugging and management
============================================================
SUMMARY
============================================================
SCPs linted: 1
Passed: 1
Failed: 0
============================================================
Features
- JSON Syntax Validation: Detects invalid JSON and file access errors
- Policy Structure Checks: Flags missing
Version/Statementfields, unknown top-level fields, and invalidEffectvalues - Size Limits: Errors for policies exceeding 5120 characters, warnings at 75% capacity
- Statement Validation: Ensures required fields (
Action,Effect) are present, rejects unsupported fields likePrincipal - Action Validation: Cross-references actions against an IAM reference database of 20,000+ actions, catching typos and providing "Did you mean?" suggestions
- Condition Block Syntax: Validates operator structure, detects unknown operators, and flags empty conditions
- Condition Key Validation: Verifies keys like
aws:SourceIpexist, supports tag-based keys such asaws:RequestTag/* - Best Practices: Warns on blanket denies without conditions, service-wide denies, and Allow-only SCPs
- Duplicate Detection: Identifies duplicate Sids and duplicate statements across your policy
- IP Address Validation: Validates IPv4/IPv6 addresses and CIDR notation in
IpAddress/NotIpAddressconditions - Date Format Validation: Validates date strings in
DateEquals,DateLessThan, and other date condition operators - ARN Format Validation: Validates Resource ARN structure and partition values
Lint Rules
Errors (E-codes) - Invalid policies that will not work
| Code | Description |
|---|---|
| E001 | File not found |
| E002 | Cannot read file |
| E003 | Invalid JSON syntax |
| E010 | Policy exceeds 5120 character limit |
| E020 | Missing Version field |
| E021 | Missing Statement field |
| E022 | Statement is not an object or array |
| E030 | Statement is not an object |
| E031 | Missing Effect field |
| E032 | Invalid Effect value (must be Allow or Deny) |
| E033 | Missing Action or NotAction field |
| E034 | Principal/NotPrincipal not allowed in SCPs |
| E040 | Action must be string or array |
| E041 | Action array item is not a string |
| E050 | Condition block must be an object |
| E051 | Condition operator value must be an object |
| E052 | Unknown condition operator |
Warnings (W-codes) - Valid but potentially problematic
| Code | Description |
|---|---|
| W010 | Policy is over 75% of size limit |
| W020 | Unusual Version value |
| W021 | Unknown top-level field |
| W030 | Empty statements array |
| W031 | More than 20 statements |
| W032 | Unknown statement field |
| W040 | Action missing service prefix |
| W041 | Unknown service prefix |
| W042 | Unknown action name (with suggestions) |
| W043 | Unknown condition key |
| W044 | Empty condition block |
| W045 | Empty condition operator block |
| W046 | Empty or null condition value |
| W050 | Blanket Deny * without conditions |
| W051 | Service-wide deny without conditions |
| W060 | Duplicate Sid found in multiple statements |
| W061 | Duplicate statements (identical Effect, Action, Resource, Condition) |
| W070 | Invalid IP address or CIDR format |
| W071 | Invalid date format |
| W080 | Invalid ARN format |
Info (I-codes) - Best practice suggestions
| Code | Description |
|---|---|
| I030 | No Resource specified (defaults to *) |
| I031 | No Sid specified |
| I050 | Policy contains only Allow statements |
| W090 | Statement uses NotAction (inverse logic) |
| W091 | Statement uses NotResource (inverse logic) |
GitHub Actions Integration
Add SCP linting to your CI/CD pipeline:
name: SCP Lint
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install scp-lint
run: pip install scp-lint
- name: Lint SCPs
run: scp-lint ./policies/
License
MIT - See LICENSE for details.
Metadata
Release files for scp-lint 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| scp_lint-0.2.3.tar.gz | 229.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| scp_lint-0.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 457.5 kB
Release files / scp_lint-0.2.3.tar.gz
| Download URL | scp_lint-0.2.3.tar.gz |
|---|---|
| Size | 229.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d0014ccac3fdeb9f6b573f2891cb2aeb033bb558a96aaf3dc8c11b0159c7af0b
|
|
BLAKE2b-256 checksum How to use checksums |
1320950871349487850d39cb945031c72be0841189d2f9444b29ce64262adbcc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.2
|
Release files / scp_lint-0.2.3-py3-none-any.whl
| Download URL | scp_lint-0.2.3-py3-none-any.whl |
|---|---|
| Size | 228.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
61e3c7b3b0e1a3f4e2c4031316439723ecb78894cc470f70df5c16cc7efdf8c6
|
|
BLAKE2b-256 checksum How to use checksums |
527ffe773e85c2d3c0cde50586a8e27a547e0f283f243cdeea04ce7512b9ce3f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.2
|
Security for scp-lint 0.2.3
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.