Compare commits

...
7 Commits
16 changed files with 3118 additions and 2925 deletions

No files matched your search

+7
View File
@@ -1,5 +1,12 @@
# Change Log
## 1.2.14 (2026-03-12)
- Prevent UI crash when a malformed URL is sent to the server.
- Fix potential edge-case bugs in expression evaluation and session
serialization.
## 1.2.13 (2024-06-06)
- Increase connection timeout for UI and NBI from 30 to 120 seconds to avoid
+4 -4
View File
@@ -2,13 +2,13 @@ import { PermissionSet, Expression } from "../types.ts";
import { evaluate, or } from "./expression/util.ts";
export default class Authorizer {
private declare permissionSets: PermissionSet[];
private declare validatorCache: WeakMap<
declare private permissionSets: PermissionSet[];
declare private validatorCache: WeakMap<
any,
(mutationType, mutation, any) => boolean
>;
private declare hasAccessCache: Map<string, boolean>;
private declare getFilterCache: Map<string, Expression>;
declare private hasAccessCache: Map<string, boolean>;
declare private getFilterCache: Map<string, Expression>;
public constructor(permissionSets: PermissionSet[]) {
this.permissionSets = permissionSets;
+3 -3
View File
@@ -9,8 +9,8 @@ const TWO = BI.BigInt(2);
const NEGATIVE_ONE = BI.BigInt(-1);
class Indeterminates {
public declare map: Map<string, number>;
public declare sortedKeys: string[];
declare public map: Map<string, number>;
declare public sortedKeys: string[];
public constructor(str?: string) {
this.map = new Map();
@@ -98,7 +98,7 @@ function findGcd(a: BI.bigint, b: BI.bigint): BI.bigint {
}
class Polynomial {
public declare terms: Term[];
declare public terms: Term[];
public constructor(terms: Term[]) {
this.terms = terms;
+1 -1
View File
@@ -955,7 +955,7 @@ export function covers(expr1: Expression, expr2: Expression): boolean {
expr2 = normalize(expr2);
if (!expr2) return true;
expr1 = normalize(expr1);
if (!Array.isArray(expr1)) return !!expr1;
if (!Array.isArray(expr1) && expr1) return true;
const synt1 = Clause.fromExpression(expr1);
const synt2 = Clause.fromExpression(expr2);
+3 -3
View File
@@ -1,9 +1,9 @@
import Path from "./path.ts";
export default class PathSet {
private declare lengthIndex: Set<Path>[];
private declare fragmentIndex: Map<string, Set<Path>>[];
private declare stringIndex: Map<string, Path>;
declare private lengthIndex: Set<Path>[];
declare private fragmentIndex: Map<string, Set<Path>>[];
declare private stringIndex: Map<string, Path>;
public constructor() {
this.lengthIndex = [];
+5 -5
View File
@@ -30,11 +30,11 @@ function legalChar(c: number): boolean {
}
export default class Path {
public declare readonly segments: Segments;
public declare readonly wildcard: number;
public declare readonly alias: number;
protected declare _string: string;
protected declare _stringIndex: number[];
declare public readonly segments: Segments;
declare public readonly wildcard: number;
declare public readonly alias: number;
declare protected _string: string;
declare protected _stringIndex: number[];
protected static parseAliasValue(
pattern: string,
+1 -1
View File
@@ -3,7 +3,7 @@ interface Instance {
}
export default class InstanceSet {
private declare set: Set<Instance>;
declare private set: Set<Instance>;
public constructor() {
this.set = new Set();
+15 -10
View File
@@ -1,10 +1,10 @@
import { constants } from "node:zlib";
import Koa from "koa";
import Router from "koa-router";
import Router from "@koa/router";
import * as jwt from "jsonwebtoken";
import koaStatic from "koa-static";
import koaSend from "koa-send";
import koaCompress from "koa-compress";
import koaBodyParser from "koa-bodyparser";
import koaBodyParser from "@koa/bodyparser";
import koaJwt from "koa-jwt";
import * as config from "./config.ts";
import api from "./ui/api.ts";
@@ -18,12 +18,6 @@ import { version as VERSION } from "../package.json";
import memoize from "./common/memoize.ts";
import { APP_JS, APP_CSS, FAVICON_PNG } from "../build/assets.ts";
declare module "koa" {
interface Request {
body: any;
}
}
const koa = new Koa();
const router = new Router();
@@ -262,6 +256,17 @@ koa.use(
);
koa.use(router.routes());
koa.use(koaStatic(config.ROOT_DIR + "/public"));
koa.use(async (ctx, next) => {
await next();
if (ctx.method !== "HEAD" && ctx.method !== "GET") return;
if (ctx.body != null || ctx.status !== 404) return;
if (/(?:^|[\\/])\.\.(?:[\\/]|$)/.test(ctx.path)) return;
try {
await koaSend(ctx, ctx.path, { root: config.ROOT_DIR + "/public" });
} catch (err) {
if (err.status !== 404) throw err;
}
});
export const listener = koa.callback();
+1 -1
View File
@@ -1,5 +1,5 @@
import { Readable } from "node:stream";
import Router from "koa-router";
import Router from "@koa/router";
import { ObjectId } from "mongodb";
import * as db from "./db.ts";
import * as apiFunctions from "../api-functions.ts";
+9 -5
View File
@@ -7,10 +7,10 @@ interface Revisions<V> {
}
export default class VersionedMap<K, V> {
private declare _sizeDiff: number[];
private declare _revision: number;
private declare map: Map<K, (V | symbol)[]>;
public declare dirty: number;
declare private _sizeDiff: number[];
declare private _revision: number;
declare private map: Map<K, (V | symbol)[]>;
declare public dirty: number;
public constructor() {
this._sizeDiff = [0];
@@ -112,6 +112,8 @@ export default class VersionedMap<K, V> {
prev = v;
}
if (prev === NONEXISTENT && !res.delete) return null;
return res;
}
@@ -122,6 +124,8 @@ export default class VersionedMap<K, V> {
del,
);
if (!mutations) return;
const revisions = [];
let prev: V | symbol = NONEXISTENT;
@@ -149,7 +153,7 @@ export default class VersionedMap<K, V> {
public *diff(): IterableIterator<[K, V, V]> {
for (const [key, revisions] of this.map) {
if (revisions.length === 1) continue;
if (revisions.length <= 1) continue;
let first = revisions[0];
let last = revisions[revisions.length - 1];
if (first === NONEXISTENT && last === NONEXISTENT) continue;
+3000 -2856
View File
File diff suppressed because it is too large. Load diff
+24 -26
View File
@@ -1,6 +1,6 @@
{
"name": "genieacs",
"version": "1.2.13",
"version": "1.2.14",
"description": "A TR-069 Auto Configuration Server (ACS)",
"repository": {
"type": "git",
@@ -26,43 +26,41 @@
},
"dependencies": {
"@breejs/later": "^4.2.0",
"@koa/bodyparser": "^5.1.2",
"@koa/router": "^13.1.1",
"bson": "^4.7.2",
"espresso-iisojs": "^1.0.8",
"iconv-lite": "^0.6.3",
"ipaddr.js": "^2.2.0",
"jsonwebtoken": "^9.0.2",
"koa": "^2.15.3",
"koa-bodyparser": "^4.3.0",
"koa-compress": "^5.1.0",
"ipaddr.js": "^2.3.0",
"jsonwebtoken": "^9.0.3",
"koa": "^2.16.4",
"koa-compress": "^5.2.0",
"koa-jwt": "^4.0.3",
"koa-router": "^12.0.0",
"koa-static": "^5.0.0",
"koa-send": "^5.0.1",
"mongodb": "^4.16.0",
"parsimmon": "^1.18.1",
"seedrandom": "^3.0.5"
},
"devDependencies": {
"@types/codemirror": "^5.60.15",
"@types/jsonwebtoken": "^9.0.6",
"@types/codemirror": "^5.60.17",
"@types/jsonwebtoken": "^9.0.10",
"@types/koa": "^2.15.0",
"@types/koa-compress": "^4.0.6",
"@types/koa-router": "^7.4.8",
"@types/koa-static": "^4.0.4",
"@types/mithril": "^2.2.6",
"@types/node": "^20.14.2",
"@types/koa-compress": "^4.0.7",
"@types/mithril": "^2.2.7",
"@types/node": "^20.19.37",
"@types/parsimmon": "^1.10.9",
"@types/seedrandom": "^3.0.8",
"@typescript-eslint/eslint-plugin": "^7.12.0",
"@typescript-eslint/parser": "^7.12.0",
"codemirror": "^5.65.16",
"esbuild": "^0.21.4",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"mithril": "^2.2.2",
"prettier": "^3.3.1",
"sql.js": "^1.10.3",
"svgo": "^3.3.2",
"typescript": "^5.4.5",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"codemirror": "^5.65.21",
"esbuild": "^0.27.4",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.2",
"mithril": "^2.3.8",
"prettier": "^3.8.1",
"sql.js": "^1.14.1",
"svgo": "^3.3.3",
"typescript": "^5.9.3",
"yaml": "^1.10.2"
},
"engines": {
+35 -1
View File
@@ -1,7 +1,7 @@
import test from "node:test";
import assert from "node:assert";
import initSqlJs from "sql.js/dist/sql-asm.js";
import { minimize, unionDiff } from "../lib/common/expression/synth.ts";
import { covers, minimize, unionDiff } from "../lib/common/expression/synth.ts";
import { parse, stringify } from "../lib/common/expression/parser.ts";
const STRING_VALUES = [null, "", "a", "ab", "ab10", "ab-10"];
@@ -115,3 +115,37 @@ void test("unionDiff", async () => {
assert.strictEqual(setsEqual(res4, diffSet), true);
}
});
void test("covers", async () => {
assert.strictEqual(covers(false, false), true);
assert.strictEqual(covers(false, parse("decimal > 5 AND decimal < 3")), true);
assert.strictEqual(covers(parse("true"), parse("decimal > 0")), true);
assert.strictEqual(covers(parse("true"), parse("false")), true);
assert.strictEqual(covers(parse("false"), parse("false")), true);
assert.strictEqual(covers(parse("false"), parse("decimal > 0")), false);
assert.strictEqual(covers(parse("decimal >= 0"), parse("decimal > 0")), true);
assert.strictEqual(
covers(parse("decimal > 0"), parse("decimal >= 0")),
false,
);
const cases = [
["decimal >= 0", "decimal > 0"],
["decimal > 0", "decimal > 5"],
["string IS NOT NULL", "string = 'a'"],
["true", "decimal > 0"],
];
for (const [c1, c2] of cases) {
const res1 = await query(c1);
const res2 = await query(c2);
const coversResult = covers(parse(c1), parse(c2));
const actuallyCovers = Array.from(res2).every((r) => res1.has(r));
assert.strictEqual(
coversResult,
actuallyCovers,
`covers(${c1}, ${c2}) should match actual coverage`,
);
}
});
+2 -1
View File
@@ -11,7 +11,8 @@
"strictFunctionTypes": true,
"isolatedModules": true,
"noEmit": true,
"allowImportingTsExtensions": true
"allowImportingTsExtensions": true,
"skipLibCheck": true
},
"include": [
"./bin/*.ts",
+7 -7
View File
@@ -4,13 +4,13 @@ type AutocompleteCallback = (
) => void;
export default class Autocomplete {
private declare callback: AutocompleteCallback;
private declare element: HTMLInputElement;
private declare hideTimeout: NodeJS.Timeout;
private declare visible: boolean;
private declare default: string;
private declare selection: number;
private declare container: HTMLElement;
declare private callback: AutocompleteCallback;
declare private element: HTMLInputElement;
declare private hideTimeout: NodeJS.Timeout;
declare private visible: boolean;
declare private default: string;
declare private selection: number;
declare private container: HTMLElement;
public constructor(className: string, callback: AutocompleteCallback) {
this.callback = callback;
+1 -1
View File
@@ -79,7 +79,7 @@ export function structureConfig(config: Config[]): any {
if (idx >= 0 && idx < MAX_ARRAY_SIZE && String(idx) === key) {
const pos = Math.floor(idx / MAX_BITS);
if (!indexes[pos]) indexes[pos] = 0;
indexes[pos] |= 1 << idx % MAX_BITS;
indexes[pos] |= 1 << (idx % MAX_BITS);
} else {
indexes = [];
break;