Skip to content

Releases: l0rdg3x/OPNGMS

v0.22.5

Choose a tag to compare

@github-actions github-actions released this 29 Jul 22:54
df23bb4

[0.22.5] - 2026-07-30

Security

  • The backend image no longer ships pip. Trivy reported two HIGH findings against the image, and
    both came from pip's own vendored dependencies — msgpack 1.1.2 (GHSA-6v7p-g79w-8964, SEGV/DoS
    when an Unpacker is reused after an error) and setuptools 70.3.0 (CVE-2025-47273, path traversal
    in PackageIndex) — declared in pip/_vendor/vendor.txt. Neither is installed standalone, and
    neither is reachable: the container only ever runs uvicorn, arq or alembic, never pip.
    Upgrading pip cannot clear them either — no release, 26.2 or main, vendors patched versions. So the
    Dockerfile now uninstalls pip once the application is installed, which removes the vulnerable code
    from the image and drops a package manager from a production container on general principle.
    python -m ensurepip restores pip inside a running container if it is ever wanted for debugging; the
    bundled wheel stays and Trivy does not look inside it.
    • Verified by rebuilding the real image and rescanning it with the exact CI invocation
      (trivy:0.65.0 image --severity HIGH,CRITICAL --ignore-unfixed): 2 findings → 0. app.main
      still imports, and uvicorn, alembic and arq all still start.
  • Correction to the 0.22.4 notes, which claim "zero open alerts". That was verified true before the
    release branch was pushed — and it stopped being true nine minutes before the merge, when the release
    PR's own Trivy scan created the two alerts above. Two lessons, recorded rather than glossed: the
    Trivy (backend) / Trivy (frontend) checks are green by design (--exit-code 0), so they are a
    baseline that publishes findings to the Security tab, not a gate that blocks on them; and an alert
    count read before a branch is pushed says nothing about the tree that gets tagged. Re-read it after the
    final push, not before.

v0.22.4

Choose a tag to compare

@github-actions github-actions released this 29 Jul 22:27
03d7973

[0.22.4] - 2026-07-29

Changed

  • Dependency maintenance — ten merged pull requests, no behaviour change. Every one landed with the
    full CI suite green:
    • CI: actions/setup-python 6 → 7, actions/setup-node 6 → 7, and docker/login-action
      4.4.0 → 4.6.0
      (the actions-minor group). Pull requests put both majors through their paces via
      ci.yml, but they also sit outside it — setup-python in publish-catalogs.yml and
      scheduled-audit.yml, setup-node in scheduled-audit.yml — and no pull request runs those. The
      refreshed login-action digest is used by trivy.yml, already green on main, and by
      publish-images.yml, where this release is its first run.
    • Backend: the declared floors now match what CI had already been resolving — pydantic-settings
      ≥ 2.14.2
      , httpx ≥ 0.28.1, webauthn ≥ 3.0.0, and ruff ≥ 0.16.0 (dev). These four are
      floors with no ceiling, so pip takes the newest release the rest of the graph allows on every CI
      run; the bumps close the gap between the declaration and the versions the suite had been passing
      against all along. (Where a ceiling does exist, the newest release is not what gets installed —
      see the redis entry below.)
    • Frontend: the frontend-minor group — Mantine 9.4.2 → 9.5.0 across all six packages,
      recharts 3.10.0 → 3.10.1, eslint 10.7.0 → 10.8.0, globals 17.7.0 → 17.8.0 — plus the
      test-only jsdom 29.1.1 → 30.0.1 and @testing-library/jest-dom 6.9.1 → 7.0.0.

