remit-actions 1__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.
- remit_actions-1/CHANGELOG.md +51 -0
- remit_actions-1/CONTRIBUTING.md +85 -0
- remit_actions-1/LICENSE +21 -0
- remit_actions-1/MANIFEST.in +9 -0
- remit_actions-1/PKG-INFO +260 -0
- remit_actions-1/README.md +232 -0
- remit_actions-1/SECURITY.md +24 -0
- remit_actions-1/SETUP.md +169 -0
- remit_actions-1/action.yml +63 -0
- remit_actions-1/benchmarks/README.md +51 -0
- remit_actions-1/benchmarks/expected-summary.json +18 -0
- remit_actions-1/benchmarks/manifest.jsonl +74 -0
- remit_actions-1/benchmarks/manifest.sha256 +1 -0
- remit_actions-1/docs/ARCHITECTURE.md +95 -0
- remit_actions-1/docs/RULES.md +256 -0
- remit_actions-1/examples/association-base/.github/workflows/deploy.yml +15 -0
- remit_actions-1/examples/association-head/.github/workflows/deploy.yml +15 -0
- remit_actions-1/examples/base/.github/workflows/release.yml +8 -0
- remit_actions-1/examples/github-action-template.yml +45 -0
- remit_actions-1/examples/head/.github/workflows/release.yml +13 -0
- remit_actions-1/examples/sample-report.json +70 -0
- remit_actions-1/examples/sample-report.md +33 -0
- remit_actions-1/pyproject.toml +54 -0
- remit_actions-1/requirements.txt +75 -0
- remit_actions-1/scripts/action_entry.py +55 -0
- remit_actions-1/scripts/action_runner.py +210 -0
- remit_actions-1/scripts/compare_zizmor.py +94 -0
- remit_actions-1/scripts/hosted_fixture.py +52 -0
- remit_actions-1/scripts/offline_smoke.py +125 -0
- remit_actions-1/scripts/replay_benchmark.py +96 -0
- remit_actions-1/scripts/run-action.sh +5 -0
- remit_actions-1/scripts/validate.py +45 -0
- remit_actions-1/scripts/vendor_pyyaml.py +68 -0
- remit_actions-1/setup.cfg +4 -0
- remit_actions-1/src/remit/__init__.py +3 -0
- remit_actions-1/src/remit/__main__.py +3 -0
- remit_actions-1/src/remit/cli.py +101 -0
- remit_actions-1/src/remit/contexts.py +279 -0
- remit_actions-1/src/remit/expressions.py +7 -0
- remit_actions-1/src/remit/flows.py +483 -0
- remit_actions-1/src/remit/report.py +142 -0
- remit_actions-1/src/remit/sarif.py +74 -0
- remit_actions-1/src/remit/scan.py +644 -0
- remit_actions-1/src/remit/sources.py +223 -0
- remit_actions-1/src/remit/yaml_limits.py +52 -0
- remit_actions-1/src/remit_actions.egg-info/PKG-INFO +260 -0
- remit_actions-1/src/remit_actions.egg-info/SOURCES.txt +88 -0
- remit_actions-1/src/remit_actions.egg-info/dependency_links.txt +1 -0
- remit_actions-1/src/remit_actions.egg-info/entry_points.txt +2 -0
- remit_actions-1/src/remit_actions.egg-info/requires.txt +1 -0
- remit_actions-1/src/remit_actions.egg-info/top_level.txt +1 -0
- remit_actions-1/tests/alias_cases.json +199 -0
- remit_actions-1/tests/cases.json +394 -0
- remit_actions-1/tests/hosted_cases.json +168 -0
- remit_actions-1/tests/test_action_outcomes.py +228 -0
- remit_actions-1/tests/test_aliases.py +121 -0
- remit_actions-1/tests/test_associations.py +233 -0
- remit_actions-1/tests/test_audit.py +181 -0
- remit_actions-1/tests/test_contexts.py +178 -0
- remit_actions-1/tests/test_expression_identifiers.py +84 -0
- remit_actions-1/tests/test_flows.py +353 -0
- remit_actions-1/tests/test_hosted_fixtures.py +45 -0
- remit_actions-1/tests/test_indirect_flows.py +132 -0
- remit_actions-1/tests/test_installation.py +72 -0
- remit_actions-1/tests/test_mvp.py +579 -0
- remit_actions-1/tests/test_offline_runtime.py +58 -0
- remit_actions-1/tests/test_report.py +139 -0
- remit_actions-1/tests/test_review_fixes.py +131 -0
- remit_actions-1/tests/test_sarif.py +69 -0
- remit_actions-1/tests/test_vendor_refresh.py +73 -0
- remit_actions-1/vendor/README.md +23 -0
- remit_actions-1/vendor/pyyaml/LICENSE +20 -0
- remit_actions-1/vendor/pyyaml/manifest.json +28 -0
- remit_actions-1/vendor/pyyaml/yaml/__init__.py +390 -0
- remit_actions-1/vendor/pyyaml/yaml/composer.py +139 -0
- remit_actions-1/vendor/pyyaml/yaml/constructor.py +748 -0
- remit_actions-1/vendor/pyyaml/yaml/cyaml.py +101 -0
- remit_actions-1/vendor/pyyaml/yaml/dumper.py +62 -0
- remit_actions-1/vendor/pyyaml/yaml/emitter.py +1137 -0
- remit_actions-1/vendor/pyyaml/yaml/error.py +75 -0
- remit_actions-1/vendor/pyyaml/yaml/events.py +86 -0
- remit_actions-1/vendor/pyyaml/yaml/loader.py +63 -0
- remit_actions-1/vendor/pyyaml/yaml/nodes.py +49 -0
- remit_actions-1/vendor/pyyaml/yaml/parser.py +589 -0
- remit_actions-1/vendor/pyyaml/yaml/reader.py +185 -0
- remit_actions-1/vendor/pyyaml/yaml/representer.py +389 -0
- remit_actions-1/vendor/pyyaml/yaml/resolver.py +227 -0
- remit_actions-1/vendor/pyyaml/yaml/scanner.py +1435 -0
- remit_actions-1/vendor/pyyaml/yaml/serializer.py +111 -0
- remit_actions-1/vendor/pyyaml/yaml/tokens.py +104 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Version 1 — 2026-11-04
|
|
4
|
+
|
|
5
|
+
First public release.
|
|
6
|
+
|
|
7
|
+
- **Ten rules** for privilege changes between a pull request's base and head:
|
|
8
|
+
token scope increases (WF001), jobs relying on or leaving the repository's
|
|
9
|
+
default token permissions (WF002), review-sensitive triggers such as
|
|
10
|
+
`pull_request_target` (WF003), new secret references and forwarding (WF004),
|
|
11
|
+
self-hosted, custom or group runners (WF005), environment changes (WF007),
|
|
12
|
+
runner/environment re-pairing in matrices (WF008), new third-party code in a
|
|
13
|
+
privileged job (WF009), pull request content brought into a
|
|
14
|
+
`pull_request_target`, `workflow_run` or `issue_comment` job (WF010), and
|
|
15
|
+
mutable pull request references checked out after a label, comment or
|
|
16
|
+
environment approval (WF011, time-of-check to time-of-use).
|
|
17
|
+
- **Compares from the merge-base.** When the base comes from the pull request
|
|
18
|
+
event, the Action compares from where the PR branched off, as GitHub's Files
|
|
19
|
+
changed view does, so a stale branch doesn't show base-branch changes reversed.
|
|
20
|
+
- **Indirect references are followed:** WF010 and WF011 trace pull request
|
|
21
|
+
references through environment variables, `$GITHUB_ENV`, step outputs,
|
|
22
|
+
`needs` job outputs and action inputs, and treat runtime lookups such as
|
|
23
|
+
`gh pr view` as pull request content. Besides checkouts, WF010 recognizes
|
|
24
|
+
clones of the head repository, `gh pr diff | git apply`, archive downloads of
|
|
25
|
+
the head commit, and artifact downloads with `gh run download` or
|
|
26
|
+
`github-script`.
|
|
27
|
+
- **Comment commands count as approval only when checked:** an `issue_comment`
|
|
28
|
+
job is treated as gated (WF011) only when it, or a job it needs, checks who
|
|
29
|
+
commented. A check of the comment's text alone is WF010, since anyone can
|
|
30
|
+
type it.
|
|
31
|
+
- **`remit audit`** reports WF010 and WF011 in a repository's current state,
|
|
32
|
+
for a directory or any Git commit, as JSON, Markdown or SARIF, with an
|
|
33
|
+
optional `--fail-on-findings` exit code for scheduled checks.
|
|
34
|
+
- **Local code is followed:** steps inside local composite actions and local
|
|
35
|
+
reusable workflows (`./path` and `$/path`) count, and a change to them is
|
|
36
|
+
caught even when no workflow file changed. A local file that can't be read or
|
|
37
|
+
parsed makes the analysis `incomplete` for every job that uses it.
|
|
38
|
+
- **Reports** as pull request annotations on the line that introduced each
|
|
39
|
+
finding (an incomplete analysis first, then the most serious rules, with a
|
|
40
|
+
job's WF001 scopes combined so GitHub's 10-annotation limit doesn't hide them), a Markdown job summary with a review question per rule, JSON, and
|
|
41
|
+
SARIF for GitHub code scanning.
|
|
42
|
+
- **GitHub Action** with `advisory` and `require-complete` modes. Findings never
|
|
43
|
+
fail the step; incomplete or failed analysis is reported explicitly.
|
|
44
|
+
- **Safe on untrusted pull requests:** the head is read as Git data and never
|
|
45
|
+
executed, the analysis makes no network calls, and the Action ships a
|
|
46
|
+
hash-verified PyYAML bundle. Git reads ignore caller redirection and
|
|
47
|
+
replacement refs, and YAML anchors are bounded before parsing.
|
|
48
|
+
- **CLI** `remit` (`pip install remit-actions`), for directories or local Git commits.
|
|
49
|
+
- **Validation:** the test suite on Python 3.11–3.14, an end-to-end Action run
|
|
50
|
+
with networking disabled, a hosted contract of 18 cases in both modes, a
|
|
51
|
+
replay of 74 historical public PRs, and a reproducible comparison with zizmor.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Development
|
|
4
|
+
|
|
5
|
+
Python 3.11+ and Git are required.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
python3 -m venv .venv
|
|
9
|
+
. .venv/bin/activate
|
|
10
|
+
pip install -e .
|
|
11
|
+
python scripts/validate.py
|
|
12
|
+
python scripts/offline_smoke.py
|
|
13
|
+
pip install ruff==0.16.9 && ruff check
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
To run one test file: `python -m unittest discover -s tests -p 'test_contexts.py'`.
|
|
17
|
+
|
|
18
|
+
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for how the code is organised.
|
|
19
|
+
|
|
20
|
+
## Tests
|
|
21
|
+
|
|
22
|
+
| Command | What it covers |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `python scripts/validate.py` | The full unit and integration suite: rules, parser limits, source readers, CLI, the Action controller, and the template's fetch step against local Git repositories. Writes `validation-results.json` (git-ignored) with versions and source hashes. |
|
|
25
|
+
| `python scripts/offline_smoke.py` | Runs the real Action controller against local Git fixtures in both modes. In CI, `--network-namespace` runs it with networking disabled. |
|
|
26
|
+
| `scripts/replay_benchmark.py` | Replays [74 historical public PRs](benchmarks/README.md) and checks the expected WF001/WF003/WF004 findings. |
|
|
27
|
+
| `scripts/compare_zizmor.py` | Needs `zizmor` and a replay cache. Measures how many findings zizmor's new-findings view also surfaces. |
|
|
28
|
+
| *Action outcome contract* workflow | Manually dispatched. Runs the published, pinned Action against the fixtures in `tests/hosted_cases.json` in both modes. |
|
|
29
|
+
|
|
30
|
+
CI runs `ruff check`, plus `validate.py` and the network-isolated smoke test on
|
|
31
|
+
Python 3.11–3.14. The repository also runs its own Action on every PR
|
|
32
|
+
([`privilege-review.yml`](.github/workflows/privilege-review.yml)), loaded from
|
|
33
|
+
the PR's base commit.
|
|
34
|
+
|
|
35
|
+
## Changing behaviour
|
|
36
|
+
|
|
37
|
+
- Add a regression test that fails without the change, plus a nearby case that
|
|
38
|
+
should stay quiet. Cover `incomplete`/`error` outcomes too.
|
|
39
|
+
- Findings already established must survive uncertainty elsewhere in the file.
|
|
40
|
+
- Analysis must stay local: no executing workflow content, no network access, no
|
|
41
|
+
querying live repository settings.
|
|
42
|
+
- Keep rule IDs stable. Bump `schemaVersion` only for incompatible changes to the
|
|
43
|
+
JSON report.
|
|
44
|
+
- Update [docs/RULES.md](docs/RULES.md) and the `Unreleased` section of the
|
|
45
|
+
[changelog](CHANGELOG.md).
|
|
46
|
+
- If report output changes, regenerate `examples/sample-report.md` and
|
|
47
|
+
`examples/sample-report.json` (a test checks them).
|
|
48
|
+
- For rule changes, run the replay and look at any new findings. Don't change an
|
|
49
|
+
expected result just to make the replay pass.
|
|
50
|
+
- Changes to the Action controller, template or bundled dependency need the
|
|
51
|
+
offline smoke test, and the hosted contract before release.
|
|
52
|
+
|
|
53
|
+
## Releases
|
|
54
|
+
|
|
55
|
+
1. Update the version in `pyproject.toml` and `src/remit/__init__.py`,
|
|
56
|
+
and move `Unreleased` entries in the changelog under the new version.
|
|
57
|
+
2. Run CI, the offline smoke test, the replay and the hosted contract on the
|
|
58
|
+
release candidate.
|
|
59
|
+
3. Merge to `main`, then run [`publish.yml`](.github/workflows/publish.yml)
|
|
60
|
+
from the Actions tab (Run workflow, branch `main`). It refuses other
|
|
61
|
+
branches, builds with hash-pinned tools, tests the wheel and uploads to PyPI
|
|
62
|
+
through trusted publishing. Record the CI and contract runs in the
|
|
63
|
+
changelog entry.
|
|
64
|
+
4. Update the pinned SHA in `README.md`, `examples/github-action-template.yml`
|
|
65
|
+
and the contract workflow.
|
|
66
|
+
|
|
67
|
+
PyPI trusted publishing needs a one-time setup on pypi.org: add a pending
|
|
68
|
+
publisher for project `remit-actions`, owner `adi271213`, repository
|
|
69
|
+
`remit`, workflow `publish.yml`, environment `pypi`.
|
|
70
|
+
|
|
71
|
+
Tags are never moved or deleted after publication.
|
|
72
|
+
|
|
73
|
+
## Feedback
|
|
74
|
+
|
|
75
|
+
If you've tried the check on a repository, the *Trial feedback* issue template
|
|
76
|
+
is the quickest way to say what was useful, what was noise and what it missed.
|
|
77
|
+
|
|
78
|
+
## Pull requests
|
|
79
|
+
|
|
80
|
+
Describe the problem, the new behaviour and the checks you ran. Include a small
|
|
81
|
+
public or synthetic example for bugs. Don't include credentials or private logs.
|
|
82
|
+
Report vulnerabilities privately; see [SECURITY.md](SECURITY.md).
|
|
83
|
+
|
|
84
|
+
`main` is protected: changes go through a pull request with passing CI on all
|
|
85
|
+
four Python versions.
|
remit_actions-1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Remit contributors
|
|
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.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
include action.yml requirements.txt CHANGELOG.md CONTRIBUTING.md SECURITY.md SETUP.md
|
|
2
|
+
recursive-include scripts *.py *.sh
|
|
3
|
+
recursive-include tests *.py *.json
|
|
4
|
+
graft examples
|
|
5
|
+
graft docs
|
|
6
|
+
graft benchmarks
|
|
7
|
+
graft vendor
|
|
8
|
+
prune .benchmark-cache
|
|
9
|
+
global-exclude __pycache__ *.py[cod] .DS_Store
|
remit_actions-1/PKG-INFO
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: remit-actions
|
|
3
|
+
Version: 1
|
|
4
|
+
Summary: See what a pull request changes about what your GitHub Actions workflows are allowed to do.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Source, https://github.com/adi271213/remit
|
|
7
|
+
Project-URL: Issues, https://github.com/adi271213/remit/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/adi271213/remit/blob/main/SETUP.md
|
|
9
|
+
Keywords: github-actions,security,ci,workflows,permissions,supply-chain,code-review
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: PyYAML==6.0.3
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
<div align="center">
|
|
30
|
+
|
|
31
|
+
# Remit
|
|
32
|
+
|
|
33
|
+
**Know when a pull request widens your workflows' authority.**
|
|
34
|
+
|
|
35
|
+
[](https://github.com/adi271213/remit/actions/workflows/ci.yml)
|
|
36
|
+
[](https://pypi.org/project/remit-actions/)
|
|
37
|
+
[](https://pypi.org/project/remit-actions/)
|
|
38
|
+
[](https://github.com/adi271213/remit/blob/main/LICENSE)
|
|
39
|
+
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
The changes that matter in a workflow PR are often a single line: `contents: read`
|
|
43
|
+
becomes `write`, a `pull_request_target` trigger appears, a release job starts
|
|
44
|
+
running a new third-party action with your secrets. Remit compares the base and
|
|
45
|
+
head of every pull request and puts each of those changes on the line that
|
|
46
|
+
introduced it, so reviewers can't miss them.
|
|
47
|
+
|
|
48
|
+
- **Only what changed.** Unchanged configuration stays quiet, so ordinary PRs get no noise.
|
|
49
|
+
- **Flags common pwn-request patterns.** Privileged workflows that start checking out, fetching or downloading pull request content, including the time-of-check to time-of-use trap after a label or comment approval.
|
|
50
|
+
- **Right on the diff.** Findings appear as annotations in the PR's *Files changed* tab.
|
|
51
|
+
- **Safe on untrusted PRs.** The PR is read as data and never executed; the analysis makes no network calls.
|
|
52
|
+
- **Nothing to install.** A pinned Action with a hash-verified bundled parser; no secrets, `contents: read` only.
|
|
53
|
+
|
|
54
|
+
## What it catches
|
|
55
|
+
|
|
56
|
+
A PR edits a release workflow:
|
|
57
|
+
|
|
58
|
+
```diff
|
|
59
|
+
name: Release
|
|
60
|
+
-on: push
|
|
61
|
+
+on: [push, pull_request_target]
|
|
62
|
+
permissions: {}
|
|
63
|
+
jobs:
|
|
64
|
+
publish:
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
permissions:
|
|
67
|
+
- contents: read
|
|
68
|
+
+ contents: write
|
|
69
|
+
+ id-token: write
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v4
|
|
72
|
+
- run: make dist
|
|
73
|
+
+ - uses: someone/release-action@v1
|
|
74
|
+
+ with:
|
|
75
|
+
+ token: ${{ secrets.RELEASE_TOKEN }}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Remit annotates five lines of that diff:
|
|
79
|
+
|
|
80
|
+
| Line | Rule | Finding |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| 2 | WF003 | Review-sensitive trigger `pull_request_target` introduced. |
|
|
83
|
+
| 8 | WF001 | Job publish is newly configured to use `contents: write`. |
|
|
84
|
+
| 9 | WF001 | Job publish is newly configured to request an OIDC identity token. |
|
|
85
|
+
| 13 | WF009 | Job publish now runs third-party code from `someone/release-action@v1`, which can use `contents: write`; `id-token: write`; secrets: `RELEASE_TOKEN`. |
|
|
86
|
+
| 15 | WF004 | Job publish adds a reference or forwarding path to `RELEASE_TOKEN`. |
|
|
87
|
+
|
|
88
|
+
The job summary groups the same findings by workflow and job, with a question
|
|
89
|
+
for the reviewer on each ([sample](https://github.com/adi271213/remit/blob/main/examples/sample-report.md)).
|
|
90
|
+
JSON and SARIF reports carry the same data for tooling and GitHub code scanning.
|
|
91
|
+
|
|
92
|
+
## Quick start
|
|
93
|
+
|
|
94
|
+
1. Copy [`examples/github-action-template.yml`](https://github.com/adi271213/remit/blob/main/examples/github-action-template.yml)
|
|
95
|
+
(45 lines) to `.github/workflows/remit.yml`.
|
|
96
|
+
2. Merge it.
|
|
97
|
+
3. Open a pull request that touches a workflow and look at *Files changed*.
|
|
98
|
+
|
|
99
|
+
The template checks out the base commit, fetches the PR head without checking it
|
|
100
|
+
out, and runs the Action pinned to a full commit SHA:
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
- uses: adi271213/remit@3db5c481c527a7f60bf92c0499083fe15f7829be # version 1
|
|
104
|
+
with:
|
|
105
|
+
mode: advisory
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Use the whole template rather than this step alone: the step needs both commits
|
|
109
|
+
in the local clone. The [setup guide](https://github.com/adi271213/remit/blob/main/SETUP.md)
|
|
110
|
+
covers inputs, outputs, code scanning and troubleshooting.
|
|
111
|
+
|
|
112
|
+
## Rules
|
|
113
|
+
|
|
114
|
+
| Rule | Reports |
|
|
115
|
+
|---|---|
|
|
116
|
+
| WF001 | A job's token scope increases (`none` → `read` → `write`), including new `id-token: write` (OIDC) |
|
|
117
|
+
| WF002 | A job starts relying on the repository's default token permissions, or replaces them with permissions that may exceed the default |
|
|
118
|
+
| WF003 | A newly added `pull_request_target`, `workflow_run`, `issue_comment`, `issues`, `discussion` or `discussion_comment` trigger |
|
|
119
|
+
| WF004 | A job newly references a secret, or forwards all secrets (`secrets: inherit`, `toJSON(secrets)`) |
|
|
120
|
+
| WF005 | A job's runner changes to a self-hosted, custom-label, runner-group or unresolvable selection |
|
|
121
|
+
| WF007 | A job's deployment environment is added, removed or changed |
|
|
122
|
+
| WF008 | A matrix job pairs a runner with a different environment while the overall runner and environment sets stay the same |
|
|
123
|
+
| WF009 | Third-party code (an action or reusable workflow) newly runs in a job with write scopes, OIDC, secrets or an environment |
|
|
124
|
+
| WF010 | A `pull_request_target`, `workflow_run` or `issue_comment` job newly checks out, fetches or downloads pull request content: the "pwn request" |
|
|
125
|
+
| WF011 | An approval-gated privileged job checks out a pull request reference that can change after approval: time-of-check to time-of-use |
|
|
126
|
+
|
|
127
|
+
Rules follow steps into local composite actions and local reusable workflows
|
|
128
|
+
(`./` and `$/` references), so a change inside `.github/actions/` is caught even
|
|
129
|
+
when no workflow file changed.
|
|
130
|
+
|
|
131
|
+
[docs/RULES.md](https://github.com/adi271213/remit/blob/main/docs/RULES.md) describes the exact behaviour of each rule.
|
|
132
|
+
|
|
133
|
+
## Remit and zizmor
|
|
134
|
+
|
|
135
|
+
[zizmor](https://github.com/zizmorcore/zizmor) audits what your workflows *are*
|
|
136
|
+
for risky patterns. Remit reports what a pull request *changed*, including
|
|
137
|
+
increases that never become a zizmor alert because the end state isn't "bad"
|
|
138
|
+
by a state rule: a job gaining `id-token: write`, a new secret reference, a
|
|
139
|
+
changed environment, or a new action receiving write access.
|
|
140
|
+
|
|
141
|
+
On [74 historical public PRs](https://github.com/adi271213/remit/blob/main/benchmarks/README.md),
|
|
142
|
+
Remit reported 72 findings. For 46 of them (64%), zizmor 1.32 (pedantic,
|
|
143
|
+
offline) reported no new finding of a related audit in the same workflow and
|
|
144
|
+
job, including all 8 new secret references. The PRs were selected and labelled
|
|
145
|
+
by this project and the matching is approximate, so read this as an indication,
|
|
146
|
+
not a benchmark. Run zizmor for what is dangerous, and Remit for what got wider.
|
|
147
|
+
|
|
148
|
+
## Results
|
|
149
|
+
|
|
150
|
+
Every run reports an **analysis** status and a **review** status:
|
|
151
|
+
|
|
152
|
+
| Analysis | Meaning | CLI exit | `require-complete` (default) | `advisory` |
|
|
153
|
+
|---|---|---|---|---|
|
|
154
|
+
| `complete` | All changed workflows were compared | 0 | pass | pass |
|
|
155
|
+
| `incomplete` | Some context is unknown, e.g. a dynamic expression | 2 | fail | pass with warning |
|
|
156
|
+
| `error` | Some input couldn't be parsed or read | 2 | fail | pass with warning |
|
|
157
|
+
|
|
158
|
+
Review is `suggested` when there are findings, `none` when analysis is complete
|
|
159
|
+
with no findings, and (in the Action) `unknown` when analysis is incomplete and
|
|
160
|
+
nothing was found. Known findings are reported alongside incomplete or failed parts.
|
|
161
|
+
|
|
162
|
+
**Findings never fail the step.** Remit is a review aid, not a merge gate: a
|
|
163
|
+
clean report doesn't mean a PR is safe, and a PR can edit the workflow that runs
|
|
164
|
+
the check.
|
|
165
|
+
|
|
166
|
+
<details>
|
|
167
|
+
<summary><b>Inherited token permissions</b></summary>
|
|
168
|
+
|
|
169
|
+
A job without a `permissions` block gets the repository or organization default,
|
|
170
|
+
which Remit can't see. When a PR adds such a job, or removes a job's
|
|
171
|
+
`permissions` block, WF002 asks the author to declare permissions explicitly.
|
|
172
|
+
When a job that used the default starts declaring permissions, only levels above
|
|
173
|
+
GitHub's narrowest possible default (`contents: read`, `packages: read`) are
|
|
174
|
+
reported, so ordinary hardening PRs stay quiet. To compare against your real
|
|
175
|
+
default instead, pass it in:
|
|
176
|
+
|
|
177
|
+
```yaml
|
|
178
|
+
with:
|
|
179
|
+
default-permissions: '{"contents": "read"}'
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
</details>
|
|
183
|
+
|
|
184
|
+
<details>
|
|
185
|
+
<summary><b>Limitations</b></summary>
|
|
186
|
+
|
|
187
|
+
- Workflows are read from `.github/workflows/`, and local composite actions and
|
|
188
|
+
reusable workflows are followed. Actions and reusable workflows from other
|
|
189
|
+
repositories, and the contents of scripts, are not analysed.
|
|
190
|
+
- Repository and organization settings are not visible: default token
|
|
191
|
+
permissions, runner inventory, environment protection rules and secrets.
|
|
192
|
+
- WF003 looks at trigger names only. Filter changes on existing triggers
|
|
193
|
+
(branches, types, paths) are not analysed.
|
|
194
|
+
- Expressions are not evaluated. Only direct `${{ matrix.<field> }}` references
|
|
195
|
+
into a static matrix of up to 256 rows are resolved; anything else is reported
|
|
196
|
+
as unresolved.
|
|
197
|
+
- YAML merge keys (`<<`) and recursive aliases are rejected as errors.
|
|
198
|
+
- A renamed file is matched only if its content is identical; an edited rename
|
|
199
|
+
is treated as a new file and can produce findings for unchanged settings.
|
|
200
|
+
- Input limits: 200 workflow files and 4 MiB per commit; per file, 256 KiB and
|
|
201
|
+
60,000 YAML tokens (a very dense file can reach the token limit first).
|
|
202
|
+
- WF010 and WF011 follow pull request references through environment variables,
|
|
203
|
+
`$GITHUB_ENV`, step and job outputs, and action inputs within the repository.
|
|
204
|
+
Values computed in other ways (a script that builds a ref from pieces, files
|
|
205
|
+
passed between jobs) are not traced.
|
|
206
|
+
|
|
207
|
+
</details>
|
|
208
|
+
|
|
209
|
+
## Command line
|
|
210
|
+
|
|
211
|
+
The package is `remit-actions` on PyPI; the command it installs is `remit`.
|
|
212
|
+
|
|
213
|
+
```sh
|
|
214
|
+
pip install remit-actions
|
|
215
|
+
remit scan --repo . --base main --head my-branch --format markdown
|
|
216
|
+
remit scan --dirs path/to/before path/to/after --format sarif --output remit.sarif
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Output is JSON by default; `--format markdown` and `--format sarif` are also
|
|
220
|
+
available. `--summary FILE` appends the Markdown summary and `--sarif FILE`
|
|
221
|
+
also writes SARIF. The exit code is 0 for complete analysis and 2 otherwise.
|
|
222
|
+
|
|
223
|
+
## Audit an existing repository
|
|
224
|
+
|
|
225
|
+
Pull request checks only see what a PR changes. `remit audit` reports what is
|
|
226
|
+
already there: every place a `pull_request_target`, `workflow_run` or
|
|
227
|
+
`issue_comment` job brings in pull request content (WF010), and every mutable
|
|
228
|
+
checkout after a label, comment or environment approval (WF011), including
|
|
229
|
+
through local actions and reusable workflows.
|
|
230
|
+
|
|
231
|
+
```sh
|
|
232
|
+
remit audit --repo . # the current commit
|
|
233
|
+
remit audit --repo . --ref v2.3.0 # any commit or tag
|
|
234
|
+
remit audit --dir path/to/checkout --format markdown
|
|
235
|
+
remit audit --repo . --format sarif --output remit-audit.sarif
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Like `scan`, it reads committed Git objects (or a directory) as data and runs
|
|
239
|
+
nothing. The exit code is 0 for a complete audit, 2 when something couldn't be
|
|
240
|
+
analysed, and with `--fail-on-findings`, 1 when the audit found something. The
|
|
241
|
+
[setup guide](https://github.com/adi271213/remit/blob/main/SETUP.md#scheduled-audit)
|
|
242
|
+
has a weekly scheduled audit workflow.
|
|
243
|
+
|
|
244
|
+
## Status
|
|
245
|
+
|
|
246
|
+
Rule IDs, Action inputs and outputs, and the JSON report format stay compatible
|
|
247
|
+
within a version; an incompatible change gets a new version number. Each release
|
|
248
|
+
runs the test suite on Python 3.11–3.14, runs the Action end to end with networking
|
|
249
|
+
disabled, and replays 74 historical public PRs. The replay's expected results
|
|
250
|
+
were labelled by the project, so treat it as a regression suite rather than an
|
|
251
|
+
accuracy measurement. Feedback from real repositories is the most useful thing
|
|
252
|
+
you can send: open a [trial feedback issue](https://github.com/adi271213/remit/issues/new?template=trial_feedback.md).
|
|
253
|
+
|
|
254
|
+
[Setup](https://github.com/adi271213/remit/blob/main/SETUP.md) ·
|
|
255
|
+
[Rules](https://github.com/adi271213/remit/blob/main/docs/RULES.md) ·
|
|
256
|
+
[Architecture](https://github.com/adi271213/remit/blob/main/docs/ARCHITECTURE.md) ·
|
|
257
|
+
[Contributing](https://github.com/adi271213/remit/blob/main/CONTRIBUTING.md) ·
|
|
258
|
+
[Security](https://github.com/adi271213/remit/blob/main/SECURITY.md) ·
|
|
259
|
+
[Changelog](https://github.com/adi271213/remit/blob/main/CHANGELOG.md) ·
|
|
260
|
+
[MIT license](https://github.com/adi271213/remit/blob/main/LICENSE)
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# Remit
|
|
4
|
+
|
|
5
|
+
**Know when a pull request widens your workflows' authority.**
|
|
6
|
+
|
|
7
|
+
[](https://github.com/adi271213/remit/actions/workflows/ci.yml)
|
|
8
|
+
[](https://pypi.org/project/remit-actions/)
|
|
9
|
+
[](https://pypi.org/project/remit-actions/)
|
|
10
|
+
[](https://github.com/adi271213/remit/blob/main/LICENSE)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
The changes that matter in a workflow PR are often a single line: `contents: read`
|
|
15
|
+
becomes `write`, a `pull_request_target` trigger appears, a release job starts
|
|
16
|
+
running a new third-party action with your secrets. Remit compares the base and
|
|
17
|
+
head of every pull request and puts each of those changes on the line that
|
|
18
|
+
introduced it, so reviewers can't miss them.
|
|
19
|
+
|
|
20
|
+
- **Only what changed.** Unchanged configuration stays quiet, so ordinary PRs get no noise.
|
|
21
|
+
- **Flags common pwn-request patterns.** Privileged workflows that start checking out, fetching or downloading pull request content, including the time-of-check to time-of-use trap after a label or comment approval.
|
|
22
|
+
- **Right on the diff.** Findings appear as annotations in the PR's *Files changed* tab.
|
|
23
|
+
- **Safe on untrusted PRs.** The PR is read as data and never executed; the analysis makes no network calls.
|
|
24
|
+
- **Nothing to install.** A pinned Action with a hash-verified bundled parser; no secrets, `contents: read` only.
|
|
25
|
+
|
|
26
|
+
## What it catches
|
|
27
|
+
|
|
28
|
+
A PR edits a release workflow:
|
|
29
|
+
|
|
30
|
+
```diff
|
|
31
|
+
name: Release
|
|
32
|
+
-on: push
|
|
33
|
+
+on: [push, pull_request_target]
|
|
34
|
+
permissions: {}
|
|
35
|
+
jobs:
|
|
36
|
+
publish:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
permissions:
|
|
39
|
+
- contents: read
|
|
40
|
+
+ contents: write
|
|
41
|
+
+ id-token: write
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
- run: make dist
|
|
45
|
+
+ - uses: someone/release-action@v1
|
|
46
|
+
+ with:
|
|
47
|
+
+ token: ${{ secrets.RELEASE_TOKEN }}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Remit annotates five lines of that diff:
|
|
51
|
+
|
|
52
|
+
| Line | Rule | Finding |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| 2 | WF003 | Review-sensitive trigger `pull_request_target` introduced. |
|
|
55
|
+
| 8 | WF001 | Job publish is newly configured to use `contents: write`. |
|
|
56
|
+
| 9 | WF001 | Job publish is newly configured to request an OIDC identity token. |
|
|
57
|
+
| 13 | WF009 | Job publish now runs third-party code from `someone/release-action@v1`, which can use `contents: write`; `id-token: write`; secrets: `RELEASE_TOKEN`. |
|
|
58
|
+
| 15 | WF004 | Job publish adds a reference or forwarding path to `RELEASE_TOKEN`. |
|
|
59
|
+
|
|
60
|
+
The job summary groups the same findings by workflow and job, with a question
|
|
61
|
+
for the reviewer on each ([sample](https://github.com/adi271213/remit/blob/main/examples/sample-report.md)).
|
|
62
|
+
JSON and SARIF reports carry the same data for tooling and GitHub code scanning.
|
|
63
|
+
|
|
64
|
+
## Quick start
|
|
65
|
+
|
|
66
|
+
1. Copy [`examples/github-action-template.yml`](https://github.com/adi271213/remit/blob/main/examples/github-action-template.yml)
|
|
67
|
+
(45 lines) to `.github/workflows/remit.yml`.
|
|
68
|
+
2. Merge it.
|
|
69
|
+
3. Open a pull request that touches a workflow and look at *Files changed*.
|
|
70
|
+
|
|
71
|
+
The template checks out the base commit, fetches the PR head without checking it
|
|
72
|
+
out, and runs the Action pinned to a full commit SHA:
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
- uses: adi271213/remit@3db5c481c527a7f60bf92c0499083fe15f7829be # version 1
|
|
76
|
+
with:
|
|
77
|
+
mode: advisory
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use the whole template rather than this step alone: the step needs both commits
|
|
81
|
+
in the local clone. The [setup guide](https://github.com/adi271213/remit/blob/main/SETUP.md)
|
|
82
|
+
covers inputs, outputs, code scanning and troubleshooting.
|
|
83
|
+
|
|
84
|
+
## Rules
|
|
85
|
+
|
|
86
|
+
| Rule | Reports |
|
|
87
|
+
|---|---|
|
|
88
|
+
| WF001 | A job's token scope increases (`none` → `read` → `write`), including new `id-token: write` (OIDC) |
|
|
89
|
+
| WF002 | A job starts relying on the repository's default token permissions, or replaces them with permissions that may exceed the default |
|
|
90
|
+
| WF003 | A newly added `pull_request_target`, `workflow_run`, `issue_comment`, `issues`, `discussion` or `discussion_comment` trigger |
|
|
91
|
+
| WF004 | A job newly references a secret, or forwards all secrets (`secrets: inherit`, `toJSON(secrets)`) |
|
|
92
|
+
| WF005 | A job's runner changes to a self-hosted, custom-label, runner-group or unresolvable selection |
|
|
93
|
+
| WF007 | A job's deployment environment is added, removed or changed |
|
|
94
|
+
| WF008 | A matrix job pairs a runner with a different environment while the overall runner and environment sets stay the same |
|
|
95
|
+
| WF009 | Third-party code (an action or reusable workflow) newly runs in a job with write scopes, OIDC, secrets or an environment |
|
|
96
|
+
| WF010 | A `pull_request_target`, `workflow_run` or `issue_comment` job newly checks out, fetches or downloads pull request content: the "pwn request" |
|
|
97
|
+
| WF011 | An approval-gated privileged job checks out a pull request reference that can change after approval: time-of-check to time-of-use |
|
|
98
|
+
|
|
99
|
+
Rules follow steps into local composite actions and local reusable workflows
|
|
100
|
+
(`./` and `$/` references), so a change inside `.github/actions/` is caught even
|
|
101
|
+
when no workflow file changed.
|
|
102
|
+
|
|
103
|
+
[docs/RULES.md](https://github.com/adi271213/remit/blob/main/docs/RULES.md) describes the exact behaviour of each rule.
|
|
104
|
+
|
|
105
|
+
## Remit and zizmor
|
|
106
|
+
|
|
107
|
+
[zizmor](https://github.com/zizmorcore/zizmor) audits what your workflows *are*
|
|
108
|
+
for risky patterns. Remit reports what a pull request *changed*, including
|
|
109
|
+
increases that never become a zizmor alert because the end state isn't "bad"
|
|
110
|
+
by a state rule: a job gaining `id-token: write`, a new secret reference, a
|
|
111
|
+
changed environment, or a new action receiving write access.
|
|
112
|
+
|
|
113
|
+
On [74 historical public PRs](https://github.com/adi271213/remit/blob/main/benchmarks/README.md),
|
|
114
|
+
Remit reported 72 findings. For 46 of them (64%), zizmor 1.32 (pedantic,
|
|
115
|
+
offline) reported no new finding of a related audit in the same workflow and
|
|
116
|
+
job, including all 8 new secret references. The PRs were selected and labelled
|
|
117
|
+
by this project and the matching is approximate, so read this as an indication,
|
|
118
|
+
not a benchmark. Run zizmor for what is dangerous, and Remit for what got wider.
|
|
119
|
+
|
|
120
|
+
## Results
|
|
121
|
+
|
|
122
|
+
Every run reports an **analysis** status and a **review** status:
|
|
123
|
+
|
|
124
|
+
| Analysis | Meaning | CLI exit | `require-complete` (default) | `advisory` |
|
|
125
|
+
|---|---|---|---|---|
|
|
126
|
+
| `complete` | All changed workflows were compared | 0 | pass | pass |
|
|
127
|
+
| `incomplete` | Some context is unknown, e.g. a dynamic expression | 2 | fail | pass with warning |
|
|
128
|
+
| `error` | Some input couldn't be parsed or read | 2 | fail | pass with warning |
|
|
129
|
+
|
|
130
|
+
Review is `suggested` when there are findings, `none` when analysis is complete
|
|
131
|
+
with no findings, and (in the Action) `unknown` when analysis is incomplete and
|
|
132
|
+
nothing was found. Known findings are reported alongside incomplete or failed parts.
|
|
133
|
+
|
|
134
|
+
**Findings never fail the step.** Remit is a review aid, not a merge gate: a
|
|
135
|
+
clean report doesn't mean a PR is safe, and a PR can edit the workflow that runs
|
|
136
|
+
the check.
|
|
137
|
+
|
|
138
|
+
<details>
|
|
139
|
+
<summary><b>Inherited token permissions</b></summary>
|
|
140
|
+
|
|
141
|
+
A job without a `permissions` block gets the repository or organization default,
|
|
142
|
+
which Remit can't see. When a PR adds such a job, or removes a job's
|
|
143
|
+
`permissions` block, WF002 asks the author to declare permissions explicitly.
|
|
144
|
+
When a job that used the default starts declaring permissions, only levels above
|
|
145
|
+
GitHub's narrowest possible default (`contents: read`, `packages: read`) are
|
|
146
|
+
reported, so ordinary hardening PRs stay quiet. To compare against your real
|
|
147
|
+
default instead, pass it in:
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
with:
|
|
151
|
+
default-permissions: '{"contents": "read"}'
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
</details>
|
|
155
|
+
|
|
156
|
+
<details>
|
|
157
|
+
<summary><b>Limitations</b></summary>
|
|
158
|
+
|
|
159
|
+
- Workflows are read from `.github/workflows/`, and local composite actions and
|
|
160
|
+
reusable workflows are followed. Actions and reusable workflows from other
|
|
161
|
+
repositories, and the contents of scripts, are not analysed.
|
|
162
|
+
- Repository and organization settings are not visible: default token
|
|
163
|
+
permissions, runner inventory, environment protection rules and secrets.
|
|
164
|
+
- WF003 looks at trigger names only. Filter changes on existing triggers
|
|
165
|
+
(branches, types, paths) are not analysed.
|
|
166
|
+
- Expressions are not evaluated. Only direct `${{ matrix.<field> }}` references
|
|
167
|
+
into a static matrix of up to 256 rows are resolved; anything else is reported
|
|
168
|
+
as unresolved.
|
|
169
|
+
- YAML merge keys (`<<`) and recursive aliases are rejected as errors.
|
|
170
|
+
- A renamed file is matched only if its content is identical; an edited rename
|
|
171
|
+
is treated as a new file and can produce findings for unchanged settings.
|
|
172
|
+
- Input limits: 200 workflow files and 4 MiB per commit; per file, 256 KiB and
|
|
173
|
+
60,000 YAML tokens (a very dense file can reach the token limit first).
|
|
174
|
+
- WF010 and WF011 follow pull request references through environment variables,
|
|
175
|
+
`$GITHUB_ENV`, step and job outputs, and action inputs within the repository.
|
|
176
|
+
Values computed in other ways (a script that builds a ref from pieces, files
|
|
177
|
+
passed between jobs) are not traced.
|
|
178
|
+
|
|
179
|
+
</details>
|
|
180
|
+
|
|
181
|
+
## Command line
|
|
182
|
+
|
|
183
|
+
The package is `remit-actions` on PyPI; the command it installs is `remit`.
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
pip install remit-actions
|
|
187
|
+
remit scan --repo . --base main --head my-branch --format markdown
|
|
188
|
+
remit scan --dirs path/to/before path/to/after --format sarif --output remit.sarif
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Output is JSON by default; `--format markdown` and `--format sarif` are also
|
|
192
|
+
available. `--summary FILE` appends the Markdown summary and `--sarif FILE`
|
|
193
|
+
also writes SARIF. The exit code is 0 for complete analysis and 2 otherwise.
|
|
194
|
+
|
|
195
|
+
## Audit an existing repository
|
|
196
|
+
|
|
197
|
+
Pull request checks only see what a PR changes. `remit audit` reports what is
|
|
198
|
+
already there: every place a `pull_request_target`, `workflow_run` or
|
|
199
|
+
`issue_comment` job brings in pull request content (WF010), and every mutable
|
|
200
|
+
checkout after a label, comment or environment approval (WF011), including
|
|
201
|
+
through local actions and reusable workflows.
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
remit audit --repo . # the current commit
|
|
205
|
+
remit audit --repo . --ref v2.3.0 # any commit or tag
|
|
206
|
+
remit audit --dir path/to/checkout --format markdown
|
|
207
|
+
remit audit --repo . --format sarif --output remit-audit.sarif
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Like `scan`, it reads committed Git objects (or a directory) as data and runs
|
|
211
|
+
nothing. The exit code is 0 for a complete audit, 2 when something couldn't be
|
|
212
|
+
analysed, and with `--fail-on-findings`, 1 when the audit found something. The
|
|
213
|
+
[setup guide](https://github.com/adi271213/remit/blob/main/SETUP.md#scheduled-audit)
|
|
214
|
+
has a weekly scheduled audit workflow.
|
|
215
|
+
|
|
216
|
+
## Status
|
|
217
|
+
|
|
218
|
+
Rule IDs, Action inputs and outputs, and the JSON report format stay compatible
|
|
219
|
+
within a version; an incompatible change gets a new version number. Each release
|
|
220
|
+
runs the test suite on Python 3.11–3.14, runs the Action end to end with networking
|
|
221
|
+
disabled, and replays 74 historical public PRs. The replay's expected results
|
|
222
|
+
were labelled by the project, so treat it as a regression suite rather than an
|
|
223
|
+
accuracy measurement. Feedback from real repositories is the most useful thing
|
|
224
|
+
you can send: open a [trial feedback issue](https://github.com/adi271213/remit/issues/new?template=trial_feedback.md).
|
|
225
|
+
|
|
226
|
+
[Setup](https://github.com/adi271213/remit/blob/main/SETUP.md) ·
|
|
227
|
+
[Rules](https://github.com/adi271213/remit/blob/main/docs/RULES.md) ·
|
|
228
|
+
[Architecture](https://github.com/adi271213/remit/blob/main/docs/ARCHITECTURE.md) ·
|
|
229
|
+
[Contributing](https://github.com/adi271213/remit/blob/main/CONTRIBUTING.md) ·
|
|
230
|
+
[Security](https://github.com/adi271213/remit/blob/main/SECURITY.md) ·
|
|
231
|
+
[Changelog](https://github.com/adi271213/remit/blob/main/CHANGELOG.md) ·
|
|
232
|
+
[MIT license](https://github.com/adi271213/remit/blob/main/LICENSE)
|