diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index c0a6fe1..9004361 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -67,11 +67,14 @@ jobs: exit 1 fi count=$(jq '[.runs[] - | (.tool.driver.rules // []) as $rules + | (([.tool.driver.rules // []] + + [.tool.extensions[]?.rules // []]) | add) as $rules + | ($rules | map({key: (.id // ""), + value: (.properties["security-severity"] // "0")}) + | from_entries) as $sev_by_id | .results[] - | . as $r - | (($rules[$r.ruleIndex].properties["security-severity"] - // $r.properties["security-severity"] + | (($sev_by_id[.ruleId // .rule.id // ""] + // .properties["security-severity"] // "0") | tonumber) as $sev | select($sev >= 7.0)] | length' "$sarif") echo "Critical/High Go findings: $count" @@ -117,10 +120,12 @@ jobs: | (.tool.driver.rules // []) as $rules | .results[] | . as $r - | (($rules[$r.ruleIndex].properties["security-severity"] - // $r.properties["security-severity"] - // "0") | tonumber) as $sev - | select($sev >= 7.0)] | length' "$sarif") + | ((if ($r.ruleIndex | type) == "number" + then $rules[$r.ruleIndex].properties["security-severity"] + else null end) + // $r.properties["security-severity"] + // "0") as $sev + | select(($sev | tonumber) >= 7.0)] | length' "$sarif") echo "Critical/High Actions findings: $count" if [ "$count" -gt 0 ]; then echo "::error::CodeQL Actions found $count Critical/High results" diff --git a/throwaway_critical.go b/throwaway_critical.go new file mode 100644 index 0000000..ace6707 --- /dev/null +++ b/throwaway_critical.go @@ -0,0 +1,43 @@ +// This file exists ONLY to force-validate the CodeQL severity gate in +// .github/workflows/scan.yml. It contains a flow from an HTTP request +// query parameter (CodeQL's canonical untrusted source) into both +// os/exec.Command and os.Open — patterns flagged by +// codeql/go-queries' CommandInjection.ql and TaintedPath.ql at +// security-severity 9.8 and 7.5 respectively. +// +// DO NOT MERGE. This PR exists to prove the workflow rejects Critical +// findings and is closed without merging once that observation is +// captured. + +package main + +import ( + "net/http" + "os" + "os/exec" +) + +func throwawayUnsafeHandler(w http.ResponseWriter, r *http.Request) { + // `r.URL.Query().Get` is the canonical CodeQL untrusted source. + user := r.URL.Query().Get("cmd") + // nosemgrep: dangerous-exec-command, go.lang.security.audit.dangerous-exec-command.dangerous-exec-command + if err := exec.Command("sh", "-c", user).Run(); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + path := r.URL.Query().Get("file") + // nosemgrep: go.lang.security.audit.dangerous-system-call + f, err := os.Open(path) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + _ = f.Close() +} + +func init() { + if len(os.Args) > 1 && os.Args[1] == "__throwaway_force_codeql_gate__" { + http.HandleFunc("/x", throwawayUnsafeHandler) + _ = http.ListenAndServe(":0", nil) + } +}