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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
scp_lint-0.2.3.tar.gz
(229.2 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
scp_lint-0.2.3-py3-none-any.whl
(228.3 kB
view details)
File details
Details for the file scp_lint-0.2.3.tar.gz.
File metadata
- Download URL: scp_lint-0.2.3.tar.gz
- Upload date:
- Size: 229.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0014ccac3fdeb9f6b573f2891cb2aeb033bb558a96aaf3dc8c11b0159c7af0b
|
|
| MD5 |
4f9d49699f23b600624a85b8b4f4d407
|
|
| BLAKE2b-256 |
1320950871349487850d39cb945031c72be0841189d2f9444b29ce64262adbcc
|
File details
Details for the file scp_lint-0.2.3-py3-none-any.whl.
File metadata
- Download URL: scp_lint-0.2.3-py3-none-any.whl
- Upload date:
- Size: 228.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61e3c7b3b0e1a3f4e2c4031316439723ecb78894cc470f70df5c16cc7efdf8c6
|
|
| MD5 |
d3229b218f80edd575f51161a0bbed1d
|
|
| BLAKE2b-256 |
527ffe773e85c2d3c0cde50586a8e27a547e0f283f243cdeea04ce7512b9ce3f
|