P7 r2 fixes: drift guard wired through pushIfNoDrift (acl_drift_detected ProviderError, never overwrites drifted remote) + unknown-device forward_auth audit + drift regression tests
This commit is contained in:
4 files changed
+84
-3
No files matched your search
@@ -42,17 +42,20 @@ describe('acl-sync adapter (createAclSync)', () => {
|
||||
await expect(sync.compile(bad)).rejects.toBeInstanceOf(LockoutError);
|
||||
});
|
||||
|
||||
test('preview/push delegate to the pusher; getState to getLastKnownGood', async () => {
|
||||
test('preview delegates to the pusher; push goes through the drift guard; getState to getLastKnownGood', async () => {
|
||||
const pusher = {
|
||||
previewPolicy: jest.fn(async () => ({ feasible: true, errors: [] })),
|
||||
pushIfNoDrift: jest.fn(async (policy) => ({ drifted: false, ...(await pusher.pushPolicy(policy)) })),
|
||||
pushPolicy: jest.fn(async () => ({ pushed: true, hash: 'sha256:x' })),
|
||||
getLastKnownGood: jest.fn(() => ({ hash: 'sha256:x', policy: { acls: [] } })),
|
||||
};
|
||||
const sync = createAclSync({ pusher });
|
||||
await expect(sync.preview({ acls: [] })).resolves.toEqual({ feasible: true, errors: [] });
|
||||
await expect(sync.push({ acls: [] })).resolves.toEqual({ pushed: true, hash: 'sha256:x' });
|
||||
await expect(sync.push({ acls: [] })).resolves.toEqual({ drifted: false, pushed: true, hash: 'sha256:x' });
|
||||
expect(sync.getState()).toMatchObject({ hash: 'sha256:x' });
|
||||
expect(pusher.previewPolicy).toHaveBeenCalledWith({ acls: [] });
|
||||
// push now flows through the drift guard, not bare pushPolicy
|
||||
expect(pusher.pushIfNoDrift).toHaveBeenCalledWith({ acls: [] });
|
||||
expect(pusher.pushPolicy).toHaveBeenCalledWith({ acls: [] });
|
||||
});
|
||||
|
||||
@@ -78,3 +81,52 @@ describe('acl-sync adapter (createAclSync)', () => {
|
||||
await expect(sync.preview({ acls: [] })).rejects.toBeInstanceOf(AclPushError);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// ── Drift-guard regression (judge r2 blocker 1) ──────────────────────────
|
||||
describe('acl-sync drift guard (pushIfNoDrift wiring)', () => {
|
||||
test('push: drifted remote policy → acl_drift_detected, pushPolicy NEVER called', async () => {
|
||||
const policy = { acls: [] };
|
||||
const pusher = {
|
||||
previewPolicy: jest.fn(async () => ({ feasible: true, errors: [] })),
|
||||
pushIfNoDrift: jest.fn(async () => ({ drifted: true, pushed: false })),
|
||||
pushPolicy: jest.fn(async () => ({ pushed: true, hash: 'h' })),
|
||||
getLastKnownGood: jest.fn(() => null),
|
||||
};
|
||||
const sync = createAclSync({ pusher });
|
||||
await expect(sync.push(policy)).rejects.toMatchObject({
|
||||
code: 'acl_drift_detected',
|
||||
});
|
||||
expect(pusher.pushIfNoDrift).toHaveBeenCalledTimes(1);
|
||||
expect(pusher.pushPolicy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('push: no drift → delegates to success shape', async () => {
|
||||
const pusher = {
|
||||
pushIfNoDrift: jest.fn(async () => ({ drifted: false, pushed: true, hash: 'abc' })),
|
||||
pushPolicy: jest.fn(),
|
||||
getLastKnownGood: jest.fn(() => null),
|
||||
};
|
||||
const sync = createAclSync({ pusher });
|
||||
const result = await sync.push({ acls: [] });
|
||||
expect(result).toEqual({ drifted: false, pushed: true, hash: 'abc' });
|
||||
});
|
||||
|
||||
test('provider: tailscale syncAccess surfaces drifted remote as ProviderError acl_drift_detected (nothing pushed)', async () => {
|
||||
const { createTailscaleProvider } = require('../src/network/providers/tailscale');
|
||||
const { ProviderError } = require('../src/network/errors');
|
||||
const engine = {
|
||||
compile: jest.fn(async () => ({ policy: { acls: [] }, hash: 'h', usersAffected: 1 })),
|
||||
preview: jest.fn(async () => ({ feasible: true, errors: [] })),
|
||||
push: jest.fn(async () => { const e = new Error('drift'); e.code = 'acl_drift_detected'; e.drift = { drifted: true }; throw e; }),
|
||||
getState: jest.fn(() => null),
|
||||
};
|
||||
const provider = createTailscaleProvider({ coordClient: { request: jest.fn() }, aclSync: engine });
|
||||
await expect(provider.syncAccess(tsInput())).rejects.toMatchObject({
|
||||
code: 'acl_drift_detected',
|
||||
retryable: false,
|
||||
});
|
||||
await expect(provider.syncAccess(tsInput())).rejects.toBeInstanceOf(ProviderError);
|
||||
expect(engine.push).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user