Compare commits

...
10 Commits
Author SHA1 Message Date
Zaid Humaimidi 38e9dfd0e6 1.2.16 2026-03-29 13:59:06 -07:00
Zaid Humaimidi b6102bddbc Changelog for v1.2.16 2026-03-29 13:58:40 -07:00
Zaid Humaimidi e22b0c499e Update npm-shrinkwrap.json 2026-03-29 13:35:03 -07:00
Zaid Humaimidi 969dfaf943 Revert "Fix device ID parsing regression in Mithril 2.3.8"
This reverts commit f9ee39dfd0.
2026-03-29 12:58:36 -07:00
Zaid Humaimidi fb7e973d8b Revert Mithril to v2.2.2
Revert Mithril from v2.3.8 back to v2.2.2 (used in v1.2.13) to fix
regression bugs in the latest version that have not been resolved.
2026-03-29 12:57:58 -07:00
Zaid Humaimidi 944a690d96 1.2.15 2026-03-26 14:12:26 -07:00
Zaid Humaimidi e7facc3bba Changelog for v1.2.15 2026-03-26 14:10:18 -07:00
Zaid Humaimidi aa83b62064 Update dependencies 2026-03-26 13:50:37 -07:00
Zaid Humaimidi f9ee39dfd0 Fix device ID parsing regression in Mithril 2.3.8
Device IDs containing literal % characters (e.g., "202BC1-CODA%2D5519-000000")
are double-encoded in URLs. Mithril 2.3.8 incorrectly double-decodes route
parameters, corrupting device IDs with special characters.

Workaround: Extract device ID directly from window.location.hash and decode
once to get the correct value.

See: https://github.com/MithrilJS/mithril.js/issues/3064
2026-03-26 13:20:19 -07:00
Zaid Humaimidi 087b95388f Fix RCE vulnerability in /api/ping endpoint
This addresses a follow-up to CVE-2021-46704 where the host validation
could be bypassed via domainToASCII stripping path components.

Changes:
- Normalize host with domainToASCII before validation
- Add round-trip check with domainToUnicode to catch path injection
- Switch from exec to execFile to prevent shell injection
- Add -- separator to ping arguments

Credit: JuHwiSang for reporting this vulnerability
2026-03-25 15:00:00 -07:00
4 changed files with 795 additions and 740 deletions

No files matched your search

