Skip to content

Commit f74a0f5

Browse files
committed
otp scan PRs for vulnerabilities
- scan PRs for vendor vulnerabilities. - the submission of the vendor SBOM should happen only on push events. - vulnerability scanning of dependencies must happen on a per PR basis, and on a per push basis (although Dependatbot should inform us of this).
1 parent d512808 commit f74a0f5

File tree

11 files changed

+162
-12
lines changed

11 files changed

+162
-12
lines changed

.github/scripts/otp-compliance.es

+93-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,18 @@ cli() ->
216216
> .github/scripts/otp-compliance.es sbom vendor --sbom-file otp.spdx.json
217217
""",
218218
arguments => [ sbom_option()],
219-
handler => fun sbom_vendor/1}
219+
handler => fun sbom_vendor/1},
220+
221+
"osv-scan" =>
222+
#{ help =>
223+
"""
224+
Performs vulnerability scanning on vendor libraries
225+
226+
Example:
227+
228+
> .github/scripts/otp-compliance.es sbom osv-scan
229+
""",
230+
handler => fun osv_scan/1}
220231
}},
221232
"explore" =>
222233
#{ help => """
@@ -1205,6 +1216,87 @@ generate_vendor_purl(Package) ->
12051216
[create_externalRef_purl(Description, <<Purl/binary, "@", Vsn/binary>>)]
12061217
end.
12071218

