diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-23 15:49:22 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-23 15:49:22 +0200 |
| commit | 73d00113f5ed5fe99e365d2053dc70a6da8a81ee (patch) | |
| tree | cb43b29be24768bf4a8f4baa5590664e279e7fb9 /internal/liturgy/fetch.go | |
| parent | 905b7473dd3987928e9ac9c81405548ba7be4c8d (diff) | |
| download | lectio-73d00113f5ed5fe99e365d2053dc70a6da8a81ee.tar.gz lectio-73d00113f5ed5fe99e365d2053dc70a6da8a81ee.zip | |
web,liturgy: validate date against path traversal; bind lectio-web to localhost
An unvalidated ?date= query param flowed straight into
liturgy.Load's filepath.Join(dir, date+".json"/".html") before any
network call, letting a crafted date (e.g. "../../../../etc/hostname")
read an arbitrary file whose JSON, if present, unmarshals into
[]liturgy.Section and renders back to the client. Fix both layers:
resolveQuery now falls back to today() on empty or non-YYYY-MM-DD
date (mirroring requestDisplay's normalize-don't-trust pattern), and
liturgy.Load itself rejects a non-matching date before building any
cache path, protecting every caller even if a future one forgets to
validate.
Also bind lectio-web's listener to 127.0.0.1 instead of all
interfaces: it is a personal tool whose Run already prints
http://localhost:<port>, so it should not be reachable from the LAN.
Diffstat (limited to 'internal/liturgy/fetch.go')
| -rw-r--r-- | internal/liturgy/fetch.go | 12 |
1 files changed, 12 insertions, 0 deletions
diff --git a/internal/liturgy/fetch.go b/internal/liturgy/fetch.go index 5447552..32eaaad 100644 --- a/internal/liturgy/fetch.go +++ b/internal/liturgy/fetch.go @@ -33,6 +33,14 @@ const userAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " + // ewangelia.py's fetch() for the original behaviour this mirrors. var publishedRe = regexp.MustCompile(`id="\w*0all"`) +// dateRe is the same YYYY-MM-DD shape internal/cli's dateRe validates +// against. Load checks opts.Date against it before building any filesystem +// path (jsonPath/htmlPath below are built by string concatenation, so an +// unvalidated Date is a path-traversal vector) -- defense-in-depth so every +// caller (web, cli, tui) is protected even if a future caller forgets to +// validate its own input first. +var dateRe = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`) + // Options controls how Load resolves a day's readings. type Options struct { // Date is the day to load, formatted YYYY-MM-DD. @@ -71,6 +79,10 @@ func cacheDir() string { // has been harvested, and only surfaces the original fetch error if // that fallback also fails. func Load(opts Options) ([]Section, error) { + if !dateRe.MatchString(opts.Date) { + return nil, fmt.Errorf("invalid date %q: want YYYY-MM-DD", opts.Date) + } + if opts.Offline { return LoadOffline(opts.Date) } |
