Commit 2df39252c0
Verified · cmc
Layout: unified · split
Threat-Model.org +24
| @@ -71,6 +71,30 @@ webhook dialer re-checks at connect time so a DNS answer that changes | |||
| 71 | after validation still cannot reach private space. Redirects are never | 71 | after validation still cannot reach private space. Redirects are never |
| 72 | followed. | 72 | followed. |
| 73 | 73 | ||
| 74 | * Rendering pushed markup | ||
| 75 | |||
| 76 | Rendered markup is attacker-controlled: a README, a wiki page and a | ||
| 77 | profile's about text are all whatever someone pushed or typed. The risk | ||
| 78 | is not only what the output contains but what the *parser* is willing to | ||
| 79 | go and fetch — the filesystem counterpart of the SSRF guard above. | ||
| 80 | |||
| 81 | Org is rendered by go-org, whose default configuration resolves | ||
| 82 | =#+INCLUDE:= and =#+SETUPFILE:= targets with =os.ReadFile=. Both are | ||
| 83 | refused outright (=orgConfig()= in =internal/httpd=): the file is never | ||
| 84 | opened and the keyword stays the inert text it already was, so the rest | ||
| 85 | of the document renders normally. There is no safe subset to allow | ||
| 86 | instead — an absolute path skips go-org's relative-path join, a relative | ||
| 87 | one resolves against the daemon's working directory, and the content | ||
| 88 | came from a git object rather than a checkout, so there is no directory | ||
| 89 | to scope a read to. Markdown is goldmark, which has no include | ||
| 90 | mechanism. go-org's parse warnings are discarded rather than logged, so | ||
| 91 | pushed content cannot write to the server's log. | ||
| 92 | |||
| 93 | Org output is then sanitized (bluemonday UGC policy) because go-org | ||
| 94 | passes raw HTML through — export blocks and inline export snippets — | ||
| 95 | while goldmark drops it and needs no pass. The policy admits chroma's | ||
| 96 | short token classes and nothing else. | ||
| 97 | |||
| 74 | * Web responses | 98 | * Web responses |
| 75 | 99 | ||
| 76 | Every response carries =Content-Security-Policy= (no scripts, no plugins, | 100 | Every response carries =Content-Security-Policy= (no scripts, no plugins, |