1219+
osv_scan(_) ->
1220+
application:ensure_all_started([ssl, inets]),
1221+
1222+
VendorSrcFiles = find_vendor_src_files("."),
1223+
Packages = generate_vendor_info_package(VendorSrcFiles),
1224+
1225+
%% Test if this works in a Github Workflow
1226+
OSVQueryResults = generate_osv_results(Packages),
1227+
io:format("~s", [json:format(OSVQueryResults)]).
1228+
%% file:write_file(File, json:format(OSVQueryResults)).
1229+
1230+
%% OSVQuery = generate_osv_query(Packages),
1231+
%% io:format("[OSV] Information sent~n~s~n", [json:format(OSVQuery)]),
1232+
%% OSV = json:encode(OSVQuery),
1233+
1234+
%% Content = {URI, [], Format, OSV},
1235+
%% Result = httpc:request(post, Content, [], []),
1236+
%% case Result of
1237+
%% {ok,{{_, 200,_}, _Headers, Body}} ->
1238+
%% #{~"results" := OSVResults} = json:decode(erlang:list_to_binary(Body)),
1239+
%% Vulnerabilities = lists:filter(fun (#{~"vulns" := _Ids}) -> true; (_) -> false end, OSVResults),
1240+
%% case Vulnerabilities of
1241+
%% [] ->
1242+
%% io:format("[OSV] No vulnerabilities found.~n");
1243+
%% _ ->
1244+
%% FormatVulns = format_vulnerabilities(OSVQuery, OSVResults),
1245+
%% fail("[OSV] There are existing vulnerabilities:~n~s", [FormatVulns])
1246+
%% end;
1247+
%% {error, Error} ->
1248+
%% fail("[OSV] POST request to ~p errors: ~p", [URI, Error])
1249+
%% end.
1250+
1251+
%% format_vulnerabilities(OSVQuery, OSVResults) ->
1252+
%% NameVulnerabilities = lists:zip(osv_names(OSVQuery), OSVResults),
1253+
%% ExistingVulnerabilities = lists:filtermap(fun ({Name, #{~"vulns" := Ids}}) ->
1254+
%% {true, {Name, [Id || #{~"id" := Id} <- Ids]}};
1255+
%% (_) ->
1256+
%% false
1257+
%% end, NameVulnerabilities),
1258+
%% lists:map(fun ({N, Ids}) ->
1259+
%% io_lib:format("- ~s: ~s~n", [N, lists:join(",", Ids)])
1260+
%% end, ExistingVulnerabilities).
1261+
1262+
%% osv_names(#{~"queries" := Packages}) ->
1263+
%% lists:map(fun osv_names/1, Packages);
1264+
%% osv_names(#{~"package" := #{~"name" := Name }}) ->
1265+
%% Name.
1266+
1267+
%% generate_osv_query(Packages) ->
1268+
%% #{~"queries" => lists:foldl(fun generate_osv_query/2, [], Packages)}.
1269+
%% generate_osv_query(#{~"versionInfo" := Vsn, ~"ecosystem" := Ecosystem, ~"name" := Name}, Acc) ->
1270+
%% Package = #{~"package" => #{~"name" => Name, ~"ecosystem" => Ecosystem}, ~"version" => Vsn},
1271+
%% [Package | Acc];
1272+
%% generate_osv_query(#{~"sha" := SHA, ~"downloadLocation" := Location}, Acc) ->
1273+
%% case string:prefix(Location, ~"https://") of
1274+
%% nomatch ->
1275+
%% Acc;
1276+
%% URI ->
1277+
%% Package = #{~"package" => #{~"name" => URI}, ~"commit" => SHA},
1278+
%% [Package | Acc]
1279+
%% end;
1280+
%% generate_osv_query(_, Acc) ->
1281+
%% Acc.
1282+
1283+
1284+
generate_osv_results(Packages) ->
1285+
#{~"results" => [#{~"packages" => lists:foldl(fun generate_osv_results/2, [], Packages)}]}.
1286+
generate_osv_results(#{~"versionInfo" := Vsn, ~"ecosystem" := Ecosystem, ~"name" := Name}, Acc) ->
1287+
Package = #{~"package" => #{~"name" => Name, ~"ecosystem" => Ecosystem, ~"version" => Vsn}},
1288+
[Package | Acc];
1289+
generate_osv_results(#{~"sha" := SHA, ~"downloadLocation" := Location}, Acc) ->
1290+
case string:prefix(Location, ~"https://") of
1291+
nomatch ->
1292+
Acc;
1293+
URI ->
1294+
Package = #{~"package" => #{~"name" => URI, ~"commit" => SHA}},
1295+
[Package | Acc]
1296+
end;
1297+
generate_osv_results(_, Acc) ->
1298+
Acc.
1299+
12081300
cleanup_path(<<"./", Path/binary>>) when is_binary(Path) -> Path;
12091301
cleanup_path(Path) when is_binary(Path) -> Path.
12101302

.github/workflows/main.yaml

+47-3
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,50 @@ jobs:
425425
docker run otp "erl ${OPTION} -noshell -s init stop"
426426
done
427427
428+
extract-deps:
429+
name: Extract Dependencies
430+
runs-on: ubuntu-latest
431+
needs:
432+
- pack
433+
steps:
434+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/[email protected]
435+
- uses: ./.github/actions/build-base-image
436+
with:
437+
BASE_BRANCH: ${{ env.BASE_BRANCH }}
438+
439+
# check that PRs do not introduce vulnerabilities in vendor dependencies
440+
- name: 'Vendor Vulnerability Scanning'
441+
run: |
442+
docker run -v $PWD/:/github otp \
443+
"/github/.github/scripts/otp-compliance.es sbom osv-scan > /github/osv-scanner.json"
444+
sudo chown -R `whoami` osv-scanner.json
445+
446+
# Upload the deps
447+
- name: "upload osv-scanner deps"
448+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/[email protected]
449+
with:
450+
name: converted-OSV-Scanner-deps
451+
path: /home/runner/work/otp/otp/osv-scanner.json
452+
retention-days: 2
453+
454+
scan-pr:
455+
# run-scheduled-scan triggers this job
456+
# PRs and pushes trigger this job
457+
needs: extract-deps
458+
permissions:
459+
# Require writing security events to upload SARIF file to security tab
460+
security-events: write
461+
# Required to upload SARIF file to CodeQL.
462+
# See: https://github.com/github/codeql-action/issues/2117
463+
actions: read
464+
contents: read
465+
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@6fc714450122bda9d00e4ad5d639ad6a39eedb1f" # ratchet:google/osv-scanner-action/.github/workflows/[email protected]
466+
with:
467+
download-artifact: converted-OSV-Scanner-deps
468+
upload-sarif: ${{ github.repository == 'erlang/otp' }}
469+
scan-args: |-
470+
--lockfile=osv-scanner:osv-scanner.json
471+
428472
build:
429473
name: Build Erlang/OTP
430474
runs-on: ubuntu-latest
@@ -847,10 +891,9 @@ jobs:
847891
fail-on: ${{ github.ref_type == 'tag' && '' || '' }} # 'violations,issues' }}
848892
sw-version: ${{ env.OTP_SBOM_VERSION }}
849893

850-
vendor-analysis:
851-
name: Vendor Dependency Analysis
894+
vendor-dependency-upload:
895+
name: Vendor Dependency Upload
852896
runs-on: ubuntu-latest
853-
if: github.event_name == 'push'
854897
needs:
855898
- sbom
856899
- pack
@@ -878,6 +921,7 @@ jobs:
878921
879922
# allows Dependabot to give us alert of the vendor libraries that use semantic versioning
880923
- name: Upload SBOM to Github Dependency API
924+
if: github.event_name == 'push'
881925
uses: advanced-security/spdx-dependency-submission-action@5530bab9ee4bbe66420ce8280624036c77f89746 # ratchet:advanced-security/[email protected]
882926

883927
## If this is an "OTP-*" tag that has been pushed we do some release work

HOWTO/SBOM.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ This file may be a list of JSON objects. For simplicity, we document the fields
183183
"licenseDeclared": "Zlib",
184184
"name": "asmjit",
185185
"versionInfo": "a465fe71ab3d0e224b2b4bd0fac69ae68ab9239d",
186+
"sha": "a465fe71ab3d0e224b2b4bd0fac69ae68ab9239d",
186187
"path": "./erts/emulator/asmjit",
187188
"supplier": "Person: Petr Kobalicek",
188189
"purl": "pkg:github/asmjit/asmjit"
@@ -199,11 +200,15 @@ Fields summary:
199200
- If you are unsure about the name of the `SPDX-TOP-LEVEL-PACKAGE`, take a look at the source SBOM to identify packages (under key `packages` in the SBOM).
200201
- `description`: a brief description of what this vendor library does.
201202
- `copyrightText`: copyright text associated with the top-level package/library/3pp using [SPDX License Identifiers](https://spdx.org/licenses/).
202-
- `downloadLocation`: URI of the vendor library to download.
203+
- `downloadLocation`: URI of the vendor library to download. If using Github, use preferably `https//` rather than `git+https//` or similars.
204+
This is because the download location is used for vulnerability scanning in `.github/scripts/otp-compliance.es`.
203205
- `homepage`: homepage of the vendor library.
204206
- `licenseDeclared`: license as declared by the vendor, following a [SPDX license identifier](https://spdx.org/licenses/).
205207
- `name`: name of the library.
206208
- `versionInfo`: version of the library/project/3pp. In case of no version number being available, write the commit sha.
209+
- `sha`: sha commit for `versionInfo`, they need to be updated together!
210+
- `ecosystem`: List of valid ecosystems in [OSV Ecosystems](https://ossf.github.io/osv-schema/#defined-ecosystems)
211+
where this value is omitted for C/C++ code (e.g., `asmjit`, `pcre2`, `zlib`, `zstd`, etc), and used in `vendor.json` for `jquery`.
207212
- `path`: path to the vendor library inside Erlang/OTP. This can point to a folder or a list of files.
208213
- Folder: any file inside the folder is considered part of the vendor library (e.g., asmjit [vendor.info](../erts/emulator/asmjit/vendor.info)).
209214
- List of files: only the files listed here are part of a vendor library (e.g., erts-config [vendor.info](../erts/autoconf/vendor.info)).

erts/emulator/asmjit/vendor.info

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"licenseDeclared": "Zlib",
1616
"name": "asmjit",
1717
"versionInfo": "a465fe71ab3d0e224b2b4bd0fac69ae68ab9239d",
18+
"sha": "a465fe71ab3d0e224b2b4bd0fac69ae68ab9239d",
1819
"path": "./erts/emulator/asmjit",
1920
"supplier": "Person: Petr Kobalicek",
2021
"purl": "pkg:github/asmjit/asmjit"

erts/emulator/openssl/vendor.info

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"licenseDeclared": "Apache-2.0",
1616
"name": "openssl",
1717
"versionInfo": "3.5",
18+
"sha": "cdfb0923a66155ce97640fca68ae57b3a2972029",
1819
"path": "./erts/emulator/openssl",
1920
"supplier": "Organization: OpenSSL Mission",
2021
"purl": "pkg:generic/openssl"

erts/emulator/pcre/vendor.info

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010
"ID": "erts-pcre",
1111
"description": "PCRE2 library",
1212
"copyrightText": "NOASSERTION",
13-
"downloadLocation": "git+https://github.com/PCRE2Project/pcre2.git",
13+
"downloadLocation": "https://github.com/PCRE2Project/pcre2",
1414
"homepage": "https://pcre2project.github.io/pcre2/",
1515
"licenseDeclared": "BSD-3-Clause",
1616
"name": "pcre2",
17-
"versionInfo": "10.44",
17+
"versionInfo": "10.45",
1818
"path": "./erts/emulator/pcre",
19+
"sha": "1e09555d6950bfcf83bd98fa597b0c6440d43c9c",
1920
"supplier": "Person: Philip Hazel",
2021
"purl": "pkg:generic/pcre2"
2122
}

erts/emulator/zlib/vendor.info

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
"ID": "erts-zlib",
1111
"description": "interface of the 'zlib' general purpose compression library",
1212
"copyrightText": "Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler",
13-
"downloadLocation": "https://zlib.net/",
13+
"downloadLocation": "https://github.com/madler/zlib",
1414
"homepage": "https://zlib.net/",
1515
"licenseDeclared": "Zlib",
1616
"name": "zlib",
1717
"versionInfo": "1.3.1",
18+
"sha": "1a8db63788c34a50e39e273d39b7e1033208aea2",
1819
"path": "./erts/emulator/zlib",
1920
"supplier": "Person: Mark Adler ([email protected])",
20-
"purl": "pkg:generic/zlib"
21+
"purl": "pkg:github/madler/zlib"
2122
}
2223
]

erts/emulator/zstd/vendor.info

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"licenseDeclared": "BSD-3-Clause OR GPL-2.0-only",
1717
"name": "zstd",
1818
"versionInfo": "v1.5.7",
19+
"sha": "f8745da6ff1ad1e7bab384bd1f9d742439278e99",
1920
"path": "./erts/emulator/zstd",
2021
"supplier": "Organization: Meta",
2122
"purl": "pkg:github/facebook/zstd",

lib/common_test/priv/vendor.info

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"downloadLocation": "https://github.com/jquery/jquery",
1414
"homepage": "https://jquery.com",
1515
"licenseDeclared": "MIT",
16+
"ecosystem": "npm",
1617
"name": "jquery",
1718
"versionInfo": "3.7.1",
1819
"path": ["./lib/common_test/priv/jquery-latest.js"],
@@ -26,7 +27,8 @@
2627
"downloadLocation": "https://github.com/Mottie/tablesorter",
2728
"homepage": "https://github.com/Mottie/tablesorter",
2829
"licenseDeclared": "BSD-3-Clause OR GPL-2.0-only",
29-
"name": "jquery-tablesorter",
30+
"ecosystem": "npm",
31+
"name": "tablesorter",
3032
"versionInfo": "2.32",
3133
"path": ["./lib/common_test/priv/jquery.tablesorter.min.js"],
3234
"supplier": "Person: Christian Bach",

lib/erl_interface/src/openssl/vendor.info

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"licenseDeclared": "Apache-2.0",
1616
"name": "openssl",
1717
"versionInfo": "3.5",
18+
"sha": "636dfadc70ce26f2473870570bfd9ec352806b1d",
1819
"path": "./lib/erl_interface/src/openssl",
1920
"supplier": "Organization: OpenSSL Mission",
2021
"purl": "pkg:generic/openssl"

lib/wx/vendor.info

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
"homepage": "https://github.com/wxWidgets/wxWidgets",
1515
"licenseDeclared": "LicenseRef-scancode-wxwindows-free-doc-3",
1616
"name": "wx",
17-
"versionInfo": "dc585039bbd426829e3433002023a93f9bedd0c2",
17+
"versionInfo": "f2918a9ac823074901ce27de939baa57788beb3d",
18+
"sha": "fd0f60daea24e9c62d372d774be9e32ce2b0849d",
1819
"path": "./lib/wx",
19-
"comments": "This only applies to the source code of Erlang files in 'src', and specifically to the documentation embedded in them",
20+
"comments": "This only applies to the source code of Erlang files in 'src', and specifically to the documentation embedded in them.",
2021
"supplier": "NOASSERTION",
2122
"purl": "pkg:github/wxwidgets/wxwidgets"
2223
}

0 commit comments

Comments
 (0)