SEC2 index: security review of 2026-10-16 (86 issues across the core, plugins and extensions) #992

Closed
opened 2026-10-17 10:41:22 +00:00 by tiagoagueda · 1 comment
Owner

Index of the security review of 2026-10-16 (second review, SEC2-NNN), read-only, every finding checked by a second independent pass before filing. Core at 56fc9d531; plugin and extension repos at their heads of 2026-10-10 to 2026-10-12.

Earlier reviews, not repeated here: the audit of 2026-09-15 (#215-#241), the review of 2026-09-29 (#354-#645 and the plugin repos) and the security audit of 2026-10-10 (#730-#827, index #828).

Count

Repo Critical High Medium Low Informational Total
postulo/postulo 0 2 6 21 20 49
postulo/postulo-apprise 0 0 1 1 0 2
postulo/postulo-chromium 0 0 0 4 4 8
postulo/postulo-dav 0 0 0 4 0 4
postulo/postulo-firefox 0 0 0 2 1 3
postulo/postulo-helloworld 0 0 0 1 1 2
postulo/postulo-imap 0 0 0 1 0 1
postulo/postulo-mcp 0 0 0 2 3 5
postulo/postulo-paperless 0 0 0 2 0 2
postulo/postulo-templates 0 0 0 1 0 1
postulo/postulo-thunderbird 0 0 0 1 3 4
tiagoagueda/postulo-francetravail 0 0 0 2 3 5

Issues

postulo/postulo

High

  • #956 — Deleting a shared contact deletes other members' reference-letter uploads (Authentication/Authorization)
  • #966 — The account archive exports every appointment at a shared or group-owned company, not only the exporter's own (Data Exposure)

Medium

  • #957 — The employer-structure helpers are not narrowed, so a private company's name is shown on a shared company's page (Data Exposure)
  • #958 — Delete-confirmation facts count other members' private rows and are drawn for every reader (Data Exposure)
  • #961 — With data protection on, deleting a shared contact skips the copy other members are promised and cascades their referee entries (Authentication/Authorization)
  • #974 — Backup restore treats an archive whose signature was removed as "written before signing" and restores its database and media (Cryptography)
  • #975 — The monthly dependency workflow runs freshly resolved packages as root in the container that later holds the bot's write token (Configuration)
  • #987 — django-allauth 65.19.5 is locked while 65.19.6-65.20.0 carry security fixes for MFA and lift the oauthlib advisory CI ignores (Dependencies)

Low

  • #943 — The share form confirms whether an email address or username has an account, and maps an address to its username, unthrottled (Data Exposure)
  • #944 — Deactivating an account and changing someone's username under People need no fresh proof, unlike the neighbouring actions (Authentication/Authorization)
  • #945 — An administrator can join any group unnoticed: members cannot see who is in their group, and the log does not name the change (Authentication/Authorization)
  • #949 — PATCH /companies/{id} lets a read/write grantee probe the owner's other company names through the rename clash check (Data Exposure)
  • #950 — PUT /captures/{id}/rendering still hands a captures-only token a capture's address, company, location and source (#761 incomplete) (Authentication/Authorization)
  • #952 — The API's iCalendar feeds carry no Cache-Control, which #788's middleware gives only to attachments (Data Exposure)
  • #959 — JSON from untrusted input is parsed with no nesting bound, so a deeply nested document ends in a RecursionError (Input Validation)
  • #960 — The logo-fetch allowance is charged to the company's owner, and to a shared "None" bucket for group-owned companies (Business Logic)
  • #962 — Capture notifications build their links from the request's Host rather than from POSTULO_PUBLIC_URL, undoing the rule #747 set (Configuration)
  • #963 — Calendar files carry private interview notes and company contacts as ATTENDEE, against the form's promise (Data Exposure)
  • #964 — Uploaded photographs are kept and forwarded with their embedded metadata, unlike every other picture Postulo stores (Data Exposure)
  • #967 — A plugin's settings page is not held to the access list's "may change its settings": only the button is (Authentication/Authorization)
  • #968 — An installed plugin may register under a built-in's name and is then treated as shipped inside Postulo (Business Logic)
  • #970 — The webhook notifier accepts a plain http:// receiver, so notification text leaves in clear (Data Exposure)
  • #973 — The company merge preview lists every member's appointments at the other company, not only the viewer's (Data Exposure)
  • #978 — POSTULO_BACKUP_DOWNLOAD falls open to "every administrator" on any value it does not recognise, and no start-up check reads it (Configuration)
  • #979 — The JSON log scrubs only invitation and recovery tokens, so a password-reset or email-confirmation key can reach postulo.log (Data Exposure)
  • #982 — Company edit page marks a translated catalogue string |safe, against the project's own #806 rule (Injection)
  • #983 — Markup inside translated text is trusted everywhere and no check keeps a translation's tags to the source's (Injection)
  • #986 — The core locks Django 6.1.1 while 6.1.2 (2026-10-06) carries four security fixes, and the weekly audit did not notice (Dependencies)
  • #991 — A web link saved from a card or an import is stored with no scheme check in the core's save_only_link (Input Validation)

Informational

  • #946 — Giving a record to a group fails with an unhandled exception when what it points at points at something the group cannot see (Business Logic)
  • #947 — Sharing refuses deactivated accounts, but giving a record, group membership and plugin access rules accept them (Business Logic)
  • #948 — Check-then-create without a lock on a grant and on a group name (Business Logic)
  • #951 — Wrong tokens sent to openapi.json, an unknown API address or a wrong method are neither counted nor barred (#799 incomplete) (Authentication/Authorization)
  • #953 — Free text written through the API has no length bound where the listing history has one (Input Validation)
  • #954 — A capture refusal tells a token holder whether a name resolves to a private address (Data Exposure)
  • #955 — The threat model's list of what stays a person's own on a shared record omits the contacts they add (Authentication/Authorization)
  • #965 — The threat model says a theme is "never" handed a referee's entry or contact, but the template context carries the whole CV (Configuration)
  • #969 — read_wheel reads a wheel's metadata members without a size cap (Input Validation)
  • #971 — A plugin's templates/ directory is searched before every app-directory template, contrary to what themes.py promises (Configuration)
  • #972 — The catalogue's rollback protection rests on a cache entry that can be culled, and the official index signs no expiry (Cryptography)
  • #976 — The request id is judged by the rewritten client address, so a trusted proxy's X-Request-ID is dropped (Configuration)
  • #977 — /healthz treats a request as forwarded only on X-Forwarded-For, so behind a loopback proxy every visitor is a "probe" (Data Exposure)
  • #980 — seed_demo --password takes a password on the command line (Configuration)
  • #981 — The image build trusts a signing key fetched over HTTPS with no fingerprint check (Dependencies)
  • #984 — The dedicated CSP browser test overrides the real policy with a stale hand copy (Configuration)
  • #985 — Board fold links build hx-vals JSON by text interpolation rather than JSON encoding (Injection)
  • #988 — A sync connection's lease is released unconditionally, so a run that outlived it frees the next run's lease (as fixed in #576) (Business Logic)
  • #989 — Removing the last members of a record-owning group is check-then-act without a lock, so concurrent removals can orphan its records (Business Logic)
  • #990 — SECURITY.md says a fresh library disclosure is noticed automatically, but the audit cannot see django-allauth's security releases (Configuration)

postulo/postulo-apprise

Medium

  • postulo/postulo-apprise#21 — Notifier gives Apprise the title and body with no body_format, so HTML services receive a captured page's words as markup (Injection)

Low

  • postulo/postulo-apprise#22 — The dev lock carries pyjwt 2.13.0 and urllib3 2.7.0, both with published advisories, and pins a core commit 210 behind (Dependencies)

postulo/postulo-chromium

Low

  • postulo/postulo-chromium#32 — postulo-chromium's dev chain locks web-ext 8.10.0 with sixteen open advisories (three critical), and CI runs no npm audit (Dependencies)
  • postulo/postulo-chromium#33 — Instance address keeps a user name and password typed into it, and they reach the popup footer and the downloadable log (Input Validation)
  • postulo/postulo-chromium#37 — A hostile JSON-LD description makes the page reader take quadratic time before anything is cut to size (Input Validation)
  • postulo/postulo-chromium#38 — Test accepts a token wider than captures without a word, so the threat model's bound on a leaked extension token is unchecked (Authentication/Authorization)

Informational

  • postulo/postulo-chromium#34 — Plain http is allowed to every *.local host, not only to this machine, and the settings hint says otherwise (Configuration)
  • postulo/postulo-chromium#35 — Requests that carry the bearer token follow redirects (Data Exposure)
  • postulo/postulo-chromium#36 — Server-supplied addresses (review_url, listing_url) are opened in the review window without an instance-origin check (Input Validation)
  • postulo/postulo-chromium#39 — shipped() lets a catalogue key override the build's languageTag and direction (Input Validation)

postulo/postulo-dav

Low

  • postulo/postulo-dav#31 — postulo-dav locks pyjwt 2.13.0, urllib3 2.7.0 and django 6.1.1, carrying 22 published advisories, and CI never audits the lock (Dependencies)
  • postulo/postulo-dav#32 — A URL pulled from a card is stored as a contact's web link with no scheme check, and pages render it as href (Injection)
  • postulo/postulo-dav#33 — DAV responses are read whole with no size limit and no total deadline (Input Validation)
  • postulo/postulo-dav#34 — One malformed date or an over-long remote identifier fails the whole run, on every run (Input Validation)

postulo/postulo-firefox

Low

  • postulo/postulo-firefox#9 — postulo-firefox's development chain locks web-ext 8.10.0 with fifteen open advisories (three critical), and CI runs no npm audit (Dependencies)
  • postulo/postulo-firefox#10 — Firefox workflows keep the checkout credential in the working copy while running code the pull request controls (Configuration)

Informational

postulo/postulo-helloworld

Low

  • postulo/postulo-helloworld#14 — The dev lock pins a core commit 211 behind and carries pyjwt 2.13.0 and urllib3 2.7.0 with published advisories (Dependencies)

Informational

  • postulo/postulo-helloworld#13 — Reference notifier's test() echoes the OSError text, server-side path included, to the account holder (Data Exposure)

postulo/postulo-imap

Low

  • postulo/postulo-imap#24 — postulo-imap locks pyjwt 2.13.0, urllib3 2.7.0 and django 6.1.1, carrying 22 published advisories, and CI never audits the lock (Dependencies)

postulo/postulo-mcp

Low

  • postulo/postulo-mcp#17 — postulo-mcp locks mcp 2.1.1, whose Streamable HTTP server keeps sessions without bound (CVE-2026-59951), and CI never audits it (Dependencies)
  • postulo/postulo-mcp#20 — CI installs uv from the moving install.sh with no version and no checksum; the core fixed this in #790 (Configuration)

Informational

  • postulo/postulo-mcp#18 — Postulo._call reads every answer whole, with no size ceiling and no deadline for the whole exchange (Input Validation)
  • postulo/postulo-mcp#19 — The error channel is outside the fence: text from the answerer's body reaches the model unmarked (Injection)
  • postulo/postulo-mcp#21 — The HTTP transports accept unlimited wrong bearer secrets; the core throttles wrong API tokens per address (Authentication/Authorization)

postulo/postulo-paperless

Low

  • postulo/postulo-paperless#16 — Store reads every Paperless answer whole with no deadline, so a server the account holder names can hold the shared scheduler (Input Validation)
  • postulo/postulo-paperless#17 — The dev lock pins a core commit 210 behind and carries pyjwt 2.13.0 and urllib3 2.7.0 with published advisories (Dependencies)

postulo/postulo-templates

Low

  • postulo/postulo-templates#15 — CI installs uv with an unpinned curl | sh and audits no dependency, and no existing issue names this repository (Configuration)

postulo/postulo-thunderbird

Low

  • postulo/postulo-thunderbird#13 — postulo-thunderbird's dev chain locks web-ext 8.10.0 with thirteen open advisories (three critical), and CI runs no npm audit (Dependencies)

Informational

  • postulo/postulo-thunderbird#14 — The extension asks for a captures token, while the threat model's mail-client row promises listings:bind and nothing else (Authentication/Authorization)
  • postulo/postulo-thunderbird#15 — Shared copies are 14 postulo-chromium commits behind, missing the non-JSON-answer and request-hardening fixes to api.js (Dependencies)
  • postulo/postulo-thunderbird#16 — The API token sits in storage.local in clear, outside Thunderbird's primary password, and the privacy text does not say so (Data Exposure)

tiagoagueda/postulo-francetravail

Five issues filed on that private repository and listed there.

Highest priority

Also done

Not filed

The second pass dropped: a merge refusal on trailed or shared records (a functional bug with no security consequence), document PDFs drawn without a deadline (outside the threat model), a per-account upload quota (already #268), and CI items that follow the project's documented action-pinning policy (#790).

Index of the security review of 2026-10-16 (second review, `SEC2-NNN`), read-only, every finding checked by a second independent pass before filing. Core at `56fc9d531`; plugin and extension repos at their heads of 2026-10-10 to 2026-10-12. Earlier reviews, not repeated here: the audit of 2026-09-15 (#215-#241), the review of 2026-09-29 (#354-#645 and the plugin repos) and the security audit of 2026-10-10 (#730-#827, index #828). ## Count | Repo | Critical | High | Medium | Low | Informational | Total | |---|---|---|---|---|---|---| | postulo/postulo | 0 | 2 | 6 | 21 | 20 | 49 | | postulo/postulo-apprise | 0 | 0 | 1 | 1 | 0 | 2 | | postulo/postulo-chromium | 0 | 0 | 0 | 4 | 4 | 8 | | postulo/postulo-dav | 0 | 0 | 0 | 4 | 0 | 4 | | postulo/postulo-firefox | 0 | 0 | 0 | 2 | 1 | 3 | | postulo/postulo-helloworld | 0 | 0 | 0 | 1 | 1 | 2 | | postulo/postulo-imap | 0 | 0 | 0 | 1 | 0 | 1 | | postulo/postulo-mcp | 0 | 0 | 0 | 2 | 3 | 5 | | postulo/postulo-paperless | 0 | 0 | 0 | 2 | 0 | 2 | | postulo/postulo-templates | 0 | 0 | 0 | 1 | 0 | 1 | | postulo/postulo-thunderbird | 0 | 0 | 0 | 1 | 3 | 4 | | tiagoagueda/postulo-francetravail | 0 | 0 | 0 | 2 | 3 | 5 | ## Issues ### postulo/postulo **High** - #956 — Deleting a shared contact deletes other members' reference-letter uploads (Authentication/Authorization) - #966 — The account archive exports every appointment at a shared or group-owned company, not only the exporter's own (Data Exposure) **Medium** - #957 — The employer-structure helpers are not narrowed, so a private company's name is shown on a shared company's page (Data Exposure) - #958 — Delete-confirmation facts count other members' private rows and are drawn for every reader (Data Exposure) - #961 — With data protection on, deleting a shared contact skips the copy other members are promised and cascades their referee entries (Authentication/Authorization) - #974 — Backup restore treats an archive whose signature was removed as "written before signing" and restores its database and media (Cryptography) - #975 — The monthly dependency workflow runs freshly resolved packages as root in the container that later holds the bot's write token (Configuration) - #987 — django-allauth 65.19.5 is locked while 65.19.6-65.20.0 carry security fixes for MFA and lift the oauthlib advisory CI ignores (Dependencies) **Low** - #943 — The share form confirms whether an email address or username has an account, and maps an address to its username, unthrottled (Data Exposure) - #944 — Deactivating an account and changing someone's username under *People* need no fresh proof, unlike the neighbouring actions (Authentication/Authorization) - #945 — An administrator can join any group unnoticed: members cannot see who is in their group, and the log does not name the change (Authentication/Authorization) - #949 — PATCH /companies/{id} lets a read/write grantee probe the owner's other company names through the rename clash check (Data Exposure) - #950 — PUT /captures/{id}/rendering still hands a captures-only token a capture's address, company, location and source (#761 incomplete) (Authentication/Authorization) - #952 — The API's iCalendar feeds carry no Cache-Control, which #788's middleware gives only to attachments (Data Exposure) - #959 — JSON from untrusted input is parsed with no nesting bound, so a deeply nested document ends in a RecursionError (Input Validation) - #960 — The logo-fetch allowance is charged to the company's owner, and to a shared "None" bucket for group-owned companies (Business Logic) - #962 — Capture notifications build their links from the request's Host rather than from `POSTULO_PUBLIC_URL`, undoing the rule #747 set (Configuration) - #963 — Calendar files carry private interview notes and company contacts as `ATTENDEE`, against the form's promise (Data Exposure) - #964 — Uploaded photographs are kept and forwarded with their embedded metadata, unlike every other picture Postulo stores (Data Exposure) - #967 — A plugin's settings page is not held to the access list's "may change its settings": only the button is (Authentication/Authorization) - #968 — An installed plugin may register under a built-in's name and is then treated as shipped inside Postulo (Business Logic) - #970 — The webhook notifier accepts a plain `http://` receiver, so notification text leaves in clear (Data Exposure) - #973 — The company merge preview lists every member's appointments at the other company, not only the viewer's (Data Exposure) - #978 — `POSTULO_BACKUP_DOWNLOAD` falls open to "every administrator" on any value it does not recognise, and no start-up check reads it (Configuration) - #979 — The JSON log scrubs only invitation and recovery tokens, so a password-reset or email-confirmation key can reach `postulo.log` (Data Exposure) - #982 — Company edit page marks a translated catalogue string `|safe`, against the project's own #806 rule (Injection) - #983 — Markup inside translated text is trusted everywhere and no check keeps a translation's tags to the source's (Injection) - #986 — The core locks Django 6.1.1 while 6.1.2 (2026-10-06) carries four security fixes, and the weekly audit did not notice (Dependencies) - #991 — A web link saved from a card or an import is stored with no scheme check in the core's save_only_link (Input Validation) **Informational** - #946 — Giving a record to a group fails with an unhandled exception when what it points at points at something the group cannot see (Business Logic) - #947 — Sharing refuses deactivated accounts, but giving a record, group membership and plugin access rules accept them (Business Logic) - #948 — Check-then-create without a lock on a grant and on a group name (Business Logic) - #951 — Wrong tokens sent to openapi.json, an unknown API address or a wrong method are neither counted nor barred (#799 incomplete) (Authentication/Authorization) - #953 — Free text written through the API has no length bound where the listing history has one (Input Validation) - #954 — A capture refusal tells a token holder whether a name resolves to a private address (Data Exposure) - #955 — The threat model's list of what stays a person's own on a shared record omits the contacts they add (Authentication/Authorization) - #965 — The threat model says a theme is "never" handed a referee's entry or contact, but the template context carries the whole CV (Configuration) - #969 — `read_wheel` reads a wheel's metadata members without a size cap (Input Validation) - #971 — A plugin's `templates/` directory is searched before every app-directory template, contrary to what `themes.py` promises (Configuration) - #972 — The catalogue's rollback protection rests on a cache entry that can be culled, and the official index signs no expiry (Cryptography) - #976 — The request id is judged by the rewritten client address, so a trusted proxy's `X-Request-ID` is dropped (Configuration) - #977 — `/healthz` treats a request as forwarded only on `X-Forwarded-For`, so behind a loopback proxy every visitor is a "probe" (Data Exposure) - #980 — `seed_demo --password` takes a password on the command line (Configuration) - #981 — The image build trusts a signing key fetched over HTTPS with no fingerprint check (Dependencies) - #984 — The dedicated CSP browser test overrides the real policy with a stale hand copy (Configuration) - #985 — Board fold links build `hx-vals` JSON by text interpolation rather than JSON encoding (Injection) - #988 — A sync connection's lease is released unconditionally, so a run that outlived it frees the next run's lease (as fixed in #576) (Business Logic) - #989 — Removing the last members of a record-owning group is check-then-act without a lock, so concurrent removals can orphan its records (Business Logic) - #990 — SECURITY.md says a fresh library disclosure is noticed automatically, but the audit cannot see django-allauth's security releases (Configuration) ### postulo/postulo-apprise **Medium** - postulo/postulo-apprise#21 — Notifier gives Apprise the title and body with no `body_format`, so HTML services receive a captured page's words as markup (Injection) **Low** - postulo/postulo-apprise#22 — The dev lock carries pyjwt 2.13.0 and urllib3 2.7.0, both with published advisories, and pins a core commit 210 behind (Dependencies) ### postulo/postulo-chromium **Low** - postulo/postulo-chromium#32 — postulo-chromium's dev chain locks web-ext 8.10.0 with sixteen open advisories (three critical), and CI runs no `npm audit` (Dependencies) - postulo/postulo-chromium#33 — Instance address keeps a user name and password typed into it, and they reach the popup footer and the downloadable log (Input Validation) - postulo/postulo-chromium#37 — A hostile JSON-LD description makes the page reader take quadratic time before anything is cut to size (Input Validation) - postulo/postulo-chromium#38 — Test accepts a token wider than `captures` without a word, so the threat model's bound on a leaked extension token is unchecked (Authentication/Authorization) **Informational** - postulo/postulo-chromium#34 — Plain http is allowed to every `*.local` host, not only to this machine, and the settings hint says otherwise (Configuration) - postulo/postulo-chromium#35 — Requests that carry the bearer token follow redirects (Data Exposure) - postulo/postulo-chromium#36 — Server-supplied addresses (`review_url`, `listing_url`) are opened in the review window without an instance-origin check (Input Validation) - postulo/postulo-chromium#39 — `shipped()` lets a catalogue key override the build's `languageTag` and `direction` (Input Validation) ### postulo/postulo-dav **Low** - postulo/postulo-dav#31 — postulo-dav locks pyjwt 2.13.0, urllib3 2.7.0 and django 6.1.1, carrying 22 published advisories, and CI never audits the lock (Dependencies) - postulo/postulo-dav#32 — A URL pulled from a card is stored as a contact's web link with no scheme check, and pages render it as `href` (Injection) - postulo/postulo-dav#33 — DAV responses are read whole with no size limit and no total deadline (Input Validation) - postulo/postulo-dav#34 — One malformed date or an over-long remote identifier fails the whole run, on every run (Input Validation) ### postulo/postulo-firefox **Low** - postulo/postulo-firefox#9 — postulo-firefox's development chain locks web-ext 8.10.0 with fifteen open advisories (three critical), and CI runs no `npm audit` (Dependencies) - postulo/postulo-firefox#10 — Firefox workflows keep the checkout credential in the working copy while running code the pull request controls (Configuration) **Informational** - postulo/postulo-firefox#11 — `check-pin.mjs` accepts a tag name as the pin, and a tag can be moved (Configuration) ### postulo/postulo-helloworld **Low** - postulo/postulo-helloworld#14 — The dev lock pins a core commit 211 behind and carries pyjwt 2.13.0 and urllib3 2.7.0 with published advisories (Dependencies) **Informational** - postulo/postulo-helloworld#13 — Reference notifier's `test()` echoes the `OSError` text, server-side path included, to the account holder (Data Exposure) ### postulo/postulo-imap **Low** - postulo/postulo-imap#24 — postulo-imap locks pyjwt 2.13.0, urllib3 2.7.0 and django 6.1.1, carrying 22 published advisories, and CI never audits the lock (Dependencies) ### postulo/postulo-mcp **Low** - postulo/postulo-mcp#17 — postulo-mcp locks mcp 2.1.1, whose Streamable HTTP server keeps sessions without bound (CVE-2026-59951), and CI never audits it (Dependencies) - postulo/postulo-mcp#20 — CI installs uv from the moving `install.sh` with no version and no checksum; the core fixed this in #790 (Configuration) **Informational** - postulo/postulo-mcp#18 — `Postulo._call` reads every answer whole, with no size ceiling and no deadline for the whole exchange (Input Validation) - postulo/postulo-mcp#19 — The error channel is outside the fence: text from the answerer's body reaches the model unmarked (Injection) - postulo/postulo-mcp#21 — The HTTP transports accept unlimited wrong bearer secrets; the core throttles wrong API tokens per address (Authentication/Authorization) ### postulo/postulo-paperless **Low** - postulo/postulo-paperless#16 — Store reads every Paperless answer whole with no deadline, so a server the account holder names can hold the shared scheduler (Input Validation) - postulo/postulo-paperless#17 — The dev lock pins a core commit 210 behind and carries pyjwt 2.13.0 and urllib3 2.7.0 with published advisories (Dependencies) ### postulo/postulo-templates **Low** - postulo/postulo-templates#15 — CI installs uv with an unpinned `curl | sh` and audits no dependency, and no existing issue names this repository (Configuration) ### postulo/postulo-thunderbird **Low** - postulo/postulo-thunderbird#13 — postulo-thunderbird's dev chain locks web-ext 8.10.0 with thirteen open advisories (three critical), and CI runs no `npm audit` (Dependencies) **Informational** - postulo/postulo-thunderbird#14 — The extension asks for a `captures` token, while the threat model's mail-client row promises `listings:bind` and nothing else (Authentication/Authorization) - postulo/postulo-thunderbird#15 — Shared copies are 14 postulo-chromium commits behind, missing the non-JSON-answer and request-hardening fixes to `api.js` (Dependencies) - postulo/postulo-thunderbird#16 — The API token sits in `storage.local` in clear, outside Thunderbird's primary password, and the privacy text does not say so (Data Exposure) ### tiagoagueda/postulo-francetravail Five issues filed on that private repository and listed there. ## Highest priority - High: #956, #966. - Medium: #957, #958, #961, #974, #975, #987, postulo/postulo-apprise#21. ## Also done - Comments instead of new issues, where an open issue already covers the area: postulo/postulo-imap#15 and postulo/postulo-imap#8 (an unbounded list of threading identifiers in one database query), postulo/postulo-paperless#14 (remote text copied by `_explain`). - postulo/postulo-dav#30 labelled `security`, since a read-only share can reach `apply_card`. ## Not filed The second pass dropped: a merge refusal on trailed or shared records (a functional bug with no security consequence), document PDFs drawn without a deadline (outside the threat model), a per-account upload quota (already #268), and CI items that follow the project's documented action-pinning policy (#790).
Author
Owner

All 86 issues of this review are closed, so this index is done.

How they were fixed (all through pull requests, each with its CI green before it merged):

  • Core: #993 (shared-record leaks, including the export and merge preview), #994 (contact deletion), #995 (backup restore of an unsigned archive), #996 (CI hardening), #997 (Django 6.1.2, django-allauth 65.20.0, oauthlib 4), #998 (sharing and groups), #999 (plugin framework, plus read_body, deadline_in, until and BodyRefused on the plugin API), #1000 (API), #1010 (the remaining 13).
  • Plugins and extensions: postulo-apprise#23, postulo-dav#35, postulo-imap#25, postulo-paperless#18, postulo-helloworld#15, postulo-templates#16, postulo-mcp#22, postulo-chromium#40, postulo-firefox#12, postulo-thunderbird#17. The five findings on the France Travail plugin were fixed in its own repository.

Left open on purpose, none of them security findings of this review: postulo-dav#6, #11 and #13 (they wait on core #325, #326, #327, #330 and #332), the rest of postulo-apprise#13 (the CI audit for the other repositories), and the two feature issues of the France Travail plugin.

Follow-ups that are the operator's, not code: re-sign catalogue/official/index.json (the signer now writes a 60-day expiry), run scripts/weblate.py configure to add the safe-html check, and dispatch the Dependencies workflow once to prove its two-job split.

New decisions recorded in the threat model: the capture-API refusal wording (#954), and what a read-only grantee may add on a shared record (#955).

All 86 issues of this review are closed, so this index is done. How they were fixed (all through pull requests, each with its CI green before it merged): - **Core:** #993 (shared-record leaks, including the export and merge preview), #994 (contact deletion), #995 (backup restore of an unsigned archive), #996 (CI hardening), #997 (Django 6.1.2, django-allauth 65.20.0, oauthlib 4), #998 (sharing and groups), #999 (plugin framework, plus `read_body`, `deadline_in`, `until` and `BodyRefused` on the plugin API), #1000 (API), #1010 (the remaining 13). - **Plugins and extensions:** postulo-apprise#23, postulo-dav#35, postulo-imap#25, postulo-paperless#18, postulo-helloworld#15, postulo-templates#16, postulo-mcp#22, postulo-chromium#40, postulo-firefox#12, postulo-thunderbird#17. The five findings on the France Travail plugin were fixed in its own repository. Left open on purpose, none of them security findings of this review: postulo-dav#6, #11 and #13 (they wait on core #325, #326, #327, #330 and #332), the rest of postulo-apprise#13 (the CI audit for the other repositories), and the two feature issues of the France Travail plugin. Follow-ups that are the operator's, not code: re-sign `catalogue/official/index.json` (the signer now writes a 60-day expiry), run `scripts/weblate.py configure` to add the `safe-html` check, and dispatch the Dependencies workflow once to prove its two-job split. New decisions recorded in the threat model: the capture-API refusal wording (#954), and what a read-only grantee may add on a shared record (#955).
Sign in to join this conversation.
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#992
No description provided.