mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 23:16:46 +00:00
Fix/plugin audit findings (#2031)
* fix(plugins): enforce manifest apiVersion at install and activation * test(plugins): exercise the absent-apiVersion default * feat(plugins): generate the core-event catalog into host-facts * docs(plugins): explain the packing.listBags write-permission binding * test(plugins): cover apiVersion upload refusal; tidy docblocks and generator header * fix(plugin-sdk): validate settings options and oauth like the host * fix(plugin-sdk): model the full manifest shape and stop dropping fields * fix(plugin-sdk): drop non-string routeProfiles icons like the host * docs(plugin-sdk): document host limits, oauth contract, and ignored asUserId * feat(plugin-sdk): enforce daily budgets and meta quotas in the mock host * feat(plugin-sdk): expose the generated core-event catalog * feat(plugin-sdk): scaffold trek range >=3.4.0 <5.0.0 ahead of TREK 4.0 * feat(plugin-sdk): scaffold the oauth broker settings for oauth:client plugins * chore(plugin-sdk): fix stale mirror docs and the colliding permissions export * docs(plugin-sdk): add the rate-limit string; restore trek docblock; match host meta sizing * feat(plugin-sdk): raise the scaffold trek floor to 4.0.0 * docs(wiki): bring the plugin pages in line with the current runtime * docs: bring the workspace CLAUDE.md guides up to date and make them committable * feat(plugin-sdk): roll back failed publishes, add unrelease, sync the fork, retro-sign updates * docs(wiki): document the publish rollback, unrelease, fork sync, and retro-signing * chore(plugin-sdk): 1.6.0
This commit is contained in:
@@ -4,7 +4,7 @@ import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { validateManifest } from '../src/index.js';
|
||||
import { scaffold } from '../src/cli/create.js';
|
||||
import { PERMISSION_CATALOG, PERMISSION_FAMILIES, KNOWN_PERMISSIONS, isInteractive, missingArgs } from '../src/cli/ui.js';
|
||||
import { PERMISSION_CATALOG, PERMISSION_FAMILIES, PICKER_PERMISSIONS, isInteractive, missingArgs } from '../src/cli/ui.js';
|
||||
// The authoritative set — what the HOST accepts at activation. The picker must match it.
|
||||
import { KNOWN_PERMISSIONS as MANIFEST_PERMISSIONS } from '../src/manifest.js';
|
||||
import { resolveMenuChoice, PRIMARY_MENU, ADVANCED_MENU } from '../src/cli/menu.js';
|
||||
@@ -47,7 +47,7 @@ describe('permission catalog', () => {
|
||||
// list the host actually honours — so a new TREK permission fails here until `create`
|
||||
// can offer it.
|
||||
it('offers every permission the host accepts — no more, no less', () => {
|
||||
expect([...KNOWN_PERMISSIONS].sort()).toEqual([...MANIFEST_PERMISSIONS].sort());
|
||||
expect([...PICKER_PERMISSIONS].sort()).toEqual([...MANIFEST_PERMISSIONS].sort());
|
||||
});
|
||||
|
||||
it('describes every permission, and files it in exactly one family', () => {
|
||||
@@ -57,7 +57,7 @@ describe('permission catalog', () => {
|
||||
.toHaveLength(1);
|
||||
}
|
||||
// No duplicates across families — a permission listed twice would render twice.
|
||||
expect(new Set(KNOWN_PERMISSIONS).size).toBe(KNOWN_PERMISSIONS.length);
|
||||
expect(new Set(PICKER_PERMISSIONS).size).toBe(PICKER_PERMISSIONS.length);
|
||||
});
|
||||
|
||||
it('every family is non-empty and described (an empty one is a dead prompt entry)', () => {
|
||||
@@ -245,3 +245,36 @@ describe('capabilities.notificationChannel validation', () => {
|
||||
expect(r.errors!.join(' ')).toMatch(/requires the "hook:notification-channel" permission/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('scaffold oauth:client settings', () => {
|
||||
let tmp: string;
|
||||
beforeEach(() => { tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'oauth-')); });
|
||||
afterEach(() => { fs.rmSync(tmp, { recursive: true, force: true }); });
|
||||
|
||||
const OAUTH_KEYS = ['oauth_authorize_url', 'oauth_token_url', 'oauth_scopes', 'oauth_client_id', 'oauth_client_secret'];
|
||||
|
||||
it('scaffolds the five instance-scoped broker settings when oauth:client is granted', () => {
|
||||
scaffold('oauth-plug', 'integration', tmp, { permissions: ['db:own', 'oauth:client'] });
|
||||
const m = JSON.parse(fs.readFileSync(path.join(tmp, 'oauth-plug', 'trek-plugin.json'), 'utf8'));
|
||||
const settings = m.settings as Array<{ key: string; secret?: boolean; scope?: string }>;
|
||||
for (const key of OAUTH_KEYS) {
|
||||
expect(settings.some((s) => s.key === key), `missing setting "${key}"`).toBe(true);
|
||||
}
|
||||
const secret = settings.find((s) => s.key === 'oauth_client_secret')!;
|
||||
expect(secret.secret).toBe(true);
|
||||
for (const key of OAUTH_KEYS) {
|
||||
const s = settings.find((x) => x.key === key)!;
|
||||
expect(s.scope, `${key}.scope`).toBe('instance');
|
||||
}
|
||||
expect(validateManifest(m).ok).toBe(true);
|
||||
});
|
||||
|
||||
it('scaffolds none of the broker settings without oauth:client', () => {
|
||||
scaffold('plain-oauth-plug', 'integration', tmp, { permissions: ['db:own'] });
|
||||
const m = JSON.parse(fs.readFileSync(path.join(tmp, 'plain-oauth-plug', 'trek-plugin.json'), 'utf8'));
|
||||
const settings = (m.settings ?? []) as Array<{ key: string }>;
|
||||
for (const key of OAUTH_KEYS) {
|
||||
expect(settings.some((s) => s.key === key), `unexpected setting "${key}"`).toBe(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user