Commit 5e6af91
authored
fix(security): hardens auth, XSS, rate limiting, and repo posture (#43)
* fix(security): hardens auth, XSS, rate limiting, and repo posture
Auth lifecycle:
- clears dashboard cache on token expiry (prevents data leak to next user)
- propagates 401 from allSettled to trigger re-auth (prevents zombie polling)
- adds cross-tab auth sync via storage event listener
- moves OAuth returnTo read to after CSRF check passes
XSS / injection:
- validates pr.htmlUrl with isSafeGitHubUrl before passing to StatusDot/SizeBadge
- replaces inline style label colors with adoptedStyleSheets registry
- removes style-src-attr unsafe-inline from CSP (zero unsafe-* directives)
- refactors UserAvatarBadge inline style to Tailwind class
Rate limiting / DoS:
- caps fetchRepos pagination at 1000 repos with warning notification
- serializes discoverUpstreamRepos to prevent TOCTOU race on repo cap
- adds length bounds to VALID_REPO_NAME regex (consistent with Zod schema)
- adds soft warning at 100+ repos in RepoSelector
- adds in-memory rate limiter to Worker token exchange endpoint
- adds HSTS to Worker SECURITY_HEADERS (matches static asset headers)
Stores / cache:
- adds .max(500) to ignoredItems with FIFO eviction
- adds pruneClosedIgnoredItems (30-day TTL) called on app startup
- wraps IndexedDB QuotaExceededError retry in try/catch (logs instead of crash)
Repo posture:
- adds SECURITY.md with responsible disclosure policy
- adds CODEOWNERS
- adds .npmrc with ignore-scripts=true
- pins happy-dom to exact version (removes caret range)
- adds CSP inline script hash verification to CI
- adds Sentry security token support to Worker tunnel
- documents Worker _dsnCache safety rationale
- fixes poll coordinator mount/unmount race
* fix(security): addresses quality gate findings from domain review
* fix(security): restores style-src-attr (corvu/kobalte need it), uses expireToken for cross-tab sync
* fix(security): addresses PR review findings with tests
- removes IP from rate-limit log, adds prune threshold guard
- makes SENTRY_DSN optional in Env interface
- moves returnTo consumption after auth success in OAuthCallback
- adds race-safe coordinator guard to onAuthCleared + clearHotSets
- extracts IGNORED_ITEMS_CAP constant, fixes FALLBACK_FG consistency
- documents CSP style-src-attr requirement (Kobalte) in SECURITY.md
- adds 13 tests: cross-tab auth sync, 401 propagation, label-colors
CSS rules, fetchRepos cap, rate-limit window reset, multi-user
upstream discovery cap
* fix(security): removes dead script, adds remaining test coverage
- deletes orphaned scripts/verify-csp-hash.mjs (replaced by inline CI shell)
- updates prek.toml and deploy.yml to use inline CSP hash check
- adds tests for returnTo preserved on validateToken failure and
token exchange failure
- adds test for SENTRY_DSN: undefined (optional field coverage)
* fix(deps): regenerates lockfile for pinned happy-dom, adds lockfile sync hook
- pnpm-lock.yaml specifier was ^20.8.9, package.json was pinned to
20.8.9 — CI frozen-lockfile install failed on the mismatch
- adds lockfile-sync pre-commit hook that runs pnpm install
--frozen-lockfile when package.json or pnpm-lock.yaml changes
* refactor(security): extracts CSP hash verification into shared script
* perf(waf): parallelizes smoke tests with GNU parallel
Replaces sequential curl loops (81s) with GNU parallel (-j10, ~2s).
Test specs become a pipe-delimited data array parsed by exported
functions. Adds --max-time 10 per curl, --timeout 15 per job,
command -v guard, and empty-output infrastructure failure detection.1 parent 046b511 commit 5e6af91
File tree
35 files changed
+850
-198
lines changed- .github
- workflows
- scripts
- src
- app
- components
- dashboard
- onboarding
- lib
- pages
- services
- stores
- worker
- tests
- components
- lib
- services
- stores
- worker
35 files changed
+850
-198
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
| 18 | + | |
17 | 19 | | |
18 | 20 | | |
19 | | - | |
20 | | - | |
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
22 | | - | |
| 21 | + | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
| 40 | + | |
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
16 | 25 | | |
17 | 26 | | |
18 | 27 | | |
| |||
35 | 44 | | |
36 | 45 | | |
37 | 46 | | |
38 | | - | |
| 47 | + | |
39 | 48 | | |
40 | 49 | | |
41 | 50 | | |
| |||
This file was deleted.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
0 commit comments