| 1 | package slides |
| 2 | |
| 3 | import ( |
| 4 | "net/http" |
| 5 | |
| 6 | "amadan.net/rastrillo/rastrillo" |
| 7 | ) |
| 8 | |
| 9 | // ContentSecurityPolicy is the policy Slides serves standalone. It is |
| 10 | // rastrillo's default policy with one change: style-src keeps |
| 11 | // 'unsafe-inline', which was rastrillo's own default until the bump to |
| 12 | // fb806a2. |
| 13 | // |
| 14 | // The new default (style-src 'self' 'unsafe-hashes' plus pow's honeypot |
| 15 | // hash) refuses every style attribute and every <style> element, and a |
| 16 | // deck is drawn with them: |
| 17 | // |
| 18 | // - render.Slide, the one renderer behind the viewer, the projector, |
| 19 | // the presenter, the share link, the editor's stage and the API's |
| 20 | // .html twin, writes each slide's theme as custom properties on the |
| 21 | // <section> (colours, faces, the background picture) and each canvas |
| 22 | // block's box as left/top/width/height/z-index on the block, with |
| 23 | // its phone overrides beside them. The values are the deck's own, |
| 24 | // unbounded, so they cannot be hashed or moved into slides.css, and |
| 25 | // a per-deck stylesheet would be a new route and cache on four |
| 26 | // surfaces authenticated four different ways; |
| 27 | // - the editor puts that same server HTML on its stage with innerHTML |
| 28 | // (fixtures/stage.mjs), which is a style attribute parsed like any |
| 29 | // other; |
| 30 | // - the viewer, the projector, the presenter's notes and the remote |
| 31 | // carry their page layout in a <style> element of their own. Those |
| 32 | // could move to slides.css, but every one of those pages (bar the |
| 33 | // remote and the notes, which show pictures) also renders a slide, |
| 34 | // so moving them alone would buy nothing while the slide needs |
| 35 | // 'unsafe-inline'. |
| 36 | // |
| 37 | // The editor's drag preview, the presence marks and ProseMirror write |
| 38 | // element.style through the CSSOM, which CSP does not govern. |
| 39 | // |
| 40 | // Every other directive is unchanged, so scripts stay same-origin only |
| 41 | // and framing, base and form targets stay locked. Home mode serves |
| 42 | // connect.FormPolicy, which carries the same style-src. |
| 43 | // TestProductionCSPIsTheDefaultBarStyles keeps every other directive in |
| 44 | // step with rastrillo's default. |
| 45 | const ContentSecurityPolicy = "default-src 'self'; style-src 'self' 'unsafe-inline'; " + |
| 46 | "img-src 'self' data:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" |
| 47 | |
| 48 | // ServeOptions completes opts with what Slides serves: mux (New's), the |
| 49 | // locales, its error page and its CSP. main passes the result to |
| 50 | // rastrillo.Serve, and the CSP tests pass it to rastrillo.Handler, so |
| 51 | // the policy they check is the one production runs under. |
| 52 | func (a *app) ServeOptions(opts rastrillo.Options, mux *http.ServeMux) rastrillo.Options { |
| 53 | opts.Mux = mux |
| 54 | // Declaring locales is what installs the middleware rastrillo.T |
| 55 | // reads through; without these three every catalog lookup would |
| 56 | // return its own key onto the page. |
| 57 | opts.Locales = Locales |
| 58 | opts.DefaultLocale = DefaultLocale |
| 59 | opts.LocaleFS = LocaleFS |
| 60 | // The same page a handler's own error path renders (errors.go), so a |
| 61 | // panic and a handled failure look identical, with the viewer's own |
| 62 | // chrome. |
| 63 | opts.ErrorPage = a.ErrorPage |
| 64 | opts.CSP = ContentSecurityPolicy |
| 65 | return opts |
| 66 | } |
| 67 | |