Skip to content

Commit 9c412cb

Browse files
committed
add ignore of already open alerts
download gh alerts for otp-compliance to download those existing issues and ignore them when the PR is scan for vulnerability issues. those issues should still be sent to GH because otherwise GH considers them fixed. in the Alerts section from GH, we can mark them as fixed.
1 parent b33ed98 commit 9c412cb

File tree

4 files changed

+104
-23
lines changed

4 files changed

+104
-23
lines changed

.github/scripts/otp-compliance.es

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ cli() ->
231231
232232
> .github/scripts/otp-compliance.es sbom osv-scan
233233
""",
234-
arguments => [ versions_file(), sarif_option(), fail_option() ],
234+
arguments => [ versions_file(), sarif_option(), fail_option(), gh_alerts() ],
235235
handler => fun osv_scan/1}
236236
}},
237237
"explore" =>
@@ -351,6 +351,11 @@ fail_option() ->
351351
%% useful for pull requests since we do not want to
352352
%% add Github Security per found CVE on each PR.
353353

354+
gh_alerts() ->
355+
#{name => gh_alerts,
356+
type => binary,
357+
default => ~"undefined",
358+
long => "-gh_alerts"}.
354359

355360
ntia_checker() ->
356361
#{name => ntia_checker,
@@ -1329,7 +1334,10 @@ generate_vendor_purl(Package) ->
13291334
[create_externalRef_purl(Description, <<Purl/binary, "@", Vsn/binary>>)]
13301335
end.
13311336

1332-
osv_scan(#{version := Version, sarif := Sarif, fail_if_cve := FailIfCVEFound}) ->
1337+
osv_scan(#{version := Version,
1338+
sarif := Sarif,
1339+
fail_if_cve := FailIfCVEFound,
1340+
gh_alerts := Alerts0}) ->
13331341
application:ensure_all_started([ssl, inets]),
13341342
OSVQuery = vendor_by_version(Version),
13351343

@@ -1360,8 +1368,24 @@ osv_scan(#{version := Version, sarif := Sarif, fail_if_cve := FailIfCVEFound}) -
13601368
{error, Error} ->
13611369
{error, [URI, Error]}
13621370
end,
1363-
Vulns1 = ignore_vex_cves(Vulns),
1364-
ok = generate_sarif(Version, Sarif, Vulns1),
1371+
Alerts = case Alerts0 of
1372+
~"undefined" ->
1373+
[];
1374+
_ ->
1375+
decode(Alerts0)
1376+
end,
1377+
Vulns1 = ignore_vex_cves(Version, Vulns, Alerts),
1378+
1379+
%% Vulnerabilities already open that do not needto be reported again
1380+
%% in text or make the build fail, but the SARIF file should continue
1381+
%% reporting until we mark as fixed.
1382+
io:format("Exiting known vulnerabilities already open:~n~s~n~n",
1383+
[format_vulnerabilities(Vulns -- Vulns1)]),
1384+
1385+
%% sarif should still contain issues with CVEs
1386+
ok = generate_sarif(Version, Sarif, Vulns),
1387+
1388+
%% vulnerability reporting can fail if new issues appear
13651389
FormattedVulns = format_vulnerabilities(Vulns1),
13661390
case FailIfCVEFound of
13671391
false ->
@@ -1425,11 +1449,11 @@ generate_sarif(Branch, Vulns) ->
14251449
],
14261450
~"partialFingerprints" =>
14271451
#{ Branch => calculate_fingerprint(Branch, Dependency, Version, CVE)}
1428-
} || {Dependency, Version, CVEs} <- Vulns, CVE <- CVEs],
1452+
} || {{Dependency, Version}, CVEs} <- Vulns, CVE <- CVEs],
14291453
~"artifacts" =>
14301454
[ #{ ~"location" => #{ ~"uri" => Dependency},
14311455
~"length" => -1
1432-
} || {Dependency, _, _} <- Vulns]
1456+
} || {{Dependency, _}, _} <- Vulns]
14331457
}]
14341458
}.
14351459

@@ -1441,22 +1465,47 @@ calculate_fingerprint(Branch, Dependency, Version, CVE) ->
14411465
Bin = crypto:hash(sha, <<Branch/binary, Dependency/binary, Version/binary, CVE/binary>>),
14421466
binary:encode_hex(Bin).
14431467

1444-
%% TODO: fix by reading VEX files from erlang/vex or repo containing VEX files
1445-
ignore_vex_cves(Vulns) ->
1446-
lists:foldl(fun ({{~"github.com/wxWidgets/wxWidgets", _Version}, _CVEs}, Acc) ->
1468+
ignore_vex_cves(Branch, Vulns, Alerts) ->
1469+
Vulns1 = ignore_known_false_positives(Vulns),
1470+
ignore_dismiss_alerts(Branch, Alerts, Vulns1).
1471+
1472+
ignore_dismiss_alerts(Branch, Alerts, Vulns) ->
1473+
FilterBranch = fun (Text) -> string:find(Text, ~"Dependency", trailing) end,
1474+
CVEsTexts =
1475+
lists:map(fun (#{ ~"most_recent_instance" := #{~"message" := #{ ~"text":= Text }}}) ->
1476+
FilterBranch(Text)
1477+
end, Alerts),
1478+
1479+
lists:foldl(
1480+
fun ({{Name, Version}, CVEs}, Acc) ->
1481+
L = lists:filter(
1482+
fun(CVE) ->
1483+
T = FilterBranch(error_to_text(Branch, Name, Version, CVE)),
1484+
not lists:member(T, CVEsTexts)
1485+
end, CVEs),
1486+
case L of
1487+
[] ->
1488+
Acc;
1489+
_ ->
1490+
[{{Name, Version}, L} | Acc]
1491+
end
1492+
end, [], Vulns).
1493+
1494+
ignore_known_false_positives(Vulns) ->
1495+
lists:foldl(fun ({~"github.com/wxWidgets/wxWidgets", _CVEs}, Acc) ->
14471496
%% OTP cannot be vulnerable to wxwidgets because
14481497
%% we only take documentation.
14491498
Acc;
14501499
({{Name, Version}, CVEs}, Acc) ->
14511500
case maps:get(Name, non_vulnerable_cves(), not_found) of
14521501
not_found ->
1453-
[{Name, Version, CVEs} | Acc];
1502+
[{{Name, Version}, CVEs} | Acc];
14541503
NonCVEs ->
14551504
case CVEs -- NonCVEs of
14561505
[] ->
14571506
Acc;
14581507
Vs ->
1459-
[{Name, Version, Vs} | Acc]
1508+
[{{Name, Version}, Vs} | Acc]
14601509
end
14611510
end
14621511
end, [], Vulns).
@@ -1473,12 +1522,12 @@ non_vulnerable_cves() ->
14731522
format_vulnerabilities({error, ErrorContext}) ->
14741523
{error, ErrorContext};
14751524
format_vulnerabilities(ExistingVulnerabilities) when is_list(ExistingVulnerabilities) ->
1476-
lists:map(fun ({N, _, Ids}) ->
1525+
lists:map(fun ({{N, _}, Ids}) ->
14771526
io_lib:format("- ~s: ~s~n", [N, lists:join(",", Ids)])
14781527
end, ExistingVulnerabilities).
14791528

14801529
report_vulnerabilities([]) ->
1481-
io:format("[OSV] No vulnerabilities found.~n");
1530+
io:format("[OSV] No new vulnerabilities reported.~n");
14821531
report_vulnerabilities({error, [URI, Error]}) ->
14831532
fail("[OSV] POST request to ~p errors: ~p", [URI, Error]);
14841533
report_vulnerabilities(FormatVulns) ->

.github/workflows/main.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,8 +426,21 @@ jobs:
426426
docker run otp "erl ${OPTION} -noshell -s init stop"
427427
done
428428
429+
modified-vendor-files:
430+
name: Check if vendor files changed
431+
runs-on: ubuntu-latest
432+
outputs:
433+
vendor-files: ${{ steps.vendor-files.outputs.MODIFIED_FILES != '0' }}
434+
steps:
435+
- name: Get modified vendor files
436+
id: vendor-files
437+
run: |
438+
echo "MODIFIED_FILES=$(git diff --name-only '${{ github.base_ref }}' 'HEAD' | grep 'vendor\.info$' | wc -l || 1)"
439+
429440
# this is a call to a workflow_call
430441
pr-vendor-vulnerability-analysis:
442+
needs: modified-vendor-files
443+
if: ${{ needs.modified-vendor-files.outputs.vendor-files }}
431444
permissions:
432445
security-events: write
433446
name: Vendor Vulnerability Scanning

.github/workflows/osv-scanner-scheduled.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,5 @@ jobs:
7979
--method POST \
8080
-H "Accept: application/vnd.github+json" \
8181
-H "X-GitHub-Api-Version: 2022-11-28" \
82-
/repos/${{ env.REPO }}/actions/workflows/reusable-vendor-vulnerability-scanner.yml/dispatches \
82+
/repos/${{ github.repository }}/actions/workflows/reusable-vendor-vulnerability-scanner.yml/dispatches \
8383
-f "ref=master" -f "inputs[version]=${{ matrix.type }}" -f "inputs[sarif]=true" -f "inputs[fail_if_cve]=false"

.github/workflows/reusable-vendor-vulnerability-scanner.yml

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ on:
5050
default: false
5151
type: boolean
5252
fail_if_cve:
53-
description: 'The build fails if a CVE is found. This is ok to activate in PRs, but does not make sense in scheduled analysis since CVEs will be reported in Github Security via SARIF file upload.'
53+
# The build fails if a CVE is found. This is ok to activate in PRs, but
54+
# does not make sense in scheduled analysis since CVEs will be reported
55+
# in Github Security
56+
description: 'Fail if CVE is found'
5457
required: true
5558
default: false
5659
type: boolean
@@ -67,15 +70,16 @@ on:
6770
default: false
6871
type: boolean
6972
fail_if_cve:
70-
description: 'The build fails if a CVE is found. This is ok to activate in PRs, but does not make sense in scheduled analysis since CVEs will be reported in Github Security.'
73+
# The build fails if a CVE is found. This is ok to activate in PRs, but
74+
# does not make sense in scheduled analysis since CVEs will be reported
75+
# in Github Security.
76+
description: 'Fail if CVE is found'
7177
required: true
7278
default: false
7379
type: boolean
7480

7581
env:
7682
VERSION: ${{ inputs.version }}
77-
REPO: "erlang/otp"
78-
# when testing, change to fork, e.g., "kikofernandez/otp"
7983

8084
jobs:
8185
analysis-vendor-dependencies:
@@ -86,30 +90,45 @@ jobs:
8690
# committed into them. thus, a workflow_dispatch or workflow_call would
8791
# not work, and we would not be able to analyse vendor dependecies there.
8892
runs-on: ubuntu-latest
93+
env:
94+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
8995
permissions:
9096
security-events: write
9197
steps:
9298
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/[email protected]
9399
- uses: erlef/setup-beam@5304e04ea2b355f03681464e683d92e3b2f18451 # racket:actions/checkout@v1
94100
with:
95101
otp-version: '27'
102+
103+
- name: Download Scanning Alerts
104+
run: |
105+
gh api -H "Accept: application/vnd.github+json" \
106+
-H "X-GitHub-Api-Version: 2022-11-28" \
107+
/repos/${{ github.repository }}/code-scanning/alerts?tool_name=otp-compliance > "$HOME/gh_alerts.json"
108+
96109
- name: 'Analysis of dependencies in ${{ inputs.version }}'
110+
id: analysis
97111
run: |
98-
git clone -b ${{ env.VERSION }} https://github.com/${{ env.REPO }}.git ${{ env.VERSION }}
112+
git clone -b ${{ env.VERSION }} https://github.com/${{ github.repository }}.git ${{ env.VERSION }}
99113
mkdir -p /home/runner/work/otp/otp/${{ env.VERSION }}/.github/scripts/
100-
cp /home/runner/work/otp/otp/.github/scripts/otp-compliance.es \
114+
curl -LJO https://raw.githubusercontent.com/${{ github.repository }}/refs/heads/master/.github/scripts/otp-compliance.es
115+
chmod +x otp-compliance.es
116+
cp otp-compliance.es \
101117
/home/runner/work/otp/otp/${{ env.VERSION }}/.github/scripts/otp-compliance.es
102118
cd /home/runner/work/otp/otp/${{ env.VERSION }} && \
103-
.github/scripts/otp-compliance.es sbom osv-scan --version ${{ inputs.version }} --fail_if_cve ${{ inputs.fail_if_cve }}
119+
.github/scripts/otp-compliance.es sbom osv-scan \
120+
--version ${{ inputs.version }} \
121+
--fail_if_cve ${{ inputs.fail_if_cve }} \
122+
--gh_alerts $HOME/gh_alerts.json
104123
105124
- name: "Get SHA"
125+
if: ${{ !failure() && steps.analysis.outcome != 'skipped' && inputs.sarif }}
106126
id: sha
107-
if: ${{ !failure() && inputs.sarif }}
108127
run: |
109128
echo "sha=$(cd /home/runner/work/otp/otp/${{ env.VERSION }} && git rev-parse HEAD)" >> $GITHUB_OUTPUT
110129
111130
- name: "Upload to code-scanning"
112-
if: ${{ !failure() && inputs.sarif }}
131+
if: ${{ !failure() && steps.analysis.outcome != 'skipped' && inputs.sarif }}
113132
env:
114133
SHA: ${{ steps.sha.outputs.sha }}
115134
uses: github/codeql-action/upload-sarif@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # ratchet:github/codeql-action/upload-sarif@v3

0 commit comments

Comments
 (0)