diff --git a/.github/scripts/ossf-sarif-generator.es b/.github/scripts/ossf-sarif-generator.es new file mode 100755 index 000000000000..077fa621b0f0 --- /dev/null +++ b/.github/scripts/ossf-sarif-generator.es @@ -0,0 +1,88 @@ +#!/usr/bin/env escript + +%% This script takes a json string as argument and checks that all the compiler flags defined by the OSSF +%% are used. + +main([CompilerFlagsJson]) -> + io:format(standard_error,"~p",[os:env()]), + CFLAGS = proplists:get_value(cflags, erlang:system_info(compile_info)) ++ " " ++ os:getenv("SKIPPED_OSSF_CFLAGS"), + LDFLAGS = proplists:get_value(ldflags, erlang:system_info(compile_info)) ++ " " ++ os:getenv("SKIPPED_OSSF_LDFLAGS"), + {gnuc, {Vsn, _, _} } = erlang:system_info(c_compiler_used), + #{ ~"options" := #{ ~"recommended" := Opts } } = json:decode(unicode:characters_to_binary(CompilerFlagsJson)), + io:format(standard_error, ~s'CFLAGS="~ts"~nLDFLAGS="~ts"~n',[CFLAGS, LDFLAGS]), + Missing = [Opt || Opt <- Opts, check_option(Opt, string:split(CFLAGS, " ", all), string:split(LDFLAGS, " ", all), Vsn)], + io:format("~ts~n",[sarif(Missing)]), + ok. +check_option(#{ ~"requires" := #{ ~"gcc" := GccVsn }, ~"opt" := Opt }, CFLAGS, _LDFLAGS, CurrentGccVsn) -> + io:format(standard_error, "Looking for ~ts...",[Opt]), + case binary_to_integer(hd(string:split(GccVsn, "."))) > CurrentGccVsn of + true -> io:format(standard_error, "skipped!~n",[]), false; + false -> + check_for_flags(Opt, CFLAGS) + end; +check_option(#{ ~"requires" := #{ ~"binutils" := _ }, ~"opt" := Opt }, _CFLAGS, LDFLAGS, _CurrentGccVsn) -> + io:format(standard_error, "Looking for ~ts...",[Opt]), + check_for_flags(Opt, LDFLAGS); +check_option(#{ ~"requires" := #{ ~"libstdc++" := _ }, ~"opt" := Opt }, _CFLAGS, LDFLAGS, _CurrentGccVsn) -> + io:format(standard_error, "Looking for ~ts...",[Opt]), + check_for_flags(Opt, LDFLAGS); +check_option(#{ ~"requires" := Tool, ~"opt" := Opt }, _CFLAGS, _LDFLAGS, _CurrentGccVsn) -> + io:format(standard_error, "~ts not implemented yet using ~p!~n",[Opt, Tool]), + true. + +check_for_flags(Flag, Flags) -> + case lists:any(fun(O) -> lists:search(fun(A) -> string:equal(string:trim(O), string:trim(A)) end, Flags) =:= false end, string:split(Flag, " ", all) ) of + true -> io:format(standard_error, "missing!~n",[]), true; + false -> io:format(standard_error, "found!~n",[]), false + end. + +sarif(Missing) -> + Zip = lists:zip(lists:seq(1,length(Missing)), Missing), + json:encode( + #{ ~"version" => ~"2.1.0", + ~"$schema" => ~"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", + ~"runs" => + [ #{ + ~"tool" => + #{ ~"driver" => + #{ ~"informationUri" => ~"https://github.com/erlang/otp/.github/workflow/ossf-scanner", + ~"name" => ~"ossf-scanner", + ~"rules" => + [ #{ ~"id" => base64:encode(erlang:md5(Opt)), + ~"name" => ~"MissingCompilerFlag", + ~"shortDescription" => + #{ ~"text" => <<"Missing CFLAGS ", Opt/binary>> }, + ~"helpUri" => ~"https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++", + ~"fullDescription" => + #{ + ~"text" => <> + } + } + || {_Id, #{ ~"desc" := Desc, ~"opt" := Opt }} <- Zip], + ~"version" => ~"1.0" + } + }, + ~"artifacts" => + [ #{ + ~"location" => #{ + ~"uri" => ~".github/docker/Dockerfile.64-bit" + }, + ~"length" => -1 + } + ], + ~"results" => + [ #{ + ~"ruleId" => base64:encode(erlang:md5(Opt)), + ~"ruleIndex" => Id, + ~"level" => ~"warning", + ~"message" => #{ ~"text" => <<"Missing CFLAGS ", Opt/binary>> }, + ~"locations" => + [ #{ ~"physicalLocation" => + #{ ~"artifactLocation" => + #{ ~"uri" => ~".github/docker/Dockerfile.64-bit" } + } + } ] + } || {Id, #{ ~"opt" := Opt }} <- Zip] + } ] + }). \ No newline at end of file diff --git a/.github/workflows/ossf-compiler-flags-scanner.yaml b/.github/workflows/ossf-compiler-flags-scanner.yaml new file mode 100644 index 000000000000..2a6da60ebe6b --- /dev/null +++ b/.github/workflows/ossf-compiler-flags-scanner.yaml @@ -0,0 +1,63 @@ +## This workflow continually scan the master branch to make sure that +## the correct compiler flags are used when testing Erlang/OTP on github. + +name: Open Source Security Foundation + +on: + workflow_dispatch: + schedule: + - cron: 0 1 * * * + +permissions: + # Required to upload SARIF file to CodeQL. + # See: https://github.com/github/codeql-action/issues/2117 + actions: read + # Require writing security events to upload SARIF file to security tab + security-events: write + # Only need to read contents + contents: read + +jobs: + schedule-scan: + runs-on: ubuntu-latest + if: github.repository == 'erlang/otp' + steps: + - uses: actions/checkout@v4.2.1 + - name: Create initial pre-release tar + run: .github/scripts/init-pre-release.sh otp_src.tar.gz + - uses: actions/checkout@v4.2.1 + with: + repository: ossf/wg-best-practices-os-developers + sparse-checkout: docs/Compiler-Hardening-Guides/compiler-options-scraper + path: ossf + + - name: Setup compiler options scraper + run: | + pip3 install -r ossf/docs/Compiler-Hardening-Guides/compiler-options-scraper/requirements.txt + python3 ossf/docs/Compiler-Hardening-Guides/compiler-options-scraper/main.py + cat compiler-options.json + + - uses: ./.github/actions/build-base-image + with: + BASE_BRANCH: master + BUILD_IMAGE: true + + - name: Run compiler flag comparison + run: | + docker run -v `pwd`/.github/scripts:/github --entrypoint "" otp \ + bash -c "/github/ossf-sarif-generator.es '$(cat compiler-options.json)'" > results.sarif + + - name: "Upload artifact" + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 # v4.4.3 + with: + name: SARIF file + path: results.sarif + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + if: ${{ !cancelled() }} + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif +