Initialize GovOPlaN meta repository
This commit is contained in:
54
tools/checks/security-audit/Dockerfile
Normal file
54
tools/checks/security-audit/Dockerfile
Normal file
@@ -0,0 +1,54 @@
|
||||
FROM golang:1.26-bookworm AS go-tools
|
||||
|
||||
ARG GITLEAKS_VERSION=v8.30.1
|
||||
ARG OSV_SCANNER_VERSION=v2.4.0
|
||||
|
||||
RUN go install github.com/zricethezav/gitleaks/v8@${GITLEAKS_VERSION} \
|
||||
&& go install github.com/google/osv-scanner/v2/cmd/osv-scanner@${OSV_SCANNER_VERSION}
|
||||
|
||||
FROM python:3.12-bookworm
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
ca-certificates \
|
||||
curl \
|
||||
git \
|
||||
gnupg \
|
||||
jq \
|
||||
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
||||
&& apt-get install -y --no-install-recommends nodejs \
|
||||
&& curl -fsSL https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor -o /usr/share/keyrings/trivy.gpg \
|
||||
&& printf 'deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main\n' > /etc/apt/sources.list.d/trivy.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends trivy \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=go-tools /go/bin/gitleaks /usr/local/bin/gitleaks
|
||||
COPY --from=go-tools /go/bin/osv-scanner /usr/local/bin/osv-scanner
|
||||
COPY requirements-audit.txt /tmp/requirements-audit.txt
|
||||
|
||||
RUN python -m pip install --upgrade pip \
|
||||
&& python -m pip install --no-cache-dir -r /tmp/requirements-audit.txt \
|
||||
&& npm install -g jscpd \
|
||||
&& semgrep --version \
|
||||
&& bandit --version \
|
||||
&& ruff --version \
|
||||
&& gitleaks version \
|
||||
&& osv-scanner --version \
|
||||
&& trivy --version \
|
||||
&& jscpd --version
|
||||
|
||||
RUN mkdir -p /workspace \
|
||||
&& chmod 0777 /workspace
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
USER 65532:65532
|
||||
HEALTHCHECK NONE
|
||||
|
||||
CMD ["bash", "tools/checks/check-security-audit.sh", "--mode", "ci", "--scope", "current"]
|
||||
136
tools/checks/security-audit/run.sh
Normal file
136
tools/checks/security-audit/run.sh
Normal file
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
|
||||
IMAGE="${SECURITY_AUDIT_IMAGE:-govoplan/security-audit:local}"
|
||||
SCOPE="${SECURITY_AUDIT_SCOPE:-current}"
|
||||
MODE="${SECURITY_AUDIT_MODE:-full}"
|
||||
REPORTS_DIR="${SECURITY_AUDIT_REPORTS_DIR:-audit-reports}"
|
||||
REBUILD="${SECURITY_AUDIT_REBUILD:-0}"
|
||||
UPDATE="${SECURITY_AUDIT_UPDATE:-0}"
|
||||
BUILD_ONLY=0
|
||||
SCRIPT_ARGS=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--mode)
|
||||
MODE="${2:?missing mode}"
|
||||
shift 2
|
||||
;;
|
||||
--scope)
|
||||
SCOPE="${2:?missing scope}"
|
||||
shift 2
|
||||
;;
|
||||
--reports-dir)
|
||||
REPORTS_DIR="${2:?missing reports dir}"
|
||||
shift 2
|
||||
;;
|
||||
--rebuild)
|
||||
REBUILD=1
|
||||
shift
|
||||
;;
|
||||
--update)
|
||||
UPDATE=1
|
||||
REBUILD=1
|
||||
shift
|
||||
;;
|
||||
--build-only)
|
||||
BUILD_ONLY=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
cat <<'EOF'
|
||||
Usage: tools/checks/security-audit/run.sh [--mode quick|ci|full] [--scope current|govoplan] [--reports-dir DIR] [--rebuild] [--update] [--build-only] [--strict|--report-only]
|
||||
|
||||
Builds or reuses the containerized GovOPlaN audit toolbox, then runs
|
||||
tools/checks/check-security-audit.sh inside it.
|
||||
|
||||
Image caching:
|
||||
The image is tagged by a fingerprint of tools/checks/security-audit/Dockerfile and
|
||||
requirements-audit.txt. Existing matching images are reused.
|
||||
|
||||
Flags:
|
||||
--rebuild Rebuild the fingerprinted image using Docker cache.
|
||||
--update Rebuild with --pull --no-cache to refresh base images/tools.
|
||||
--build-only Build or refresh the image, then exit without running an audit.
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
--strict|--report-only)
|
||||
SCRIPT_ARGS+=("$1")
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
SCRIPT_ARGS+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
REBUILD="${REBUILD:-0}"
|
||||
UPDATE="${UPDATE:-0}"
|
||||
BUILD_ONLY="${BUILD_ONLY:-0}"
|
||||
|
||||
IMAGE_NAME="${IMAGE##*/}"
|
||||
if [[ "$IMAGE_NAME" == *:* ]]; then
|
||||
IMAGE_REPOSITORY="${IMAGE%:*}"
|
||||
IMAGE_ALIAS="$IMAGE"
|
||||
else
|
||||
IMAGE_REPOSITORY="$IMAGE"
|
||||
IMAGE_ALIAS="$IMAGE:local"
|
||||
fi
|
||||
|
||||
IMAGE_FINGERPRINT="$(
|
||||
{
|
||||
sha256sum "$ROOT/tools/checks/security-audit/Dockerfile"
|
||||
sha256sum "$ROOT/requirements-audit.txt"
|
||||
} | sha256sum | cut -c1-16
|
||||
)"
|
||||
FINGERPRINT_IMAGE="${SECURITY_AUDIT_FINGERPRINT_IMAGE:-$IMAGE_REPOSITORY:$IMAGE_FINGERPRINT}"
|
||||
|
||||
build_image() {
|
||||
local -a build_args=(-t "$FINGERPRINT_IMAGE" -t "$IMAGE_ALIAS" -f "$ROOT/tools/checks/security-audit/Dockerfile")
|
||||
if [[ "$UPDATE" == "1" ]]; then
|
||||
build_args=(--pull --no-cache "${build_args[@]}")
|
||||
fi
|
||||
docker build "${build_args[@]}" "$ROOT"
|
||||
}
|
||||
|
||||
if [[ "$REBUILD" == "1" ]] || ! docker image inspect "$FINGERPRINT_IMAGE" >/dev/null 2>&1; then
|
||||
echo "Building security audit toolbox image: $FINGERPRINT_IMAGE"
|
||||
build_image
|
||||
else
|
||||
echo "Reusing security audit toolbox image: $FINGERPRINT_IMAGE"
|
||||
if ! docker image inspect "$IMAGE_ALIAS" >/dev/null 2>&1; then
|
||||
docker tag "$FINGERPRINT_IMAGE" "$IMAGE_ALIAS"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$BUILD_ONLY" == "1" ]]; then
|
||||
docker image inspect "$FINGERPRINT_IMAGE" --format 'Built audit toolbox image {{.RepoTags}} {{.Id}}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$SCOPE" == "govoplan" ]]; then
|
||||
MOUNT_ROOT="$(dirname "$ROOT")"
|
||||
WORKDIR="/workspace/$(basename "$ROOT")"
|
||||
EXTRA_ENV=(-e "GOVOPLAN_REPOS_ROOT=/workspace")
|
||||
else
|
||||
MOUNT_ROOT="$ROOT"
|
||||
WORKDIR="/workspace"
|
||||
EXTRA_ENV=()
|
||||
fi
|
||||
|
||||
docker run --rm \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
-e HOME=/tmp/security-audit-home \
|
||||
-e SECURITY_AUDIT_SCOPE="$SCOPE" \
|
||||
-e SECURITY_AUDIT_MODE="$MODE" \
|
||||
-e SECURITY_AUDIT_REPORTS_DIR="$REPORTS_DIR" \
|
||||
-e SECURITY_AUDIT_FAIL_ON_FINDINGS="${SECURITY_AUDIT_FAIL_ON_FINDINGS:-0}" \
|
||||
-e SECURITY_AUDIT_REQUIRE_TOOLS="${SECURITY_AUDIT_REQUIRE_TOOLS:-0}" \
|
||||
"${EXTRA_ENV[@]}" \
|
||||
-v "$MOUNT_ROOT:/workspace" \
|
||||
-w "$WORKDIR" \
|
||||
"$FINGERPRINT_IMAGE" \
|
||||
bash tools/checks/check-security-audit.sh --mode "$MODE" --scope "$SCOPE" --reports-dir "$REPORTS_DIR" "${SCRIPT_ARGS[@]}"
|
||||
51
tools/checks/security-audit/semgrep-govoplan.yml
Normal file
51
tools/checks/security-audit/semgrep-govoplan.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
rules:
|
||||
- id: govoplan.python.subprocess-shell-true
|
||||
languages: [python]
|
||||
severity: WARNING
|
||||
message: Avoid shell=True for module/runtime commands; prefer argv lists and explicit argument validation.
|
||||
patterns:
|
||||
- pattern-either:
|
||||
- pattern: subprocess.run(..., shell=True, ...)
|
||||
- pattern: subprocess.call(..., shell=True, ...)
|
||||
- pattern: subprocess.check_call(..., shell=True, ...)
|
||||
- pattern: subprocess.check_output(..., shell=True, ...)
|
||||
|
||||
- id: govoplan.python.os-system
|
||||
languages: [python]
|
||||
severity: WARNING
|
||||
message: Avoid os.system; use subprocess with argv lists and explicit environment handling.
|
||||
pattern: os.system(...)
|
||||
|
||||
- id: govoplan.python.pickle-load
|
||||
languages: [python]
|
||||
severity: WARNING
|
||||
message: Pickle loading is unsafe for untrusted data; use structured formats or signed payloads.
|
||||
pattern-either:
|
||||
- pattern: pickle.load(...)
|
||||
- pattern: pickle.loads(...)
|
||||
|
||||
- id: govoplan.python.cors-wildcard-credentials
|
||||
languages: [python]
|
||||
severity: ERROR
|
||||
message: Credentialed CORS must not use wildcard origins.
|
||||
pattern: CORSMiddleware(..., allow_origins=["*"], allow_credentials=True, ...)
|
||||
|
||||
- id: govoplan.web.localstorage-sensitive-value
|
||||
languages: [javascript, typescript]
|
||||
severity: WARNING
|
||||
message: Do not persist tokens, API keys, credentials, or passwords in localStorage.
|
||||
patterns:
|
||||
- pattern: localStorage.setItem($KEY, $VALUE)
|
||||
- metavariable-regex:
|
||||
metavariable: $KEY
|
||||
regex: (?i).*(token|api.?key|secret|password|credential).*
|
||||
|
||||
- id: govoplan.web.window-localstorage-sensitive-value
|
||||
languages: [javascript, typescript]
|
||||
severity: WARNING
|
||||
message: Do not persist tokens, API keys, credentials, or passwords in localStorage.
|
||||
patterns:
|
||||
- pattern: window.localStorage.setItem($KEY, $VALUE)
|
||||
- metavariable-regex:
|
||||
metavariable: $KEY
|
||||
regex: (?i).*(token|api.?key|secret|password|credential).*
|
||||
Reference in New Issue
Block a user