Files
bitlogin/.semgrep.yml
thePR0M3TH3ANandClaude Fable 5 f0b5f40fd4 Audit response: fix BL-16 through BL-20 and the low findings, same day
BL-16 (high): a remote signer's auth_url is attacker-controlled input that
became a clickable href. sanitizeAuthUrl in core now admits only https
(or loopback http) -- javascript:/data:/blob:/custom schemes are dropped
without consuming the surfacing slot -- and the widget link shows the
hostname. Hostile-scheme tests at both layers.

BL-17: the post-creation PRF fallback assertion is pinned via
allowCredentials to the credential that was just created, so a multi-
passkey account sheet can no longer silently bind an older passkey's
account.

BL-18: queryOnce's timeout path sent CLOSE before settling, unguarded --
a relay dying mid-query stranded the promise and everything queued behind
the worker's serial queue. The send is guarded; settling is unconditional.
MockRelay.close() now terminates live sockets (it previously waited on
them forever), and a suppressEose switch models the silent relay.

BL-19: relays are no longer trusted to honor limits. Buffered queries
enforce filter.limit locally, drop replayed duplicate ids, and cap at
1000 events; a 20-copy replay now yields one event.

BL-20: the .semgrep.yml try/catch fallback pattern never parsed, so the
entropy rules silently did not run. Rewritten in parseable form and
verified under the pinned Semgrep 1.171.0: fires on hostile code, exits
0 on the clean tree.

Lows: NIP-07 and NIP-46 verify returned signed events (signature, pinned
session identity, field equality with the request); Nip46Client.close()
wipes the client and conversation keys (§11.10); passkey labels carry a
short suffix so multiple accounts are distinguishable; postcss bumped to
8.5.25 (GHSA-fxqj-rqcc-2cmp); GitHub Actions pinned to commit SHAs.
Deferred as interop (recorded in TODO): NIP-46 protocol logout and
switch_relays. 12 new adversarial tests; 256 total.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-04 13:10:20 -04:00

64 lines
2.2 KiB
YAML

# Local semgrep rules, run alongside `--config auto` in
# .github/workflows/security.yml.
#
# Checklist §13 of docs/entropy-audit-2026-07-31.md: make the randomness rules
# machine-enforced rather than review-enforced. Every rule here is currently
# clean across packages/ — they exist to keep it that way, not to flag a
# backlog.
rules:
- id: bitlogin-no-math-random
languages: [typescript, javascript]
severity: ERROR
message: >-
Math.random() is not a CSPRNG and must not be used anywhere in this
package, including for values that are not secrets. Use randomBytes or
randomUniformInt from @bitlogin/core/crypto. A ban with a standing
exception is where the next real misuse hides -- if you believe this
specific call is safe, the correct move is still to use the CSPRNG, which
costs nothing here.
paths:
include:
- /packages/*/src/**
pattern: Math.random(...)
- id: bitlogin-no-crypto-fallback
languages: [typescript, javascript]
severity: ERROR
message: >-
Do not fall back to another source when getRandomValues is unavailable.
Key generation must fail closed -- "continue with reduced security" is the
COLDCARD defect class. crypto/random.ts throws by design; keep it that way.
paths:
include:
- /packages/*/src/**
patterns:
- pattern-either:
- pattern: $C.getRandomValues(...) ?? $FALLBACK
- pattern: $C.getRandomValues(...) || $FALLBACK
- pattern: |
try {
...
$C.getRandomValues(...)
...
} catch ($E) {
...
return $FALLBACK;
}
- id: bitlogin-no-time-or-id-seeded-secrets
languages: [typescript, javascript]
severity: ERROR
message: >-
Timestamps, counters and identifiers are not entropy. A secret derived
from Date.now() or performance.now() is predictable to anyone who knows
roughly when it was created.
paths:
include:
- /packages/*/src/**
patterns:
- pattern-either:
- pattern: |
$KEY = $HASH(Date.now(), ...)
- pattern: |
$KEY = $HASH(performance.now(), ...)