mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 15:36:33 +00:00
fix(sdk): allow empty egress if and only if operatorEgress is true
This commit is contained in:
@@ -19,11 +19,15 @@ describe('scaffold egress (http:outbound)', () => {
|
||||
expect(validateManifest(m).ok).toBe(true);
|
||||
});
|
||||
|
||||
it('without egress the same manifest is invalid — which the wizard now prevents', () => {
|
||||
it('without egress it scaffolds an operatorEgress plugin (the admin names the hosts)', () => {
|
||||
// The author of a plugin for an always-self-hosted service has no host to write down.
|
||||
// The scaffold must NOT invent a placeholder — it declares operatorEgress instead, which
|
||||
// is the one way an empty egress[] is a valid manifest.
|
||||
scaffold('net-plug', 'integration', tmp, { permissions: ['http:outbound'] });
|
||||
const m = JSON.parse(fs.readFileSync(path.join(tmp, 'net-plug', 'trek-plugin.json'), 'utf8'));
|
||||
expect(m.egress).toBeUndefined();
|
||||
expect(validateManifest(m).ok).toBe(false); // http:outbound requires an egress allow-list
|
||||
expect(m.operatorEgress).toBe(true);
|
||||
expect(validateManifest(m).ok).toBe(true);
|
||||
});
|
||||
|
||||
it('omits egress entirely when none is given (no empty array noise)', () => {
|
||||
@@ -141,6 +145,19 @@ describe('notification-channel template', () => {
|
||||
expect(validateManifest(m).ok).toBe(true);
|
||||
});
|
||||
|
||||
it('names no host when the author names none — bare http:outbound + operatorEgress', () => {
|
||||
// The common case: a Gotify/ntfy channel targets the USER's own server. Inventing a
|
||||
// `gotify.example.com` placeholder would ship a host nobody actually calls in the
|
||||
// published manifest, and grant an outbound host the plugin never needs.
|
||||
const m = make();
|
||||
expect(m.permissions).toContain('hook:notification-channel');
|
||||
expect(m.permissions).toContain('http:outbound');
|
||||
expect(m.permissions.some((p: string) => p.startsWith('http:outbound:'))).toBe(false);
|
||||
expect('egress' in m).toBe(false);
|
||||
expect(m.operatorEgress).toBe(true);
|
||||
expect(validateManifest(m).ok).toBe(true);
|
||||
});
|
||||
|
||||
it('declares the per-user credential as a secret, required, user-scoped field', () => {
|
||||
const m = make();
|
||||
const token = m.settings.find((s: { key: string }) => s.key === 'appToken');
|
||||
|
||||
@@ -57,6 +57,15 @@ describe('validateManifest', () => {
|
||||
expect(validateManifest({ ...base, permissions: ['http:outbound'] }).ok).toBe(false);
|
||||
expect(validateManifest({ ...base, permissions: ['http:outbound'], egress: ['api.x.com'] }).ok).toBe(true);
|
||||
});
|
||||
it('waives the egress requirement for an operatorEgress plugin', () => {
|
||||
// A self-hosted target (Gotify, ntfy) has no host the author can name at publish
|
||||
// time — the admin supplies it after install, and TREK blocks all outbound until then.
|
||||
expect(validateManifest({ ...base, permissions: ['http:outbound'], operatorEgress: true }).ok).toBe(true);
|
||||
// The waiver is tied to the flag: no flag, still an error.
|
||||
expect(validateManifest({ ...base, permissions: ['http:outbound'], operatorEgress: false }).ok).toBe(false);
|
||||
// …and the flag alone never grants reach: it still needs an http:outbound permission.
|
||||
expect(validateManifest({ ...base, permissions: ['db:own'], operatorEgress: true }).ok).toBe(false);
|
||||
});
|
||||
it('rejects native modules and non-objects', () => {
|
||||
expect(validateManifest({ ...base, nativeModules: true }).ok).toBe(false);
|
||||
expect(validateManifest('nope').ok).toBe(false);
|
||||
|
||||
Reference in New Issue
Block a user