Fix the open bug and security issues: source parser, release gate, notifier path, lock and CI #15

Merged
tiagoagueda merged 5 commits from sec2-fixes into main 2026-10-19 09:49:36 +00:00
Owner

Fixes every open bug / security issue of this repository.

Per issue

  • #14 (SEC2-065) The dev group is repinned to core 42be52048 and relocked with uv lock --upgrade (pyjwt 2.15.1, urllib3 2.8.0, oauthlib 4.0.0). pip-audit 2.10.1 --strict over the exported lock (without the project and the git-pinned postulo, as in the other plugin repos) finds nothing. CI installs uv from the versioned installer with UV_VERSION / UV_INSTALLER_SHA256 checked by sha256sum -c (the values of postulo/postulo#790), and gets an audit job on push, pull request and weekly cron. The newer core offers seven more languages, so postulo-messages extract made their empty catalogues (az, be, cnr, fo, kk, nn, rm): the catalogue check would not pass without them.
  • #11 Nothing ran pytest -m release: new release.yml (tag v*, pinned installer), as in postulo-paperless. The tool fixture no longer uses importorskip: under the release marker a missing catalogue tool fails, otherwise it skips (a test holds both). On the "fails on 21 of 23 catalogues" part: Weblate has since filled the three strings (PR #12), and uv run pytest -m release passes today (23 passed). What remains empty is Weblate's and not the gate's: the non-EU catalogues, and one machine draft in sw whose placeholders differ (postulo-messages check reports it, exit 0).
  • #13 (SEC2-064) test() shows error.strerror or type(error).__name__ instead of the exception text, so the reason is said and the path is not. The test raises PermissionError(13, "Permission denied", <path>) and asserts the directory is not in the message; a second covers an error without a reason.
  • #9 _PostingReader rewritten: void elements are not counted and <br> adds a space; a field ends on the end tag of the element that opened it, and a block start tag ends an open <p> field; fields are read only inside data-posting, each from its first element; an over-long title gives None rather than a validation error. New tests (a page with <br>, <img>, unclosed <p>, site <h1> outside, a title too long) fail without the change.

Tests

ruff check, ruff format --check, postulo-messages extract --check, check, guard: clean. uv run pytest: 43 passed; uv run pytest -m release: 23 passed.

Left open

Nothing among the bug/security issues. #10, #8, #6, #5 (documentation / enhancement) are not part of this change.

Closes #9, #11, #13, #14

🤖 Generated with Claude Code

Fixes every open `bug` / `security` issue of this repository. ## Per issue - **#14 (SEC2-065)** The dev group is repinned to core `42be52048` and relocked with `uv lock --upgrade` (pyjwt 2.15.1, urllib3 2.8.0, oauthlib 4.0.0). `pip-audit 2.10.1 --strict` over the exported lock (without the project and the git-pinned `postulo`, as in the other plugin repos) finds nothing. CI installs uv from the versioned installer with `UV_VERSION` / `UV_INSTALLER_SHA256` checked by `sha256sum -c` (the values of postulo/postulo#790), and gets an `audit` job on push, pull request and weekly cron. The newer core offers seven more languages, so `postulo-messages extract` made their empty catalogues (az, be, cnr, fo, kk, nn, rm): the catalogue check would not pass without them. - **#11** Nothing ran `pytest -m release`: new `release.yml` (tag `v*`, pinned installer), as in postulo-paperless. The `tool` fixture no longer uses `importorskip`: under the `release` marker a missing catalogue tool fails, otherwise it skips (a test holds both). On the "fails on 21 of 23 catalogues" part: Weblate has since filled the three strings (PR #12), and `uv run pytest -m release` passes today (23 passed). What remains empty is Weblate's and not the gate's: the non-EU catalogues, and one machine draft in `sw` whose placeholders differ (`postulo-messages check` reports it, exit 0). - **#13 (SEC2-064)** `test()` shows `error.strerror or type(error).__name__` instead of the exception text, so the reason is said and the path is not. The test raises `PermissionError(13, "Permission denied", <path>)` and asserts the directory is not in the message; a second covers an error without a reason. - **#9** `_PostingReader` rewritten: void elements are not counted and `<br>` adds a space; a field ends on the end tag of the element that opened it, and a block start tag ends an open `<p>` field; fields are read only inside `data-posting`, each from its first element; an over-long title gives `None` rather than a validation error. New tests (a page with `<br>`, `<img>`, unclosed `<p>`, site `<h1>` outside, a title too long) fail without the change. ## Tests `ruff check`, `ruff format --check`, `postulo-messages extract --check`, `check`, `guard`: clean. `uv run pytest`: 43 passed; `uv run pytest -m release`: 23 passed. ## Left open Nothing among the bug/security issues. #10, #8, #6, #5 (documentation / enhancement) are not part of this change. Closes #9, #11, #13, #14 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The dev group pinned a core 211 commits behind, and its lock carried pyjwt 2.13.0,
urllib3 2.7.0 and oauthlib 3.3.1 with published advisories. Repin to 42be52048 and relock
with `uv lock --upgrade`: pip-audit 2.10.1 over the exported lock now finds nothing.

