compose-lint 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- compose_lint-0.2.0/.github/dependabot.yml +21 -0
- compose_lint-0.2.0/.github/workflows/ci.yml +87 -0
- compose_lint-0.2.0/.github/workflows/codeql.yml +39 -0
- compose_lint-0.2.0/.github/workflows/publish.yml +83 -0
- compose_lint-0.2.0/.github/workflows/scorecard.yml +36 -0
- compose_lint-0.2.0/.gitignore +59 -0
- compose_lint-0.2.0/.pre-commit-hooks.yaml +7 -0
- compose_lint-0.2.0/CHANGELOG.md +54 -0
- compose_lint-0.2.0/CONTRIBUTING.md +59 -0
- compose_lint-0.2.0/LICENSE +21 -0
- compose_lint-0.2.0/PKG-INFO +246 -0
- compose_lint-0.2.0/README.md +213 -0
- compose_lint-0.2.0/SECURITY.md +48 -0
- compose_lint-0.2.0/action.yml +108 -0
- compose_lint-0.2.0/docs/adr/001-python.md +14 -0
- compose_lint-0.2.0/docs/adr/002-rule-grounding.md +12 -0
- compose_lint-0.2.0/docs/adr/003-yaml-parsing.md +18 -0
- compose_lint-0.2.0/docs/adr/004-rule-architecture.md +13 -0
- compose_lint-0.2.0/docs/adr/005-rule-id-scheme.md +19 -0
- compose_lint-0.2.0/docs/adr/006-exit-codes.md +12 -0
- compose_lint-0.2.0/docs/rule-comparison.md +138 -0
- compose_lint-0.2.0/docs/rules/CL-0001.md +45 -0
- compose_lint-0.2.0/docs/rules/CL-0002.md +38 -0
- compose_lint-0.2.0/docs/rules/CL-0003.md +24 -0
- compose_lint-0.2.0/docs/rules/CL-0004.md +33 -0
- compose_lint-0.2.0/docs/rules/CL-0005.md +35 -0
- compose_lint-0.2.0/docs/rules/CL-0006.md +42 -0
- compose_lint-0.2.0/docs/rules/CL-0007.md +43 -0
- compose_lint-0.2.0/docs/rules/CL-0008.md +42 -0
- compose_lint-0.2.0/docs/rules/CL-0009.md +50 -0
- compose_lint-0.2.0/docs/rules/CL-0010.md +51 -0
- compose_lint-0.2.0/docs/severity.md +65 -0
- compose_lint-0.2.0/pyproject.toml +80 -0
- compose_lint-0.2.0/src/compose_lint/__init__.py +3 -0
- compose_lint-0.2.0/src/compose_lint/__main__.py +5 -0
- compose_lint-0.2.0/src/compose_lint/cli.py +160 -0
- compose_lint-0.2.0/src/compose_lint/config.py +89 -0
- compose_lint-0.2.0/src/compose_lint/engine.py +79 -0
- compose_lint-0.2.0/src/compose_lint/formatters/__init__.py +1 -0
- compose_lint-0.2.0/src/compose_lint/formatters/json.py +29 -0
- compose_lint-0.2.0/src/compose_lint/formatters/sarif.py +140 -0
- compose_lint-0.2.0/src/compose_lint/formatters/text.py +113 -0
- compose_lint-0.2.0/src/compose_lint/models.py +70 -0
- compose_lint-0.2.0/src/compose_lint/parser.py +138 -0
- compose_lint-0.2.0/src/compose_lint/rules/CL0001_docker_socket.py +73 -0
- compose_lint-0.2.0/src/compose_lint/rules/CL0002_privileged_mode.py +67 -0
- compose_lint-0.2.0/src/compose_lint/rules/CL0003_no_new_privileges.py +74 -0
- compose_lint-0.2.0/src/compose_lint/rules/CL0004_image_not_pinned.py +96 -0
- compose_lint-0.2.0/src/compose_lint/rules/CL0005_unbound_ports.py +144 -0
- compose_lint-0.2.0/src/compose_lint/rules/CL0006_cap_drop.py +73 -0
- compose_lint-0.2.0/src/compose_lint/rules/CL0007_read_only.py +64 -0
- compose_lint-0.2.0/src/compose_lint/rules/CL0008_host_network.py +65 -0
- compose_lint-0.2.0/src/compose_lint/rules/CL0009_security_profile.py +83 -0
- compose_lint-0.2.0/src/compose_lint/rules/CL0010_host_namespace.py +79 -0
- compose_lint-0.2.0/src/compose_lint/rules/__init__.py +71 -0
- compose_lint-0.2.0/tests/__init__.py +0 -0
- compose_lint-0.2.0/tests/compose_files/insecure_cap_drop.yml +17 -0
- compose_lint-0.2.0/tests/compose_files/insecure_host_namespace.yml +20 -0
- compose_lint-0.2.0/tests/compose_files/insecure_host_network.yml +12 -0
- compose_lint-0.2.0/tests/compose_files/insecure_image_tags.yml +19 -0
- compose_lint-0.2.0/tests/compose_files/insecure_no_new_priv.yml +14 -0
- compose_lint-0.2.0/tests/compose_files/insecure_ports.yml +31 -0
- compose_lint-0.2.0/tests/compose_files/insecure_privileged.yml +6 -0
- compose_lint-0.2.0/tests/compose_files/insecure_read_only.yml +12 -0
- compose_lint-0.2.0/tests/compose_files/insecure_security_profile.yml +24 -0
- compose_lint-0.2.0/tests/compose_files/insecure_socket.yml +11 -0
- compose_lint-0.2.0/tests/compose_files/invalid_empty.yml +0 -0
- compose_lint-0.2.0/tests/compose_files/invalid_no_services.yml +3 -0
- compose_lint-0.2.0/tests/compose_files/invalid_service_not_mapping.yml +2 -0
- compose_lint-0.2.0/tests/compose_files/invalid_services_not_mapping.yml +3 -0
- compose_lint-0.2.0/tests/compose_files/invalid_yaml.yml +7 -0
- compose_lint-0.2.0/tests/compose_files/kics_comparison.yml +121 -0
- compose_lint-0.2.0/tests/compose_files/mixed.yml +19 -0
- compose_lint-0.2.0/tests/compose_files/suppress_config.yml +6 -0
- compose_lint-0.2.0/tests/compose_files/valid_anchors.yml +12 -0
- compose_lint-0.2.0/tests/compose_files/valid_basic.yml +23 -0
- compose_lint-0.2.0/tests/compose_files/valid_env_interpolation.yml +5 -0
- compose_lint-0.2.0/tests/compose_files/valid_v2.yml +9 -0
- compose_lint-0.2.0/tests/compose_files/warnings_only.yml +9 -0
- compose_lint-0.2.0/tests/test_CL0001.py +66 -0
- compose_lint-0.2.0/tests/test_CL0002.py +49 -0
- compose_lint-0.2.0/tests/test_CL0003.py +63 -0
- compose_lint-0.2.0/tests/test_CL0004.py +71 -0
- compose_lint-0.2.0/tests/test_CL0005.py +61 -0
- compose_lint-0.2.0/tests/test_CL0006.py +75 -0
- compose_lint-0.2.0/tests/test_CL0007.py +60 -0
- compose_lint-0.2.0/tests/test_CL0008.py +81 -0
- compose_lint-0.2.0/tests/test_CL0009.py +124 -0
- compose_lint-0.2.0/tests/test_CL0010.py +93 -0
- compose_lint-0.2.0/tests/test_cli.py +65 -0
- compose_lint-0.2.0/tests/test_config.py +110 -0
- compose_lint-0.2.0/tests/test_engine.py +174 -0
- compose_lint-0.2.0/tests/test_integration.py +166 -0
- compose_lint-0.2.0/tests/test_kics_comparison.py +143 -0
- compose_lint-0.2.0/tests/test_parser.py +83 -0
- compose_lint-0.2.0/tests/test_sarif.py +205 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
# Keep GitHub Actions pinned versions current — covers ci.yml, publish.yml,
|
|
4
|
+
# codeql.yml, scorecard.yml, and the composite action.yml at repo root.
|
|
5
|
+
- package-ecosystem: github-actions
|
|
6
|
+
directory: "/"
|
|
7
|
+
schedule:
|
|
8
|
+
interval: weekly
|
|
9
|
+
groups:
|
|
10
|
+
actions:
|
|
11
|
+
patterns: ["*"]
|
|
12
|
+
|
|
13
|
+
# Python dependencies live in pyproject.toml. PyYAML is the only runtime
|
|
14
|
+
# dep; the rest are dev tools (ruff, mypy, pytest, types-pyyaml).
|
|
15
|
+
- package-ecosystem: pip
|
|
16
|
+
directory: "/"
|
|
17
|
+
schedule:
|
|
18
|
+
interval: weekly
|
|
19
|
+
groups:
|
|
20
|
+
python:
|
|
21
|
+
patterns: ["*"]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
# Cancel in-progress runs for the same branch/PR when new commits land.
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
# Default to read-only — individual jobs can request more if they need it.
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
lint:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
timeout-minutes: 5
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
|
+
with:
|
|
25
|
+
persist-credentials: false
|
|
26
|
+
- uses: actions/setup-python@v6
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.13"
|
|
29
|
+
- run: pip install ruff
|
|
30
|
+
- run: ruff check src/ tests/
|
|
31
|
+
- run: ruff format --check src/ tests/
|
|
32
|
+
|
|
33
|
+
type-check:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
timeout-minutes: 5
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v6
|
|
38
|
+
with:
|
|
39
|
+
persist-credentials: false
|
|
40
|
+
- uses: actions/setup-python@v6
|
|
41
|
+
with:
|
|
42
|
+
python-version: "3.13"
|
|
43
|
+
- run: pip install -e ".[dev]"
|
|
44
|
+
- run: mypy src/
|
|
45
|
+
|
|
46
|
+
test:
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
timeout-minutes: 10
|
|
49
|
+
strategy:
|
|
50
|
+
fail-fast: false
|
|
51
|
+
matrix:
|
|
52
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/checkout@v6
|
|
55
|
+
with:
|
|
56
|
+
persist-credentials: false
|
|
57
|
+
- uses: actions/setup-python@v6
|
|
58
|
+
with:
|
|
59
|
+
python-version: ${{ matrix.python-version }}
|
|
60
|
+
- run: pip install -e ".[dev]"
|
|
61
|
+
- run: pytest
|
|
62
|
+
|
|
63
|
+
security:
|
|
64
|
+
name: Security scan (bandit + pip-audit)
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
timeout-minutes: 5
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@v6
|
|
69
|
+
with:
|
|
70
|
+
persist-credentials: false
|
|
71
|
+
- uses: actions/setup-python@v6
|
|
72
|
+
with:
|
|
73
|
+
python-version: "3.13"
|
|
74
|
+
- name: Install scan tools
|
|
75
|
+
# Upgrade pip first so pip-audit doesn't flag CVEs in the
|
|
76
|
+
# runner-bundled pip itself (those are out of scope for this
|
|
77
|
+
# project's supply chain).
|
|
78
|
+
run: |
|
|
79
|
+
python -m pip install --upgrade pip
|
|
80
|
+
pip install -e . bandit pip-audit
|
|
81
|
+
- name: Bandit (source SAST)
|
|
82
|
+
run: bandit -r src/ -ll
|
|
83
|
+
- name: pip-audit (dependency CVEs)
|
|
84
|
+
# --skip-editable skips compose-lint itself (it isn't on PyPI
|
|
85
|
+
# for the local install path); PyYAML and any future runtime
|
|
86
|
+
# deps are still scanned.
|
|
87
|
+
run: pip-audit --skip-editable
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
schedule:
|
|
9
|
+
# Weekly run catches new advisories on unchanged code.
|
|
10
|
+
- cron: "27 4 * * 1"
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
analyze:
|
|
17
|
+
name: CodeQL (${{ matrix.language }})
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
timeout-minutes: 15
|
|
20
|
+
permissions:
|
|
21
|
+
# Required to upload SARIF results.
|
|
22
|
+
security-events: write
|
|
23
|
+
# Required for private repos; harmless on public.
|
|
24
|
+
actions: read
|
|
25
|
+
strategy:
|
|
26
|
+
fail-fast: false
|
|
27
|
+
matrix:
|
|
28
|
+
language: [python, actions]
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v6
|
|
31
|
+
with:
|
|
32
|
+
persist-credentials: false
|
|
33
|
+
- uses: github/codeql-action/init@v4
|
|
34
|
+
with:
|
|
35
|
+
languages: ${{ matrix.language }}
|
|
36
|
+
queries: security-and-quality
|
|
37
|
+
- uses: github/codeql-action/analyze@v4
|
|
38
|
+
with:
|
|
39
|
+
category: "/language:${{ matrix.language }}"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
# Default to read-only — individual jobs request what they need.
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
timeout-minutes: 10
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
with:
|
|
19
|
+
persist-credentials: false
|
|
20
|
+
- uses: actions/setup-python@v6
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.13"
|
|
23
|
+
- run: pip install build
|
|
24
|
+
- run: python -m build
|
|
25
|
+
- name: Verify dist contents
|
|
26
|
+
run: |
|
|
27
|
+
pip install twine
|
|
28
|
+
twine check dist/*
|
|
29
|
+
# Wheel must NOT contain CLAUDE.md, .env, tests, or .git.
|
|
30
|
+
if unzip -l dist/*.whl | grep -E '(CLAUDE\.md|\.env|/tests/|\.git/)'; then
|
|
31
|
+
echo "::error::Wheel contains forbidden files"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
- uses: actions/upload-artifact@v7
|
|
35
|
+
with:
|
|
36
|
+
name: dist
|
|
37
|
+
path: dist/
|
|
38
|
+
|
|
39
|
+
testpypi:
|
|
40
|
+
needs: build
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
timeout-minutes: 10
|
|
43
|
+
# Gate real PyPI on a successful TestPyPI publish — a TestPyPI failure
|
|
44
|
+
# almost always means real PyPI would fail too, so we fail fast before
|
|
45
|
+
# burning a version number on the real index.
|
|
46
|
+
environment:
|
|
47
|
+
name: testpypi
|
|
48
|
+
url: https://test.pypi.org/p/compose-lint
|
|
49
|
+
permissions:
|
|
50
|
+
# OIDC token for TestPyPI Trusted Publishing.
|
|
51
|
+
id-token: write
|
|
52
|
+
# Write Sigstore build attestations to the workflow run.
|
|
53
|
+
attestations: write
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/download-artifact@v8
|
|
56
|
+
with:
|
|
57
|
+
name: dist
|
|
58
|
+
path: dist/
|
|
59
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
60
|
+
with:
|
|
61
|
+
repository-url: https://test.pypi.org/legacy/
|
|
62
|
+
attestations: true
|
|
63
|
+
|
|
64
|
+
publish:
|
|
65
|
+
needs: testpypi
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
timeout-minutes: 10
|
|
68
|
+
environment:
|
|
69
|
+
name: pypi
|
|
70
|
+
url: https://pypi.org/p/compose-lint
|
|
71
|
+
permissions:
|
|
72
|
+
# OIDC token for PyPI Trusted Publishing.
|
|
73
|
+
id-token: write
|
|
74
|
+
# Write Sigstore build attestations to the workflow run.
|
|
75
|
+
attestations: write
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/download-artifact@v8
|
|
78
|
+
with:
|
|
79
|
+
name: dist
|
|
80
|
+
path: dist/
|
|
81
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
82
|
+
with:
|
|
83
|
+
attestations: true
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: OpenSSF Scorecard
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
branch_protection_rule:
|
|
5
|
+
schedule:
|
|
6
|
+
- cron: "13 5 * * 1"
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
|
|
10
|
+
permissions: read-all
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
analysis:
|
|
14
|
+
name: Scorecard analysis
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
timeout-minutes: 10
|
|
17
|
+
permissions:
|
|
18
|
+
security-events: write
|
|
19
|
+
id-token: write
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v6
|
|
22
|
+
with:
|
|
23
|
+
persist-credentials: false
|
|
24
|
+
- uses: ossf/scorecard-action@v2.4.3
|
|
25
|
+
with:
|
|
26
|
+
results_file: results.sarif
|
|
27
|
+
results_format: sarif
|
|
28
|
+
publish_results: true
|
|
29
|
+
- uses: actions/upload-artifact@v7
|
|
30
|
+
with:
|
|
31
|
+
name: scorecard-results
|
|
32
|
+
path: results.sarif
|
|
33
|
+
retention-days: 5
|
|
34
|
+
- uses: github/codeql-action/upload-sarif@v4
|
|
35
|
+
with:
|
|
36
|
+
sarif_file: results.sarif
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Planning and internal docs
|
|
2
|
+
compose-lint-plan.md
|
|
3
|
+
|
|
4
|
+
# AI assistant configuration
|
|
5
|
+
CLAUDE.md
|
|
6
|
+
.claude/
|
|
7
|
+
|
|
8
|
+
# Python bytecode
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
*$py.class
|
|
12
|
+
*.pyo
|
|
13
|
+
|
|
14
|
+
# Distribution / packaging
|
|
15
|
+
dist/
|
|
16
|
+
build/
|
|
17
|
+
*.egg-info/
|
|
18
|
+
*.egg
|
|
19
|
+
src/*.egg-info/
|
|
20
|
+
|
|
21
|
+
# Virtual environments
|
|
22
|
+
.venv/
|
|
23
|
+
venv/
|
|
24
|
+
ENV/
|
|
25
|
+
|
|
26
|
+
# IDE / editor
|
|
27
|
+
.vscode/
|
|
28
|
+
.idea/
|
|
29
|
+
*.swp
|
|
30
|
+
*.swo
|
|
31
|
+
*~
|
|
32
|
+
.project
|
|
33
|
+
.settings/
|
|
34
|
+
|
|
35
|
+
# Testing / coverage
|
|
36
|
+
.pytest_cache/
|
|
37
|
+
.coverage
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.nox/
|
|
41
|
+
|
|
42
|
+
# Type checking
|
|
43
|
+
.mypy_cache/
|
|
44
|
+
.dmypy.json
|
|
45
|
+
|
|
46
|
+
# Ruff
|
|
47
|
+
.ruff_cache/
|
|
48
|
+
|
|
49
|
+
# Environment / secrets
|
|
50
|
+
.env
|
|
51
|
+
.env.*
|
|
52
|
+
!.env.example
|
|
53
|
+
|
|
54
|
+
# OS files
|
|
55
|
+
.DS_Store
|
|
56
|
+
Thumbs.db
|
|
57
|
+
|
|
58
|
+
# Build isolation
|
|
59
|
+
*.spec
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2026-04-10
|
|
9
|
+
|
|
10
|
+
First public release.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- 10 security rules grounded in OWASP Docker Security Cheat Sheet and the CIS
|
|
15
|
+
Docker Benchmark:
|
|
16
|
+
- **CL-0001**: Docker socket mounted (CRITICAL)
|
|
17
|
+
- **CL-0002**: Privileged mode enabled (CRITICAL)
|
|
18
|
+
- **CL-0003**: Privilege escalation not blocked (MEDIUM)
|
|
19
|
+
- **CL-0004**: Image not pinned to version (MEDIUM)
|
|
20
|
+
- **CL-0005**: Ports bound to all interfaces (HIGH)
|
|
21
|
+
- **CL-0006**: No capability restrictions (MEDIUM)
|
|
22
|
+
- **CL-0007**: Filesystem not read-only (MEDIUM)
|
|
23
|
+
- **CL-0008**: Host network mode (HIGH)
|
|
24
|
+
- **CL-0009**: Security profile disabled (HIGH)
|
|
25
|
+
- **CL-0010**: Host namespace sharing (HIGH)
|
|
26
|
+
- CVSS-aligned severity model with a documented scoring matrix (`docs/severity.md`).
|
|
27
|
+
- Output formatters: `text` (colored, with fix guidance and references), `json`
|
|
28
|
+
(for CI integration), and `sarif` (SARIF 2.1.0, for GitHub Code Scanning).
|
|
29
|
+
- GitHub Action (`tmatens/compose-lint@v0.2.0`) with optional SARIF upload to the
|
|
30
|
+
Code Scanning tab.
|
|
31
|
+
- Auto-discovery of `compose.yml` / `docker-compose.yml` (and their `.yaml` /
|
|
32
|
+
`.override.*` variants) when no file arguments are given.
|
|
33
|
+
- Configuration via `.compose-lint.yml`: disable rules, override severity, record
|
|
34
|
+
an exception `reason` that flows through to all output formats.
|
|
35
|
+
- Suppressed-finding reporting with `--skip-suppressed` to hide them from output.
|
|
36
|
+
- Documented exit code contract (0 = clean, 1 = findings at/above threshold,
|
|
37
|
+
2 = usage error) and `--fail-on` flag to set the threshold.
|
|
38
|
+
- Pre-commit hook support via `.pre-commit-hooks.yaml`.
|
|
39
|
+
- Python 3.10–3.13 support.
|
|
40
|
+
|
|
41
|
+
### Security
|
|
42
|
+
|
|
43
|
+
- PyPI releases use Trusted Publishing (OIDC) with Sigstore build attestations.
|
|
44
|
+
No long-lived API tokens.
|
|
45
|
+
- TestPyPI publish gates the real PyPI publish — a TestPyPI failure aborts the
|
|
46
|
+
release before a version number is burned on the real index.
|
|
47
|
+
- Supply chain hardening: CodeQL (python + actions), OpenSSF Scorecard, Bandit,
|
|
48
|
+
pip-audit, and Dependabot all run on every push and weekly.
|
|
49
|
+
- GitHub Actions workflows are pinned, scoped to least-privilege permissions, and
|
|
50
|
+
use `persist-credentials: false` on checkout. The composite action passes user
|
|
51
|
+
inputs through `env:` rather than direct `${{ }}` interpolation to prevent
|
|
52
|
+
shell injection.
|
|
53
|
+
|
|
54
|
+
[0.2.0]: https://github.com/tmatens/compose-lint/releases/tag/v0.2.0
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Contributing to compose-lint
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing. This guide covers setup, code standards, and how to add new rules.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/tmatens/compose-lint.git
|
|
9
|
+
cd compose-lint
|
|
10
|
+
python -m venv .venv
|
|
11
|
+
source .venv/bin/activate
|
|
12
|
+
pip install -e ".[dev]"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Running Quality Checks
|
|
16
|
+
|
|
17
|
+
All four must pass before submitting a PR:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
ruff check src/ tests/ # Linting
|
|
21
|
+
ruff format --check src/ tests/ # Formatting
|
|
22
|
+
mypy src/ # Type checking (strict mode)
|
|
23
|
+
pytest # Tests
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Code Standards
|
|
27
|
+
|
|
28
|
+
- **Python 3.10+** required
|
|
29
|
+
- **Type annotations** on all public functions (`mypy --strict`)
|
|
30
|
+
- **PyYAML** is the only runtime dependency. Do not add others without discussion.
|
|
31
|
+
- **Rules receive plain Python types** (`dict`, `list`, `str`). Never leak parser-specific types into rule code.
|
|
32
|
+
|
|
33
|
+
## Adding a New Rule
|
|
34
|
+
|
|
35
|
+
1. Create `src/compose_lint/rules/CL{NNNN}_{snake_name}.py`
|
|
36
|
+
2. Inherit from `BaseRule`, use the `@register_rule` decorator
|
|
37
|
+
3. Set `id`, `name`, `severity`, `description`, `references`
|
|
38
|
+
4. Implement `check(service_name, service_config, global_config, lines)` yielding `Finding` objects
|
|
39
|
+
5. Add test file `tests/test_CL{NNNN}.py` with positive and negative cases
|
|
40
|
+
6. Add fixture YAML files in `tests/compose_files/`
|
|
41
|
+
7. Add rule documentation in `docs/rules/CL-{NNNN}.md`
|
|
42
|
+
|
|
43
|
+
### Rule requirements
|
|
44
|
+
|
|
45
|
+
- Every rule must reference OWASP Docker Security Cheat Sheet, CIS Docker Benchmark, or Docker official docs. No opinion-only rules.
|
|
46
|
+
- Every finding must be actionable with specific fix guidance.
|
|
47
|
+
- Severity must reflect real-world exploitability, not subjective importance.
|
|
48
|
+
- Rule IDs are permanent and never reused.
|
|
49
|
+
|
|
50
|
+
## Pull Requests
|
|
51
|
+
|
|
52
|
+
- One feature or fix per PR
|
|
53
|
+
- All quality checks must pass
|
|
54
|
+
- Include tests for any new behavior
|
|
55
|
+
- Commit messages should explain *why*, not just *what*
|
|
56
|
+
|
|
57
|
+
## Questions?
|
|
58
|
+
|
|
59
|
+
Open an issue if something is unclear.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Todd Matens
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|