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.
Files changed (96) hide show
  1. compose_lint-0.2.0/.github/dependabot.yml +21 -0
  2. compose_lint-0.2.0/.github/workflows/ci.yml +87 -0
  3. compose_lint-0.2.0/.github/workflows/codeql.yml +39 -0
  4. compose_lint-0.2.0/.github/workflows/publish.yml +83 -0
  5. compose_lint-0.2.0/.github/workflows/scorecard.yml +36 -0
  6. compose_lint-0.2.0/.gitignore +59 -0
  7. compose_lint-0.2.0/.pre-commit-hooks.yaml +7 -0
  8. compose_lint-0.2.0/CHANGELOG.md +54 -0
  9. compose_lint-0.2.0/CONTRIBUTING.md +59 -0
  10. compose_lint-0.2.0/LICENSE +21 -0
  11. compose_lint-0.2.0/PKG-INFO +246 -0
  12. compose_lint-0.2.0/README.md +213 -0
  13. compose_lint-0.2.0/SECURITY.md +48 -0
  14. compose_lint-0.2.0/action.yml +108 -0
  15. compose_lint-0.2.0/docs/adr/001-python.md +14 -0
  16. compose_lint-0.2.0/docs/adr/002-rule-grounding.md +12 -0
  17. compose_lint-0.2.0/docs/adr/003-yaml-parsing.md +18 -0
  18. compose_lint-0.2.0/docs/adr/004-rule-architecture.md +13 -0
  19. compose_lint-0.2.0/docs/adr/005-rule-id-scheme.md +19 -0
  20. compose_lint-0.2.0/docs/adr/006-exit-codes.md +12 -0
  21. compose_lint-0.2.0/docs/rule-comparison.md +138 -0
  22. compose_lint-0.2.0/docs/rules/CL-0001.md +45 -0
  23. compose_lint-0.2.0/docs/rules/CL-0002.md +38 -0
  24. compose_lint-0.2.0/docs/rules/CL-0003.md +24 -0
  25. compose_lint-0.2.0/docs/rules/CL-0004.md +33 -0
  26. compose_lint-0.2.0/docs/rules/CL-0005.md +35 -0
  27. compose_lint-0.2.0/docs/rules/CL-0006.md +42 -0
  28. compose_lint-0.2.0/docs/rules/CL-0007.md +43 -0
  29. compose_lint-0.2.0/docs/rules/CL-0008.md +42 -0
  30. compose_lint-0.2.0/docs/rules/CL-0009.md +50 -0
  31. compose_lint-0.2.0/docs/rules/CL-0010.md +51 -0
  32. compose_lint-0.2.0/docs/severity.md +65 -0
  33. compose_lint-0.2.0/pyproject.toml +80 -0
  34. compose_lint-0.2.0/src/compose_lint/__init__.py +3 -0
  35. compose_lint-0.2.0/src/compose_lint/__main__.py +5 -0
  36. compose_lint-0.2.0/src/compose_lint/cli.py +160 -0
  37. compose_lint-0.2.0/src/compose_lint/config.py +89 -0
  38. compose_lint-0.2.0/src/compose_lint/engine.py +79 -0
  39. compose_lint-0.2.0/src/compose_lint/formatters/__init__.py +1 -0
  40. compose_lint-0.2.0/src/compose_lint/formatters/json.py +29 -0
  41. compose_lint-0.2.0/src/compose_lint/formatters/sarif.py +140 -0
  42. compose_lint-0.2.0/src/compose_lint/formatters/text.py +113 -0
  43. compose_lint-0.2.0/src/compose_lint/models.py +70 -0
  44. compose_lint-0.2.0/src/compose_lint/parser.py +138 -0
  45. compose_lint-0.2.0/src/compose_lint/rules/CL0001_docker_socket.py +73 -0
  46. compose_lint-0.2.0/src/compose_lint/rules/CL0002_privileged_mode.py +67 -0
  47. compose_lint-0.2.0/src/compose_lint/rules/CL0003_no_new_privileges.py +74 -0
  48. compose_lint-0.2.0/src/compose_lint/rules/CL0004_image_not_pinned.py +96 -0
  49. compose_lint-0.2.0/src/compose_lint/rules/CL0005_unbound_ports.py +144 -0
  50. compose_lint-0.2.0/src/compose_lint/rules/CL0006_cap_drop.py +73 -0
  51. compose_lint-0.2.0/src/compose_lint/rules/CL0007_read_only.py +64 -0
  52. compose_lint-0.2.0/src/compose_lint/rules/CL0008_host_network.py +65 -0
  53. compose_lint-0.2.0/src/compose_lint/rules/CL0009_security_profile.py +83 -0
  54. compose_lint-0.2.0/src/compose_lint/rules/CL0010_host_namespace.py +79 -0
  55. compose_lint-0.2.0/src/compose_lint/rules/__init__.py +71 -0
  56. compose_lint-0.2.0/tests/__init__.py +0 -0
  57. compose_lint-0.2.0/tests/compose_files/insecure_cap_drop.yml +17 -0
  58. compose_lint-0.2.0/tests/compose_files/insecure_host_namespace.yml +20 -0
  59. compose_lint-0.2.0/tests/compose_files/insecure_host_network.yml +12 -0
  60. compose_lint-0.2.0/tests/compose_files/insecure_image_tags.yml +19 -0
  61. compose_lint-0.2.0/tests/compose_files/insecure_no_new_priv.yml +14 -0
  62. compose_lint-0.2.0/tests/compose_files/insecure_ports.yml +31 -0
  63. compose_lint-0.2.0/tests/compose_files/insecure_privileged.yml +6 -0
  64. compose_lint-0.2.0/tests/compose_files/insecure_read_only.yml +12 -0
  65. compose_lint-0.2.0/tests/compose_files/insecure_security_profile.yml +24 -0
  66. compose_lint-0.2.0/tests/compose_files/insecure_socket.yml +11 -0
  67. compose_lint-0.2.0/tests/compose_files/invalid_empty.yml +0 -0
  68. compose_lint-0.2.0/tests/compose_files/invalid_no_services.yml +3 -0
  69. compose_lint-0.2.0/tests/compose_files/invalid_service_not_mapping.yml +2 -0
  70. compose_lint-0.2.0/tests/compose_files/invalid_services_not_mapping.yml +3 -0
  71. compose_lint-0.2.0/tests/compose_files/invalid_yaml.yml +7 -0
  72. compose_lint-0.2.0/tests/compose_files/kics_comparison.yml +121 -0
  73. compose_lint-0.2.0/tests/compose_files/mixed.yml +19 -0
  74. compose_lint-0.2.0/tests/compose_files/suppress_config.yml +6 -0
  75. compose_lint-0.2.0/tests/compose_files/valid_anchors.yml +12 -0
  76. compose_lint-0.2.0/tests/compose_files/valid_basic.yml +23 -0
  77. compose_lint-0.2.0/tests/compose_files/valid_env_interpolation.yml +5 -0
  78. compose_lint-0.2.0/tests/compose_files/valid_v2.yml +9 -0
  79. compose_lint-0.2.0/tests/compose_files/warnings_only.yml +9 -0
  80. compose_lint-0.2.0/tests/test_CL0001.py +66 -0
  81. compose_lint-0.2.0/tests/test_CL0002.py +49 -0
  82. compose_lint-0.2.0/tests/test_CL0003.py +63 -0
  83. compose_lint-0.2.0/tests/test_CL0004.py +71 -0
  84. compose_lint-0.2.0/tests/test_CL0005.py +61 -0
  85. compose_lint-0.2.0/tests/test_CL0006.py +75 -0
  86. compose_lint-0.2.0/tests/test_CL0007.py +60 -0
  87. compose_lint-0.2.0/tests/test_CL0008.py +81 -0
  88. compose_lint-0.2.0/tests/test_CL0009.py +124 -0
  89. compose_lint-0.2.0/tests/test_CL0010.py +93 -0
  90. compose_lint-0.2.0/tests/test_cli.py +65 -0
  91. compose_lint-0.2.0/tests/test_config.py +110 -0
  92. compose_lint-0.2.0/tests/test_engine.py +174 -0
  93. compose_lint-0.2.0/tests/test_integration.py +166 -0
  94. compose_lint-0.2.0/tests/test_kics_comparison.py +143 -0
  95. compose_lint-0.2.0/tests/test_parser.py +83 -0
  96. 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,7 @@
1
+ - id: compose-lint
2
+ name: compose-lint
3
+ description: Security-focused linter for Docker Compose files
4
+ entry: compose-lint
5
+ language: python
6
+ types: [yaml]
7
+ files: (^|/)docker-compose[^/]*\.ya?ml$|compose[^/]*\.ya?ml$
@@ -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.