8000 fix(security): prove and scope the cgm_cbio vendor-material guard by shroominic · Pull Request #173 · shroominic/OpenGlucose · GitHub
[go: up one dir, main page]

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

vendor-material-guard:
name: Vendor Material Guard
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install checksum-pinned quality tools
run: make tooling-bootstrap

- name: Reject vendor material and prove the rule with a canary
run: ./scripts/check-cbio-vendor-material.sh

dependency-review:
name: Dependency Review
if: github.event_name == 'pull_request'
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ SHELL := /bin/sh
format-check lint typecheck test-unit test-integration test-e2e test \
build build-android build-web build-ios build-macos test-ios-native \
test-macos-native \
verify-android-release-signing check
verify-android-release-signing vendor-material-guard check

platform_checks :=
ifeq ($(shell uname -s),Darwin)
Expand All @@ -26,6 +26,9 @@ tooling-bootstrap: ## Install checksum-pinned repository quality tools.
tooling-check: tooling-bootstrap ## Run ShellCheck and actionlint with pinned versions.
@./scripts/check-tooling.sh

vendor-material-guard: tooling-bootstrap ## Reject vendor material and prove the guard with a canary.
@./scripts/check-cbio-vendor-material.sh

hooks: ## Install the pinned Lefthook Git hooks.
@./scripts/install-lefthook.sh

Expand Down Expand Up @@ -75,4 +78,4 @@ test-macos-native: ## Run the macOS RunnerTests on this 8000 Mac.
verify-android-release-signing: ## Prove Android release signing fails closed without credentials.
@./scripts/flutter-workspace.sh verify-android-release-signing

check: tooling-check format-check lint test build verify-android-release-signing $(platform_checks) ## Run every required CI gate supported by this host.
check: tooling-check vendor-material-guard format-check lint test build verify-android-release-signing $(platform_checks) ## Run every required CI gate supported by this host.
94 changes: 94 additions & 0 deletions scripts/check-cbio-vendor-material.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/bin/sh
# Proves the CBIO GS1 vendor-material rule rejects material and clears the tree.
#
# Two assertions, both required:
# 1. the checked-out tree carries no vendor material; and
# 2. a deliberate synthetic canary is reported by the same rule.
#
# The second exists so that a rule which silently stops matching fails this
# check instead of reporting a clean tree. No vendor byte, key-derived vector,
# or device address is stored in this script or in the rule it runs.

set -eu

repo_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
config="$repo_root/security/gitleaks-vendor-material.toml"
rule_id=cbio-vendor-material-constant

die() {
printf 'error: %s\n' "$*" >&2
exit 1
}

[ -f "$config" ] || die "missing guard config $config"

gitleaks_bin=${GITLEAKS_BIN:-}
if [ -z "$gitleaks_bin" ]; then
for candidate in \
"$repo_root/.dart_tool/quality-tools/bin/gitleaks" \
"$(command -v gitleaks 2>/dev/null || true)"
do
if [ -n "$candidate" ] && [ -x "$candidate" ]; then
gitleaks_bin=$candidate
break
fi
done
fi
[ -n "$gitleaks_bin" ] ||
die 'gitleaks is required; run make tooling-bootstrap or set GITLEAKS_BIN'

work_dir=$(mktemp -d "${TMPDIR:-/tmp}/openglucose-vendor-guard.XXXXXX")
cleanup() {
rm -rf -- "$work_dir"
}
trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM

# Exit codes: 0 clean, 2 findings, anything else is a scanner failure.
scan() {
"$gitleaks_bin" detect \
--no-git \
--source "$1" \
--config "$config" \
--redact \
--exit-code=2 \
--report-format json \
--report-path "$2" \
>/dev/null 2>&1
}

tree_report="$work_dir/tree.json"
tree_status=0
scan "$repo_root" "$tree_report" || tree_status=$?
case "$tree_status" in
0) ;;
2)
die "the tree compiles vendor material ($(grep -c '"RuleID"' "$tree_report") finding(s))"
;;
*) die "gitleaks could not scan the tree (exit $tree_status)" ;;
esac

