scripts/sec.sh
Rama main · réplica de lectura del repositorio original.
#!/bin/bash
set -euo pipefail
readonly log_file="${LOG_FILE:-/tmp/decidealot-sec.log}"
readonly sarif_out="${SARIF_OUT:-sec.sarif}"
readonly empty_sarif="{\"version\":\"2.1.0\",\"\$schema\":\"https://json.schemastore.org/sarif-2.1.0.json\",\"runs\":[]}"
readonly source_dir="src"
readonly report_uri="uv.lock"
work_dir="$(mktemp -d)"
readonly work_dir
log() {
local level="$1"
shift
jq -nc \
--arg time "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')" \
--arg level "$level" \
--arg file "${BASH_SOURCE[1]##*/}" \
--argjson line "${BASH_LINENO[0]}" \
--arg func "${FUNCNAME[1]:-main}" \
--arg msg "$*" \
'{time:$time,level:$level,file:$file,line:$line,func:$func,msg:$msg}' >&2
}
cleanup() {
rm -rf "$work_dir"
}
on_error() {
local status=$?
log ERROR "command failed exit=$status"
exit "$status"
}
run_bandit() {
local status
set +e
python -m bandit -q -r "$source_dir" -f json -o "$work_dir/bandit.json" >"$work_dir/bandit.log" 2>&1
status=$?
set -e
if [[ "$status" -ne 0 && ! -s "$work_dir/bandit.json" ]]; then
cat "$work_dir/bandit.log" >&2
log ERROR "bandit did not produce a JSON report"
return "$status"
fi
}
run_pip_audit() {
local status
set +e
python -m pip_audit --format json --output "$work_dir/pip-audit.json" >"$work_dir/pip-audit.log" 2>&1
status=$?
set -e
if [[ "$status" -ne 0 && ! -s "$work_dir/pip-audit.json" ]]; then
cat "$work_dir/pip-audit.log" >&2
log ERROR "pip-audit did not produce a JSON report"
return "$status"
fi
}
trap cleanup EXIT
trap on_error ERR
exec > >(tee -a "$log_file") 2>&1
if [[ -n "${DEBUG:-}" ]]; then
log DEBUG "running Python security scanners"
fi
run_bandit
run_pip_audit
if [[ -s "$work_dir/bandit.json" ]]; then
jq '{
version: "2.1.0",
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
runs: [{
tool: {driver: {name: "Bandit", rules: []}},
results: [(.results // [])[]? | {
ruleId: .test_id,
level: ({HIGH: "error", MEDIUM: "warning", LOW: "note"}[.issue_severity] // "warning"),
message: {text: .issue_text},
locations: [{physicalLocation: {
artifactLocation: {uri: .filename},
region: {startLine: .line_number}
}}]
}]
}]
}' "$work_dir/bandit.json" >"$work_dir/bandit.sarif"
fi
if [[ -s "$work_dir/pip-audit.json" ]]; then
jq --arg report_uri "$report_uri" '{
version: "2.1.0",
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
runs: [{
tool: {driver: {name: "pip-audit", rules: []}},
results: [(.dependencies // [])[]? | .name as $name | .version as $version
| (.vulns // [])[]? | {
ruleId: .id,
level: "warning",
message: {text: ($name + " " + $version + ": " + (.description // .id))},
locations: [{physicalLocation: {
artifactLocation: {uri: $report_uri},
region: {startLine: 1}
}}]
}]
}]
}' "$work_dir/pip-audit.json" >"$work_dir/pip-audit.sarif"
fi
for scanner in bandit pip-audit; do
[[ -s "$work_dir/$scanner.sarif" ]] || printf '%s' "$empty_sarif" >"$work_dir/$scanner.sarif"
done
jq -s '{
version: "2.1.0",
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
runs: (map(.runs // []) | add)
}' "$work_dir/bandit.sarif" "$work_dir/pip-audit.sarif" >"$sarif_out"
finding_count="$(jq '[.runs[].results[]?] | length' "$sarif_out")"
if [[ "$finding_count" -gt 0 ]]; then
log WARN "security scan wrote findings=$finding_count path=$sarif_out"
exit 0
fi
log INFO "security scan wrote findings=0 path=$sarif_out"