Fixed

  • Two dependency ceilings are now declared rather than rediscovered. Both were proposed as updates,
    both fail, and neither failure is caused by this codebase:

    • redis stays on 5.x. arq — the task queue the worker runs on — pins redis[hiredis]>=4.2.0,<6,
      and 0.28.0 is its latest release, so raising the floor to >=8.0.1 made the dependency set
      unsolvable (ResolutionImpossible); the failed install took three CI jobs with it (backend tests,
      dependency audit, and the backend Trivy scan, which builds the image). backend/pyproject.toml now
      declares redis>=5.0,<6 with arq named as the source, so the limit is visible where the dependency
      is written instead of being rediscovered from a red build. It duplicates arq's constraint, so the
      comment says to drop it rather than raise it once arq widens — otherwise the line we added to
      explain a blocked upgrade becomes the thing blocking one.
      • Worth stating plainly: this parks the queue's Redis client on an unmaintained line. redis-py
        5.3.1 (2025-07-25) is the last 5.x release and the branch has had no commits since. Nothing
        affects it today — pip-audit is clean and no advisory covers 5.x — but a future redis-py CVE would
        have no in-place fix; the way out would be arq widening its range, or being replaced. That is the
        accepted risk, not an absence of one.
    • TypeScript stays below 6.1, which the existing ~6.0.2 pin already enforces. On TypeScript
      7.0.2 tsc -b, vite build and all 298 frontend tests pass — npm run lint is what aborts: no
      published typescript-eslint declares support for TypeScript ≥ 6.1 (8.65.0 and even its canaries
      declare >=4.8.4 <6.1.0; upstream tracking issue typescript-eslint/typescript-eslint#10940). No
      Dependabot ignore rule was added for it — a silenced update is worse than a visible one — so the
      next proposal gets re-evaluated rather than skipped.

    Both are recorded in AGENTS.md so the next contributor reads the reason before spending a CI run
    on it.

  • A contribution rule the repository states but does not enforce. AGENTS.md, the Wiki and the
    header comment in ci.yml all asserted that a pull request must be up to date with main before it
    can merge — and ci.yml used that assertion to justify being pull-request-only, on the grounds that
    the PR run therefore validates the exact tree that lands. The ruleset has
    strict_required_status_checks_policy off: no rebase is required, so a pull_request run — which
    tests the head merged into the base tip at the time it runs — leaves a check that can be stale by
    the time the merge happens. Eight of this release's ten updates merged while behind main (#247
    went first, #254 was rebased by Dependabot). All three texts now describe that, and prescribe the
    remedy: after merging a batch, re-run the suite locally on merged main before tagging.

    • The same look at the ruleset turned up a second gap worth writing down. On a Dependabot pull request
      the three required Analyze (…) contexts never run — CodeQL default setup skips them — so
      required checks are never satisfied and the merges go through the admin bypass
      (RepositoryRole admin, bypass_mode: always). Turning the strict policy on would therefore not,
      by itself, have blocked these merges. Both are repository settings rather than code, and both are
      left as open decisions.
  • The weekly dependency audit was running on Node 20. scheduled-audit.yml — the only job that
    re-checks main between pull requests — set up Node 20 while CI uses 24. This release made that worse
    rather than better: jsdom 30 and @testing-library/jest-dom 7 both declare engines.node >= 22, as
    react-router 8 already did, so npm ci in the weekly baseline was resolving the lockfile under an
    engine none of them support. Now pinned to 24, matching CI and the documented Node 24+ requirement.

  • The CodeQL query suite was named wrong in 0.22.3. That release's notes and the Wiki's security page
    both said security-and-quality. GitHub's default setup does not offer that suite — it offers
    default or extended, and this repository is on extended, which resolves to the
    security-extended built-in suite (confirmed in the config API and in every analysis's SARIF
    codeqlConfigSummary). The difference is not cosmetic: security-and-quality would additionally run
    the whole quality query set — maintainability and reliability, in GitHub's own wording — which is not
    running here. The Wiki is corrected; the 0.22.3 entry is left as written, with the correction recorded
    here instead of edited into history.

Security

  • No new advisories, and the one carried-over residual is now actually patched. CodeQL default
    setup's extended suite (security-extended, over Python, JavaScript/TypeScript and Actions)
    reports zero open alerts — though "open" is the operative word: seven alert records remain on file
    as dismissed, and four of those findings are still produced by the current scan of main. One is
    py/request-without-cert-validation, dismissed as won't fix — the accepted-risk decision recorded
    in 0.22.3. npm audit --omit=dev and pip-audit are both clean.
  • brace-expansion 2.1.2 → 2.1.3 in the lockfile. 0.22.3 recorded this nested copy — reached by
    openapi-typescript@redocly/openapi-core 1.x → minimatch 5.x — as having no fix on the 2.x
    line. That is no longer true: upstream backported the guard (EXPANSION_MAX_LENGTH) to 2.1.3 on
    2026-07-28, and it satisfies minimatch's declared ^2.0.1, so a lockfile refresh takes it without
    touching the codegen tool. The unbounded-expansion crash (CVE-2026-14257) is therefore gone from the
    tree. A full npm audit still flags it, because the advisory's vulnerable range is the flat
    <= 5.0.7 and has not been narrowed the way its sibling GHSA-3jxr-9vmj-r5cp was — so the noise
    outlives the defect. Everything about the placement is unchanged: dev-scope, offline npm run gen:api
    only, npm ci installs it in the frontend build stage but the shipped nginx image copies dist/
    alone, and the production-only audit gate never saw it.

v0.22.3

Choose a tag to compare

@github-actions github-actions released this 25 Jul 20:44
fa6926d

[0.22.3] - 2026-07-25

Security

  • Four open dependency advisories closed — one of which had already broken the CI security gate.
    A full npm audit (rather than only the single alert Dependabot had raised) surfaced four HIGH
    findings in the frontend:
    • react-router → 8.3.0 (GHSA-qwww-vcr4-c8h2), a production dependency. The CSRF bypass lives
      only in React Router's unstable RSC code paths, which this SPA does not use — but 8.3.0 is the
      only patched version, and the advisory was already failing npm audit --omit=dev, the command
      behind the "Dependency audit" CI job. Because ci.yml is pull-request-only, main never went red;
      the next PR would have. In v8 the react-router-dom package no longer exists, so the 22 files that
      imported from it now import from react-router — that is the entire change. useMatches, the one
      breaking change v8 brings to a declarative SPA, is not used here.
    • brace-expansion → 5.0.8 (GHSA-3jxr-9vmj-r5cp exponential-time expansion, and the
      unbounded-expansion OOM advisory) and js-yaml → 4.3.0 (GHSA-52cp-r559-cp3m, quadratic CPU on
      merge-key chains). Development-scope, lockfile only. A second, nested copy of brace-expansion
      reached its 2.x maintenance ceiling at 2.1.2: that closes the expansion DoS, but the OOM
      advisory has no fix on the 2.x line, so it stays flagged in a full npm audit. It sits under the
      offline gen:api codegen tool, never ships in an image, and does not affect the production-only
      audit gate.
  • The device form now surfaces the TLS trust decision instead of burying it. Triaging a CodeQL
    finding on the connector (py/request-without-cert-validation) confirmed the flagged behaviour is
    deliberate — Verify TLS is a per-device operator setting, on by default, that exists because
    OPNsense appliances ship self-signed certificates — but it also exposed a real gap: the API accepted
    a pinned tls_fingerprint as the compensating control and no part of the UI ever offered it.
    An operator who switched verification off got no warning and no way to pin. Switching it off now
    reveals a warning stating plainly that nothing authenticates the device's certificate and that
    credentials can be intercepted, next to the fingerprint field that restores protection. The
    fingerprint is sent only when verification is actually off and a value was entered, so neither an
    empty field nor a dormant leftover is ever stored as if pinning were active. Translated across all
    13 UI languages.
    • Still open, and stated honestly: an already-onboarded device cannot have its TLS settings
      changed from the console — the API supports it, no UI exposes it. Re-add the device to change its
      pinning.

Changed

  • Routine dependency maintenance (no behaviour change; all CI-verified, and both major bumps are
    covered by tests that exercise the real API rather than a mock — the report renderer builds an actual
    PDF through WeasyPrint's URLFetcher, and the Fernet/x509 paths run end to end):
    • Backend: cryptography 42 → 49 and WeasyPrint 62 → 69 (majors), Alembic 1.13 → 1.18.5,
      FastAPI 0.138 → 0.139, aiosmtplib 5.1.1 → 5.1.2.
    • CI: the grouped actions-minor bump — docker build-push-action 7.2 → 7.3, metadata-action
      6.1 → 6.2, setup-buildx-action 4.1 → 4.2, setup-qemu-action 4.1 → 4.2, and a login-action
      digest refresh.
    • Frontend: the grouped frontend-minor bump (18 updates — Mantine 9.4.0 → 9.4.2, React 19.2.6 →
      19.2.8, Recharts 3.9 → 3.10, Vite 8.1.0 → 8.1.5, ESLint 10.5 → 10.7, plus msw, TanStack Query and
      typescript-eslint).

Fixed

  • Documentation drift in the Wiki. Its security page asserted that the repository has no CodeQL
    workflow. CodeQL is in fact active through GitHub's default setup — the security-and-quality
    suite over Python, JavaScript/TypeScript and Actions — which is precisely why no codeql.yml exists
    to be found. The scan table now lists it, and the Transport-security section documents outbound TLS
    to managed firewalls, which it had never covered.

v0.22.2

Choose a tag to compare

@github-actions github-actions released this 24 Jun 20:55
c92d4cd

[0.22.2] - 2026-06-24

Changed

  • Routine dependency maintenance (no behaviour change; all CI-verified, and the two major bumps
    API-checked against our usage). No new CVE fixes this round — the security scans (CodeQL, gitleaks,
    Trivy, and the pip-audit + npm audit dependency audit) are clean.
    • Backend: FastAPI → 0.138 (minor; the httpx / starlette upper-bound caps are still satisfied),
      maxminddb 2 → 3.1.1 (major — the production GeoIP reader path Reader(BytesIO, mode=MODE_FD) +
      .get() was verified against 3.1.1, on top of the existing real-mmdb reader tests), and dev tooling
      respx → 0.23.1, pyotp → 2.10, ruff → 0.15.19.
    • CI: actions/checkout 6 → 7 (the v7 fork-PR hardening only affects pull_request_target /
      workflow_run, neither of which this repo's workflows use).
    • Frontend: the grouped frontend-minor bump (12 minor/patch updates — Mantine 9.3.2 → 9.4.0,
      Recharts 3.8 → 3.9, Vite 8.0 → 8.1, plus TanStack Query, typescript-eslint, and friends).

v0.22.1

Choose a tag to compare

@github-actions github-actions released this 19 Jun 12:32
6b5ee79

[0.22.1] - 2026-06-19

Security

  • Dependency security updates. Two dependencies bumped for published CVE fixes:
    • Jinja2 → 3.1.6 (GHSA-cpwx-vrp4-4pq7 — the |attr filter no longer bypasses the sandbox's
      attribute-lookup checks).
    • undici → 7.28.0 (frontend dev/transitive) — closes 7 advisories, including two High-severity
      ones: a WebSocket DoS via unbounded message fragments (CVE-2026-12151) and a SOCKS5 ProxyAgent TLS
      certificate-validation bypass (CVE-2026-9697).

Changed

  • Routine dependency bumps (no behaviour change; all CI-verified, and the major bumps API-checked
    against our usage):
    • Backend: aiosmtplib 4 → 5.1.1 (API-compatible; XOAUTH2 is now first-class upstream), Babel →
      2.18
      , and dev tooling pytest → 9.1 / ruff → 0.15.17.
    • CI image build: docker/build-push-action 6 → 7, metadata-action 5 → 6, setup-buildx-action
      3 → 4
      , setup-qemu-action 3 → 4 (Node 24 action runtimes; the inputs we pass are unchanged).
    • Frontend: the grouped frontend-minor bump (11 minor/patch updates).

v0.22.0

Choose a tag to compare

@github-actions github-actions released this 17 Jun 07:59
de5711b

[0.22.0] - 2026-06-17

Added

  • SMTP OAuth "Connect" button (experimental). The OAuth2 SMTP relay (Gmail / Microsoft 365) gains a
    "Connect with Google / Microsoft 365" button under Admin → SMTP delivery that runs the
    authorization-code flow and stores the obtained refresh token automatically — instead of pasting it by
    hand. The operator still registers the OAuth app and enters the client id + secret; the button only
    removes the manual refresh-token paste. The browser flow uses a signed, expiring state (HMAC over
    the initiating superadmin + provider, 10-min TTL) as its CSRF defence, and the redirect URI + landing
    URL are built only from the new PUBLIC_BASE_URL setting (https-only; no open-redirect). The
    refresh token is Fernet-encrypted at rest like every other secret; the flow is superadmin-gated and
    audited (smtp.oauth.connected).
    • ⚠️ Experimental — untested against a live provider. The browser consent + real Google/Microsoft
      redirect could not be verified in CI (no public callback URL / registered OAuth app); the code is
      unit-tested with mocked HTTP. The UI labels the button "Experimental — untested", and pasting a
      refresh token manually remains the supported path
      . The Connect button requires PUBLIC_BASE_URL
      set and the redirect URI registered in your OAuth app.

v0.21.0

Choose a tag to compare

@github-actions github-actions released this 17 Jun 00:37
17beac1

[0.21.0] - 2026-06-17

Added

  • Remember-this-device (trusted devices). After clearing a second factor (TOTP or WebAuthn), users
    can tick "Trust this device for N days" at the login MFA step; subsequent logins from that device
    still require the password but skip the second factor until the trust expires. Trust is
    server-side and revocable: a new trusted_device table stores only an HMAC-SHA256(SESSION_SECRET)
    hash of a token that lives in an HttpOnly+Secure+SameSite=Lax cookie (a DB dump yields no usable
    tokens; rotating SESSION_SECRET invalidates every trusted device).
    • Manage your trusted devices under Two-factor auth — list (device, IP, last used, expires),
      revoke one, or revoke all (with confirmation).
    • Auto-revoke: disabling your own MFA, an admin MFA reset, or log out everywhere drops all of a
      user's trusted devices (a plain logout intentionally keeps the trust so the feature survives it).
    • Org controls (superadmin): a Remember-this-device on/off toggle (env default
      TRUSTED_DEVICE_ENABLED=true) and a runtime-tunable trusted device lifetime (trusted_device_days,
      default 30, range 1–365) under System → Runtime settings.
    • Safety rails: the password is always required; trust is bound to one user (a cookie for user A can
      never skip MFA for user B); a trusted cookie can never bypass mandatory MFA enrollment; expired or
      revoked cookies fail closed; a sweeper purges expired rows. Every action is audited
      (auth.login.trusted_device, auth.trusted_device.create / revoke / revoke_all / policy_change).

v0.20.0

Choose a tag to compare

@github-actions github-actions released this 16 Jun 21:22
b05f780

[0.20.0] - 2026-06-16

Added

  • WebAuthn / passkeys as a second login factor. Alongside TOTP, users can now register passkeys
    platform authenticators (Face/Touch ID, Windows Hello) or roaming security keys (YubiKey) — and
    satisfy the login MFA challenge with a passkey or a TOTP code. Register one or more passkeys under
    Two-factor auth (an account-password step-up is required, like TOTP enrolment); each is named and
    individually removable, and the last remaining factor can't be removed while the MFA policy requires it.
    Verification follows the WebAuthn ceremonies (challenge + RP ID + origin checked server-side; sign
    count must strictly increase
    to detect a cloned authenticator); the per-ceremony challenge is
    server-generated, single-use, and bound to the session. WebAuthn is public-key — only the public key
    • sign count are stored (no secret material). Because passkeys bind to a domain, set the relying-party
      config first (System → WebAuthn: rp_id / origin, also via WEBAUTHN_RP_ID / WEBAUTHN_ORIGIN);
      registration is disabled until it is configured. Backend + frontend (#207, #208), adversarial
      security-review + CodeQL clean. (remember-this-device is a planned follow-up.)

v0.19.0

Choose a tag to compare

@github-actions github-actions released this 16 Jun 19:19
068df93

[0.19.0] - 2026-06-16

Added

  • OAuth2 (XOAUTH2) authentication for the SMTP relay — Gmail + Microsoft 365. Google Workspace and
    Microsoft 365 are disabling basic-auth SMTP, so the report-email relay can now authenticate with
    OAuth2 instead of a password. In System → SMTP, pick OAuth2, choose the provider (Google /
    Microsoft 365), and provide the client_id / client_secret / refresh_token (plus the Azure tenant
    for Microsoft); OPNGMS exchanges the refresh token for a short-lived access token at send time and
    authenticates with SASL XOAUTH2 (via aiosmtplib's native support). Password auth is unchanged. The
    OAuth client_secret + refresh_token are Fernet-encrypted (MASTER_KEY) like every other secret —
    never returned by the API (only has_… flags) and never logged; the access token lives only in memory
    for the send. The Send-test button exercises the OAuth path too, and the MASTER_KEY rekey tooling covers
    the new encrypted columns. This is the manual-entry core; an in-app "Connect" button (OAuth
    authorization-code callback) is a planned follow-up for deployments with a public callback URL. Hardened
    against the CodeQL extended suite (the Microsoft tenant is sink-guarded against partial-SSRF). (#205)

v0.18.1

Choose a tag to compare

@github-actions github-actions released this 16 Jun 16:35
7adcfb9

[0.18.1] - 2026-06-16

Fixed

  • Business→Community map dropped Business hotfix releases. v0.18.0's business-base.json generator
    only matched a "based on the OPNsense X.Y.Z community version" header, but a Business hotfix
    (e.g. 25.4.3, 24.4.3) is instead "based on the OPNsense X.Y.Z business version" — it chains onto
    the prior Business release. Those entries silently fell out of the map. The generator now captures the
    community/business kind and follows a business→business chain transitively to the underlying
    Community base (25.4.3 → 25.4.2 → 25.1.12), skipping only genuinely unresolvable chains or cycles. It
    also no longer floors the map at major ≥ 25 (the Community-catalog floor is enforced downstream by the
    resolver), so the published map is complete — every documented Business release (38 entries from the
    current opnsense/changelog) resolves to its Community base instead of just the 8 direct-Community ones.