+14
View File
@@ -1,5 +1,19 @@
# Change Log # Change Log
## 1.2.16 (2026-03-29)
- Fix regression in v1.2.15 that broke the device search component in the UI by
pinning Mithril dependency to v2.2.2 due to multiple unresolved regressions in
later versions.
## 1.2.15 (2026-03-26)
- Fix critical RCE vulnerability in /api/ping endpoint (follow-up to
CVE-2021-46704). Credit: JuHwiSang.
- Fix device IDs containing special characters (e.g., %) not being handled
correctly in the UI due to a regression in Mithril 2.3.8.
## 1.2.14 (2026-03-12) ## 1.2.14 (2026-03-12)
- Prevent UI crash when a malformed URL is sent to the server. - Prevent UI crash when a malformed URL is sent to the server.
+16 -7
View File
@@ -1,6 +1,6 @@
import { platform } from "node:os"; import { platform } from "node:os";
import { exec } from "node:child_process"; import { execFile } from "node:child_process";
import { domainToASCII } from "node:url"; import { domainToASCII, domainToUnicode } from "node:url";
export interface PingResult { export interface PingResult {
packetsTransmitted: number; packetsTransmitted: number;
@@ -16,9 +16,16 @@ function isValidHost(host: string): boolean {
// Valid chars in IPv4, IPv6, domain names // Valid chars in IPv4, IPv6, domain names
if (/^[a-zA-Z0-9\-.:[\]-]+$/.test(host)) return true; if (/^[a-zA-Z0-9\-.:[\]-]+$/.test(host)) return true;
const lowercase = host.toLowerCase();
const ascii = domainToASCII(lowercase);
// Check if input is an IDN convert to Punycode // Check if input is an IDN convert to Punycode
// Can't merge with above because domainToASCII doesn't accept IP addresses // Can't merge with above because domainToASCII doesn't accept IP addresses
return /^[a-zA-Z0-9\-.:[\]-]+$/.test(domainToASCII(host)); if (!/^[a-zA-Z0-9\-.:[\]-]+$/.test(ascii)) return false;
// Round-trip to ensure the original host matches its normalized form.
// This prevents bypasses where domainToASCII strips paths (e.g. "example.com/;cmd").
return lowercase === domainToUnicode(ascii);
} }
export function parsePing(osPlatform: string, stdout: string): PingResult { export function parsePing(osPlatform: string, stdout: string): PingResult {
@@ -73,24 +80,26 @@ export function ping(
): void { ): void {
// Validate input to prevent possible remote code execution // Validate input to prevent possible remote code execution
// Credit to Alex Hordijk for reporting this vulnerability // Credit to Alex Hordijk for reporting this vulnerability
// Credit to JuHwiSang for reporting a follow-up vulnerability
if (!isValidHost(host)) return callback(new Error("Invalid host")); if (!isValidHost(host)) return callback(new Error("Invalid host"));
host = host.replace("[", "").replace("]", ""); host = host.replace("[", "").replace("]", "");
let cmd: string; let args: string[];
switch (platform()) { switch (platform()) {
case "linux": case "linux":
cmd = `ping -w 1 -i 0.2 -c 3 ${host}`; args = ["-w", "1", "-i", "0.2", "-c", "3", "--", host];
break; break;
case "freebsd": case "freebsd":
// Send a single packet because on FreeBSD only superuser can send // Send a single packet because on FreeBSD only superuser can send
// packets that are only 200 ms apart. // packets that are only 200 ms apart.
cmd = `ping -t 1 -c 3 ${host}`; args = ["-t", "1", "-c", "3", "--", host];
break; break;
default: default:
return callback(new Error("Platform not supported")); return callback(new Error("Platform not supported"));
} }
exec(cmd, (err, stdout) => { // Use execFile instead of exec to avoid shell interpretation of the host
execFile("ping", args, (err, stdout) => {
if (err) return callback(err); if (err) return callback(err);
const parsed: PingResult = parsePing(platform(), stdout); const parsed: PingResult = parsePing(platform(), stdout);
return callback(err, parsed, stdout); return callback(err, parsed, stdout);
+760 -728
View File
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -1,6 +1,6 @@
{ {
"name": "genieacs", "name": "genieacs",
"version": "1.2.14", "version": "1.2.16",
"description": "A TR-069 Auto Configuration Server (ACS)", "description": "A TR-069 Auto Configuration Server (ACS)",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -34,7 +34,7 @@
"ipaddr.js": "^2.3.0", "ipaddr.js": "^2.3.0",
"jsonwebtoken": "^9.0.3", "jsonwebtoken": "^9.0.3",
"koa": "^2.16.4", "koa": "^2.16.4",
"koa-compress": "^5.2.0", "koa-compress": "^5.2.1",
"koa-jwt": "^4.0.3", "koa-jwt": "^4.0.3",
"koa-send": "^5.0.1", "koa-send": "^5.0.1",
"mongodb": "^4.16.0", "mongodb": "^4.16.0",
@@ -46,7 +46,7 @@
"@types/jsonwebtoken": "^9.0.10", "@types/jsonwebtoken": "^9.0.10",
"@types/koa": "^2.15.0", "@types/koa": "^2.15.0",
"@types/koa-compress": "^4.0.7", "@types/koa-compress": "^4.0.7",
"@types/mithril": "^2.2.7", "@types/mithril": "^2.2.6",
"@types/node": "^20.19.37", "@types/node": "^20.19.37",
"@types/parsimmon": "^1.10.9", "@types/parsimmon": "^1.10.9",
"@types/seedrandom": "^3.0.8", "@types/seedrandom": "^3.0.8",
@@ -56,12 +56,12 @@
"esbuild": "^0.27.4", "esbuild": "^0.27.4",
"eslint": "^8.57.1", "eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.2", "eslint-config-prettier": "^9.1.2",
"mithril": "^2.3.8", "mithril": "2.2.2",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"sql.js": "^1.14.1", "sql.js": "^1.14.1",
"svgo": "^3.3.3", "svgo": "^3.3.3",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"yaml": "^1.10.2" "yaml": "^1.10.3"
}, },
"engines": { "engines": {
"node": ">=12.13.0" "node": ">=12.13.0"