mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
const {
|
||||
InvalidSecurityContractError,
|
||||
normalizeSecurityPolicyDecision,
|
||||
normalizeSecurityPrincipal,
|
||||
} = require('@qinglong/runtime-core/security');
|
||||
|
||||
const NOW = 10_000;
|
||||
|
||||
test('normalizes an active principal without retaining mutable input', () => {
|
||||
const input = {
|
||||
subject: { type: 'user', id: 'usr_primary' },
|
||||
authenticationId: 'session:abc123',
|
||||
authenticatedAtMs: 9_000,
|
||||
expiresAtMs: 11_000,
|
||||
assurance: 'multi_factor',
|
||||
};
|
||||
const principal = normalizeSecurityPrincipal(input, NOW);
|
||||
input.subject.id = 'usr_changed';
|
||||
assert.deepEqual(principal, {
|
||||
subject: { type: 'user', id: 'usr_primary' },
|
||||
authenticationId: 'session:abc123',
|
||||
authenticatedAtMs: 9_000,
|
||||
expiresAtMs: 11_000,
|
||||
assurance: 'multi_factor',
|
||||
});
|
||||
assert.equal(Object.isFrozen(principal), true);
|
||||
assert.equal(Object.isFrozen(principal.subject), true);
|
||||
});
|
||||
|
||||
test('rejects expired, future, malformed and widened principals', () => {
|
||||
const principal = {
|
||||
subject: { type: 'worker', id: 'worker-1' },
|
||||
authenticationId: 'mtls:worker-1',
|
||||
authenticatedAtMs: 9_000,
|
||||
expiresAtMs: 11_000,
|
||||
assurance: 'service',
|
||||
};
|
||||
for (const candidate of [
|
||||
{ ...principal, expiresAtMs: NOW },
|
||||
{ ...principal, authenticatedAtMs: NOW + 1 },
|
||||
{ ...principal, subject: { type: 'root', id: 'root' } },
|
||||
{ ...principal, debug: true },
|
||||
]) {
|
||||
assert.throws(
|
||||
() => normalizeSecurityPrincipal(candidate, NOW),
|
||||
InvalidSecurityContractError,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('normalizes bounded policy decisions and version fences', () => {
|
||||
const input = {
|
||||
effect: 'allow',
|
||||
reasons: ['role_grant'],
|
||||
fence: { projectVersion: 3, bindingVersion: 7 },
|
||||
};
|
||||
const decision = normalizeSecurityPolicyDecision(input);
|
||||
input.reasons[0] = 'changed';
|
||||
assert.deepEqual(decision, {
|
||||
effect: 'allow',
|
||||
reasons: ['role_grant'],
|
||||
fence: { projectVersion: 3, bindingVersion: 7 },
|
||||
});
|
||||
assert.equal(Object.isFrozen(decision.reasons), true);
|
||||
});
|
||||
|
||||
test('rejects empty or unbounded reasons and invalid fences', () => {
|
||||
for (const candidate of [
|
||||
{ effect: 'deny', reasons: [], fence: null },
|
||||
{ effect: 'unknown', reasons: ['unknown_effect'], fence: null },
|
||||
{
|
||||
effect: 'allow',
|
||||
reasons: ['role-grant'],
|
||||
fence: { projectVersion: 1, bindingVersion: null },
|
||||
},
|
||||
{
|
||||
effect: 'allow',
|
||||
reasons: ['role_grant'],
|
||||
fence: { projectVersion: 0, bindingVersion: null },
|
||||
},
|
||||
]) {
|
||||
assert.throws(
|
||||
() => normalizeSecurityPolicyDecision(candidate),
|
||||
InvalidSecurityContractError,
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user