fix: probe remote RBAC capability live and enforce exact stop-result membership (#1510)

The cross-node capability gate cached its verdict, so a remote replaced by older
code at the same URL stayed trusted until the cache expired, reopening the
non-admin HTTP escalation and the over-broad stop. The probe now hits the
remote's live /api/meta on every gated action (concurrent calls deduped, never
cached across requests, fail-closed), so a downgraded remote is detected
immediately.

Two stop-result gaps are also closed:

- A remote stop result must now cover exactly the confirmed stacks (one per
  stack, no extras, no omissions), not merely exclude extras, so a dropped
  confirmed stack is no longer accepted as clean. runLocalLabelStop reports one
  result per confirmed stack even when the label has vanished, so a current
  remote always satisfies the check.
- The local stop exception path now reports the full confirmed set, so a
  confirmed stack that lost its label is not dropped when the local stop throws.
This commit is contained in:
Anso
2026-06-28 20:16:45 -04:00
committed by GitHub
parent 997a6bb79a
commit a7144d4e71
7 changed files with 210 additions and 62 deletions
@@ -420,6 +420,22 @@ describe('local-stop behavior', () => {
expect(res.status).toBe(200);
expect(res.body.results).toEqual([{ stackName: 'ghostdisk-stack', success: false, error: 'Stack not found on this node' }]);
});
it('reports one failure per confirmed stack when the label no longer exists', async () => {
// The label vanished between preview and execution. With a confirmed
// allowlist the receiver must return one result per confirmed stack (so the
// control's exact-membership check sees a complete set), not an empty body.
const res = await request(app)
.post('/api/fleet-actions/labels/local-stop')
.set('Authorization', authHeader)
.send({ labelName: 'no-such-label', dryRun: true, stackNames: ['gone-a', 'gone-b'] });
expect(res.status).toBe(200);
expect(res.body.matched).toBe(false);
expect(res.body.results).toEqual([
{ stackName: 'gone-a', success: false, error: 'No longer carries this label' },
{ stackName: 'gone-b', success: false, error: 'No longer carries this label' },
]);
});
});
// Orchestrator-level binding: the control sends the exact node + stack list the