markdown · 5159 bytes Raw Blame History

Security policy

Reporting a vulnerability

Email security@shithub.sh. PGP-encrypt the report using the key fingerprint published at https://shithub.sh/.well-known/pgp-key.asc if your finding is sensitive.

The mailbox auto-acknowledges receipt within minutes. A human response (initial assessment + next steps) follows within 72 hours.

Please do not file public issues for security findings. Coordinated disclosure is the norm; we will credit you in the hall of fame on resolution unless you ask not to be named.

Scope

In scope:

  • The hosted shithub instance (shithub.sh).
  • The shithub source as published on GitHub (github.com/tenseleyFlow/shithub), exploited against any reasonably-deployed self-hosted instance running an unmodified release tag.

Out of scope:

  • Findings against third-party services we depend on (DigitalOcean, Postmark, Let's Encrypt). Report those to the vendor.
  • Misconfiguration of a self-hosted instance (e.g., operator exposed /metrics without auth) — unless the misconfiguration is the default of a current release.
  • Rate-limit-bypass via heroic distributed-IP infrastructure — outside the threat model (docs/internal/threat-model.md).
  • Issues that require physical access to the server.
  • DoS via resource exhaustion that requires sustained heavy traffic from many unique IPs.
  • Best-practice findings without an exploit path (e.g., "you're not setting X-Permitted-Cross-Domain-Policies") — file these as regular issues.

Bug bounty

shithub does not currently run a paid bounty program. We welcome findings regardless and will publicly credit you.

Severity

Coarse 4-level scale:

Severity Examples Target fix
Critical RCE; auth bypass; mass-account-takeover; private-data leak < 24h
High Per-user privilege escalation; SSRF into internal infra < 7d
Medium Stored XSS limited to an attacker's own scope; CSRF on a non-destructive route < 30d
Low Information disclosure of non-sensitive data best-effort

What you'll receive

  • Acknowledgement within 72 hours (auto-ack faster).
  • Triage decision — accepted, duplicate, out-of-scope, or needs-more-info — within 7 days for High+ and 30 days for Medium/Low.
  • Fix timeline based on severity.
  • Coordinated disclosure on patched release; we publish a brief writeup naming you (with consent) and the affected versions.

Hall of fame

Reporters who responsibly disclosed accepted findings:

(Empty for now — first credit goes to the first reporter.)

Actions runner sandbox

Workflow authors with repository write access are treated as untrusted code execution users. shithubd-runner executes each run: step in a fresh Docker or Podman container with these defaults:

  • read-only container root filesystem
  • writable, executable /tmp tmpfs capped at 1 GiB
  • writable /workspace bind mount for step-to-step job state
  • --cap-drop=ALL with only DAC_OVERRIDE, SETGID, and SETUID added back
  • --security-opt=no-new-privileges
  • pinned seccomp profile at /etc/shithubd-runner/seccomp.json
  • --user 65534:65534
  • PID, file-descriptor, process, CPU, memory, and log-size caps

The writable /workspace mount is deliberate. The v1 engine starts one container per step, so checkout/build outputs need a host-backed job workspace to survive into later steps. The root filesystem remains read-only; writeable job state is confined to the per-job workspace that the runner sweeps after completion.

DAC_OVERRIDE is the load-bearing concession that lets the non-root container user write to the bind-mounted workspace owned by the runner host user. It is not a general privilege grant: CAP_SYS_ADMIN is not present, no-new-privileges is set, and the default seccomp profile still filters dangerous syscalls.

Root containers are opt-in per job through an explicit shithub-only permissions key:

permissions:
  shithub-runner-root: write

Broad write-all permissions do not imply root. This escape hatch is for trusted maintenance workflows that need package-manager behavior inside the container. Prefer a prebuilt runner image instead.

The runner host remains trusted infrastructure because Docker socket access is equivalent to host-root in ordinary Docker deployments. Do not run shithubd-runner on the web/database host.

Runner job JWTs are signed from an HKDF subkey derived from auth.totp_key_b64 with label actions-runner-jwt-v1. To rotate the runner JWT root, rotate auth.totp_key_b64, restart web and worker processes, then restart runners so fresh claims use the new signer. Existing in-flight job JWTs are short-lived and single-use; let them expire or cancel the affected jobs before completing the rotation.

Our threat model

Published at docs/internal/threat-model.md. Useful context on what we defend against and what we don't.

View source
1 # Security policy
2
3 ## Reporting a vulnerability
4
5 Email **`security@shithub.sh`**. PGP-encrypt the report
6 using the key fingerprint published at
7 `https://shithub.sh/.well-known/pgp-key.asc` if your finding
8 is sensitive.
9
10 The mailbox auto-acknowledges receipt within minutes. A human
11 response (initial assessment + next steps) follows within
12 **72 hours**.
13
14 Please **do not** file public issues for security findings.
15 Coordinated disclosure is the norm; we will credit you in the
16 hall of fame on resolution unless you ask not to be named.
17
18 ## Scope
19
20 In scope:
21
22 - The hosted shithub instance (`shithub.sh`).
23 - The shithub source as published on GitHub
24 (`github.com/tenseleyFlow/shithub`), exploited against any
25 reasonably-deployed self-hosted instance running an unmodified
26 release tag.
27
28 Out of scope:
29
30 - Findings against third-party services we depend on
31 (DigitalOcean, Postmark, Let's Encrypt). Report those to the
32 vendor.
33 - Misconfiguration of a self-hosted instance (e.g., operator
34 exposed `/metrics` without auth) — unless the misconfiguration
35 is the *default* of a current release.
36 - Rate-limit-bypass via heroic distributed-IP infrastructure —
37 outside the threat model
38 (`docs/internal/threat-model.md`).
39 - Issues that require physical access to the server.
40 - DoS via resource exhaustion that requires sustained heavy
41 traffic from many unique IPs.
42 - Best-practice findings without an exploit path (e.g., "you're
43 not setting `X-Permitted-Cross-Domain-Policies`") — file these
44 as regular issues.
45
46 ## Bug bounty
47
48 shithub does not currently run a paid bounty program. We welcome
49 findings regardless and will publicly credit you.
50
51 ## Severity
52
53 Coarse 4-level scale:
54
55 | Severity | Examples | Target fix |
56 |----------|----------------------------------------------------------------|-----------:|
57 | Critical | RCE; auth bypass; mass-account-takeover; private-data leak | < 24h |
58 | High | Per-user privilege escalation; SSRF into internal infra | < 7d |
59 | Medium | Stored XSS limited to an attacker's own scope; CSRF on a non-destructive route | < 30d |
60 | Low | Information disclosure of non-sensitive data | best-effort |
61
62 ## What you'll receive
63
64 - **Acknowledgement** within 72 hours (auto-ack faster).
65 - **Triage decision** — accepted, duplicate, out-of-scope, or
66 needs-more-info — within 7 days for High+ and 30 days for
67 Medium/Low.
68 - **Fix timeline** based on severity.
69 - **Coordinated disclosure** on patched release; we publish a
70 brief writeup naming you (with consent) and the affected
71 versions.
72
73 ## Hall of fame
74
75 Reporters who responsibly disclosed accepted findings:
76
77 *(Empty for now — first credit goes to the first reporter.)*
78
79 ## Actions runner sandbox
80
81 Workflow authors with repository write access are treated as untrusted
82 code execution users. `shithubd-runner` executes each `run:` step in a
83 fresh Docker or Podman container with these defaults:
84
85 - read-only container root filesystem
86 - writable, executable `/tmp` tmpfs capped at 1 GiB
87 - writable `/workspace` bind mount for step-to-step job state
88 - `--cap-drop=ALL` with only `DAC_OVERRIDE`, `SETGID`, and `SETUID`
89 added back
90 - `--security-opt=no-new-privileges`
91 - pinned seccomp profile at `/etc/shithubd-runner/seccomp.json`
92 - `--user 65534:65534`
93 - PID, file-descriptor, process, CPU, memory, and log-size caps
94
95 The writable `/workspace` mount is deliberate. The v1 engine starts one
96 container per step, so checkout/build outputs need a host-backed job
97 workspace to survive into later steps. The root filesystem remains
98 read-only; writeable job state is confined to the per-job workspace that
99 the runner sweeps after completion.
100
101 `DAC_OVERRIDE` is the load-bearing concession that lets the non-root
102 container user write to the bind-mounted workspace owned by the runner
103 host user. It is not a general privilege grant: `CAP_SYS_ADMIN` is not
104 present, no-new-privileges is set, and the default seccomp profile still
105 filters dangerous syscalls.
106
107 Root containers are opt-in per job through an explicit shithub-only
108 permissions key:
109
110 ```yaml
111 permissions:
112 shithub-runner-root: write
113 ```
114
115 Broad `write-all` permissions do not imply root. This escape hatch is
116 for trusted maintenance workflows that need package-manager behavior
117 inside the container. Prefer a prebuilt runner image instead.
118
119 The runner host remains trusted infrastructure because Docker socket
120 access is equivalent to host-root in ordinary Docker deployments. Do
121 not run `shithubd-runner` on the web/database host.
122
123 Runner job JWTs are signed from an HKDF subkey derived from
124 `auth.totp_key_b64` with label `actions-runner-jwt-v1`. To rotate the
125 runner JWT root, rotate `auth.totp_key_b64`, restart web and worker
126 processes, then restart runners so fresh claims use the new signer.
127 Existing in-flight job JWTs are short-lived and single-use; let them
128 expire or cancel the affected jobs before completing the rotation.
129
130 ## Our threat model
131
132 Published at
133 [`docs/internal/threat-model.md`](./docs/internal/threat-model.md).
134 Useful context on what we defend against and what we don't.