Where: internal/config/config.go, where db_url is derived from state_dir when it is omitted: fmt.Sprintf("file:%s/state.sqlite3?_pragma=journal_mode(WAL)", c.StateDir), on next at 1fd5cf8. Found while working on #288.
What is wrong: the directory is put into a file: URL as it is, and the database driver reads that URL with percent-escapes decoded and with ? starting the parameters. So a state_dir holding % opens a different path or fails, and one holding ? or # cuts the path short and loses the journal mode setting.
How it fails: an operator sets state_dir (or PIXA_STATE_DIR) to a directory whose name holds one of those characters; pixa fails to start or keeps its database somewhere else than the state directory.
Severity: low
Definition of done:
The derived db_url names <state_dir>/state.sqlite3 exactly, whatever characters the directory holds, with the journal mode parameter kept.
Test first, for a state_dir holding %, ? and #; make check green.
Package: internal/config.
Model: opus-5-5
Where: `internal/config/config.go`, where `db_url` is derived from `state_dir` when it is omitted: `fmt.Sprintf("file:%s/state.sqlite3?_pragma=journal_mode(WAL)", c.StateDir)`, on `next` at `1fd5cf8`. Found while working on https://git.eeqj.de/sneak/pixa/issues/288.
What is wrong: the directory is put into a `file:` URL as it is, and the database driver reads that URL with percent-escapes decoded and with `?` starting the parameters. So a `state_dir` holding `%` opens a different path or fails, and one holding `?` or `#` cuts the path short and loses the journal mode setting.
How it fails: an operator sets `state_dir` (or `PIXA_STATE_DIR`) to a directory whose name holds one of those characters; pixa fails to start or keeps its database somewhere else than the state directory.
Severity: low
Definition of done:
1. The derived `db_url` names `<state_dir>/state.sqlite3` exactly, whatever characters the directory holds, with the journal mode parameter kept.
2. Test first, for a `state_dir` holding `%`, `?` and `#`; `make check` green.
Package: `internal/config`.
Model: opus-5-5
clawbot
self-assigned this 2026-10-16 02:11:55 +02:00
Plan. The SQLite driver pixa uses takes everything after the first ? as its own parameters, and SQLite reads the path of a file: URL up to the first ? or #, turning each %HH into the byte it stands for. So the derived db_url will be built with Go's net/url: a url.URL with scheme file, the path state_dir followed by /state.sqlite3, and the query _pragma=journal_mode(WAL). Its String() writes a %, ? or # in the directory name as %25, %3F and %23, which SQLite decodes back, so it opens exactly that file. OmitHost is set so the URL still reads file:/var/lib/pixa/state.sqlite3?... and a relative state_dir is not taken for a host name. The directory is used as given, not cleaned, so .. resolves the way the operating system resolves it.
A failing test goes first. It makes a temporary directory whose name holds %41, ? and #, sets state_dir to it, opens the derived db_url with the driver, and checks that the database is the file state.sqlite3 in that directory and in WAL mode. No existing test changes.
Model: opus-5-5
Plan. The SQLite driver pixa uses takes everything after the first `?` as its own parameters, and SQLite reads the path of a `file:` URL up to the first `?` or `#`, turning each `%HH` into the byte it stands for. So the derived `db_url` will be built with Go's `net/url`: a `url.URL` with scheme `file`, the path `state_dir` followed by `/state.sqlite3`, and the query `_pragma=journal_mode(WAL)`. Its `String()` writes a `%`, `?` or `#` in the directory name as `%25`, `%3F` and `%23`, which SQLite decodes back, so it opens exactly that file. `OmitHost` is set so the URL still reads `file:/var/lib/pixa/state.sqlite3?...` and a relative `state_dir` is not taken for a host name. The directory is used as given, not cleaned, so `..` resolves the way the operating system resolves it.
A failing test goes first. It makes a temporary directory whose name holds `%41`, `?` and `#`, sets `state_dir` to it, opens the derived `db_url` with the driver, and checks that the database is the file `state.sqlite3` in that directory and in WAL mode. No existing test changes.
Model: opus-5-5
Built as planned in #292. One difference from the plan: the URL is built in a small function defaultDBURL next to newFromSmartConfig, which would otherwise have gone over the linter's function length limit.
Model: opus-5-5
Built as planned in https://git.eeqj.de/sneak/pixa/pulls/292. One difference from the plan: the URL is built in a small function `defaultDBURL` next to `newFromSmartConfig`, which would otherwise have gone over the linter's function length limit.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Where:
internal/config/config.go, wheredb_urlis derived fromstate_dirwhen it is omitted:fmt.Sprintf("file:%s/state.sqlite3?_pragma=journal_mode(WAL)", c.StateDir), onnextat1fd5cf8. Found while working on #288.What is wrong: the directory is put into a
file:URL as it is, and the database driver reads that URL with percent-escapes decoded and with?starting the parameters. So astate_dirholding%opens a different path or fails, and one holding?or#cuts the path short and loses the journal mode setting.How it fails: an operator sets
state_dir(orPIXA_STATE_DIR) to a directory whose name holds one of those characters; pixa fails to start or keeps its database somewhere else than the state directory.Severity: low
Definition of done:
db_urlnames<state_dir>/state.sqlite3exactly, whatever characters the directory holds, with the journal mode parameter kept.state_dirholding%,?and#;make checkgreen.Package:
internal/config.Model: opus-5-5
Plan. The SQLite driver pixa uses takes everything after the first
?as its own parameters, and SQLite reads the path of afile:URL up to the first?or#, turning each%HHinto the byte it stands for. So the deriveddb_urlwill be built with Go'snet/url: aurl.URLwith schemefile, the pathstate_dirfollowed by/state.sqlite3, and the query_pragma=journal_mode(WAL). ItsString()writes a%,?or#in the directory name as%25,%3Fand%23, which SQLite decodes back, so it opens exactly that file.OmitHostis set so the URL still readsfile:/var/lib/pixa/state.sqlite3?...and a relativestate_diris not taken for a host name. The directory is used as given, not cleaned, so..resolves the way the operating system resolves it.A failing test goes first. It makes a temporary directory whose name holds
%41,?and#, setsstate_dirto it, opens the deriveddb_urlwith the driver, and checks that the database is the filestate.sqlite3in that directory and in WAL mode. No existing test changes.Model: opus-5-5
Built as planned in #292. One difference from the plan: the URL is built in a small function
defaultDBURLnext tonewFromSmartConfig, which would otherwise have gone over the linter's function length limit.Model: opus-5-5