@claude-flow/security 3.0.0-alpha.9 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CVE-REMEDIATION.d.ts +17 -6
- package/dist/CVE-REMEDIATION.d.ts.map +1 -1
- package/dist/CVE-REMEDIATION.js +328 -12
- package/dist/CVE-REMEDIATION.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -1
- package/dist/keychain-adapter.d.ts +48 -0
- package/dist/keychain-adapter.d.ts.map +1 -0
- package/dist/keychain-adapter.js +0 -0
- package/dist/keychain-adapter.js.map +1 -0
- package/dist/mcp-caller-identity.d.ts +101 -0
- package/dist/mcp-caller-identity.d.ts.map +1 -0
- package/dist/mcp-caller-identity.js +0 -0
- package/dist/mcp-caller-identity.js.map +1 -0
- package/dist/mcp-composition-inspector.d.ts +133 -0
- package/dist/mcp-composition-inspector.d.ts.map +1 -0
- package/dist/mcp-composition-inspector.js +254 -0
- package/dist/mcp-composition-inspector.js.map +1 -0
- package/dist/oauth/browser.d.ts +31 -0
- package/dist/oauth/browser.d.ts.map +1 -0
- package/dist/oauth/browser.js +55 -0
- package/dist/oauth/browser.js.map +1 -0
- package/dist/oauth/callback-server.d.ts +37 -0
- package/dist/oauth/callback-server.d.ts.map +1 -0
- package/dist/oauth/callback-server.js +86 -0
- package/dist/oauth/callback-server.js.map +1 -0
- package/dist/oauth/client.d.ts +57 -0
- package/dist/oauth/client.d.ts.map +1 -0
- package/dist/oauth/client.js +120 -0
- package/dist/oauth/client.js.map +1 -0
- package/dist/oauth/pkce.d.ts +25 -0
- package/dist/oauth/pkce.d.ts.map +1 -0
- package/dist/oauth/pkce.js +36 -0
- package/dist/oauth/pkce.js.map +1 -0
- package/dist/path-validator.d.ts +23 -0
- package/dist/path-validator.d.ts.map +1 -1
- package/dist/path-validator.js +128 -5
- package/dist/path-validator.js.map +1 -1
- package/dist/policy/canonical.d.ts +5 -0
- package/dist/policy/canonical.d.ts.map +1 -0
- package/dist/policy/canonical.js +30 -0
- package/dist/policy/canonical.js.map +1 -0
- package/dist/policy/engine.d.ts +30 -0
- package/dist/policy/engine.d.ts.map +1 -0
- package/dist/policy/engine.js +319 -0
- package/dist/policy/engine.js.map +1 -0
- package/dist/policy/envelope.d.ts +9 -0
- package/dist/policy/envelope.d.ts.map +1 -0
- package/dist/policy/envelope.js +90 -0
- package/dist/policy/envelope.js.map +1 -0
- package/dist/policy/evaluator.d.ts +4 -0
- package/dist/policy/evaluator.d.ts.map +1 -0
- package/dist/policy/evaluator.js +119 -0
- package/dist/policy/evaluator.js.map +1 -0
- package/dist/policy/index.d.ts +7 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +7 -0
- package/dist/policy/index.js.map +1 -0
- package/dist/policy/product-plane.d.ts +238 -0
- package/dist/policy/product-plane.d.ts.map +1 -0
- package/dist/policy/product-plane.js +1022 -0
- package/dist/policy/product-plane.js.map +1 -0
- package/dist/policy/types.d.ts +187 -0
- package/dist/policy/types.d.ts.map +1 -0
- package/dist/policy/types.js +2 -0
- package/dist/policy/types.js.map +1 -0
- package/dist/safe-git.d.ts +22 -0
- package/dist/safe-git.d.ts.map +1 -0
- package/dist/safe-git.js +63 -0
- package/dist/safe-git.js.map +1 -0
- package/package.json +17 -5
- package/.claude-flow/data/pending-insights.jsonl +0 -6
- package/.claude-flow/neural/stats.json +0 -6
- package/__tests__/acceptance/security-compliance.test.ts +0 -674
- package/__tests__/authorization-propagator.test.ts +0 -155
- package/__tests__/credential-generator.test.ts +0 -310
- package/__tests__/fixtures/configurations.ts +0 -425
- package/__tests__/fixtures/index.ts +0 -21
- package/__tests__/helpers/create-mock.ts +0 -469
- package/__tests__/helpers/index.ts +0 -32
- package/__tests__/input-validator.test.ts +0 -382
- package/__tests__/integration/security-flow.test.ts +0 -606
- package/__tests__/password-hasher.test.ts +0 -239
- package/__tests__/path-validator.test.ts +0 -302
- package/__tests__/plugin-integrity-verifier.test.ts +0 -199
- package/__tests__/safe-executor.test.ts +0 -292
- package/__tests__/token-generator.test.ts +0 -371
- package/__tests__/tool-output-guardrail.test.ts +0 -256
- package/__tests__/unit/credential-generator.test.ts +0 -182
- package/__tests__/unit/password-hasher.test.ts +0 -359
- package/__tests__/unit/path-validator.test.ts +0 -509
- package/__tests__/unit/safe-executor.test.ts +0 -667
- package/__tests__/unit/token-generator.test.ts +0 -310
- package/ruvector.db +0 -0
- package/src/CVE-REMEDIATION.ts +0 -251
- package/src/application/index.ts +0 -10
- package/src/application/services/security-application-service.ts +0 -193
- package/src/authorization/propagator.ts +0 -279
- package/src/credential-generator.ts +0 -368
- package/src/domain/entities/security-context.ts +0 -173
- package/src/domain/index.ts +0 -17
- package/src/domain/services/security-domain-service.ts +0 -296
- package/src/index.ts +0 -314
- package/src/input-validator.ts +0 -466
- package/src/password-hasher.ts +0 -277
- package/src/path-validator.ts +0 -525
- package/src/plugins/integrity-verifier.ts +0 -265
- package/src/safe-executor.ts +0 -546
- package/src/token-generator.ts +0 -463
- package/src/tool-output-guardrail.ts +0 -360
- package/tmp.json +0 -0
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -20
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for AgentAuthorizationPropagator (ADR-144 P1, ruvnet/ruflo#2248).
|
|
3
|
-
*
|
|
4
|
-
* Covers the load-bearing invariants of the action-layer security model:
|
|
5
|
-
* - wrapOutbound monotonically reduces scope (cannot grant more than holder)
|
|
6
|
-
* - delegationDepth decrements by ≥ 1 every hop; depth-0 cannot delegate
|
|
7
|
-
* - expired scopes are rejected at wrap and at check
|
|
8
|
-
* - checkToolCall returns stable, telemetry-friendly reasons
|
|
9
|
-
* - verifyServerAuth rejects empty / nullish credentials
|
|
10
|
-
* - provenance buffer ring-bound at the configured max
|
|
11
|
-
* - legacy permissive scope (the migration shim) allows everything
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { describe, it, expect } from 'vitest';
|
|
15
|
-
import {
|
|
16
|
-
AgentAuthorizationPropagator,
|
|
17
|
-
AuthorizationPropagationError,
|
|
18
|
-
makeLegacyPermissiveScope,
|
|
19
|
-
type AuthScope,
|
|
20
|
-
} from '../src/authorization/propagator.js';
|
|
21
|
-
|
|
22
|
-
const FAR_FUTURE = Date.now() + 60 * 60 * 1000; // 1 hour from now
|
|
23
|
-
|
|
24
|
-
function scope(overrides: Partial<AuthScope> = {}): AuthScope {
|
|
25
|
-
return {
|
|
26
|
-
principalId: 'principal-A',
|
|
27
|
-
grantedTools: ['memory_store', 'memory_search'],
|
|
28
|
-
grantedServers: ['server-1'],
|
|
29
|
-
delegationDepth: 3,
|
|
30
|
-
expiresAt: FAR_FUTURE,
|
|
31
|
-
...overrides,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe('wrapOutbound — scope reduction invariants', () => {
|
|
36
|
-
const prop = new AgentAuthorizationPropagator();
|
|
37
|
-
|
|
38
|
-
it('decrements delegationDepth by exactly 1', () => {
|
|
39
|
-
const env = prop.wrapOutbound({ msg: 'hi' }, scope({ delegationDepth: 3 }));
|
|
40
|
-
expect(env.scope.delegationDepth).toBe(2);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('refuses to delegate when depth has been exhausted', () => {
|
|
44
|
-
expect(() =>
|
|
45
|
-
prop.wrapOutbound({ msg: 'hi' }, scope({ delegationDepth: 0 })),
|
|
46
|
-
).toThrow(AuthorizationPropagationError);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('refuses to grant a tool not held by the parent scope', () => {
|
|
50
|
-
expect(() =>
|
|
51
|
-
prop.wrapOutbound({}, scope(), { tools: ['memory_store', 'task_create'] }),
|
|
52
|
-
).toThrow(/cannot grant tools 'task_create'/);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('allows a strict subset of the parent tool set', () => {
|
|
56
|
-
const env = prop.wrapOutbound({}, scope(), { tools: ['memory_store'] });
|
|
57
|
-
expect(env.scope.grantedTools).toEqual(['memory_store']);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('preserves principalId across the hop', () => {
|
|
61
|
-
const env = prop.wrapOutbound({}, scope({ principalId: 'principal-X' }));
|
|
62
|
-
expect(env.scope.principalId).toBe('principal-X');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('rejects an expired holder scope', () => {
|
|
66
|
-
expect(() =>
|
|
67
|
-
prop.wrapOutbound({}, scope({ expiresAt: 1 })),
|
|
68
|
-
).toThrow(/scope expired/);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('passes the payload through untouched', () => {
|
|
72
|
-
const payload = { kind: 'design', body: { topic: 'auth' } };
|
|
73
|
-
const env = prop.wrapOutbound(payload, scope());
|
|
74
|
-
expect(env.payload).toBe(payload);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('a wildcard parent scope can grant any specific tool', () => {
|
|
78
|
-
const env = prop.wrapOutbound({}, makeLegacyPermissiveScope(), {
|
|
79
|
-
tools: ['memory_store', 'task_create'],
|
|
80
|
-
});
|
|
81
|
-
expect(env.scope.grantedTools).toEqual(['memory_store', 'task_create']);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
describe('checkToolCall — typed decisions', () => {
|
|
86
|
-
const prop = new AgentAuthorizationPropagator();
|
|
87
|
-
|
|
88
|
-
it('allows a tool that is in scope', () => {
|
|
89
|
-
expect(prop.checkToolCall('memory_store', scope())).toEqual({ allowed: true });
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('denies a tool not in scope with stable reason', () => {
|
|
93
|
-
expect(prop.checkToolCall('shell_exec', scope())).toEqual({
|
|
94
|
-
allowed: false,
|
|
95
|
-
reason: 'tool-not-in-scope',
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('denies when the named server is not in scope', () => {
|
|
100
|
-
expect(
|
|
101
|
-
prop.checkToolCall('memory_store', scope(), { serverId: 'untrusted' }),
|
|
102
|
-
).toEqual({ allowed: false, reason: 'server-not-in-scope' });
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('denies an expired scope deterministically (fixed-now)', () => {
|
|
106
|
-
const expired = scope({ expiresAt: 1000 });
|
|
107
|
-
expect(prop.checkToolCall('memory_store', expired, { now: 2000 })).toEqual({
|
|
108
|
-
allowed: false,
|
|
109
|
-
reason: 'scope-expired',
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe('verifyServerAuth — fail-closed on missing credentials', () => {
|
|
115
|
-
const prop = new AgentAuthorizationPropagator();
|
|
116
|
-
|
|
117
|
-
it('rejects missing credential', () => {
|
|
118
|
-
expect(prop.verifyServerAuth('s1', null)).toBe(false);
|
|
119
|
-
expect(prop.verifyServerAuth('s1', undefined)).toBe(false);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('rejects empty-string credential', () => {
|
|
123
|
-
expect(prop.verifyServerAuth('s1', '')).toBe(false);
|
|
124
|
-
expect(prop.verifyServerAuth('s1', ' ')).toBe(false);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('rejects missing serverId', () => {
|
|
128
|
-
expect(prop.verifyServerAuth('', 'token')).toBe(false);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('accepts a non-empty credential (P1 permissive default)', () => {
|
|
132
|
-
expect(prop.verifyServerAuth('s1', 'oauth-token-xyz')).toBe(true);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe('provenance buffer', () => {
|
|
137
|
-
it('retains recorded actions in insertion order', () => {
|
|
138
|
-
const p = new AgentAuthorizationPropagator();
|
|
139
|
-
p.recordAction('agent-A', 'memory_store', scope(), 'allowed');
|
|
140
|
-
p.recordAction('agent-B', 'shell_exec', scope(), 'denied', 'tool-not-in-scope');
|
|
141
|
-
const log = p.getProvenance();
|
|
142
|
-
expect(log).toHaveLength(2);
|
|
143
|
-
expect(log[0]?.outcome).toBe('allowed');
|
|
144
|
-
expect(log[1]?.reason).toBe('tool-not-in-scope');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('ring-bounds at the configured max', () => {
|
|
148
|
-
const p = new AgentAuthorizationPropagator({ provenanceBufferMax: 3 });
|
|
149
|
-
for (let i = 0; i < 5; i++) p.recordAction('a', 't', scope(), 'allowed', `i=${i}`);
|
|
150
|
-
const log = p.getProvenance();
|
|
151
|
-
expect(log).toHaveLength(3);
|
|
152
|
-
expect(log[0]?.reason).toBe('i=2');
|
|
153
|
-
expect(log[2]?.reason).toBe('i=4');
|
|
154
|
-
});
|
|
155
|
-
});
|
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Credential Generator Tests - CVE-3 Remediation Validation
|
|
3
|
-
*
|
|
4
|
-
* Tests verify:
|
|
5
|
-
* - Cryptographically secure random generation
|
|
6
|
-
* - No hardcoded defaults
|
|
7
|
-
* - Proper entropy levels
|
|
8
|
-
* - Unique credentials per generation
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
12
|
-
import {
|
|
13
|
-
CredentialGenerator,
|
|
14
|
-
CredentialGeneratorError,
|
|
15
|
-
createCredentialGenerator,
|
|
16
|
-
generateCredentials,
|
|
17
|
-
} from '../src/credential-generator.js';
|
|
18
|
-
|
|
19
|
-
describe('CredentialGenerator', () => {
|
|
20
|
-
let generator: CredentialGenerator;
|
|
21
|
-
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
generator = new CredentialGenerator();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe('Configuration', () => {
|
|
27
|
-
it('should use default configuration', () => {
|
|
28
|
-
expect(() => new CredentialGenerator()).not.toThrow();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('should reject password length below 16', () => {
|
|
32
|
-
expect(() => new CredentialGenerator({
|
|
33
|
-
passwordLength: 8,
|
|
34
|
-
})).toThrow(CredentialGeneratorError);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should reject API key length below 32', () => {
|
|
38
|
-
expect(() => new CredentialGenerator({
|
|
39
|
-
apiKeyLength: 16,
|
|
40
|
-
})).toThrow(CredentialGeneratorError);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should reject secret length below 32', () => {
|
|
44
|
-
expect(() => new CredentialGenerator({
|
|
45
|
-
secretLength: 16,
|
|
46
|
-
})).toThrow(CredentialGeneratorError);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe('Password Generation', () => {
|
|
51
|
-
it('should generate password of specified length', () => {
|
|
52
|
-
const password = generator.generatePassword();
|
|
53
|
-
expect(password.length).toBeGreaterThanOrEqual(32);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should generate unique passwords', () => {
|
|
57
|
-
const passwords = new Set<string>();
|
|
58
|
-
for (let i = 0; i < 100; i++) {
|
|
59
|
-
passwords.add(generator.generatePassword());
|
|
60
|
-
}
|
|
61
|
-
expect(passwords.size).toBe(100);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should include required character types', () => {
|
|
65
|
-
for (let i = 0; i < 10; i++) {
|
|
66
|
-
const password = generator.generatePassword();
|
|
67
|
-
expect(password).toMatch(/[A-Z]/); // Uppercase
|
|
68
|
-
expect(password).toMatch(/[a-z]/); // Lowercase
|
|
69
|
-
expect(password).toMatch(/\d/); // Digit
|
|
70
|
-
expect(password).toMatch(/[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/); // Special
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe('API Key Generation', () => {
|
|
76
|
-
it('should generate API key with prefix', () => {
|
|
77
|
-
const { key, prefix } = generator.generateApiKey('cf_');
|
|
78
|
-
expect(key.startsWith('cf_')).toBe(true);
|
|
79
|
-
expect(prefix).toBe('cf_');
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('should generate unique keys', () => {
|
|
83
|
-
const keys = new Set<string>();
|
|
84
|
-
for (let i = 0; i < 100; i++) {
|
|
85
|
-
keys.add(generator.generateApiKey().key);
|
|
86
|
-
}
|
|
87
|
-
expect(keys.size).toBe(100);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should include keyId', () => {
|
|
91
|
-
const { keyId } = generator.generateApiKey();
|
|
92
|
-
// Should be a valid UUID
|
|
93
|
-
expect(keyId).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should include createdAt timestamp', () => {
|
|
97
|
-
const { createdAt } = generator.generateApiKey();
|
|
98
|
-
expect(createdAt).toBeInstanceOf(Date);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
describe('Secret Generation', () => {
|
|
103
|
-
it('should generate hex-encoded secret', () => {
|
|
104
|
-
const secret = generator.generateSecret();
|
|
105
|
-
expect(secret).toMatch(/^[0-9a-f]+$/);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should generate secret of specified length', () => {
|
|
109
|
-
const secret = generator.generateSecret(64);
|
|
110
|
-
expect(secret.length).toBe(64);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should generate unique secrets', () => {
|
|
114
|
-
const secrets = new Set<string>();
|
|
115
|
-
for (let i = 0; i < 100; i++) {
|
|
116
|
-
secrets.add(generator.generateSecret());
|
|
117
|
-
}
|
|
118
|
-
expect(secrets.size).toBe(100);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe('Encryption Key Generation', () => {
|
|
123
|
-
it('should generate 32-byte (256-bit) key as hex', () => {
|
|
124
|
-
const key = generator.generateEncryptionKey();
|
|
125
|
-
// 32 bytes = 64 hex characters
|
|
126
|
-
expect(key.length).toBe(64);
|
|
127
|
-
expect(key).toMatch(/^[0-9a-f]+$/);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should generate unique keys', () => {
|
|
131
|
-
const keys = new Set<string>();
|
|
132
|
-
for (let i = 0; i < 100; i++) {
|
|
133
|
-
keys.add(generator.generateEncryptionKey());
|
|
134
|
-
}
|
|
135
|
-
expect(keys.size).toBe(100);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
describe('Installation Credentials', () => {
|
|
140
|
-
it('should generate complete credential set', () => {
|
|
141
|
-
const credentials = generator.generateInstallationCredentials();
|
|
142
|
-
|
|
143
|
-
expect(credentials.adminPassword).toBeDefined();
|
|
144
|
-
expect(credentials.servicePassword).toBeDefined();
|
|
145
|
-
expect(credentials.jwtSecret).toBeDefined();
|
|
146
|
-
expect(credentials.sessionSecret).toBeDefined();
|
|
147
|
-
expect(credentials.encryptionKey).toBeDefined();
|
|
148
|
-
expect(credentials.generatedAt).toBeInstanceOf(Date);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('should set expiration when specified', () => {
|
|
152
|
-
const credentials = generator.generateInstallationCredentials(30);
|
|
153
|
-
|
|
154
|
-
expect(credentials.expiresAt).toBeInstanceOf(Date);
|
|
155
|
-
|
|
156
|
-
// Should be approximately 30 days in the future
|
|
157
|
-
const expectedExpiration = Date.now() + 30 * 24 * 60 * 60 * 1000;
|
|
158
|
-
const actualExpiration = credentials.expiresAt!.getTime();
|
|
159
|
-
expect(Math.abs(actualExpiration - expectedExpiration)).toBeLessThan(1000);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should generate unique credentials each time', () => {
|
|
163
|
-
const cred1 = generator.generateInstallationCredentials();
|
|
164
|
-
const cred2 = generator.generateInstallationCredentials();
|
|
165
|
-
|
|
166
|
-
expect(cred1.adminPassword).not.toBe(cred2.adminPassword);
|
|
167
|
-
expect(cred1.servicePassword).not.toBe(cred2.servicePassword);
|
|
168
|
-
expect(cred1.jwtSecret).not.toBe(cred2.jwtSecret);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe('Token Generation', () => {
|
|
173
|
-
it('should generate session token', () => {
|
|
174
|
-
const token = generator.generateSessionToken();
|
|
175
|
-
expect(token).toBeDefined();
|
|
176
|
-
expect(token.length).toBeGreaterThan(0);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it('should generate CSRF token', () => {
|
|
180
|
-
const token = generator.generateCsrfToken();
|
|
181
|
-
expect(token).toBeDefined();
|
|
182
|
-
// Base64url encoded
|
|
183
|
-
expect(token).toMatch(/^[A-Za-z0-9_-]+$/);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('should generate nonce', () => {
|
|
187
|
-
const nonce = generator.generateNonce();
|
|
188
|
-
expect(nonce).toBeDefined();
|
|
189
|
-
// Hex encoded, 16 bytes = 32 chars
|
|
190
|
-
expect(nonce.length).toBe(32);
|
|
191
|
-
expect(nonce).toMatch(/^[0-9a-f]+$/);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
describe('Environment Script Generation', () => {
|
|
196
|
-
it('should generate valid shell script', () => {
|
|
197
|
-
const credentials = generator.generateInstallationCredentials();
|
|
198
|
-
const script = generator.createEnvScript(credentials);
|
|
199
|
-
|
|
200
|
-
expect(script).toContain('export CLAUDE_FLOW_ADMIN_PASSWORD=');
|
|
201
|
-
expect(script).toContain('export CLAUDE_FLOW_SERVICE_PASSWORD=');
|
|
202
|
-
expect(script).toContain('export CLAUDE_FLOW_JWT_SECRET=');
|
|
203
|
-
expect(script).toContain('export CLAUDE_FLOW_SESSION_SECRET=');
|
|
204
|
-
expect(script).toContain('export CLAUDE_FLOW_ENCRYPTION_KEY=');
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('should include warning comment', () => {
|
|
208
|
-
const credentials = generator.generateInstallationCredentials();
|
|
209
|
-
const script = generator.createEnvScript(credentials);
|
|
210
|
-
|
|
211
|
-
expect(script).toContain('Store these securely');
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
describe('JSON Config Generation', () => {
|
|
216
|
-
it('should generate valid JSON', () => {
|
|
217
|
-
const credentials = generator.generateInstallationCredentials();
|
|
218
|
-
const json = generator.createJsonConfig(credentials);
|
|
219
|
-
|
|
220
|
-
expect(() => JSON.parse(json)).not.toThrow();
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('should include all credentials', () => {
|
|
224
|
-
const credentials = generator.generateInstallationCredentials();
|
|
225
|
-
const json = generator.createJsonConfig(credentials);
|
|
226
|
-
const parsed = JSON.parse(json);
|
|
227
|
-
|
|
228
|
-
expect(parsed['claude-flow/admin-password']).toBe(credentials.adminPassword);
|
|
229
|
-
expect(parsed['claude-flow/service-password']).toBe(credentials.servicePassword);
|
|
230
|
-
expect(parsed['claude-flow/jwt-secret']).toBe(credentials.jwtSecret);
|
|
231
|
-
expect(parsed['claude-flow/session-secret']).toBe(credentials.sessionSecret);
|
|
232
|
-
expect(parsed['claude-flow/encryption-key']).toBe(credentials.encryptionKey);
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
describe('Factory Functions', () => {
|
|
237
|
-
it('should create generator with factory function', () => {
|
|
238
|
-
const gen = createCredentialGenerator();
|
|
239
|
-
expect(gen).toBeInstanceOf(CredentialGenerator);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it('should generate credentials with quick function', () => {
|
|
243
|
-
const credentials = generateCredentials();
|
|
244
|
-
expect(credentials.adminPassword).toBeDefined();
|
|
245
|
-
expect(credentials.jwtSecret).toBeDefined();
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
describe('CVE-3 Security Verification', () => {
|
|
250
|
-
it('should NOT produce hardcoded admin123 password', () => {
|
|
251
|
-
for (let i = 0; i < 100; i++) {
|
|
252
|
-
const credentials = generator.generateInstallationCredentials();
|
|
253
|
-
expect(credentials.adminPassword).not.toBe('admin123');
|
|
254
|
-
expect(credentials.servicePassword).not.toBe('service123');
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it('should NOT produce predictable patterns', () => {
|
|
259
|
-
const passwords = new Set<string>();
|
|
260
|
-
for (let i = 0; i < 1000; i++) {
|
|
261
|
-
passwords.add(generator.generatePassword());
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// All should be unique
|
|
265
|
-
expect(passwords.size).toBe(1000);
|
|
266
|
-
|
|
267
|
-
// None should match common patterns
|
|
268
|
-
const commonPatterns = ['password', 'admin', 'service', '123456', 'qwerty'];
|
|
269
|
-
for (const password of passwords) {
|
|
270
|
-
for (const pattern of commonPatterns) {
|
|
271
|
-
expect(password.toLowerCase()).not.toContain(pattern);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
it('should use cryptographically secure random', () => {
|
|
277
|
-
// Generate many passwords and verify entropy
|
|
278
|
-
const passwords: string[] = [];
|
|
279
|
-
for (let i = 0; i < 100; i++) {
|
|
280
|
-
passwords.push(generator.generatePassword());
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Calculate average character distribution
|
|
284
|
-
const charCounts = new Map<string, number>();
|
|
285
|
-
for (const password of passwords) {
|
|
286
|
-
for (const char of password) {
|
|
287
|
-
charCounts.set(char, (charCounts.get(char) || 0) + 1);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Should have good distribution (many different characters)
|
|
292
|
-
expect(charCounts.size).toBeGreaterThan(30);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('should generate installation credentials with sufficient entropy', () => {
|
|
296
|
-
const credentials = generator.generateInstallationCredentials();
|
|
297
|
-
|
|
298
|
-
// Password should be at least 256 bits of entropy at minimum
|
|
299
|
-
expect(credentials.adminPassword.length).toBeGreaterThanOrEqual(32);
|
|
300
|
-
expect(credentials.servicePassword.length).toBeGreaterThanOrEqual(32);
|
|
301
|
-
|
|
302
|
-
// Secrets should be 512 bits
|
|
303
|
-
expect(credentials.jwtSecret.length).toBeGreaterThanOrEqual(64);
|
|
304
|
-
expect(credentials.sessionSecret.length).toBeGreaterThanOrEqual(64);
|
|
305
|
-
|
|
306
|
-
// Encryption key should be exactly 256 bits (64 hex chars)
|
|
307
|
-
expect(credentials.encryptionKey.length).toBe(64);
|
|
308
|
-
});
|
|
309
|
-
});
|
|
310
|
-
});
|