* Distrust compromised eMudhra intermediates in certifi and Deno (#854)
* Distrust compromised eMudhra intermediates in certifi and Deno
The private keys of eight eMudhra intermediate CAs can be recovered from
their certificates, so anyone can mint a trusted certificate for any host.
* Require certifi>=2026.10.19; yt-dlp, requests and curl-cffi use its bundle,
and 2026.10.19 removes the emSign ECC Root CA - G3 root they chain to.
* Set DENO_TLS_CA_STORE=system for the API (systemd + Docker) and the archive
container so Deno follows the apt-patched system store instead of roots
that only change with a Deno upgrade.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep Deno's bundled roots in the archive container
browserless/chrome:1.61.1 is Ubuntu 22.04 whose newest ca-certificates is
from 2023, older than Deno 2.8.3's bundled roots. The system store only
becomes the better choice once the archive base image is upgraded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* build(deps): bump dompurify from 3.4.16 to 3.4.17 in /app
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.16 to 3.4.17.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.16...3.4.17)
---
updated-dependencies:
- dependency-name: dompurify
dependency-version: 3.4.17
dependency-type: direct:production
...
Signed-off-by: dependabot[bot] <support@github.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: lrnselfreliance <33263708+lrnselfreliance@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Preview markdown files formatted, with a raw toggle
Markdown files are typed text/markdown (libmagic reports them as text/plain, or
as a programming language when they hold code) and keep the text indexer, so
they stay deep-searchable. Existing files switch type on their next refresh.
The file preview renders markdown with markdown-it into the page, so it follows
the theme tokens and the night media filter. A Formatted/Raw toggle shows the
source instead, and the choice is remembered. Relative links and images
resolve against the file's directory; URLs that climb out of /media/ are
dropped. Raw HTML in a file is escaped rather than parsed, and the output is
also sanitized with DOMPurify.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Do not load images from other sites in the markdown preview
An image from another site (http:, https:, or protocol-relative //) loaded as
soon as the preview opened, telling that site who was reading the file and
when. Such an image now becomes a link labeled with its alt text, so the
reader chooses whether to follow it. Inside a link (a README badge) it becomes
the outer link's text, since links cannot nest. Media and embedded data:
images still load.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Cypress component testing resolves the top-level webpack-dev-server
and only accepts major 5, so the bump crashed cypress.config.js on
every run ("Unexpected major version of webpack-dev-server"). The
react-scripts dev server was unaffected because it uses its own
nested 4.x, which is what the pre-merge check looked at.
Pin webpack-dev-server below 6 in dependabot.yml until Cypress
supports it.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* test: the component suite has not run since cypress 15, on any machine
Six component spec files -- 334 tests, including the 4,300-line layout suite -- could not
start. Cypress exited before printing anything, and under its default Electron browser it
did not even do that: it hung at 0% CPU until killed. Chrome prints the error in seconds,
which is the only reason this was findable.
Three things broke, all of them from the 13.17.0 -> 15.20.0 bump.
`baseUrl: "src"`. Cypress 15 loads its config with tsx, and tsx honours tsconfig path
resolution. Cypress requires Node's core `events` module; with that baseUrl the bare
specifier resolves against `src`, and on a case-insensitive filesystem it matches
`src/Events.js`. That file imports the app, so the whole component tree was required in
Node: JSX in `.js` files, which esbuild will not parse.
This is why CI stayed green while every local run died. Linux is case-sensitive, `events`
never matched `Events.js`, and it resolved to the built-in. The baseUrl buys nothing --
there is not one absolute import in the codebase -- so it goes.
`framework: "create-react-app"`. Cypress 14 removed that dev server; the handler is in the
13.17.0 binary and absent from 15. The webpack config is handed over explicitly now, taken
from react-scripts so components compile exactly as `npm start` compiles them. Two things
follow from that: `webpack-dev-server` 5 is a devDependency because Cypress requires 5 and
react-scripts pins 4 (npm nests react-scripts' copy, and the production build is unchanged),
and babel-loader is widened to `cypress/`, which react-scripts does not compile -- the
support file is JSX and was reaching babel with no preset.
The suite ran nowhere. `cypress run` means e2e -- its specPattern is `cypress/e2e/**` --
and these specs sit beside the components in `src`, so no job has ever executed them. A
`cypress-component-tests` job runs them now; without it this breaks again in silence.
One test fails the moment the suite can run, and it is mine, from #660: it asserted the URL
column is the widest in the row. It is not, and never was -- at that width the fixed columns
take ~307px of 400, leaving the URL ~93 against Control's 146. It shipped green because
nothing could run it. It asserts a usable remainder instead.
334 component tests pass, 69 suites / 1214 jest tests pass, `npm run build` is clean.
* review: an assertion with a floor, and comments that told the story instead of the rule
The leftover-width test had a lower bound and nothing else, so it passed on any column
wide enough to have padding -- including one that had grown, which is what losing
table-layout: fixed does. It measures the leftover now: the sized columns are held to
the widths they declare, and the truncating column to exactly what they did not take.
Comments in the config, the CI job and the spec cut back to the rule a reader needs. The
version history and the measurements are in the PR.
The pins that fixed the blank React /map were removed as collateral when the
Mantine commit rewrote `overrides` for TypeScript 5. Nothing failed, so nothing
objected: the map kept working only because package-lock.json still happened to
resolve @babel/core to 7.20.12, and devices run `npm install`, not `npm ci`.
Verified on this tree rather than assumed. Raising @babel/core to 7.29.7 and
reinstalling floats the whole pipeline (traverse/generator/types to 7.29.8) and
`npm run build` then fails outright: "'opera_mobile' is not a valid target",
from a helper-compilation-targets that floated away from the browserslist data
it was pinned against. With the pins restored the build compiles, all 141
resolved copies match, and /map renders from the built bundle.
The test walks every path in the lockfile, not just node_modules/<name>: npm
nests a second copy whenever versions conflict, and a nested copy that escaped
the override would put the new pipeline back into the build while the top-level
entry still read correctly.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rebasing onto master brought @testing-library/dom 8.20.0 -> 10.4.1, while
user-event stayed at 13.5.0. v13 targets dom v8 and reaches into internals v10
moved, so its keyboard simulation silently stopped dispatching: two SearchBox
tests failed with `Number of calls: 0` -- the arrow keys and Enter did nothing.
Test-harness only. @testing-library/dom never reaches a build, and no product
code changed in the rebase; the combination was simply never installed together
until master's bump met this branch.
v14's direct API is call-compatible with what the ten suites already do, so no
test needed rewriting -- the calls were already awaited.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`semantic-ui-react`, `react-semantic-toasts-2` and `semantic-ui-offline` are
gone from package.json, the lockfile and node_modules -- 19 MB of dependencies
that nothing had imported since the migration finished.
Four things still referenced them and would have broken on uninstall. None was
caught by a build or by jest:
Four test files still ran `jest.mock('react-semantic-toasts-2', ...)` for
components that no longer use it. A jest.mock of a module that does not exist
throws, so the packages could not have come out without this.
`src/components/Download.cy.js` and `cypress/support/component.js` both
imported semantic.min.css. The support file now loads what the app loads --
index.css, the font and token stylesheets, and Mantine's.
The ratchet changes job rather than being deleted. An import of a missing module
fails loudly now, but three quieter mistakes would not: a `className="ui button"`
that styles nothing, a file reaching back through Theme.tsx, and someone
reinstalling a package to "just use one Semantic component". It asserts all
three, plus that package.json names nothing matching /semantic/i.
Two pre-existing problems in the cypress component harness surfaced while
checking the uninstall had not broken it, both fixed because that file had to be
edited anyway:
It imported `react-router-dom`, which is not installed -- this app is on
react-router v7. Dates to 5621d473, so the component suite has been
un-runnable since well before this migration.
Its mount helper supplied no MantineProvider, so every library component threw
-- the same gap test-utils.js had for jest.
The component specs now load and four pass; eleven assertions are still written
against markup from before they stopped running. Repairing those is its own
piece of work, deliberately not bundled in here. The e2e suite is unaffected:
43/43.
Adds src/components/ui: buttons, messages, labels, progress, status, tables,
form controls, panels, cards, statistics, modals, tooltips, tabs, and icons.
Everything resolves its colors through theme tokens, so no component branches
on the current theme, and Semantic UI keeps rendering the app meanwhile.
Mantine is bridged rather than restyled: its own CSS variables are pointed at
our tokens. Four details were not obvious, and each caused a visible defect
in night mode before being fixed:
- Structural colors must go in the resolver's light/dark blocks. Mantine
emits its values under [data-mantine-color-scheme], which outranks the
:root block that `variables` becomes, so its grays were winning.
- The dark and gray palettes are mapped shade by shade to tokens. Mantine
reads them directly for table borders, zebra stripes and input chrome;
they can be neither flattened nor left alone.
- Mantine writes --button-* as inline styles, which no stylesheet can
outrank, so the dashed danger treatment sets real properties instead.
- Switch colors are written onto inner elements, so they need scoped CSS
rather than a variable on the root. Adds a --knob token for control
handles, because Mantine's default thumb follows theme.white, which is
near-black in the dark-based themes.
An audit of every painted pixel in the /theme-sample gallery now finds no
non-red channel in night mode.
Icons come from @tabler/icons-react with the 102 Semantic names the app uses
mapped to components; they stroke with currentColor, so they follow the theme
without any rule of their own. Toasts keep the react-semantic-toasts-2
signature so those call sites migrate by changing an import.
TypeScript 5 is required: Mantine's declarations use `const` type parameters,
which 4.9 cannot parse (Mantine 7 is the same, so downgrading is no escape).
That needs two npm overrides to keep clean installs working, both verified
against a Docker build: react-scripts declares peerOptional typescript ^3||^4,
and fork-ts-checker 6.5.2 assigns typescript.performance.mark, which TS 5
made getter-only - crashing the dev server's type checker on every rebuild.
semantic-baseline.test.js ratchets the removal: it fails if a file not already
listed imports Semantic UI. An ESLint rule would have printed 75 warnings per
rebuild, and CI unsets CI= for its build step, so it would guard nothing there.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(app): depend on @testing-library/dom directly
jest-dom 7 moved @testing-library/dom from a bundled dependency to a peer
dependency. Nothing else pulls the top-level copy in — @testing-library/react
13 carries its own nested v8 — so it was present only as a peer, and CI
installs with --legacy-peer-deps, which skips peers. Every suite then failed
to load setupTests.js with "Cannot find module '@testing-library/dom'".
Declare it as a devDependency so it installs under either resolution mode.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(app): import mount from cypress/react
Cypress 14 removed the cypress/react18 subpath; cypress/react mounts React
18 itself. The e2e support file loads commands.js, so the stale import broke
bundling for every spec, not just component tests:
Module not found: Package path ./react18 is not exported from package cypress
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* ci: install and cache Cypress the way the bump requires
The Cypress binary lives in ~/.cache/Cypress, outside node_modules, but only
node_modules was cached. A restored cache satisfied npm, so npm skipped the
postinstall download and the 15.x binary was never fetched:
The cypress npm package is installed, but the Cypress binary is missing.
Cache both paths and install the binary explicitly.
Also align the JS toolchain with what actually ships. Debian 13 devices run
nodejs 20.19.2 and install with a plain `npm install`; the Docker app image
does the same. CI pinned older Node and passed --legacy-peer-deps, which
skips peer dependencies -- that is why jest-dom 7's peer requirement passed
review and broke CI. Match the devices instead.
Pin caddy to 2.11.4, the version Debian 13 devices run, rather than latest.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* ci: test on Python 3.13, the version that ships
Debian 13 devices run Python 3.13.5 and the api/controller/help images are
python:3.13-trixie, but CI ran 3.11 (and one job on 3.12), so the runtime
under test was two minors behind the runtime in production.
Verified on 3.13 before switching: backend 1712 passed, 5 skipped; controller
883 passed. The only new warning is SQLAlchemy 1.3.22 reaching Python's
deprecated datetime.utcfromtimestamp(), which is noise rather than a break.
Drop api-tests-3-12: no workflow referenced it, it duplicated api-tests, and
its "Sanic does not yet support 3.12" note is stale now that devices run 3.13.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The React /map page rendered blank (only pins) on fresh builds while the
standalone :8084 viewer worked. maplibre-gl's Web Worker (inlined into the
production main.js) threw `ReferenceError: a is not defined` in its setLayers
handler, so the worker's style-layer index never built and every vector
source's tiles failed to parse ("Cannot read properties of undefined
(reading '<source>')"). The unminified dev build was unaffected — it is a
production minification bug: Terser mangles the inlined worker into broken code.
Root cause: the "npm audit fix" in 62660529 (intended for shell-quote/
form-data/ws) regenerated the lockfile and dragged @babel/core 7.20.12 ->
7.29.7 (whole @babel core pipeline). Babel 7.29 transpiles the maplibre worker
differently; CRA's (unchanged) Terser mangle pass then breaks it. Proven on
10.0.0.8: rebuilding the minified prod bundle with @babel 7.20 renders the map
(0 worker errors); rebuilding back to 7.29 breaks it again with the identical
bundle hash. The transform plugins were never bumped, so pinning @babel/core
alone is insufficient — the core AST pipeline (traverse/generator/types/
helpers) must be pinned too.
Fix: pin the 16 @babel packages that differ from the last-good tree
(6b7bd079) via package.json overrides, keeping the shell-quote/form-data/ws
security fixes. Frontend jest suite passes (810/810).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Applies non-breaking transitive lockfile bumps for three still-present
advisories on the app dependency tree:
* shell-quote 1.8.0 -> 1.10.0 (critical: unescaped newlines in quote())
* form-data 3.0.4 -> 3.0.5 / 4.0.4 -> 4.0.6 (high: CRLF injection via
unescaped multipart field names/filenames)
* ws 7.5.10 -> 7.5.12 / 8.18.3 -> 8.21.1 (high: memory-exhaustion DoS)
package-lock.json only; no package.json / direct-dependency changes. Runs
`npm audit fix` without --force, so no breaking dev-chain (react-scripts)
upgrades are pulled in. Verified on trixie/Node 20: jest 760/760 and the
production build both green.
Supersedes the stale dependabot PRs #487 (ws), #486 (form-data), and
equal-or-newer patched versions.
Co-authored-by: Roland <roland.henrie@o2x.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Browser-based ESP32/ESP8266 flasher (esptool-js + Web Serial); nothing is
uploaded to WROLPi, the flash is written straight to the device over USB.
Firmware sources (themed, tabbed): a saved set, the WROLPi media directory,
or a local file. Local files can be dropped anywhere on the page, which jumps
to the "Add from computer" dropzone. The media picker searches .bin files by
path and can detect the connected chip to filter firmware to matching images.
- Indexed file_group.suffix + path filter for fast .bin search (alembic
migration backfills via split_path_stem_and_suffix)
- modules/flasher: chip detection from ESP image headers, /search endpoint,
and flasher.yaml saved configurations (/configs CRUD)
- Flash progress modal with ETA, connect timeout, boot-mode hint, auto-
scrolling logs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>