Commit Graph

5368 Commits

Author SHA1 Message Date
Kristjan ESPERANTO
79ea2633a7 refactor: rewrite Module as an ES6 class (#4151)
This PR rewrites `module.js` to use a native ES6 `class` instead of
`Class.extend()` - the same old inheritance helper that was removed from
`node_helper.js` in #4147.

The normal module API stays the same: modules still use
`Module.register({...})`. Internally, `Module.create()` now creates a
named subclass for each module, copies over a cloned definition, and
only calls `init()` when it is actually a function.

Outcome: one less file in the browser bundle, no more magic `_super()`
wiring, better stack traces, and tests for the module creation path that
did not exist before. `module.js` is also now a plain class with no
external dependencies - an intentional step towards `export default
Module` when browser scripts eventually move to native ES modules.

Since these changes touch the core of how modules are created, I'd
appreciate a close review and any feedback on edge cases I may have
missed.
2026-05-12 20:01:12 +02:00
dependabot[bot]
b38c7b7aa2 Bump actions/dependency-review-action from 4 to 5 (#4152)
Bumps
[actions/dependency-review-action](https://github.com/actions/dependency-review-action)
from 4 to 5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/dependency-review-action/releases">actions/dependency-review-action's
releases</a>.</em></p>
<blockquote>
<h2>5.0.0</h2>
<p>This is a new major version of the Dependency Review Action which
updates the runtime to node24. This requires a minimum Actions Runner
version <a
href="https://github.com/actions/runner/releases/tag/v2.327.1">v2.327.1</a>
to run.</p>
<h2>What's Changed</h2>
<ul>
<li>Add .github/copilot-instructions.md for Copilot coding agent by <a
href="https://github.com/ahpook"><code>@​ahpook</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1067">actions/dependency-review-action#1067</a></li>
<li>Update Node.js runtime from 20 to 24 by <a
href="https://github.com/scottschreckengaust"><code>@​scottschreckengaust</code></a>
in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1084">actions/dependency-review-action#1084</a></li>
<li>Bump spdx-license-ids from 3.0.20 to 3.0.23 by <a
href="https://github.com/mongolyy"><code>@​mongolyy</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1091">actions/dependency-review-action#1091</a></li>
<li>docs: bump actions/checkout from v4 to v6 in workflow examples by <a
href="https://github.com/Marukome0743"><code>@​Marukome0743</code></a>
in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1077">actions/dependency-review-action#1077</a></li>
<li>fix: patched version display for advisories with non-strict semver
ranges (e.g. Maven beta versions) by <a
href="https://github.com/tspascoal"><code>@​tspascoal</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1076">actions/dependency-review-action#1076</a></li>
<li>Resolve security findings by <a
href="https://github.com/AshelyTC"><code>@​AshelyTC</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1094">actions/dependency-review-action#1094</a></li>
<li>v5.0.0 release branch by <a
href="https://github.com/ahpook"><code>@​ahpook</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1098">actions/dependency-review-action#1098</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/scottschreckengaust"><code>@​scottschreckengaust</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1084">actions/dependency-review-action#1084</a></li>
<li><a href="https://github.com/mongolyy"><code>@​mongolyy</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1091">actions/dependency-review-action#1091</a></li>
<li><a
href="https://github.com/Marukome0743"><code>@​Marukome0743</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1077">actions/dependency-review-action#1077</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/dependency-review-action/compare/v4.9.0...v5.0.0">https://github.com/actions/dependency-review-action/compare/v4.9.0...v5.0.0</a></p>
<h2>Dependency Review Action 4.9.0</h2>
<p>This feature release contains a couple of notable changes:</p>
<ul>
<li>There is a new configuration option
<code>show_patched_versions</code> which will add a column to the
output, showing the fix version of each vulnerable dependency. Thanks <a
href="https://github.com/felickz"><code>@​felickz</code></a>!</li>
<li>Runs which do not display OpenSSF scorecards no longer fetch
scorecard information; previously it was fetched regardless of whether
or not it was displayed, causing unneccessary slowness. Great catch <a
href="https://github.com/jantiebot"><code>@​jantiebot</code></a>!</li>
<li>There are a couple of fixes to purl parsing which should improve
match accuracy for <code>allow-package-dependency</code> lists,
including case (in)sensitivity and url-encoded namespaces Thanks <a
href="https://github.com/juxtin"><code>@​juxtin</code></a>!</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>Compare normalized purls to account for encoding quirks by <a
href="https://github.com/juxtin"><code>@​juxtin</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1056">actions/dependency-review-action#1056</a></li>
<li>Make purl comparisons case insensitive by <a
href="https://github.com/juxtin"><code>@​juxtin</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1057">actions/dependency-review-action#1057</a></li>
<li>Feat: Add <code>Patched Version</code> to
<code>Vulnerabilities</code> summary by <a
href="https://github.com/felickz"><code>@​felickz</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1045">actions/dependency-review-action#1045</a></li>
<li>fix: only get scorecard levels if user wants to see the OpenSSF
scorecard by <a
href="https://github.com/jantiebot"><code>@​jantiebot</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1060">actions/dependency-review-action#1060</a></li>
<li>Bump actions/stale from 10.1.0 to 10.2.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1058">actions/dependency-review-action#1058</a></li>
<li>Bump actions/checkout from 4 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1021">actions/dependency-review-action#1021</a></li>
<li>Updates for release 4.9.0 by <a
href="https://github.com/ahpook"><code>@​ahpook</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1064">actions/dependency-review-action#1064</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/jantiebot"><code>@​jantiebot</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1060">actions/dependency-review-action#1060</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/dependency-review-action/compare/v4.8.3...v4.9.0">https://github.com/actions/dependency-review-action/compare/v4.8.3...v4.9.0</a></p>
<h2>4.8.3</h2>
<h2>Dependency Review Action v4.8.3</h2>
<p>This is a bugfix release that updates a number of upstream
dependencies and includes a fix for the earlier feature that detected
oversized summaries and upload them as artifacts, which could
occasionally crash the action.</p>
<p>We have also updated the release process to use a long-lived
<code>v4</code> <strong>branch</strong> for the action, instead of a
force-pushed tag, which aligns better with git branching strategies; the
change should be transparent to end users.</p>
<h2>What's Changed</h2>
<ul>
<li>GitHub Actions can't push to our protected main by <a
href="https://github.com/dangoor"><code>@​dangoor</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1017">actions/dependency-review-action#1017</a></li>
<li>Bump actions/stale from 9.1.0 to 10.1.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/995">actions/dependency-review-action#995</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a1d282b36b"><code>a1d282b</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/dependency-review-action/issues/1098">#1098</a>
from actions/ahpook/v5-release</li>
<li><a
href="eb6c199c5a"><code>eb6c199</code></a>
update examples to show <a
href="https://github.com/v5"><code>@​v5</code></a></li>
<li><a
href="3943c2c5be"><code>3943c2c</code></a>
v5.0.0 release branch</li>
<li><a
href="454943c880"><code>454943c</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/dependency-review-action/issues/1094">#1094</a>
from actions/ashelytc/security-findings</li>
<li><a
href="6d92a1228e"><code>6d92a12</code></a>
revert <code>@​typescript-eslint/parser</code> update</li>
<li><a
href="a8e5a7e936"><code>a8e5a7e</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/dependency-review-action/issues/1076">#1076</a>
from tspascoal/fix-version-matching-for-non-string-s...</li>
<li><a
href="b6b7079031"><code>b6b7079</code></a>
update <code>@​typescript-eslint/parser</code> to 8.40.0</li>
<li><a
href="821a21dd69"><code>821a21d</code></a>
update more dependencies</li>
<li><a
href="05aaaae45c"><code>05aaaae</code></a>
run npm audit fix</li>
<li><a
href="55d3e75450"><code>55d3e75</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/dependency-review-action/issues/1077">#1077</a>
from Marukome0743/docs/checkout</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/dependency-review-action/compare/v4...v5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/dependency-review-action&package-manager=github_actions&previous-version=4&new-version=5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-12 19:13:34 +02:00
Karsten Hassel
170c4a9883 remove warning in unit tests (for nodejs >= v25) (#4149)
The unit tests were full of warnings `ExperimentalWarning: localStorage
is not available because --localstorage-file was not provided.` for node
versions >=25.

Additionally this PR nails node to `v26.0.0` in the automated-tests
workflow until the [playwright
issue](https://github.com/microsoft/playwright/issues/40724) is fixed.
Will revert this after fixed playwright version is available.
2026-05-09 23:00:16 +02:00
Kristjan ESPERANTO
2850e14172 Unify linting: replace Stylelint and markdownlint with ESLint (#4148)
We were running three separate lint tools (ESLint, Stylelint,
markdownlint-cli2) and a separate Prettier step. This PR consolidates
everything: `@eslint/css` and `@eslint/markdown` bring CSS and Markdown
linting into ESLint, so one tool now covers JS, CSS, and Markdown. The
Prettier step got merged into the lint scripts too, so there's now just
`test:lint` to check things and `lint:fix` to fix them.

Outcome: fewer tools to maintain, simpler/fewer scripts, fewer config
files.

As a side effect, the dependabot alerts
https://github.com/MagicMirrorOrg/MagicMirror/security/dependabot/140
and
https://github.com/MagicMirrorOrg/MagicMirror/security/dependabot/141
are resolved, as we no longer use the vulnerable `fast-uri` package
(which was a dependency of `stylelint`).
2026-05-09 12:01:18 +02:00
Kristjan ESPERANTO
67db41cfcb refactor: rewrite NodeHelper as an ES6 class (#4147)
This PR rewrites `node_helper.js` to use a native ES6 `class` instead of
`Class.extend()` - a manual inheritance helper from 2008, written back
when `class` syntax didn't exist yet. Node.js has supported native
classes since v6, so there's no reason to keep the workaround around.

The public API is unchanged - module authors still write the same
`NodeHelper.create({...})` they always have.

Outcome: same behavior, normal modern JavaScript, better stack traces,
and `node_helper.js` no longer pulls in `class.js` on the server side.
Removing `class.js` altogether is a follow-up (it's still used on
browser side).
2026-05-08 19:45:47 +02:00
Kristjan ESPERANTO
9386c44928 fix: resolve CodeQL alerts #24 and #26 (#4145)
**[#24](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/24)
– `js/class.js`**

`fnTest` works by serialising a function to a string and checking if
`"xyz"` appears in it - the function is never actually called. The bare
`xyz;` is never executed, so CodeQL is right to flag it. `return xyz;`
makes the intent clear. So this is purely a cosmetic change.


**[#26](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/26)
– `tests/e2e/helpers/global-setup.js`**

CodeQL flagged `if (exec) exec;` as a useless expression - and it was
right. But the real find was one level deeper.

`startApplication` hardcoded `const port = 8080`, so `MM_PORT` was
always overwritten before the app started. The test named "Set port 8100
on environment variable MM_PORT" was actually testing port 8080 the
whole time - it just happened to pass anyway.

Removed the dead `exec` parameter, made `startApplication` read
`MM_PORT` from the environment, and fixed the test so it actually checks
what it says it checks.
2026-05-07 00:28:02 +02:00
Kristjan ESPERANTO
7da9e5af02 perf(calendar): use async ICS parsing to avoid blocking event loop (#4143)
This switches the calendar fetcher from synchronous to asynchronous ICS
parsing.

It does not necessarily make parsing faster overall, but it avoids long
event-loop stalls with large calendar files (especially on slower
devices and with multiple feeds).

So this does not fully solve #4103 on its own, but it clearly mitigates
it.

It should also combine well with a future pre-filter approach discussed
in the issue:
- with pre-filter = less data to parse (future PR)
- with async parsing = less blocking while parsing (this PR)

Together, that is likely the strongest path to fully address #4103.
2026-05-06 20:40:44 +02:00
Karsten Hassel
842beef97d update eletron to v42 (#4144)
additional things:
- removed fix chrome-sandbox permissions (runs without)
- added `npx install-electron` which downloads the electron binaries

new in v42: 

> Electron now downloads its binary into node_modules dynamically on
first launch instead of running a postinstall script. Added the
install-electron script to manually trigger the download as well.
https://github.com/electron/electron/pull/49328
2026-05-06 20:25:37 +02:00
Kristjan ESPERANTO
67911546ed refactor(utils): drop ajv dependency (#4142)
This PR removes `ajv` from module position validation in `js/utils.js`
and replaces it with straightforward JavaScript checks. The old schema
only covered a few basic structure rules, so using `ajv` here felt
heavier than necessary. The updated validation keeps the same expected
behavior for valid configs and unknown-position warnings, but the code
path is now easier to read and easier to reason about when something
goes wrong.

Outcome: same validation behavior, one less dependency, less lines of
production code and better test coverage.
2026-05-06 19:11:47 +02:00
Kristjan ESPERANTO
c97d0947b6 fix(systeminformation): output right 'used node' version (from parent process) (#4141)
I noticed that in the System Information output, `used node` and
`installed node` were always identical when starting via Electron. That
usually shouldn't be the case. After digging into it, I found that since
PR #4002, `used node` in the subprocess effectively reported the system
Node version, not the parent process (which runs in Electron) version.

This PR fixes that by passing `used node` from the parent process and
logging it correctly.

**Before:**

`VERSIONS: electron: 41.3.0; used node: 26.0.0; installed node: 26.0.0;
...`

**After:**

`VERSIONS: electron: 41.3.0; used node: 24.15.0; installed node: 26.0.0;
...`
2026-05-06 19:10:57 +02:00
Karsten Hassel
461dbc04aa update dependencies and workflows to node v26 (#4140) 2026-05-06 09:15:42 +02:00
Andrés Vanegas Jiménez
b474198267 fix: skip postinstall git clean when not in a git repository (#4139)
## What does this PR accomplish?

The `postinstall` script runs `git clean -df fonts vendor
modules/default`
unconditionally. When `magicmirror` is installed as an npm dependency
(e.g. `npm install magicmirror` or as a transitive dep in another
project),
npm runs `postinstall` in a directory that is **not** a git repository.
This causes:

```
fatal: not a git repository (or any of the parent directories): .git
```

npm treats the non-zero exit as a failure → the entire installation
aborts
with `code 128`. This makes it impossible to use `magicmirror` as an npm
dependency in any third-party project.

The issue has been present since at least **v2.34.0**. In **v2.35.0**
`modules/default` was added to the clean targets, but the root cause
predates that change.

## Fix

Added `scripts/postinstall.js` — a cross-platform Node.js script that
guards the `git clean` call with a `git rev-parse --git-dir` check.

- When running inside a real git repository: behaviour is identical to
before.
- When running outside one (e.g. installed as an npm package): step is
silently skipped.

`package.json` `postinstall` updated from the bare `git clean` call to:

```json
"postinstall": "node scripts/postinstall.js"
```

This approach is cross-platform (Linux, macOS, Windows) and keeps the
logic readable rather than inlined as a one-liner.

Does this solve a related issue?

No existing issue tracked. Discovered while using magicmirror as a
devDependency in a companion tooling project — installation failed
unconditionally on all platforms.

Checklist

- Targets the develop branch
- No visual changes (script + package.json only)
- node --run lint:prettier run — no changes needed for .js script
2026-05-05 22:05:46 +02:00
Kristjan ESPERANTO
623e1e23d1 fix(updatenotification): fix ref diff parsing for fetch --dry-run (#4138)
The regex captured the full fetch output line including the branch name.
Before #4115, the command was built as a shell string, so the shell
split the arguments correctly. After #4115 switched to `execFile`, the
range and branch name were passed as a single argument
(`60e0377..332e429 develop`) - which git rejects as ambiguous.

The fix replaces the regex with column-based line parsing: find the line
for the current branch, take the first column. Falls back to
`<branch>..origin/<branch>` when fetch reports no changes (same behavior
as before).

Also adds unit tests for the new helper and corrects existing test
snapshots that encoded the broken behavior.

Fixes #4137.
2026-05-03 22:16:17 +02:00
Kristjan ESPERANTO
d20306cc4f fix(electron): resolve CodeQL alerts #22 and #25 in electron.js (#4136)
I reviewed the CodeQL alerts for `js/electron.js`:

-
[#25](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/25)
https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/25
-
[#22](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/22)
https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/22

Both point to real bugs.

-
[#25](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/25):
The window size fallback was written as a comma expression (`(800,
600)`), so it did not produce the expected object structure `{ width,
height }`. I am not surprised it went unnoticed because it sits in a
fallback path.
-
[#22](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/22):
`...new Set(electronSwitchesDefaults, config.electronSwitches)` silently
ignored the second parameter. As a result, custom `electronSwitches`
were never applied. I am wondering: this has been broken since PR #2643
introduced it, so I'm quite sure it could not have worked as intended in
that form. Why didn't anyone (not even @eouia) notice that? 🤔

## Changes

- Fix for
[#25](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/25):
- Corrects the fallback from `(800, 600)` to a valid size object `{
width: 800, height: 600 }`.
- Fix for
[#22](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/22):
  - Sets the default switch explicitly as a correct key-value pair:
- `app.commandLine.appendSwitch("autoplay-policy",
"no-user-gesture-required")`
  - Applies custom `config.electronSwitches` individually afterward.
2026-05-03 15:59:17 +02:00
Kristjan ESPERANTO
3335781af4 Remove unnecessary conditionals and fix falsy property check in imperial conversion (#4135)
While removing unnecessary conditionals (for `compliments` reported in
https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/27
and for `weather` reported in
https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/28),
I noticed a bug in the imperial conversion path: falsy property checks
like `if (imperialWeatherObject.temperature)` silently skip the
conversion when the value is `0`. So at exactly 0 °C, the result would
show `0 °F` instead of the correct `32 °F`.

I wrote unit tests for `convertWeatherObjectToImperial` first, which
confirmed the bug, then fixed it by replacing the falsy checks with the
`in` operator. Which I also find more intuitive to read.

Weather APIs deliver floating point values, so hitting exactly `0.0`
might be rare in practice - more likely `0.1` or `-0.1`, which are
truthy and convert fine. That should explain why it went unnoticed by
users.
2026-05-03 12:49:34 +02:00
Kristjan ESPERANTO
f2759ad4f6 refactor(updatenotification): replace pm2 usage with node logic (#4134)
We can rely on PM2's native restart-on-exit behavior instead of the
programmatic pm2 API.

Fixes
https://github.com/MagicMirrorOrg/MagicMirror/security/dependabot/82 by
removing pm2.

Note: Originally this PR was intended to update pm2, but after
discussion, we decided to replace it instead. See the discussion below.
2026-05-03 09:51:08 +02:00
Kristjan ESPERANTO
0905d66a91 polish HTTP 304 docs/test/handling (#4129)
After #4120 was merged, here's a bit of polish on top.

- docs: Optimize JSDoc and comments.
- test: The existing 304 test is moved to the dedicated status code
block and extended with an `errorSpy` to also verify that no `error`
event is emitted.
- refactor: The success branch is moved before the error branch in
`fetch()`. This is more intuitive.

The bottom line is that this PR improves maintainability. For users,
this PR doesn't change anything.
2026-05-01 00:02:01 +02:00
Karsten Hassel
01ae1b443d update version in package.json 2026-04-30 22:55:47 +02:00
Karsten Hassel
99a37ad818 Merge remote-tracking branch 'origin/master' into develop 2026-04-30 22:39:06 +02:00
Karsten Hassel
8dbee62730 Prepare Release 2.36.0 (#4126) 2026-04-30 22:26:57 +02:00
Karsten Hassel
5a82c9d132 update dependencies (#4124) 2026-04-29 06:32:24 +02:00
Kristjan ESPERANTO
8af3d02cbb fix(weather): use nearest openmeteo hourly data (#4123)
Open-Meteo updates `current_weather` every 15 minutes, but the hourly
array only has entries at full hours. The old code did an exact
timestamp match - so at 14:15, 14:30 or 14:45 it never found a match and
silently fell back to index 0, showing midnight values for humidity,
feels-like temp, precipitation, etc.

Fix: `findLastIndex((hour) => hour.time <= currentMs)` - the last hourly
entry at or before the current time.

While fixing the bug I found several dead branches left over from the
#4032 refactor: a path for pre-transposed hourly data that
`#parseWeatherApiResponse` makes unreachable, an `Array.isArray` guard
that's always true, and a `Log.debug` inside the dead branch. Removing
those accounts for most of the ~40 deleted lines - the actual fix is one
line.

Fixes #4122.
2026-04-28 21:42:38 +02:00
Kristjan ESPERANTO
4c2a373ae3 fix(weather): avoid loading state after reconnect (#4121)
When a client reconnects while the backend is still in its rate-limit
protection phase, the weather module has no data to show and stays on
`Loading...` until the next scheduled API call. This mainly affects
server mode setups, where the server keeps running while a remote client
temporarily loses its connection and reloads. It was [raised in the
forum](https://forum.magicmirror.builders/topic/20218/request-loop-loading...-in-standard-weather-module-open-meteo-after-update/11?_=1777106416020)
and is worthy of a fix to improve the user experience.

With this PR the node helper caches the last successful `WEATHER_DATA`
payload per instance and replays it immediately on reconnect. The client
gets its last known state right away instead of waiting for the next
fetch. The cache is cleaned up when the provider stops.

Tests are included to cover reconnect with and without cached data, and
the cleanup path.
2026-04-27 23:15:01 +02:00
Sonny B
b8548f2203 Allow HTTPFetcher to pass through 304 responses (#4120)
## Summary

This updates `js/http_fetcher.js` so HTTP `304 Not Modified` responses
are emitted through the normal `response` event instead of being treated
as errors.

That aligns the core fetcher with the built-in `yr` weather provider in
`v2.35.0`, which already includes explicit logic to handle `304` and
reuse cached data.

Closes #4119.

## Root cause

`HTTPFetcher` currently treats all non-OK responses as errors. Since
`304` is not an OK response, it never reaches providers listening on the
`response` event.

At the same time, `defaultmodules/weather/providers/yr.js` expects to
receive `304` and handle it by reusing cached data.

## What changed

- special-case HTTP `304` in `HTTPFetcher`
- emit `304` through the normal `response` event
- preserve the existing error path for other non-OK statuses

## Validation

- confirmed `defaultmodules/weather/providers/yr.js` already expects
`response.status === 304`
- compared the unpatched `v2.35.0` `HTTPFetcher` behavior to the
provider logic
- ran `node --check js/http_fetcher.js`

Co-authored-by: sonnyb9 <sonnyb9@users.noreply.github.com>
2026-04-27 23:09:20 +02:00
Kristjan ESPERANTO
9dc4b7a88f fix(updatenotification): harden git command execution + simplify checkUpdates (#4115)
This fixes CodeQL alert
[#16](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/16)
by replacing shell-built git commands with `execFile` + `cwd` in
updatenotification’s `git_helper`.

It also includes a small cleanup in `checkUpdates()` (remove unnecessary
async/Promise wrapper) and updates the related unit tests.
2026-04-27 23:06:29 +02:00
Kristjan ESPERANTO
7e1286257c fix(http-fetcher): fall back to reloadInterval after retries exhausted (#4113)
As reported in #4109, the weather module retries much more frequently
than expected after network errors. #4092 already fixed the main cause
(duplicate fetchers), but the backoff logic in `HTTPFetcher` still has a
gap: once retries are exhausted, `calculateBackoffDelay` keeps returning
a short fixed delay (60s) instead of falling back to `reloadInterval`.
The same problem existed for 5xx errors, where the delay grew to 8× the
configured interval.

Inspired by #4110 (thanks @CodeLine9), this PR makes both error paths
fall back to `reloadInterval` after retries are exhausted. I also
simplified the catch block, extracted a `#shortenUrl()` helper for log
messages, and added tests for the backoff progression.
2026-04-27 23:01:42 +02:00
Kristjan ESPERANTO
3f2a0302eb ci(actions): set explicit token permissions (#4114)
Add minimal GITHUB_TOKEN permissions to electron-rebuild workflow to
enforce least privilege and satisfy CodeQL alert
[#14](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/14).
2026-04-19 19:36:39 +02:00
Kristjan ESPERANTO
de3f57f8af weather: fix UV index display and add WeatherFlow precipitation (#4108)
While looking at the WeatherFlow provider (to evaluate #4107), I noticed
a few things that weren't quite right.

1. **UV index was broken for most providers in forecast/hourly views.**
The templates read `uv_index`, but only the WeatherAPI provider actually
wrote that key. All other providers (OpenWeatherMap, WeatherFlow,
PirateWeather, etc.) use `uvIndex` - so UV was silently never displayed
for them. This went unnoticed because `showUVIndex` defaults to `false`
and there were no test assertions for it. Standardized everything on
`uvIndex` and added test coverage.

2. **WeatherFlow didn't map precipitation for current weather.** The API
provides `precip_accum_local_day` and `precip_probability`, but they
weren't passed through. While adding them I also noticed the template
used truthiness checks, which hid valid zero values. Fixed both.

3. **`||` vs `??` in WeatherFlow provider.** Several numeric fields used
`|| null`, replacing valid `0` with `null`. Switched to `??` for
correctness.
2026-04-19 12:50:20 +02:00
Karsten Hassel
d3a3ad9caf config endpoint must handle functions in module configs (#4106)
Fixes #4105

```bash
In JavaScript, standard JSON does not support functions.
If you use JSON.stringify() on an object containing functions,
those functions will be omitted (if they are object properties)
or changed to null (if they are in an array).
```

---------

Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com>
2026-04-12 15:50:00 -05:00
Karsten Hassel
61870ae623 fix replaceSecretPlaceholder (#4104)
Disable secret substitution only for cors=allowAll, the last attempt in
#4102 was to restrictive.

Secret substitution should also work if cors=disabled.

---------

Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com>
2026-04-12 00:46:10 +02:00
Karsten Hassel
39578d69b4 restrict replaceSecretPlaceholder to cors with allowWhitelist (#4102)
and cleanup spelling
2026-04-10 23:11:57 +02:00
Kristjan ESPERANTO
22a58d4ae5 fix(weather): restore OpenWeatherMap v2.5 support (#4101)
After the big weather refactor (#4032), OpenWeatherMap was effectively
hard-wired to One Call v3. One Call 2.5 is deprecated and no longer
available, so it looked like v2.5 support was effectively over — but the
classic `/weather` and `/forecast` endpoints were never actually
dropped. This restores support for those.

Fixes #4100.

## What this PR does
- handles OpenWeatherMap responses by endpoint again (`/onecall`,
`/weather`, `/forecast`)
- restores v2.5 current and forecast support (including hourly via
3-hour forecast slots)
- filters outdated hourly entries centrally while keeping the current
hour visible (if available)

## Screenshot
<img width="768" height="481" alt="bildo"
src="https://github.com/user-attachments/assets/9bce3531-3731-4fd7-b41e-e20603afa725"
/>
2026-04-10 22:20:41 +02:00
Kristjan ESPERANTO
2e97e29ab5 fix(http_fetcher): use undici.fetch when dispatcher is present (#4097)
### What's the problem?

The `selfSignedCert` option passes an undici `Agent` as `dispatcher` to
`fetch()`. But Node's built-in `fetch()` and undici@8's `Agent` use
different internal handler APIs - passing them together throws:

```
invalid onRequestStart method
```

### What's the fix?

When `selfSignedCert` is enabled (i.e. a `dispatcher` is set), use
undici's own `fetch()` instead of the global one. For all other
requests, keep using `globalThis.fetch`.

```js
const fetchFn = requestOptions.dispatcher ? undiciFetch : globalThis.fetch;
```

### Why not just always use undici's fetch?

That would fix the crash - but it would break some tests. MSW (Mock
Service Worker), which is used in our test suite to intercept HTTP
requests, only hooks into `globalThis.fetch`. Undici's fetch bypasses
those interceptors entirely, so tests would start making real network
requests instead of getting the mocked responses. We could rewrite all
tests to use undici-compatible mocking instead - but that would be a
massive change for no real benefit.

----

Fixes #4093
2026-04-08 18:42:30 +02:00
Kristjan ESPERANTO
d8c29d5ec3 fix: prevent crash when config is undefined in socket handler (#4096)
If a module uses this.io.of() to register a custom socket.io namespace,
connections on that namespace trigger the onAny handler in setSocketIO
before config is set, causing a TypeError.

Fixes #4089
2026-04-07 21:15:25 +02:00
Karsten Hassel
9b97add1ae fix cors function for alpine linux (#4091)
I provide docker images with alpine linux and tested the new cors
approach.

It didn't work because after calling

```js
  const dispatcher = new Agent({ connect: { lookup: (_h, _o, cb) => cb(null, address, family) } });
```

the dispatcher variable was undefined.

This PR solves this and I tested this under debian too.

The mix of internal fetch and newer undici did not work and alpine needs
additionally the `process.nextTick`.

---------

Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com>
2026-04-05 21:42:32 +02:00
Kristjan ESPERANTO
0da343b961 fix(weather): use stable instanceId to prevent duplicate fetchers (#4092)
During the server-side migration (PR #4032), the `instanceId` was built
with `Date.now()`, making it unique per client reload. This approach was
fine in the old browser-based architecture where reloads cleaned up
everything automatically. But now the node_helper persists across
reloads, so each reconnect created a new `HTTPFetcher` while the old one
kept polling - silently multiplying API calls over time.

The fix is simple: use `this.identifier` as the `instanceId`, which is
already stable and unique per module slot. This is the same approach the
Calendar module uses.

On the node_helper side, when a provider already exists for the same
`instanceId`, we now skip re-creation and just resend
`WEATHER_INITIALIZED` so the client picks up where it left off.

Fixes https://forum.magicmirror.builders/topic/20199
2026-04-05 21:42:02 +02:00
Kristjan ESPERANTO
96c18ec8b0 fix(cors): prevent SSRF via DNS rebinding (#4090)
PR #4084 blocked SSRF by checking the IP before `fetch()` — but
`fetch()` resolves DNS again on its own. With DNS rebinding (TTL=0,
alternating IPs) an attacker can slip a private IP through between check
and connection.

Fix: resolve DNS once, validate, pin the validated IP for the
connection.

No second DNS query → no rebinding window. `isPrivateTarget()` is gone,
code is shorter than before.

Not a likely attack for a typical MagicMirror setup, but it doesn't add
complexity so there's no reason not to close the gap.
2026-04-04 20:40:14 +02:00
Kristjan ESPERANTO
f079324426 ci(codeql): also scan develop branch on push and PR (#4086)
Previously only master was scanned via the default CodeQL setup. Since
development happens on develop, this PR replaces the default setup with
a custom workflow that covers both branches. This gives an overview of
the security status across the current release (master) and the
development branch (develop).

As a result we should also see issues in the develop branch here:
https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning
2026-04-04 16:39:53 +02:00
Kristjan ESPERANTO
512e7461e4 chore: update dependencies (#4088)
`node-ical` has released a new version that fixes an issue reported by
one of our users (https://github.com/jens-maus/node-ical/issues/495).
I'd like to have that in the develop branch. Instead of just updating
that, I think it would make sense to include the others as well.

`undici` had a major release but according to the release notes we are
not affected.
2026-04-04 15:01:27 +02:00
Karsten Hassel
abf6989faa add option to disable or restrict cors endpoint (#4087) 2026-04-04 11:55:13 +02:00
Kristjan ESPERANTO
03f268934a fix: prevent SSRF via /cors endpoint by blocking private/reserved IPs (#4084)
Resolve target hostname before proxying and reject any address that is
not globally routable (loopback, RFC 1918, link-local, etc.) using
ipaddr.js and dns.lookup().
2026-04-03 17:57:26 +02:00
Kristjan ESPERANTO
dce2df63af refactor: replace implicit global config with explicit global.config (#4085)
In PR #4072 the GitHub bot complained about a missing variable
declaration for `config` in `app.js` and suggested adding `let config`.
Applying that suggestion broke the app because `server_functions.js` was
accessing `config` as an implicit global variable - the `let`
declaration made it unreachable from there.

So instead of the `let` declaration, I replaced all bare `config`
references with explicit `global.config`. This makes the dependency on
the global variable visible without changing runtime behavior,
consistent with how other globals like `global.root_path` and
`global.version` are already handled throughout the codebase.

Related to #4073
2026-04-03 17:56:11 +02:00
Kristjan ESPERANTO
19c6489e6f fix(newsfeed): prevent duplicate parse error callback when using pipeline (#4083)
In PR #4072 GitHub Bot complained that `newsfeedfetcher.js` used the old
`.pipe()` method to connect streams (HTTP body → iconv decoding → RSS
parser). `.pipe()` has a weakness: errors in one stream are **not**
automatically forwarded to downstream streams. An I/O or decoding error
would silently disappear.

## Solution

Replaced `.pipe()` with `await stream.promises.pipeline()`. The
`pipeline()` API is designed to propagate errors correctly through the
entire chain and to clean up all streams on failure. Errors now reliably
land in the `catch` block and call `fetchFailedCallback` exactly once.
The redundant `parser.on("error")` handler was removed, as it would have
caught the same error again and called the callback a second time.

## Why not the bot's suggested fix?

The GitHub Bot suggested the older callback-based
`stream.pipeline(callback)` variant:

```js
stream.pipeline(nodeStream, iconv.decodeStream(...), parser, (error) => {
    if (!error) return;
    // error handling...
});
```

This has two drawbacks compared to my approach:

1. It uses the older callback style — `stream.promises.pipeline()` with
`async/await` is the modern, more readable API.
2. The bot's suggestion kept both the `parser.on("error")` handler
**and** the `catch` block, which would not have fixed the
double-callback problem.

----

Related to #4073
2026-04-03 12:09:24 +02:00
cgillinger
d2304af331 SMHI: migrate to SNOW1gv1 API (replace deprecated PMP3gv2) (#4082)
## Summary
This PR migrates the SMHI weather provider from the deprecated PMP3gv2
API to the new SNOW1gv1 API.

The old API (pmp3g/version/2) started returning HTTP 404 on 2026-03-31.

## Changes
- Updated API endpoint:
  - `pmp3g/version/2` → `snow1g/version/1`
- Updated response parsing:
  - `validTime` → `time`
  - `parameters[]` → `data` (flat structure)
- Updated parameter names:
  - `t` → `air_temperature`
  - `ws` → `wind_speed`
  - etc.
- Updated precipitation handling to match new
`predominant_precipitation_type_at_surface`
- Updated coordinate parsing (flat `[lon, lat]`)
- Added missing value handling (`9999 → null`)

## Notes
- Maintains backward compatibility for `precipitationValue` config
- No UI changes
- Symbol mapping unchanged (same codes 1–27)

## Testing
- Verified against live SMHI SNOW1gv1 API responses
- Confirmed old API returns HTTP 404

## Impact
Fixes broken SMHI provider due to API deprecation.
2026-04-03 11:04:09 +02:00
Kristjan ESPERANTO
8e1630e8bf refactor: enable ESLint rule "no-unused-vars" and handle related issues (#4080)
In PR #4072 GitHub Bot complained about an unused var. Instead of just
removing that one, I checked why ESLint hadn't complained about it: We
had disabled the rule for it.

So I enabled rule and resolved the issues that ESLint then detected.

Related to #4073
2026-04-02 08:56:27 +02:00
Kristjan ESPERANTO
32aa5c8721 chore: add permissions section to enforce pull-request rules workflow (#4079)
GitHub Bot complained about that in PR #4072.

Related to #4073
2026-04-01 21:55:27 +02:00
Kristjan ESPERANTO
d7c4ad178e fix(tests): correct import path for git_helper module in updatenotification tests (#4078)
This removes a warning which appeared running a test:

```bash
$ npx vitest --run tests/unit/functions/updatenotification_spec.js

 RUN  v4.1.2

7:46:46 PM [vite] (ssr) warning: invalid import "../../../${defaults.defaultModulesDir}/updatenotification/git_helper". A file extension must be included in the static part of the import. For example: import(`./foo/${bar}.js`).
```

Related to #4073
2026-04-01 20:09:04 +02:00
Karsten Hassel
751b97fc7e update version for develop 2026-04-01 12:40:11 +02:00
Karsten Hassel
d05ea751d9 Release 2.35.0 (#4072)
## Release Notes
Thanks to: @angeldeejay, @in-voker, @JHWelch, @khassel,
@KristjanESPERANTO, @rejas, @sdetweil
> ⚠️ This release needs nodejs version >=22.21.1 <23 || >=24 (no change
to previous release)

[Compare to previous Release
v2.34.0](https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.34.0...v2.25.0)

> ⚠️ We introduced some internal changes with this release, please read
[this forum
post](https://forum.magicmirror.builders/topic/20138/upcoming-release-april-1-2026-breaking-changes-some-operational-changes)
before upgrading!

### [core]
- Prepare Release 2.35.0 (#4071)
- docs: add security policy and vulnerability reporting guidelines
(#4069)
- refactor: simplify internal `require()` calls (#4056)
- allow environment variables in cors urls (#4033)
- fix cors proxy getting binary data (e.g. png, webp) (#4030)
- fix: correct secret redaction and optimize loadConfig (#4031)
- change loading config.js, allow variables in config.js and try to
protect sensitive data (#4029)
- remove kioskmode (#4027)
- Add dark theme logo (#4026)
- move custom.css from css to config (#4020)
- move default modules from /modules/default to /defaultmodules (#4019)
- update node versions in workflows (#4018)
- [core] refactor: extract and centralize HTTP fetcher (#4016)
- fix systeminformation not displaying electron version (#4012)
- Update node-ical and support it's rrule-temporal changes (#4010)
- Change default start scripts from X11 to Wayland (#4011)
- refactor: unify favicon for index.html and Electron (#4006)
- [core] run systeminformation in subprocess so the info is always
displayed (#4002)
- set next release dev number (#4000)

### [dependencies]
- update dependencies (#4068)
- update dependencies incl. electron to v41 (#4058)
- chore: upgrade ESLint to v10 and fix newly surfaced issues (#4057)
- chore: update ESLint and plugins, simplify config, apply new rules
(#4052)
- chore: update dependencies + add exports, files, and sideEffects
fields to package.json (#4040)
- [core] refactor: enable ESLint rule require-await and handle detected
issues (#4038)
- Update node-ical and other deps (#4025)
- chore: update dependencies (#4021)
- chore(eslint): migrate from eslint-plugin-vitest to
@vitest/eslint-plugin and run rules only on test files (#4014)
- Update deps as requested by dependabot (#4008)
- update Collaboration.md and dependencies (#4001)

### [logging]
- refactor: further logger clean-up (#4050)
- Fix Node.js v25 logging prefix and modernize logger (#4049)

### [modules/calendar]
- fix(calendar): make showEnd behavior more consistent across time
formats (#4059)
- test(calendar): fix hardcoded date in event shape test (#4055)
- [calendar] refactor: delegate event expansion to node-ical's
expandRecurringEvent (#4047)
- calendar.js: remove useless hasCalendarURL function (#4028)
- fix(calendar): update to node-ical 0.23.1 and fix full-day recurrence
lookup (#4013)
- fix(calendar): correct day-of-week for full-day recurring events
across all timezones (#4004)

### [modules/newsfeed]
- fix(newsfeed): fix full article view and add framing check (#4039)
- [newsfeed] refactor: migrate to centralized HTTPFetcher (#4023)

### [modules/weather]
- fix(weather): fix openmeteo forecast stuck in the past (#4064)
- fix(weather): fix weathergov forecast day labels off by one (#4065)
- weather: fixes for templates (#4054)
- weather: add possibility to override njk's and css (#4051)
- Use getDateString in openmeteo (#4046)
- [weather] refactor: migrate to server-side providers with centralized
HTTPFetcher (#4032)
- [weather] feat: add Weather API Provider  (#4036)

### [testing]
- chore: remove obsolete Jest config and unit test global setup (#4044)
- replace template_spec test with config_variables test (#4034)
- refactor(clientonly): modernize code structure and add comprehensive
tests (#4022)
- Switch to undici Agent for HTTPS requests (#4015)
- chore: migrate CI workflows to ubuntu-slim for faster startup times
(#4007)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com>
Co-authored-by: Bugsounet - Cédric <github@bugsounet.fr>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: sam detweiler <sdetweil@gmail.com>
Co-authored-by: Veeck <github@veeck.de>
Co-authored-by: veeck <gitkraken@veeck.de>
Co-authored-by: Magnus <34011212+MagMar94@users.noreply.github.com>
Co-authored-by: Ikko Eltociear Ashimine <eltociear@gmail.com>
Co-authored-by: DevIncomin <56730075+Developer-Incoming@users.noreply.github.com>
Co-authored-by: Nathan <n8nyoung@gmail.com>
Co-authored-by: mixasgr <mixasgr@users.noreply.github.com>
Co-authored-by: Savvas Adamtziloglou <savvas-gr@greeklug.gr>
Co-authored-by: Konstantinos <geraki@gmail.com>
Co-authored-by: OWL4C <124401812+OWL4C@users.noreply.github.com>
Co-authored-by: BugHaver <43462320+bughaver@users.noreply.github.com>
Co-authored-by: BugHaver <43462320+lsaadeh@users.noreply.github.com>
Co-authored-by: Koen Konst <koenspero@gmail.com>
Co-authored-by: Koen Konst <c.h.konst@avisi.nl>
Co-authored-by: dathbe <github@beffa.us>
Co-authored-by: Marcel <m-idler@users.noreply.github.com>
Co-authored-by: Kevin G. <crazylegstoo@gmail.com>
Co-authored-by: Jboucly <33218155+jboucly@users.noreply.github.com>
Co-authored-by: Jboucly <contact@jboucly.fr>
Co-authored-by: Jarno <54169345+jarnoml@users.noreply.github.com>
Co-authored-by: Jordan Welch <JordanHWelch@gmail.com>
Co-authored-by: Blackspirits <blackspirits@gmail.com>
Co-authored-by: Samed Ozdemir <samed@xsor.io>
Co-authored-by: in-voker <58696565+in-voker@users.noreply.github.com>
Co-authored-by: Andrés Vanegas Jiménez <142350+angeldeejay@users.noreply.github.com>
v2.35.0
2026-04-01 12:35:16 +02:00
Karsten Hassel
b6f2876e96 Merge branch 'master' into develop 2026-04-01 12:15:15 +02:00