master
sh 108 lines 4.4 KB
Raw
1 #!/usr/bin/env bash
2 # Common helpers for coverity-audit scripts.
3 # Sourced from the per-action scripts; not executed directly.
4
5 set -euo pipefail
6
7 # ANSI colors for transparent output (per the project's run() pattern).
8 #
9 # IMPORTANT: define with $'...' so the variables contain real ESC bytes,
10 # not the literal four-character string "\033". This way both `echo -e
11 # "${COV_RED}..."` and `printf '%s' "${COV_RED}..."` render correctly --
12 # without forcing every printf format string to be the variable itself
13 # (which trips shellcheck SC2059) or %b (which adds inconsistency).
14 #
15 # Color vars are referenced by sourcing scripts; shellcheck cannot see that.
16 # shellcheck disable=SC2034
17 COV_RED=$'\033[0;31m'
18 # shellcheck disable=SC2034
19 COV_GREEN=$'\033[0;32m'
20 # shellcheck disable=SC2034
21 COV_YELLOW=$'\033[1;33m'
22 # shellcheck disable=SC2034
23 COV_GRAY=$'\033[0;90m'
24 # shellcheck disable=SC2034
25 COV_NC=$'\033[0m'
26
27 # Locate the repo root by walking up from the script directory.
28 # This way the scripts work no matter where the user runs them from.
29 cov_repo_root() {
30 git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel
31 }
32
33 # Source `<repo-root>/.env` if it exists. .env is the user's local-only
34 # secrets file (gitignored). It must export at least:
35 # COVERITY_COOKIE — the full Cookie header value pasted from a curl
36 # copy-as-cURL captured in DevTools
37 # COVERITY_PROJECT_ID — Coverity Scan numeric projectId (constant per project)
38 # COVERITY_HOST — defaults to https://scan4.scan.coverity.com
39 # Optional:
40 # COVERITY_VIEW_OUTSTANDING — viewId of the Outstanding view
41 # COVERITY_USER_AGENT — overridable UA string
42 cov_load_env() {
43 local root env
44 root="$(cov_repo_root)"
45 env="${root}/.env"
46 if [[ ! -f "${env}" || ! -r "${env}" ]]; then
47 echo -e "${COV_RED}[ERROR]${COV_NC} Missing ${env}. See SKILL.md for the .env template." >&2
48 return 1
49 fi
50 set -a
51 # shellcheck disable=SC1090
52 source "${env}"
53 set +a
54
55 : "${COVERITY_COOKIE:?COVERITY_COOKIE is empty in .env — paste a fresh cookie from the browser}"
56 : "${COVERITY_PROJECT_ID:?COVERITY_PROJECT_ID is empty in .env}"
57 if [[ ! "${COVERITY_PROJECT_ID}" =~ ^[1-9][0-9]*$ ]]; then
58 echo -e "${COV_RED}[ERROR]${COV_NC} COVERITY_PROJECT_ID must be a positive integer, got: '${COVERITY_PROJECT_ID}'" >&2
59 return 1
60 fi
61 : "${COVERITY_HOST:=https://scan4.scan.coverity.com}"
62 : "${COVERITY_USER_AGENT:=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36}"
63 export COVERITY_COOKIE COVERITY_PROJECT_ID COVERITY_HOST COVERITY_USER_AGENT
64
65 # Extract XSRF-TOKEN from the cookie string.
66 COVERITY_XSRF="$(printf '%s' "${COVERITY_COOKIE}" | sed -n 's/.*XSRF-TOKEN=\([^;]*\).*/\1/p')"
67 if [[ -z "${COVERITY_XSRF}" ]]; then
68 echo -e "${COV_RED}[ERROR]${COV_NC} XSRF-TOKEN not found inside COVERITY_COOKIE. Did you paste the full Cookie header?" >&2
69 return 1
70 fi
71 export COVERITY_XSRF
72 }
73
74 # Audit artifacts go under .local/audits/coverity/ at the repo root.
75 # .local/ is gitignored -- see AGENTS.md for the convention.
76 # Creates the directory on first call so callers can redirect output into
77 # subpaths without thinking about it.
78 cov_audit_dir() {
79 local root dir
80 root="$(cov_repo_root)"
81 dir="${root}/.local/audits/coverity"
82 mkdir -p "${dir}"
83 echo "${dir}"
84 }
85
86 # Reject non-ASCII bytes in a string. Coverity's edge (Cloudflare) rejects
87 # em-dashes and smart quotes with a 403 challenge; far better to fail before
88 # the network round-trip than to debug a Cloudflare block.
89 #
90 # `tr -d '\000-\177'` deletes ALL ASCII bytes; anything left is non-ASCII.
91 # This is portable across GNU and BSD/macOS (unlike `grep -P`, which is GNU-only).
92 cov_require_ascii() {
93 local s="$1"
94 if LC_ALL=C printf '%s' "${s}" | LC_ALL=C tr -d '\000-\177' | grep -q .; then
95 echo -e "${COV_RED}[ERROR]${COV_NC} Comment contains non-ASCII characters. Cloudflare blocks them. Replace em-dashes with '--' and curly quotes with straight quotes." >&2
96 return 1
97 fi
98 }
99
100 # CID validation: Coverity CIDs are positive integers (>= 1). Reject anything
101 # else before interpolating into jq filters, URLs, or paths.
102 cov_require_numeric_cid() {
103 local cid="$1"
104 if [[ ! "${cid}" =~ ^[1-9][0-9]*$ ]]; then
105 echo -e "${COV_RED}[ERROR]${COV_NC} CID must be a positive integer, got: '${cid}'" >&2
106 return 1
107 fi
108 }