mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 23:46:35 +00:00
fix(plugins): close the 4 PARTIAL findings + regressions from the fix-verify pass
A second adversarial pass over the first remediation found four findings only partially closed and five issues the fixes themselves introduced. This closes them: Partial → closed - re-consent gate keyed on `granted.length > 0`, so a plugin first activated with ZERO permissions (granted '[]') was treated as never-consented and a later widening was granted silently. Now discovery marks a never-consented plugin with granted_permissions '' and activate() gates on "ever consented" (any non-empty string, including '[]'). - db:own DoS: block WITH RECURSIVE outright (the one construct that spins the synchronous host unboundedly regardless of the size/row caps, via query OR exec). - dgram: also wrap `new dgram.Socket(...)` (bypassed createSocket) to inject the IP-vetting lookup, and lock createSocket/Socket. - frame self-navigation: documented as a bounded best-effort mitigation (inherent to sandboxed iframes; exposure is the plugin's own routes + already-held context, never the httpOnly cookie). Regressions introduced by the first pass → fixed - proxy: only real redirects (301/302/303/307/308) are gated, to a RELATIVE in-app Location (supports OAuth-callback bounce, blocks open redirect); 300/304 pass through; attachment only on non-redirects. - supervisor: measure RSS via /proc/<pid>/status VmRSS (page-size independent); activation-timeout awaits kill() before disposing the db handle. - manifest HOST_RE: allow single-label hosts (self-hoster sibling services) while keeping wildcards multi-label; mirrored in the SDK + frame CSP filter. Regression tests added (re-consent incl. the '[]' case, WITH RECURSIVE + row cap, single-label host). 236 plugin tests + 24 SDK tests green.
This commit is contained in:
@@ -36,15 +36,29 @@ describe('PluginDataDb', () => {
|
||||
expect(fs.existsSync(path.join(tmp, 'notes', 'plugin.db'))).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects statements that would escape the plugin file, and bad sql', () => {
|
||||
it('rejects statements that would escape the plugin file, DoS, or exceed limits', () => {
|
||||
const db = new PluginDataDb('guard');
|
||||
expect(() => db.exec("ATTACH DATABASE 'trek.db' AS core")).toThrow(/not allowed/);
|
||||
expect(() => db.query('PRAGMA table_info(x)')).toThrow(/not allowed/);
|
||||
// WITH RECURSIVE is the unbounded-CPU vector on the synchronous host — refused
|
||||
expect(() => db.query('WITH RECURSIVE r(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM r) SELECT x FROM r')).toThrow(/not allowed/);
|
||||
expect(() => db.exec('WITH RECURSIVE r(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM r) INSERT INTO t SELECT x FROM r')).toThrow(/not allowed/);
|
||||
expect(() => db.exec(123 as unknown as string)).toThrow(/must be a string/);
|
||||
expect(() => db.query('x'.repeat(100_001))).toThrow(/too long/);
|
||||
db.close();
|
||||
});
|
||||
|
||||
it('row-caps a query so it cannot materialize an unbounded result set', () => {
|
||||
const db = new PluginDataDb('rowcap');
|
||||
db.exec('CREATE TABLE seq (n INTEGER)');
|
||||
// Insert a modest table and a self-cross-join that would explode past the cap.
|
||||
const insert = 'INSERT INTO seq (n) VALUES ' + Array.from({ length: 400 }, (_, i) => `(${i})`).join(',');
|
||||
db.exec(insert);
|
||||
// 400 * 400 = 160k rows > MAX_ROWS (100k) → must throw, not return them all
|
||||
expect(() => db.query('SELECT a.n FROM seq a, seq b')).toThrow(/more than/);
|
||||
db.close();
|
||||
});
|
||||
|
||||
it('removePluginData deletes the whole data dir', () => {
|
||||
const db = new PluginDataDb('temp');
|
||||
db.migrate('001', 'CREATE TABLE t (id INTEGER)');
|
||||
|
||||
Reference in New Issue
Block a user