@bugb-technologies/bravos 0.0.0-stage → 1.0.0
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.
- package/LICENSE +70 -0
- package/README.md +225 -2
- package/dist/account.js +140 -0
- package/dist/artifacts.js +450 -0
- package/dist/ci.js +52 -0
- package/dist/cli.js +963 -0
- package/dist/codegraph.js +268 -0
- package/dist/cursor.js +70 -0
- package/dist/detect.js +26 -0
- package/dist/doctor.js +99 -0
- package/dist/global.js +95 -0
- package/dist/graphbin.js +136 -0
- package/dist/guardlink.js +123 -0
- package/dist/guide.js +255 -0
- package/dist/help.js +334 -0
- package/dist/hooks.js +167 -0
- package/dist/install.js +209 -0
- package/dist/licence.js +41 -0
- package/dist/lock.js +50 -0
- package/dist/mcp.js +139 -0
- package/dist/migrate.js +275 -0
- package/dist/omp.js +46 -0
- package/dist/opencode.js +118 -0
- package/dist/paths.js +33 -0
- package/dist/plugin.js +101 -0
- package/dist/report/check.js +252 -0
- package/dist/report/data.js +401 -0
- package/dist/report/export.js +105 -0
- package/dist/report/markdown.js +310 -0
- package/dist/report/meta.js +48 -0
- package/dist/report/pdf.js +96 -0
- package/dist/review-server.js +7 -0
- package/dist/review.js +274 -0
- package/dist/setup.js +121 -0
- package/dist/skills.js +81 -0
- package/dist/types.js +9 -0
- package/dist/uninstall.js +192 -0
- package/dist/versions.js +8 -0
- package/dist/watch-frame.js +593 -0
- package/dist/watch.js +873 -0
- package/dist/workspace.js +316 -0
- package/docs/GUIDE.md +323 -0
- package/hooks/autopilot.mjs +45 -0
- package/hooks/lib/autopilot.mjs +191 -0
- package/hooks/lib/briefing.mjs +750 -0
- package/hooks/lib/licence.mjs +452 -0
- package/hooks/lib/pre-pr.mjs +209 -0
- package/hooks/lib/sast.mjs +207 -0
- package/hooks/lib/setup.mjs +107 -0
- package/hooks/lib/workspace.mjs +78 -0
- package/hooks/pre-pr.mjs +54 -0
- package/hooks/session-start.mjs +82 -0
- package/package.json +49 -4
- package/skills/bravos/SKILL.md +63 -0
- package/skills/bravos-annotate/SKILL.md +56 -0
- package/skills/bravos-annotate/scripts/lib/pick.mjs +127 -0
- package/skills/bravos-annotate/scripts/slice.mjs +141 -0
- package/skills/bravos-campaign/SKILL.md +49 -0
- package/skills/bravos-cicd/SKILL.md +48 -0
- package/skills/bravos-codegraph/SKILL.md +57 -0
- package/skills/bravos-context/SKILL.md +51 -0
- package/skills/bravos-context/intent.example.md +48 -0
- package/skills/bravos-env/SKILL.md +48 -0
- package/skills/bravos-env/scope.example.yaml +50 -0
- package/skills/bravos-prove/SKILL.md +52 -0
- package/skills/bravos-recheck/SKILL.md +47 -0
- package/skills/bravos-report/SKILL.md +53 -0
- package/skills/bravos-report/assessment.md +54 -0
- package/skills/bravos-report/brief.md +23 -0
- package/skills/bravos-report/finding.md +74 -0
- package/skills/bravos-report/retest.md +29 -0
- package/skills/bravos-report/style.md +44 -0
- package/skills/bravos-sast/SKILL.md +48 -0
- package/skills/bravos-schedule/SKILL.md +44 -0
- package/skills/bravos-schedule/loop.md +16 -0
- package/skills/bravos-ship/SKILL.md +58 -0
- package/skills/bravos-templatize/SKILL.md +48 -0
- package/skills/bravos-templatize/operator-guidance.md +20 -0
- package/templates/omp/bravos.js +62 -0
- package/templates/opencode/bravos.js +33 -0
- package/templates/report/report.html +88 -0
- package/templates/review/review.html +327 -0
- package/templates/workflows/bravos-nightly-claude.yml +173 -0
- package/templates/workflows/bravos-nightly-codex.yml +172 -0
- package/templates/workflows/bravos-pr-gate.yml +142 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# bravos:generated — written by `bravos init --ci`; `bravos sync --ci` refreshes it while this line stays.
|
|
2
|
+
# The nightly "bulk bee": Codex covers the next slice of unannotated files with the
|
|
3
|
+
# bravos-annotate skill, the lint gate checks what it wrote, and the job opens one small PR.
|
|
4
|
+
# Optionally it then replays the committed retest kits against the target the reviewed
|
|
5
|
+
# .guardlink/scope.yaml authorizes (see the "Authorized scan (gated)" step); without that scope
|
|
6
|
+
# file's human-written attestation the scan step skips and the job annotates only.
|
|
7
|
+
# The nightly is on the Team plan: add the repo secret BRAVOS_LICENCE (the token from
|
|
8
|
+
# account.bugb.io/dashboard/license) so the agent's paid skills run; without it they say so and stop.
|
|
9
|
+
# Needs the repo secret OPENAI_API_KEY, and Settings > Actions > General > "Allow GitHub
|
|
10
|
+
# Actions to create and approve pull requests". Pin the actions to commit SHAs if your
|
|
11
|
+
# organisation requires it.
|
|
12
|
+
name: bravos nightly (Codex)
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
schedule:
|
|
16
|
+
- cron: '17 3 * * 1-5'
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: write
|
|
21
|
+
pull-requests: write
|
|
22
|
+
|
|
23
|
+
concurrency:
|
|
24
|
+
group: bravos-nightly
|
|
25
|
+
cancel-in-progress: false
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
annotate:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
env:
|
|
31
|
+
BRAVOS_LICENCE: ${{ secrets.BRAVOS_LICENCE }}
|
|
32
|
+
timeout-minutes: 60
|
|
33
|
+
defaults:
|
|
34
|
+
run:
|
|
35
|
+
shell: bash
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
with:
|
|
39
|
+
fetch-depth: 0
|
|
40
|
+
|
|
41
|
+
- uses: actions/setup-node@v4
|
|
42
|
+
with:
|
|
43
|
+
node-version: 22
|
|
44
|
+
|
|
45
|
+
- name: Install guardlink
|
|
46
|
+
run: npm install -g guardlink
|
|
47
|
+
|
|
48
|
+
- name: Starting point
|
|
49
|
+
id: start
|
|
50
|
+
run: |
|
|
51
|
+
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
|
52
|
+
guardlink ci -f json > "$RUNNER_TEMP/ci-before.json" || true
|
|
53
|
+
echo "coverage=$(jq -r '.summary.coverage.percent // "unknown"' "$RUNNER_TEMP/ci-before.json")" >> "$GITHUB_OUTPUT"
|
|
54
|
+
|
|
55
|
+
- name: Annotate the next slice
|
|
56
|
+
uses: openai/codex-action@v1
|
|
57
|
+
with:
|
|
58
|
+
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
|
|
59
|
+
permission-profile: ":workspace"
|
|
60
|
+
safety-strategy: drop-sudo
|
|
61
|
+
prompt: |
|
|
62
|
+
Follow .agents/skills/bravos-annotate/SKILL.md in slice mode (the default) to cover the
|
|
63
|
+
next unannotated files that `guardlink unannotated --json` lists. You are the agent: do
|
|
64
|
+
the work in this session and never run guardlink annotate with an agent flag. Stop when
|
|
65
|
+
`guardlink lint . --since ${{ steps.start.outputs.sha }} --json` reports 0 errors and
|
|
66
|
+
`guardlink validate .` passes. Leave the changes uncommitted; a later step commits them
|
|
67
|
+
and opens the pull request.
|
|
68
|
+
|
|
69
|
+
- name: Gate what the agent wrote
|
|
70
|
+
env:
|
|
71
|
+
START: ${{ steps.start.outputs.sha }}
|
|
72
|
+
run: |
|
|
73
|
+
guardlink validate .
|
|
74
|
+
guardlink lint . --since "$START" --json > "$RUNNER_TEMP/lint.json" || true
|
|
75
|
+
errors=$(jq -r '.summary.errors' "$RUNNER_TEMP/lint.json")
|
|
76
|
+
if [ "$errors" != "0" ]; then
|
|
77
|
+
echo "::error::guardlink lint reports $errors error(s) in what the agent wrote"
|
|
78
|
+
jq . "$RUNNER_TEMP/lint.json"
|
|
79
|
+
exit 1
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# Opt-in: replays the committed retest kits against the target the reviewed scope authorizes.
|
|
83
|
+
# It runs only when .guardlink/scope.yaml carries a human-written authorization_attestation and
|
|
84
|
+
# vars.BRAVOS_SCAN_TARGET is loopback (started by vars.BRAVOS_APP_START) or in the scope's
|
|
85
|
+
# host_allowlist. It replays kits only: no probe generation and no destructive classes.
|
|
86
|
+
- name: Authorized scan (gated)
|
|
87
|
+
id: scan
|
|
88
|
+
env:
|
|
89
|
+
TARGET: ${{ vars.BRAVOS_SCAN_TARGET }}
|
|
90
|
+
APP_START: ${{ vars.BRAVOS_APP_START }}
|
|
91
|
+
AUTH_PROFILE: ${{ vars.BRAVOS_SCAN_AUTH }}
|
|
92
|
+
AUTH_STATE: ${{ secrets.BRAVOS_AUTH_STATE }}
|
|
93
|
+
run: |
|
|
94
|
+
skip() { echo "::notice::nightly scan skipped: $1"; echo "ran=false" >> "$GITHUB_OUTPUT"; exit 0; }
|
|
95
|
+
scope=.guardlink/scope.yaml
|
|
96
|
+
[ -n "$TARGET" ] || skip "vars.BRAVOS_SCAN_TARGET is not set"
|
|
97
|
+
[ -f "$scope" ] || skip "$scope is not committed"
|
|
98
|
+
attest=$(sed -n 's/^authorization_attestation:[[:space:]]*//p' "$scope" | sed 's/^"\(.*\)"$/\1/')
|
|
99
|
+
[ -n "$attest" ] || skip "$scope has no authorization_attestation; a human must write it and get it reviewed"
|
|
100
|
+
ls .guardlink/cxg-replay/*.js >/dev/null 2>&1 || skip "no retest kits in .guardlink/cxg-replay/"
|
|
101
|
+
host=$(printf '%s' "$TARGET" | sed -E 's#^[a-z]+://##; s#[:/].*$##')
|
|
102
|
+
case "$host" in
|
|
103
|
+
127.0.0.1|localhost|::1|'[::1]') loopback=yes ;;
|
|
104
|
+
*) loopback=no ;;
|
|
105
|
+
esac
|
|
106
|
+
if [ "$loopback" = no ] && ! grep -qF "$host" <(sed -n '/^host_allowlist:/,/^[a-z_]*:/p' "$scope"); then
|
|
107
|
+
skip "$host is neither loopback nor in the scope's host_allowlist"
|
|
108
|
+
fi
|
|
109
|
+
if [ "$loopback" = yes ]; then
|
|
110
|
+
[ -n "$APP_START" ] || skip "a loopback target needs vars.BRAVOS_APP_START to start the app"
|
|
111
|
+
bash -c "$APP_START"
|
|
112
|
+
for i in $(seq 1 60); do curl -fsS -o /dev/null "$TARGET" && break; sleep 2; done
|
|
113
|
+
fi
|
|
114
|
+
cargo install --locked cert-x-gen
|
|
115
|
+
cxg pentest install
|
|
116
|
+
auth=()
|
|
117
|
+
if [ -n "$AUTH_PROFILE" ]; then
|
|
118
|
+
[ -n "$AUTH_STATE" ] || skip "vars.BRAVOS_SCAN_AUTH is set but secrets.BRAVOS_AUTH_STATE is empty"
|
|
119
|
+
printf '%s' "$AUTH_STATE" | base64 -d > "$RUNNER_TEMP/state.json"
|
|
120
|
+
cxg pentest auth import --storage-state "$RUNNER_TEMP/state.json" --profile "$AUTH_PROFILE" --target "$TARGET"
|
|
121
|
+
auth=(--auth "$AUTH_PROFILE")
|
|
122
|
+
fi
|
|
123
|
+
guardlink sarif . --profile pentest -o whitebox/findings.sarif
|
|
124
|
+
run=".guardlink/pentest-findings/nightly-$(date -u +%Y%m%d)"
|
|
125
|
+
set +e
|
|
126
|
+
cxg pentest run --codebase . --target "$TARGET" "${auth[@]}" --template-dir .guardlink/cxg-replay/ --scope-file "$scope" --attestation "$attest" --session-dir "$run" --ci
|
|
127
|
+
code=$?
|
|
128
|
+
set -e
|
|
129
|
+
echo "exit=$code" >> "$GITHUB_OUTPUT"
|
|
130
|
+
case "$code" in
|
|
131
|
+
0|2) guardlink hypothesis confirm --from-scan "$run/report.json" --write || true
|
|
132
|
+
echo "ran=true" >> "$GITHUB_OUTPUT" ;;
|
|
133
|
+
*) echo "::warning::cxg pentest run exited $code: nothing was proven"; echo "ran=false" >> "$GITHUB_OUTPUT" ;;
|
|
134
|
+
esac
|
|
135
|
+
|
|
136
|
+
- name: Open the pull request
|
|
137
|
+
env:
|
|
138
|
+
SCAN_RAN: ${{ steps.scan.outputs.ran }}
|
|
139
|
+
SCAN_EXIT: ${{ steps.scan.outputs.exit }}
|
|
140
|
+
SCAN_TARGET: ${{ vars.BRAVOS_SCAN_TARGET }}
|
|
141
|
+
GH_TOKEN: ${{ github.token }}
|
|
142
|
+
START: ${{ steps.start.outputs.sha }}
|
|
143
|
+
BEFORE: ${{ steps.start.outputs.coverage }}
|
|
144
|
+
run: |
|
|
145
|
+
if [ -z "$(git status --porcelain)" ]; then
|
|
146
|
+
echo "Nothing new to annotate."
|
|
147
|
+
exit 0
|
|
148
|
+
fi
|
|
149
|
+
guardlink ci -f json > "$RUNNER_TEMP/ci-after.json" || true
|
|
150
|
+
after=$(jq -r '.summary.coverage.percent // "unknown"' "$RUNNER_TEMP/ci-after.json")
|
|
151
|
+
base=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)
|
|
152
|
+
branch="bravos/nightly-$(date -u +%Y%m%d-%H%M)"
|
|
153
|
+
git config user.name "bravos-nightly[bot]"
|
|
154
|
+
git config user.email "bravos-nightly@users.noreply.github.com"
|
|
155
|
+
git switch -c "$branch"
|
|
156
|
+
git add -A
|
|
157
|
+
git commit -m "annotate: nightly coverage slice (${BEFORE}% -> ${after}%)"
|
|
158
|
+
git push origin "$branch"
|
|
159
|
+
{
|
|
160
|
+
echo "Nightly coverage slice by bravos-annotate: ${BEFORE}% -> ${after}%."
|
|
161
|
+
echo
|
|
162
|
+
echo "## Security gate"
|
|
163
|
+
echo "validate ✔ · lint 0 errors · exposures recorded by this slice are listed below and stay open"
|
|
164
|
+
if [ "$SCAN_RAN" = "true" ]; then
|
|
165
|
+
echo "Tested: retest kits replayed against $SCAN_TARGET (exit code $SCAN_EXIT); outcomes recorded in .guardlink/hypotheses.json."
|
|
166
|
+
else
|
|
167
|
+
echo "Not tested: the gated scan did not run (see the job's notices)."
|
|
168
|
+
fi
|
|
169
|
+
echo
|
|
170
|
+
guardlink diff "$START" --markdown || true
|
|
171
|
+
} > "$RUNNER_TEMP/body.md"
|
|
172
|
+
gh pr create --base "$base" --head "$branch" --title "annotate: nightly coverage slice" --body-file "$RUNNER_TEMP/body.md"
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# bravos:generated — written by `bravos init --ci`; `bravos sync --ci` refreshes it while this line stays.
|
|
2
|
+
# The bravos-ship gate as a pull-request check, the same rulings as the opt-in pre-PR hook.
|
|
3
|
+
# Deterministic: it runs guardlink only, spends no model, and needs no secret. A gate input that
|
|
4
|
+
# fails or does not parse fails the job as "unknown", never passes. Pin the actions below to
|
|
5
|
+
# commit SHAs if your organisation requires it.
|
|
6
|
+
name: bravos security gate
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
pull_request:
|
|
10
|
+
# edited: adding the disclosure to the PR body re-runs the gate.
|
|
11
|
+
types: [opened, synchronize, reopened, edited]
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
security-events: write
|
|
16
|
+
# upload-sarif reads the run's metadata; private repositories need this to upload.
|
|
17
|
+
actions: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
gate:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 20
|
|
23
|
+
defaults:
|
|
24
|
+
run:
|
|
25
|
+
shell: bash
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
with:
|
|
29
|
+
fetch-depth: 0
|
|
30
|
+
|
|
31
|
+
- uses: actions/setup-node@v4
|
|
32
|
+
with:
|
|
33
|
+
node-version: 22
|
|
34
|
+
|
|
35
|
+
- name: Install guardlink
|
|
36
|
+
run: npm install -g guardlink
|
|
37
|
+
|
|
38
|
+
- name: Merge base
|
|
39
|
+
id: base
|
|
40
|
+
env:
|
|
41
|
+
BASE_REF: ${{ github.base_ref }}
|
|
42
|
+
run: echo "sha=$(git merge-base HEAD "origin/$BASE_REF")" >> "$GITHUB_OUTPUT"
|
|
43
|
+
|
|
44
|
+
# Whole-token id matching shared by the disclosure and @ticket checks: `#api` is not `#api-key`.
|
|
45
|
+
- name: jq helpers
|
|
46
|
+
run: |
|
|
47
|
+
cat > "$RUNNER_TEMP/named.jq" <<'EOF'
|
|
48
|
+
def esc: gsub("(?<c>[.*+?^${}()|\\[\\]\\\\])"; "\\\(.c)");
|
|
49
|
+
def named($text; $id): ($id | type) == "string" and ($text | test("(?<![\\w#.-])" + ($id | esc) + "(?![\\w-])"));
|
|
50
|
+
EOF
|
|
51
|
+
|
|
52
|
+
- name: Threat model parses and resolves
|
|
53
|
+
run: guardlink validate .
|
|
54
|
+
|
|
55
|
+
- name: New claims meet the evidence bar
|
|
56
|
+
env:
|
|
57
|
+
BASE_SHA: ${{ steps.base.outputs.sha }}
|
|
58
|
+
run: |
|
|
59
|
+
status=0
|
|
60
|
+
guardlink lint . --since "$BASE_SHA" --json > lint.json || status=$?
|
|
61
|
+
if ! errors=$(jq -er '.summary.errors | numbers' lint.json); then
|
|
62
|
+
echo "::error::unknown: guardlink lint --since $BASE_SHA exited $status without guardlink.lint JSON"
|
|
63
|
+
exit 1
|
|
64
|
+
fi
|
|
65
|
+
if [ "$errors" != "0" ]; then
|
|
66
|
+
echo "::error::guardlink lint reports $errors error(s) in this PR's claims; apply each violation's fix (bravos-ship)."
|
|
67
|
+
jq '.violations' lint.json
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# Each new unmitigated exposure must be disclosed in the PR body by both its threat and its
|
|
72
|
+
# asset id (bravos-ship's Security gate section lists them), so an honest @exposes + @audit is
|
|
73
|
+
# reported rather than blocked forever. The body reaches the shell through env, never inline,
|
|
74
|
+
# so its text cannot run as a command.
|
|
75
|
+
- name: No undisclosed new exposure
|
|
76
|
+
env:
|
|
77
|
+
PR_BODY: ${{ github.event.pull_request.body }}
|
|
78
|
+
BASE_SHA: ${{ steps.base.outputs.sha }}
|
|
79
|
+
run: |
|
|
80
|
+
if ! guardlink diff "$BASE_SHA" --json > diff.json; then
|
|
81
|
+
echo "::error::unknown: guardlink diff $BASE_SHA --json failed"
|
|
82
|
+
exit 1
|
|
83
|
+
fi
|
|
84
|
+
if ! jq -e '.newUnmitigatedExposures | arrays' diff.json > /dev/null; then
|
|
85
|
+
echo "::error::unknown: guardlink diff $BASE_SHA --json has no newUnmitigatedExposures"
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
jq -r '.newUnmitigatedExposures[] | "- \(.threat) on \(.asset) — \(.location.file):\(.location.line)"' diff.json >> "$GITHUB_STEP_SUMMARY"
|
|
89
|
+
jq -r -L "$RUNNER_TEMP" --arg body "$PR_BODY" 'include "named";
|
|
90
|
+
.newUnmitigatedExposures[] | select((named($body; .threat) and named($body; .asset)) | not)
|
|
91
|
+
| "\(.threat) on \(.asset) — \(.location.file):\(.location.line)"' diff.json > undisclosed.txt
|
|
92
|
+
if [ -s undisclosed.txt ]; then
|
|
93
|
+
echo "::error::This PR adds unmitigated exposure the PR body does not disclose. Name each threat and asset id in its Security gate section (bravos-ship writes it):"
|
|
94
|
+
cat undisclosed.txt
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# A confirmed-exploitable claim on a file this PR changes blocks it, unless that same file
|
|
99
|
+
# carries a ticket line (the @ticket verb) for the same threat and asset (bravos-ship step 7).
|
|
100
|
+
- name: No confirmed exploit on a changed path
|
|
101
|
+
env:
|
|
102
|
+
BASE_SHA: ${{ steps.base.outputs.sha }}
|
|
103
|
+
run: |
|
|
104
|
+
if ! guardlink ci -f json > ci.json; then
|
|
105
|
+
echo "::error::unknown: guardlink ci -f json failed"
|
|
106
|
+
exit 1
|
|
107
|
+
fi
|
|
108
|
+
if ! jq -e '.confirmed | arrays' ci.json > /dev/null; then
|
|
109
|
+
echo "::error::unknown: guardlink ci -f json did not produce guardlink.ci JSON with confirmed[]"
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
git diff --name-only "$BASE_SHA"...HEAD > changed.txt
|
|
113
|
+
jq -r '.confirmed[] | [.location.file, (.location.line | tostring), .threat, .asset, (.severity // "unrated")] | @tsv' ci.json > confirmed.tsv
|
|
114
|
+
hits=""
|
|
115
|
+
while IFS=$'\t' read -r file line threat asset severity; do
|
|
116
|
+
grep -qxF -- "$file" changed.txt || continue
|
|
117
|
+
if [ -f "$file" ] && jq -Rne -L "$RUNNER_TEMP" --arg t "$threat" --arg a "$asset" 'include "named";
|
|
118
|
+
[inputs | select(contains("@ticket") and named(.; $t) and named(.; $a))] | length > 0' < "$file" > /dev/null; then
|
|
119
|
+
continue
|
|
120
|
+
fi
|
|
121
|
+
hits+="$severity $threat on $asset — $file:$line"$'\n'
|
|
122
|
+
done < confirmed.tsv
|
|
123
|
+
if [ -n "$hits" ]; then
|
|
124
|
+
echo "::error::Confirmed-exploitable claims on changed files, with no @ticket for them in that file:"
|
|
125
|
+
printf '%s' "$hits"
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
- name: Undefended paths (informational)
|
|
130
|
+
continue-on-error: true
|
|
131
|
+
run: guardlink paths --json > paths.json
|
|
132
|
+
|
|
133
|
+
- name: SARIF for code scanning
|
|
134
|
+
run: guardlink sarif . --profile github -o guardlink.sarif
|
|
135
|
+
|
|
136
|
+
# A fork's pull_request token cannot write security events; skip the upload there.
|
|
137
|
+
- name: Upload SARIF
|
|
138
|
+
if: github.event.pull_request.head.repo.full_name == github.repository
|
|
139
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
140
|
+
with:
|
|
141
|
+
sarif_file: guardlink.sarif
|
|
142
|
+
category: guardlink
|