canary_root="$work_dir/canary"
canary_file="$canary_root/packages/cgm_cbio/lib/src/vendor_material_canary.dart"
mkdir -p "$(dirname -- "$canary_file")"
# The binding name and the payload are assembled here rather than written as one
# literal, so this script does not itself carry a material-shaped constant for
# the rule, or any other scanner, to match.
canary_binding=cbioVendorStreamKey
canary_payload='<int>[0x00, 0x01, 0x02, 0x03]'
{
printf '// Synthetic canary: not the vendor material, same binding shape.\n'
printf 'const List<int> %s = %s;\n' "$canary_binding" "$canary_payload"
} >"$canary_file"

canary_report="$work_dir/canary.json"
canary_status=0
scan "$canary_root" "$canary_report" || canary_status=$?
[ & 8000 quot;$canary_status" = 2 ] ||
die "the guard did not report its own canary (exit $canary_status); the rule is not working"
grep -q "$rule_id" "$canary_report" ||
die "the canary was reported by a rule other than $rule_id"

printf 'vendor-material guard: %s rejected the canary and the tree is clean.\n' "$rule_id"
34 changes: 29 additions & 5 deletions scripts/install-quality-tools.sh
8000
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ repo_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
tools_bin="$repo_root/.dart_tool/quality-tools/bin"
shellcheck_version=0.11.0
actionlint_version=1.7.12
gitleaks_version=8.24.3

die() {
printf 'error: %s\n' "$*" >&2
Expand Down Expand Up @@ -40,8 +41,15 @@ if [ -x "$tools_bin/actionlint" ]; then
[ "$installed_actionlint" != "$actionlint_version" ] || actionlint_installed=true
fi

if [ "$shellcheck_installed" = true ] && [ "$actionlint_installed" = true ]; then
printf 'ShellCheck %s and actionlint %s are installed.\n' "$shellcheck_version" "$actionlint_version"
gitleaks_installed=false
if [ -x "$tools_bin/gitleaks" ]; then
installed_gitleaks=$("$tools_bin/gitleaks" version)
[ "$installed_gitleaks" != "$gitleaks_version" ] || gitleaks_installed=true
fi

if [ "$shellcheck_installed" = true ] && [ "$actionlint_installed" = true ] && [ "$gitleaks_installed" = true ]; then
printf 'ShellCheck %s, actionlint %s, and gitleaks %s are installed.\n' \
"$shellcheck_version" "$actionlint_version" "$gitleaks_version"
exit 0
fi

Expand All @@ -54,24 +62,32 @@ case "$(uname -s):$(uname -m)" in
shellcheck_sha=339b930feb1ea764467013cc1f72d09cd6b869ebf1013296ba9055ab2ffbd26f
actionlint_platform=darwin_arm64
actionlint_sha=aba9ced2dee8d27fecca3dc7feb1a7f9a52caefa1eb46f3271ea66b6e0e6953f
gitleaks_platform=darwin_arm64
gitleaks_sha=b90f13bb8c90ab72083d9b0c842e39dafb82c0e5c3f872f407366b7a58909013
;;
Darwin:x86_64)
shellcheck_platform=darwin.x86_64
shellcheck_sha=c2c15e08df0e8fbc374c335b230a7ee958c313fa5714817a59aa59f1aa594f51
actionlint_platform=darwin_amd64
actionlint_sha=5b44c3bc2255115c9b69e30efc0fecdf498fdb63c5d58e17084fd5f16324c644
gitleaks_platform=darwin_x64
gitleaks_sha=41c44ae8ad1d6eef57d4526ad0fd67d8129eee9a856f55c2b3b9395fd3d9ec0f
;;
Linux:aarch64|Linux:arm64)
shellcheck_platform=linux.aarch64
shellcheck_sha=68a8133197a50beb8803f8d42f9908d1af1c5540d4bb05fdfca8c1fa47decefc
actionlint_platform=linux_arm64
actionlint_sha=325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6
gitleaks_platform=linux_arm64
gitleaks_sha=5f2edbe1f49f7b920f9e06e90759947d3c5dfc16f752fb93aaafc17e9d14cf07
;;
Linux:x86_64|Linux:amd64)
shellcheck_platform=linux.x86_64
shellcheck_sha=b7af85e41cc99489dcc21d66c6d5f3685138f06d34651e6d34b42ec6d54fe6f6
actionlint_platform=linux_amd64
actionlint_sha=8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8
gitleaks_platform=linux_x64
gitleaks_sha=9991e0b2903da4c8f6122b5c3186448b927a5da4deef1fe45271c3793f4ee29c
;;
*) die "unsupported quality-tool platform: $(uname -s) $(uname -m)" ;;
esac
Expand All @@ -87,6 +103,7 @@ trap 'exit 143' TERM