The newer core offers seven more languages (az, be, cnr, fo, kk, nn, rm), so the catalogue
check failed until `postulo-messages extract` made their empty catalogues; they are
Weblate's to fill.

CI installed uv with an unpinned `curl | sh`. It now downloads the versioned installer and
checks its sha256 before running it (UV_VERSION / UV_INSTALLER_SHA256, the values of
Postulo's own workflow, postulo/postulo#790), and an `audit` job exports the lock and runs
a pinned `pip-audit --strict` on every push, pull request and weekly.

Closes #14

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Nothing ran `pytest -m release`: the marker is deselected by default and no workflow
selected it, so the promise in the README held only if somebody remembered. Add
`release.yml`, triggered by a `v*` tag, as the other plugin repositories have it (uv
installer pinned and checked like ci.yml).

The `tool` fixture used `pytest.importorskip`, which turns a core without the catalogue
tool into a skip, so a gate could go green by running nothing, and this is the file others
copy. It now imports in a try, and fails under the `release` marker and skips otherwise;
a test holds both outcomes.

The gate itself passes today: the three strings the file-name field brought in have since
been filled in all 23 European Union catalogues by Weblate (23 passed). Nothing is left
for a commit to fill; what Weblate still owes are the non-EU catalogues, which the gate
does not ask for.

Closes #11

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Closes #13

The Test button echoed str(OSError), which carries the file's full path
("[Errno 13] Permission denied: '/srv/postulo/data/helloworld/x.log'"), to the person
who pressed it, although file_for() takes pains to keep the operator's directory out of
their hands. It now shows error.strerror ("Permission denied"), or the exception's type
name when there is none. The test raises an error that carries a filename, as the
operating system does; the old one carried none and so could not see the leak.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reference source: stop a <br>, <img> or unclosed <p> from holding a field open
Some checks failed
CI / test (pull_request) Has been cancelled
CI / Audit the locked dependencies (pull_request) Has been cancelled
3224b4d982
The reader counted every start tag inside a field and every end tag, and closed the field
on an end tag at depth zero. An element with no end tag left the count one too high, so the
field's own closing tag only brought it back to zero and the field swallowed everything up
to some unrelated end tag: `<h1>Engineer<br>Research</h1>` gave a title holding the whole
article and every other field empty. This is the file others copy.

- void elements are not counted, and `<br>` adds a space so the words either side are not
  joined;
- a field ends on the end tag of the element that opened it (only that tag name is
  counted), and a block-level start tag ends an open `<p>` field, as HTML does;
- fields are read only inside the `data-posting` element, each from the first element that
  carries it, so a site `<h1>` outside the article is not joined to the title;
- a page whose title or url is more than Postulo keeps now gives `None` instead of a
  validation error out of `parse`.

The new tests fail without the change.

Closes #9

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
CI: put the audit command's line continuations back
All checks were successful
CI / Audit the locked dependencies (pull_request) Successful in 1m44s
CI / test (pull_request) Successful in 2m10s
6c76fd1e58
The audit step's two commands had lost their line breaks, leaving runs of spaces in the
middle of a line. Same commands, readable again.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo-helloworld!15
No description provided.