summaryrefslogtreecommitdiff
path: root/main.go
Commit message (Collapse)AuthorAgeFilesLines
* update org nameChristian Cleberg44 hours1-1/+1
|
* fix: reject forged subdomains in the media proxy (#9)Christian Cleberg2026-07-151-1/+10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * fix: make memcache concurrency-safe and stamp the version at link time memcache was a crash waiting for traffic. Readers touched tempFS without holding mx, while a per-entry goroutine deleted from it under the lock: a concurrent map read and map write, which the runtime treats as a fatal error that recover cannot catch. The option ships in config.example.json and was the one cache key SETUP.md never documented, so it read like a free win to enable. Put every map and field access behind the mutex, and age the whole map from one janitor instead of a goroutine per cached file, each of which looped forever holding its entry alive. mx is now a plain Mutex: every operation here mutates something, and the old code took an RLock to write. Document the option, and cover it with tests that run the readers, writers and janitor concurrently. Split the disk/origin fetch out of DownloadAndSendMedia while there, so the error path returns instead of falling through to write an empty body after the error page. Release.Version was hardcoded to 1.3.2, so images tagged v1.3.6 reported 1.3.2 from --help and /api/instance, and --help linked to the wrong release. Take it from a main.version string the release workflow links in from the git tag. * fix: reject forged subdomains in the media proxy DownloadAndSendMedia built its upstream URL by concatenation, pasting the subdomain segment of the request path straight into the host position. That segment reaches the handler already percent-decoded, so it can carry "@", "#", "?" and "/" — the characters that end a host. A request for /media/file/[email protected]:8080%2F/f/x.jpg built a URL whose host parsed as 127.0.0.1:8080, with images-wixmp-x demoted to userinfo, letting any caller aim the instance's fetcher at any address it could reach, including services behind the firewall. Validate the label against ^[a-zA-Z0-9-]+$ and refuse anything else with a 400. Rejecting rather than escaping is what closes this: the label is the host, and url.URL passes a host through verbatim, so building the URL structurally is not sufficient on its own. DeviantArt's own media URLs use a hex-and-dash label, and ParseMedia already splits on the first dot, so a legitimate label cannot contain one. Build the URL from url.URL fields as well, which escapes the path, and encode the token argument, which reached the request unescaped. Reported by CodeQL as go/request-forgery (CWE-918).
* fix: serve media again by unsetting download-proxy and scoping Host per requestChristian Cleberg2026-07-151-1/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | Two independent faults made every image fail while pages still rendered. config.example.json shipped download-proxy=http://127.0.0.1:8080. Only media fetches go through that proxy — pages reach DeviantArt via devianter on the default transport — so when nothing listens there, images 502 and the rest of the page looks fine. In a scratch container 127.0.0.1 is the container itself, so the default could never work under Docker. Unset it and document that it must stay empty unless an operator really runs a proxy. Host was a package global reassigned by every request, so a concurrent request could overwrite it mid-render and emit URLs on another origin's host and port. The instance's own default-src 'self' CSP then blocked those images. Thread the request's host through skunkyart instead, and take it as an explicit argument in URLBuilder, ParseMedia, ParseDescription, BuildUserPlate and ConvertDeviantArtURLToSkunkyArt. Feeds keep their absolute URLs. Also start RefreshInstances after ExecuteConfig rather than before it: the goroutine read CFG while json.Unmarshal was writing it (a race the detector flags), and its fetch escaped both the throttle and the configured User-Agent. Verified: 300 concurrent requests with distinct Host headers now round-trip their own host (was 1 leak per 300), go test -race is clean, and cache+proxy both enabled serves 200 image/jpeg cold and from cache.
* feat: throttle and time out outbound DeviantArt requestsChristian Cleberg2026-07-141-0/+4
| | | | | | | | | | | | DeviantArt fronts its API with CloudFront + WAF, which bans egress IPs that hit it too hard. devianter issues requests with a bare http.Client, so unbounded concurrent handlers each pulled ~150-200 KB of JSON, which both risked a ban and could exhaust the process under a bot flood. Wrap the default transport to bound rate and concurrency for deviantart.com and add timeouts. Other hosts (wixmp image CDN) pass straight through, so media stays fast, and ProxyFromEnvironment is preserved so HTTPS_PROXY egress still works.
* deps: migrate to github.com/zerolabsco/devianter v0.3.1Christian Cleberg2026-07-141-1/+1
| | | | | | | | | | Repoint the import path from git.macaw.me/skunky/devianter, replacing a placeholder v0.0.0 require that had no go.sum entry and did not resolve. v0.3.1 reorders the last two return values of PerformSearch, Group.Get and Group.Gallery from (..., error, Error) to (..., Error, error); adapt the call sites. The two types differ, so the compiler enforces this.
* Add da.opnxng.com + lost-skunk.cc is now in Finlandlost+skunk2025-04-071-0/+0
|
* v1.3.2v1.3.2lost+skunk2024-09-231-1/+1
|
* Два API-эндпоинтаlost+skunk2024-09-031-0/+2
|
* темплейты в бинарнике и улучшенная ↵lost+skunk2024-08-131-1/+2
| | | | система кеша
* v1.3lost+skunk2024-08-011-4/+14
|
* Реки и копирование темплейтов в озуlost+skunk2024-07-061-0/+1
|
* Реализация конфигаlost+skunk2024-07-061-0/+1
|
* крымские наработкиlost+skunk2024-06-271-1/+4
|
* панель навигации, ответы и ежедневные артыlost+skunk2024-06-141-1/+7
|
* альфа: работают только посты и поискlost+skunk2024-06-131-0/+7