Internal pen-test record
Records the S39 internal pen-test (3 days of focused effort by the project author against the staging instance). Findings logged here with their disposition.
Status. Like the a11y record, this file's structure is in place; the body is filled in at audit time. Nothing here yet because the live test happens against the deployed staging instance (S37) once it's stood up — that's the operator's call, not a code-time deliverable.
Scope
Per the S39 spec:
- Top OWASP risks (injection, broken auth, sensitive data exposure, XXE, broken access control, security misconfiguration, XSS, insecure deserialization, vulnerable components, insufficient logging).
- Auth surfaces: signup, login, password reset, 2FA, PATs, sessions, SSH key add/remove, session-epoch revocation, per-account "sign out everywhere".
- Git protocols: HTTPS smart-HTTP push/pull, SSH (when shipped), hook subprocess privilege boundary.
- Webhook SSRF: URL validation, redirect-following defense, IP block-list coverage.
Out of scope (covered separately or post-launch):
- Third-party penetration test — post-launch.
- Public bug bounty — post-launch.
- Side-channel attacks on the host — OS/runtime concern.
- Physical access — standard ops practice.
Methodology
- Re-run the
security auditCLI from S35 — every finding triaged. - Manual exploration of the auth surfaces. Account takeover scenarios (password reuse, session fixation, CSRF on state-changing forms, TOTP recovery race).
- Git protocol review. Authorization for push (pre-receive), read access for fetch (visibility check), AKC privilege boundary.
- Webhook fuzzing. SSRF attempts against private-IP ranges, redirect chains, DNS rebinding, payload size manipulation.
- Authorization grid. Each policy.Action × actor-shape — verify
policy.Canreturns the expected decision. The per-action table frominternal/auth/policy/is the checklist.
Findings template
### P-NN — <short title>
- **Severity:** critical / high / medium / low
- **Class:** auth / git / webhook / xss / csrf / ssrf / injection / info-leak / dos
- **Found by:** security audit CLI / manual / fuzzing
- **Route or surface:** /…/…
- **Description:** what's wrong + how to reproduce.
- **Disposition:** fixed in <commit-sha> / accepted: <rationale> / deferred to <sprint>
- **Re-tested on:** <date>
Findings
(none yet)
Accepted with rationale
(none yet)
Areas NOT looked at
Documented so the gap is the post-launch third-party scope:
- Race conditions in concurrent webhook delivery.
- TOCTOU bugs on file-system operations during git push.
- Side-channel timing on argon2 verification. (Mitigation: argon2id is constant-time per implementation.)
- Cryptanalysis of HMAC-signed cursors / unsubscribe links.
Tooling notes
- The
security auditCLI lives atcmd/shithubd/admin.go— sub-command list at S35. - Burp / ZAP are not part of the toolchain; manual + curl + the in-binary helpers cover what we need at MVP.
internal/security/ssrfships with its own unit tests; the fuzzing pass exercises the integration of SSRF defense with the webhook delivery path, not the unit logic.
Re-audit cadence
- Every release with auth or git surface changes.
- Quarterly full pass.
- After any incident with a security flavor — investigation + audit go together.
View source
| 1 | # Internal pen-test record |
| 2 | |
| 3 | Records the S39 internal pen-test (3 days of focused effort by |
| 4 | the project author against the staging instance). Findings logged |
| 5 | here with their disposition. |
| 6 | |
| 7 | > **Status.** Like the a11y record, this file's structure is in |
| 8 | > place; the body is filled in at audit time. Nothing here yet |
| 9 | > because the live test happens against the deployed staging |
| 10 | > instance (S37) once it's stood up — that's the operator's call, |
| 11 | > not a code-time deliverable. |
| 12 | |
| 13 | ## Scope |
| 14 | |
| 15 | Per the S39 spec: |
| 16 | |
| 17 | - Top OWASP risks (injection, broken auth, sensitive data |
| 18 | exposure, XXE, broken access control, security |
| 19 | misconfiguration, XSS, insecure deserialization, vulnerable |
| 20 | components, insufficient logging). |
| 21 | - Auth surfaces: signup, login, password reset, 2FA, PATs, |
| 22 | sessions, SSH key add/remove, session-epoch revocation, |
| 23 | per-account "sign out everywhere". |
| 24 | - Git protocols: HTTPS smart-HTTP push/pull, SSH (when shipped), |
| 25 | hook subprocess privilege boundary. |
| 26 | - Webhook SSRF: URL validation, redirect-following defense, |
| 27 | IP block-list coverage. |
| 28 | |
| 29 | Out of scope (covered separately or post-launch): |
| 30 | |
| 31 | - Third-party penetration test — post-launch. |
| 32 | - Public bug bounty — post-launch. |
| 33 | - Side-channel attacks on the host — OS/runtime concern. |
| 34 | - Physical access — standard ops practice. |
| 35 | |
| 36 | ## Methodology |
| 37 | |
| 38 | 1. Re-run the `security audit` CLI from S35 — every finding |
| 39 | triaged. |
| 40 | 2. Manual exploration of the auth surfaces. Account takeover |
| 41 | scenarios (password reuse, session fixation, CSRF on |
| 42 | state-changing forms, TOTP recovery race). |
| 43 | 3. Git protocol review. Authorization for push (pre-receive), |
| 44 | read access for fetch (visibility check), AKC privilege |
| 45 | boundary. |
| 46 | 4. Webhook fuzzing. SSRF attempts against private-IP ranges, |
| 47 | redirect chains, DNS rebinding, payload size manipulation. |
| 48 | 5. Authorization grid. Each policy.Action × actor-shape — verify |
| 49 | `policy.Can` returns the expected decision. The per-action |
| 50 | table from `internal/auth/policy/` is the checklist. |
| 51 | |
| 52 | ## Findings template |
| 53 | |
| 54 | ``` |
| 55 | ### P-NN — <short title> |
| 56 | |
| 57 | - **Severity:** critical / high / medium / low |
| 58 | - **Class:** auth / git / webhook / xss / csrf / ssrf / injection / info-leak / dos |
| 59 | - **Found by:** security audit CLI / manual / fuzzing |
| 60 | - **Route or surface:** /…/… |
| 61 | - **Description:** what's wrong + how to reproduce. |
| 62 | - **Disposition:** fixed in <commit-sha> / accepted: <rationale> / deferred to <sprint> |
| 63 | - **Re-tested on:** <date> |
| 64 | ``` |
| 65 | |
| 66 | ## Findings |
| 67 | |
| 68 | (none yet) |
| 69 | |
| 70 | ## Accepted with rationale |
| 71 | |
| 72 | (none yet) |
| 73 | |
| 74 | ## Areas NOT looked at |
| 75 | |
| 76 | Documented so the gap is the post-launch third-party scope: |
| 77 | |
| 78 | - Race conditions in concurrent webhook delivery. |
| 79 | - TOCTOU bugs on file-system operations during git push. |
| 80 | - Side-channel timing on argon2 verification. (Mitigation: |
| 81 | argon2id is constant-time per implementation.) |
| 82 | - Cryptanalysis of HMAC-signed cursors / unsubscribe links. |
| 83 | |
| 84 | ## Tooling notes |
| 85 | |
| 86 | - The `security audit` CLI lives at |
| 87 | `cmd/shithubd/admin.go` — sub-command list at S35. |
| 88 | - Burp / ZAP are not part of the toolchain; manual + curl + the |
| 89 | in-binary helpers cover what we need at MVP. |
| 90 | - `internal/security/ssrf` ships with its own unit tests; the |
| 91 | fuzzing pass exercises the **integration** of SSRF defense |
| 92 | with the webhook delivery path, not the unit logic. |
| 93 | |
| 94 | ## Re-audit cadence |
| 95 | |
| 96 | - Every release with auth or git surface changes. |
| 97 | - Quarterly full pass. |
| 98 | - After any incident with a security flavor — investigation + |
| 99 | audit go together. |