diff --git a/keeper/__tests__/data/keys.json b/keeper/__tests__/data/keys.json new file mode 100644 index 00000000..f7a905be --- /dev/null +++ b/keeper/__tests__/data/keys.json @@ -0,0 +1,16 @@ +{ + "testkey": { + "keyId": "testkey", + "publicPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAtYxJV8CpS8HLjo3vFwFrc5hXTHWtKTKESzDaSI6Nt5U=\n-----END PUBLIC KEY-----\n", + "algorithm": "ed25519", + "active": true, + "createdAt": "2026-06-01T13:25:56.063Z" + }, + "k1": { + "keyId": "k1", + "publicPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEACu2Y/F98xCZDTyFwaFUX/ERg2u87ELNkZqVtcX7XgLE=\n-----END PUBLIC KEY-----\n", + "algorithm": "ed25519", + "active": true, + "createdAt": "2026-06-01T13:25:56.069Z" + } +} \ No newline at end of file diff --git a/keeper/__tests__/security.test.js b/keeper/__tests__/security.test.js new file mode 100644 index 00000000..c6bdf8a8 --- /dev/null +++ b/keeper/__tests__/security.test.js @@ -0,0 +1,36 @@ +const { buildSecurityStack } = require('../src/keeperSecurity'); + +describe('Keeper security stack', () => { + let stack; + + beforeEach(() => { + stack = buildSecurityStack({ storageDir: __dirname + '/data', auditFile: __dirname + '/data/audit.log' }); + }); + + test('creates keys in HSM and lists them', async () => { + const k = await stack.keyManager.createKey({ keyId: 'testkey' }); + expect(k.keyId).toBe('testkey'); + const list = await stack.keyManager.listKeys(); + expect(list.find(l => l.keyId === 'testkey')).toBeTruthy(); + }); + + test('permissions grant allows signing and revocation blocks it', async () => { + await stack.keyManager.createKey({ keyId: 'k1' }); + const grant = stack.permissions.createGrant({ subject: 'alice', resource: 'k1', actions: ['sign'], scope: 'tx' }); + const { signature } = await stack.signing.sign({ requester: 'alice', keyId: 'k1', payload: 'hello', purpose: 'tx' }); + expect(signature).toBeTruthy(); + + // revoke and assert unauthorized + stack.permissions.revokeGrant(grant.grantId); + await expect(stack.signing.sign({ requester: 'alice', keyId: 'k1', payload: 'hello', purpose: 'tx' })).rejects.toThrow(/Unauthorized/); + }); + + test('audit log records events', () => { + const tail = stack.audit.tail(10); + expect(Array.isArray(tail)).toBeTruthy(); + // create a new grant and ensure audit recorded + const g = stack.permissions.createGrant({ subject: 'bob', resource: 'k1' }); + const tail2 = stack.audit.tail(5); + expect(tail2.some(e => e.eventType === 'permission.grant.create' && e.details.grantId === g.grantId)).toBeTruthy(); + }); +}); diff --git a/keeper/coverage/coverage-summary.json b/keeper/coverage/coverage-summary.json index 3d6e50ae..754a46d9 100644 --- a/keeper/coverage/coverage-summary.json +++ b/keeper/coverage/coverage-summary.json @@ -1,8 +1,8 @@ -{"total": {"lines":{"total":706,"covered":22,"skipped":0,"pct":3.11},"statements":{"total":725,"covered":22,"skipped":0,"pct":3.03},"functions":{"total":112,"covered":9,"skipped":0,"pct":8.03},"branches":{"total":530,"covered":7,"skipped":0,"pct":1.32},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":"Unknown"}} -,"/Users/aliphatic/Desktop/SoroTask/keeper/src/concurrency.js": {"lines":{"total":50,"covered":0,"skipped":0,"pct":0},"functions":{"total":8,"covered":0,"skipped":0,"pct":0},"statements":{"total":51,"covered":0,"skipped":0,"pct":0},"branches":{"total":26,"covered":0,"skipped":0,"pct":0}} -,"/Users/aliphatic/Desktop/SoroTask/keeper/src/logger.js": {"lines":{"total":44,"covered":22,"skipped":0,"pct":50},"functions":{"total":27,"covered":9,"skipped":0,"pct":33.33},"statements":{"total":44,"covered":22,"skipped":0,"pct":50},"branches":{"total":26,"covered":7,"skipped":0,"pct":26.92}} -,"/Users/aliphatic/Desktop/SoroTask/keeper/src/poller.js": {"lines":{"total":258,"covered":0,"skipped":0,"pct":0},"functions":{"total":22,"covered":0,"skipped":0,"pct":0},"statements":{"total":260,"covered":0,"skipped":0,"pct":0},"branches":{"total":195,"covered":0,"skipped":0,"pct":0}} -,"/Users/aliphatic/Desktop/SoroTask/keeper/src/queue.js": {"lines":{"total":147,"covered":0,"skipped":0,"pct":0},"functions":{"total":19,"covered":0,"skipped":0,"pct":0},"statements":{"total":150,"covered":0,"skipped":0,"pct":0},"branches":{"total":108,"covered":0,"skipped":0,"pct":0}} -,"/Users/aliphatic/Desktop/SoroTask/keeper/src/registry.js": {"lines":{"total":136,"covered":0,"skipped":0,"pct":0},"functions":{"total":23,"covered":0,"skipped":0,"pct":0},"statements":{"total":145,"covered":0,"skipped":0,"pct":0},"branches":{"total":97,"covered":0,"skipped":0,"pct":0}} -,"/Users/aliphatic/Desktop/SoroTask/keeper/src/retry.js": {"lines":{"total":71,"covered":0,"skipped":0,"pct":0},"functions":{"total":13,"covered":0,"skipped":0,"pct":0},"statements":{"total":75,"covered":0,"skipped":0,"pct":0},"branches":{"total":78,"covered":0,"skipped":0,"pct":0}} +{"total": {"lines":{"total":706,"covered":274,"skipped":0,"pct":38.81},"statements":{"total":725,"covered":279,"skipped":0,"pct":38.48},"functions":{"total":112,"covered":49,"skipped":0,"pct":43.75},"branches":{"total":530,"covered":183,"skipped":0,"pct":34.52},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":"Unknown"}} +,"/home/ezekiel001/SoroTask/keeper/src/concurrency.js": {"lines":{"total":50,"covered":47,"skipped":0,"pct":94},"functions":{"total":8,"covered":6,"skipped":0,"pct":75},"statements":{"total":51,"covered":47,"skipped":0,"pct":92.15},"branches":{"total":26,"covered":23,"skipped":0,"pct":88.46}} +,"/home/ezekiel001/SoroTask/keeper/src/logger.js": {"lines":{"total":44,"covered":33,"skipped":0,"pct":75},"functions":{"total":27,"covered":18,"skipped":0,"pct":66.66},"statements":{"total":44,"covered":33,"skipped":0,"pct":75},"branches":{"total":26,"covered":15,"skipped":0,"pct":57.69}} +,"/home/ezekiel001/SoroTask/keeper/src/poller.js": {"lines":{"total":258,"covered":0,"skipped":0,"pct":0},"functions":{"total":22,"covered":0,"skipped":0,"pct":0},"statements":{"total":260,"covered":0,"skipped":0,"pct":0},"branches":{"total":195,"covered":0,"skipped":0,"pct":0}} +,"/home/ezekiel001/SoroTask/keeper/src/queue.js": {"lines":{"total":147,"covered":124,"skipped":0,"pct":84.35},"functions":{"total":19,"covered":12,"skipped":0,"pct":63.15},"statements":{"total":150,"covered":125,"skipped":0,"pct":83.33},"branches":{"total":108,"covered":74,"skipped":0,"pct":68.51}} +,"/home/ezekiel001/SoroTask/keeper/src/registry.js": {"lines":{"total":136,"covered":0,"skipped":0,"pct":0},"functions":{"total":23,"covered":0,"skipped":0,"pct":0},"statements":{"total":145,"covered":0,"skipped":0,"pct":0},"branches":{"total":97,"covered":0,"skipped":0,"pct":0}} +,"/home/ezekiel001/SoroTask/keeper/src/retry.js": {"lines":{"total":71,"covered":70,"skipped":0,"pct":98.59},"functions":{"total":13,"covered":13,"skipped":0,"pct":100},"statements":{"total":75,"covered":74,"skipped":0,"pct":98.66},"branches":{"total":78,"covered":71,"skipped":0,"pct":91.02}} } diff --git a/keeper/coverage/lcov-report/concurrency.js.html b/keeper/coverage/lcov-report/concurrency.js.html index 430402e4..35536fad 100644 --- a/keeper/coverage/lcov-report/concurrency.js.html +++ b/keeper/coverage/lcov-report/concurrency.js.html @@ -23,30 +23,30 @@
1
2
@@ -192,37 +192,37 @@ All files concurrency.js- - - - - +58x +58x +58x +58x +58x - - +58x +58x - - - +58x +140x +47x - - +93x +10x - - +83x +77x - - +77x +12x - - - - +77x +25x +3x +3x @@ -232,61 +232,61 @@All files concurrency.js- - +25x +3x - - - - +25x +25x +25x +25x - - - +58x +3x +3x - - +58x +58x - - +58x +52x - +58x - - +54x +54x - - - - +58x +61x +61x +61x - - - - +58x +3x +2x +2x - +58x - +58x @@ -296,7 +296,7 @@All files concurrency.js- +2x | /** * Creates a rate limiter that controls both concurrency (active tasks) * and throughput (requests per second). @@ -308,38 +308,38 @@ |