shellcheck_archive="$temporary_dir/shellcheck.tar.gz"
actionlint_archive="$temporary_dir/actionlint.tar.gz"
gitleaks_archive="$temporary_dir/gitleaks.tar.gz"

curl --fail --location --proto '=https' --tlsv1.2 --retry 3 \
--output "$shellcheck_archive" \
Expand All @@ -98,13 +115,20 @@ curl --fail --location --proto '=https' --tlsv1.2 --retry 3 \
"https://github.com/rhysd/actionlint/releases/download/v${actionlint_version}/actionlint_${actionlint_version}_${actionlint_platform}.tar.gz"
verify_checksum "$actionlint_archive" "$actionlint_sha"

mkdir -p "$temporary_dir/shellcheck" "$temporary_dir/actionlint" "$tools_bin"
curl --fail --location --proto '=https' --tlsv1.2 --retry 3 \
--output "$gitleaks_archive" \
"https://github.com/gitleaks/gitleaks/releases/download/v${gitleaks_version}/gitleaks_${gitleaks_version}_${gitleaks_platform}.tar.gz"
verify_checksum "$gitleaks_archive" "$gitleaks_sha"

mkdir -p "$temporary_dir/shellcheck" "$temporary_dir/actionlint" "$temporary_dir/gitleaks" "$tools_bin"
tar -xzf "$shellcheck_archive" -C "$temporary_dir/shellcheck"
tar -xzf "$actionlint_archive" -C "$temporary_dir/actionlint"
tar -xzf "$gitleaks_archive" -C "$temporary_dir/gitleaks"
install -m 0755 \
"$temporary_dir/shellcheck/shellcheck-v${shellcheck_version}/shellcheck" \
"$tools_bin/shellcheck"
install -m 0755 "$temporary_dir/actionlint/actionlint" "$tools_bin/actionlint"
install -m 0755 "$temporary_dir/gitleaks/gitleaks" "$tools_bin/gitleaks"

printf 'Installed checksum-pinned ShellCheck %s and actionlint %s.\n' \
"$shellcheck_version" "$actionlint_version"
printf 'Installed checksum-pinned ShellCheck %s, actionlint %s, and gitleaks %s.\n' \
"$shellcheck_version" "$actionlint_version" "$gitleaks_version"
33 changes: 33 additions & 0 deletions security/gitleaks-vendor-material.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Vendor-material guard for the CBIO GS1 link.
#
# This file is consumed only with `--no-git --source <tree>`, so it inspects the
# checked-out tree and never history. `scripts/check-cbio-vendor-material.sh`
# runs it against the repository and then asserts the same rule still fires on a
# deliberate synthetic canary, so a rule that silently stops matching cannot
# report green.
#
# Why it is not part of `.gitleaks.toml`: the material and a key-derived masked
# vector are already in this repository's public history, so a history-wide rule
# would fail forever and no change on the tip could clear it. The tip is where a
# fix can clear it, and that is what this file guards.
#
# The generic ruleset does not match this material when it is committed as a
# Dart byte-list constant: the pinned scanner reports a synthetic fixture of the
# same shape and misses the credential itself. This rule therefore matches on
# the binding name plus a literal payload. It deliberately restates no byte, no
# key-derived vector, and no device address.
#
# There is no allowlist on purpose. An exemption here would have to name the
# value it exempts, and a legitimate synthetic fixture belongs under a
# non-material binding name instead of in an exemption.

title = "OpenGlucose CBIO GS1 vendor-material guard"

[extend]
useDefault = false

[[rules]]
id = "cbio-vendor-material-constant"
description = "Vendor GS1 stream material committed as a literal payload"
regex = '''(?i)(?:cbio[a-z0-9_]*(?:streamkey|authmaterial|authenticationtrigger)|_authmasked)[a-z0-9_]*\s*[:=]\s*(?:const\s+)?(?:<int>\s*\[\s*0x|\[\s*0x|'[0-9a-f]{16,}|"[0-9a-f]{16,}")'''
tags = ["key", "vendormaterial"]
Loading
0