-
Notifications
You must be signed in to change notification settings - Fork 1
Close the split-token and reflection bypasses, and self-test the guard #473
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| #!/usr/bin/env bash | ||
| # Self-test for check-rng-hygiene.sh. | ||
| # | ||
| # This guard keeps non-CSPRNG generators, weakened SecureRandom and | ||
| # caller-supplied GCM IVs out of production Kotlin and Java. A scanner that | ||
| # quietly stops scanning reports a clean tree exactly like a clean tree does, so | ||
| # the rules are asserted rather than trusted. | ||
| # | ||
| # Probes go under app/src/main/kotlin: the guard deliberately skips test, | ||
| # androidTest and testFixtures, so a probe placed there passes and looks like a | ||
| # bypass when it is only a misplaced probe. The positive control catches that. | ||
| # | ||
| # Each reject case requires the guard to NAME the probe file. Without that, a | ||
| # guard aborting for an unrelated reason would be credited as a detection and | ||
| # every case below would pass while detecting nothing. | ||
| # | ||
| # Probes are staged into a throwaway GIT_INDEX_FILE, so the real index is never | ||
| # touched. The file itself must exist on disk while the guard runs, because the | ||
| # scanner reads bytes; it is removed on every path including the EXIT trap. | ||
| set -uo pipefail | ||
|
|
||
| cd "$(dirname "$0")/.." || { echo "FAIL: cannot cd to the repo root"; exit 1; } | ||
| GUARD=scripts/check-rng-hygiene.sh | ||
| [ -x "$GUARD" ] || { echo "FAIL: $GUARD not found or not executable"; exit 1; } | ||
|
|
||
| TMPD=$(mktemp -d) | ||
| PROBE="" | ||
| cleanup() { rm -rf "$TMPD"; [ -n "$PROBE" ] && rm -f "$PROBE"; } | ||
| trap cleanup EXIT | ||
|
|
||
| fails=0 | ||
|
|
||
| # run_probe <path> <content> <pass|fail> <description> | ||
| run_probe() { | ||
| local name="$1" content="$2" expect="$3" desc="$4" | ||
|
|
||
| if [ -e "$name" ]; then | ||
| echo " HARNESS BROKEN: $name exists; refusing to overwrite a real file" | ||
| fails=$((fails + 1)); return | ||
| fi | ||
| PROBE="$name" | ||
| printf '%s' "$content" > "$name" | ||
|
|
||
| rm -f "$TMPD/index" | ||
| GIT_INDEX_FILE="$TMPD/index" git read-tree HEAD 2>/dev/null | ||
| GIT_INDEX_FILE="$TMPD/index" git add -f "$name" 2>/dev/null | ||
|
|
||
| # Both that the tree is populated AND that this probe is in it. Discarding | ||
| # the git errors above means a failed `git add` would otherwise leave the | ||
| # guard scanning a probe-free tree, and the case would be judged on a file | ||
| # the guard never saw. | ||
| local staged | ||
| staged=$(GIT_INDEX_FILE="$TMPD/index" git ls-files | wc -l) | ||
| if [ "$staged" -lt 10 ]; then | ||
| echo " HARNESS BROKEN: only $staged file(s) staged; the guard would scan almost nothing" | ||
| fails=$((fails + 1)); rm -f "$name"; PROBE=""; return | ||
| fi | ||
| if ! GIT_INDEX_FILE="$TMPD/index" git ls-files --error-unmatch "$name" >/dev/null 2>&1; then | ||
| echo " HARNESS BROKEN: $name was not staged; the guard would never see it" | ||
| fails=$((fails + 1)); rm -f "$name"; PROBE=""; return | ||
| fi | ||
|
|
||
| local rc=0 out | ||
| out=$(GIT_INDEX_FILE="$TMPD/index" "$GUARD" 2>&1) || rc=$? | ||
| rm -f "$name"; PROBE="" | ||
|
|
||
| if [ "$expect" = fail ]; then | ||
| if [ "$rc" -eq 0 ]; then | ||
| echo " BYPASS: $desc"; fails=$((fails + 1)) | ||
| elif ! printf '%s' "$out" | grep -qF "$name"; then | ||
| echo " WRONG REASON: $desc (guard failed without naming $name)"; fails=$((fails + 1)) | ||
| else | ||
| echo " ok: $desc" | ||
| fi | ||
| else | ||
| if [ "$rc" -ne 0 ]; then | ||
| echo " FALSE POSITIVE: $desc" | ||
| printf '%s\n' "$out" | sed 's/^/ /' | head -4 | ||
| fails=$((fails + 1)) | ||
| else | ||
| echo " ok: $desc" | ||
| fi | ||
| fi | ||
| } | ||
|
|
||
| echo "== rejects what it must reject ==" | ||
|
|
||
| SRC=app/src/main/kotlin/io/privkey/keep | ||
|
|
||
| run_probe $SRC/ProbeCtl.kt 'val x = Math.random() | ||
| ' fail "Math.random() (positive control: if this passes, nothing below means anything)" | ||
|
|
||
| run_probe $SRC/ProbeSplit.kt 'val x = Math. | ||
| random() | ||
| ' fail "member access split after a trailing dot" | ||
|
|
||
| run_probe $SRC/ProbeSplit2.kt 'val r = java.util. | ||
| Random() | ||
| ' fail "qualified name split after the package dot" | ||
|
|
||
| run_probe $SRC/ProbeReflect.kt 'val c = Class.forName("java.util." + "Random") | ||
| ' fail "reflection with a concatenated class name" | ||
|
|
||
| run_probe $SRC/ProbeLoad.kt 'val c = javaClass.classLoader.loadClass("java.util.Random") | ||
| ' fail "loadClass reaching a generator by name" | ||
|
|
||
| run_probe $SRC/ProbeSeed.kt 'val r = java.security.SecureRandom(); r.setSeed(1L) | ||
| ' fail "setSeed weakening SecureRandom" | ||
|
|
||
| run_probe $SRC/ProbeTlr.kt 'val x = ThreadLocalRandom.current().nextInt() | ||
| ' fail "ThreadLocalRandom" | ||
|
|
||
| echo "== accepts what it must accept ==" | ||
|
|
||
| run_probe $SRC/ProbeClean.kt 'val x = 1 | ||
| ' pass "ordinary code" | ||
|
|
||
| run_probe $SRC/ProbeComment.kt '// Math.random() is named here in prose only | ||
| val x = 1 | ||
| ' pass "a banned token inside a comment is not code" | ||
|
|
||
| run_probe $SRC/ProbeTest.kt 'val x = 1 | ||
| ' pass "ordinary code in the production tree" | ||
|
|
||
| echo | ||
| if [ "$fails" -ne 0 ]; then | ||
| echo "FAIL: $fails case(s) did not behave as required" | ||
| exit 1 | ||
| fi | ||
| echo "OK: check-rng-hygiene.sh rejects every known bypass and accepts sanctioned use" | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.