@datacules/agent-identity-compliance 0.11.0 → 0.11.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/LICENSE +109 -0
- package/dist/cjs/index.js +292 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.js +285 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/types/index.d.ts +186 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +18 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Datacules Agent Identity License — Version 1.0
|
|
2
|
+
Copyright (c) 2026 Datacules LLC. All rights reserved.
|
|
3
|
+
|
|
4
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
PREAMBLE
|
|
6
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
This software — Agent Identity & Auth Patterns — is developed and owned by
|
|
9
|
+
Datacules LLC. It is made available to the public as open-source software
|
|
10
|
+
under the permissive terms below.
|
|
11
|
+
|
|
12
|
+
Datacules LLC retains ownership and authorship of this software while
|
|
13
|
+
granting broad, royalty-free rights for anyone to use, copy, modify, and
|
|
14
|
+
distribute it — in commercial or non-commercial contexts — without requiring
|
|
15
|
+
that derivative works also become open source.
|
|
16
|
+
|
|
17
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
TERMS AND CONDITIONS
|
|
19
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
1. PERMISSION TO USE
|
|
22
|
+
|
|
23
|
+
Permission is hereby granted, free of charge, to any person or
|
|
24
|
+
organization obtaining a copy of this software and associated
|
|
25
|
+
documentation files (the "Software"), to use, copy, modify, merge,
|
|
26
|
+
publish, distribute, sublicense, and/or sell copies of the Software,
|
|
27
|
+
and to permit persons to whom the Software is furnished to do so,
|
|
28
|
+
subject to the conditions below.
|
|
29
|
+
|
|
30
|
+
2. ATTRIBUTION
|
|
31
|
+
|
|
32
|
+
a. Redistributions of source code must retain this copyright notice,
|
|
33
|
+
this list of conditions, and the disclaimer below.
|
|
34
|
+
|
|
35
|
+
b. Redistributions in binary form or as a product must reproduce this
|
|
36
|
+
copyright notice, this list of conditions, and the disclaimer in the
|
|
37
|
+
documentation and/or other materials provided with the distribution.
|
|
38
|
+
|
|
39
|
+
c. Neither the name "Datacules LLC" nor the names of its contributors
|
|
40
|
+
may be used to endorse or promote products derived from this Software
|
|
41
|
+
without prior written permission from Datacules LLC.
|
|
42
|
+
|
|
43
|
+
3. COMMERCIAL USE
|
|
44
|
+
|
|
45
|
+
Use of this Software in commercial products, SaaS platforms, internal
|
|
46
|
+
enterprise tools, or any revenue-generating context is explicitly
|
|
47
|
+
permitted without royalty, fee, or additional licensing agreement,
|
|
48
|
+
provided that the conditions in Section 2 (Attribution) are met.
|
|
49
|
+
|
|
50
|
+
4. NO COPYLEFT / NO VIRAL REQUIREMENT
|
|
51
|
+
|
|
52
|
+
This license does NOT require that derivative works, modifications,
|
|
53
|
+
or software that uses or embeds this Software be made open source.
|
|
54
|
+
You may incorporate this Software into proprietary or closed-source
|
|
55
|
+
products under your own license terms.
|
|
56
|
+
|
|
57
|
+
5. MODIFICATIONS
|
|
58
|
+
|
|
59
|
+
Modified versions of the Software may be distributed under the same
|
|
60
|
+
terms as this license or under any other permissive open-source
|
|
61
|
+
license (e.g. MIT, Apache 2.0, BSD), provided that:
|
|
62
|
+
|
|
63
|
+
a. The original copyright notice of Datacules LLC is preserved.
|
|
64
|
+
b. Modifications are clearly documented and distinguished from the
|
|
65
|
+
original work.
|
|
66
|
+
|
|
67
|
+
6. COMPATIBILITY
|
|
68
|
+
|
|
69
|
+
This license is compatible with other permissive open-source licenses
|
|
70
|
+
such as MIT, BSD 2-Clause, BSD 3-Clause, and Apache License 2.0. It
|
|
71
|
+
is also GPL-compatible — this Software may coexist with GPL-licensed
|
|
72
|
+
code, though this Software itself is not distributed under the GPL.
|
|
73
|
+
|
|
74
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
75
|
+
DISCLAIMER
|
|
76
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
THIS SOFTWARE IS PROVIDED BY DATACULES LLC AND CONTRIBUTORS "AS IS" AND
|
|
79
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
80
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
|
|
81
|
+
AND NON-INFRINGEMENT ARE DISCLAIMED.
|
|
82
|
+
|
|
83
|
+
IN NO EVENT SHALL DATACULES LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
84
|
+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
85
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
86
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
87
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
88
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
89
|
+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
90
|
+
|
|
91
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
92
|
+
SUMMARY (non-binding)
|
|
93
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
✔ Use freely — commercial, proprietary, or open-source projects
|
|
96
|
+
✔ Modify and distribute with or without changes
|
|
97
|
+
✔ Sell products built on this Software
|
|
98
|
+
✔ No royalties or fees
|
|
99
|
+
✔ No requirement to open-source your own code
|
|
100
|
+
✔ Attribution to Datacules LLC required in source and binary distributions
|
|
101
|
+
✗ Do not use "Datacules LLC" to endorse derived products without permission
|
|
102
|
+
|
|
103
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
104
|
+
CONTACT
|
|
105
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
Datacules LLC
|
|
108
|
+
For licensing enquiries: legal@datacules.com
|
|
109
|
+
Product: https://github.com/hvrcharon1/agent-identity
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ChainVerifier = exports.HashChainAuditLogger = exports.ComplianceReportGenerator = exports.MemoryReportStore = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
/** In-memory store for testing — populate with entries from your audit sink */
|
|
6
|
+
class MemoryReportStore {
|
|
7
|
+
constructor(entries) {
|
|
8
|
+
this.entries = entries;
|
|
9
|
+
}
|
|
10
|
+
async queryEntries(from, to) {
|
|
11
|
+
const start = new Date(from).getTime();
|
|
12
|
+
const end = new Date(to).getTime();
|
|
13
|
+
return this.entries.filter((e) => {
|
|
14
|
+
const t = new Date(e.timestamp).getTime();
|
|
15
|
+
return t >= start && t <= end;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.MemoryReportStore = MemoryReportStore;
|
|
20
|
+
// ─── Business hours helper ───────────────────────────────────────────────────────
|
|
21
|
+
function isOffHours(timestamp, startHour = 9, endHour = 18) {
|
|
22
|
+
const d = new Date(timestamp);
|
|
23
|
+
const hour = d.getUTCHours();
|
|
24
|
+
const day = d.getUTCDay(); // 0 = Sun, 6 = Sat
|
|
25
|
+
if (day === 0 || day === 6)
|
|
26
|
+
return true; // weekends
|
|
27
|
+
return hour < startHour || hour >= endHour;
|
|
28
|
+
}
|
|
29
|
+
class ComplianceReportGenerator {
|
|
30
|
+
constructor(config) {
|
|
31
|
+
this.config = config;
|
|
32
|
+
this.piiTags = config.piiTags ?? ['pii', 'phi', 'personal'];
|
|
33
|
+
this.bhStart = config.businessHoursStart ?? 9;
|
|
34
|
+
this.bhEnd = config.businessHoursEnd ?? 18;
|
|
35
|
+
}
|
|
36
|
+
async generate(request) {
|
|
37
|
+
const allEntries = await this.config.store.queryEntries(request.from, request.to);
|
|
38
|
+
// Filter by agent IDs if specified
|
|
39
|
+
const entries = request.agentIds?.length
|
|
40
|
+
? allEntries.filter((e) => request.agentIds.includes(e.userId))
|
|
41
|
+
: allEntries;
|
|
42
|
+
const agentAccessSummary = this.buildAgentAccessSummary(entries);
|
|
43
|
+
const piiResourceAccess = this.findPiiAccess(entries);
|
|
44
|
+
const offHoursAccess = this.findOffHoursAccess(entries);
|
|
45
|
+
const credentialRotationHistory = this.findRotationEvents(entries);
|
|
46
|
+
const anomalyEvents = this.findAnomalyEvents(entries);
|
|
47
|
+
const report = {
|
|
48
|
+
type: request.type,
|
|
49
|
+
generatedAt: new Date().toISOString(),
|
|
50
|
+
periodFrom: request.from,
|
|
51
|
+
periodTo: request.to,
|
|
52
|
+
agentAccessSummary,
|
|
53
|
+
piiResourceAccess,
|
|
54
|
+
offHoursAccess,
|
|
55
|
+
credentialRotationHistory,
|
|
56
|
+
anomalyEvents,
|
|
57
|
+
totalEntries: entries.length,
|
|
58
|
+
summary: this.buildSummary(request.type, entries.length, piiResourceAccess.length, offHoursAccess.length, anomalyEvents.length),
|
|
59
|
+
};
|
|
60
|
+
if (request.format === 'markdown') {
|
|
61
|
+
return { ...report, summary: this.buildMarkdownReport(report) };
|
|
62
|
+
}
|
|
63
|
+
return report;
|
|
64
|
+
}
|
|
65
|
+
buildAgentAccessSummary(entries) {
|
|
66
|
+
const map = new Map();
|
|
67
|
+
for (const e of entries) {
|
|
68
|
+
if (e.action.startsWith('credential.'))
|
|
69
|
+
continue; // system events
|
|
70
|
+
let s = map.get(e.userId);
|
|
71
|
+
if (!s) {
|
|
72
|
+
s = { userId: e.userId, credentialIds: [], resourceIds: [], actionCounts: {}, resolutionCount: 0, firstSeen: e.timestamp, lastSeen: e.timestamp };
|
|
73
|
+
map.set(e.userId, s);
|
|
74
|
+
}
|
|
75
|
+
if (!s.credentialIds.includes(e.credentialId))
|
|
76
|
+
s.credentialIds.push(e.credentialId);
|
|
77
|
+
if (!s.resourceIds.includes(e.resourceId))
|
|
78
|
+
s.resourceIds.push(e.resourceId);
|
|
79
|
+
s.actionCounts[e.action] = (s.actionCounts[e.action] ?? 0) + 1;
|
|
80
|
+
s.resolutionCount += 1;
|
|
81
|
+
if (e.timestamp < s.firstSeen)
|
|
82
|
+
s.firstSeen = e.timestamp;
|
|
83
|
+
if (e.timestamp > s.lastSeen)
|
|
84
|
+
s.lastSeen = e.timestamp;
|
|
85
|
+
}
|
|
86
|
+
return Array.from(map.values()).sort((a, b) => b.resolutionCount - a.resolutionCount);
|
|
87
|
+
}
|
|
88
|
+
findPiiAccess(entries) {
|
|
89
|
+
return entries.filter((e) => this.piiTags.some((tag) => e.resourceId.toLowerCase().includes(tag)));
|
|
90
|
+
}
|
|
91
|
+
findOffHoursAccess(entries) {
|
|
92
|
+
return entries
|
|
93
|
+
.filter((e) => !e.action.startsWith('credential.') && isOffHours(e.timestamp, this.bhStart, this.bhEnd))
|
|
94
|
+
.map((e) => ({ timestamp: e.timestamp, userId: e.userId, action: e.action, resourceId: e.resourceId, credentialId: e.credentialId }));
|
|
95
|
+
}
|
|
96
|
+
findRotationEvents(entries) {
|
|
97
|
+
return entries
|
|
98
|
+
.filter((e) => e.action === 'credential.rotated')
|
|
99
|
+
.map((e) => ({ credentialId: e.credentialId, rotatedAt: e.timestamp, triggeredBy: e.userId }));
|
|
100
|
+
}
|
|
101
|
+
findAnomalyEvents(entries) {
|
|
102
|
+
return entries
|
|
103
|
+
.filter((e) => e.action === 'credential.anomaly')
|
|
104
|
+
.map((e) => ({
|
|
105
|
+
timestamp: e.timestamp,
|
|
106
|
+
userId: e.userId,
|
|
107
|
+
credentialId: e.credentialId,
|
|
108
|
+
signal: e.signal ?? 'unknown',
|
|
109
|
+
severity: e.severity ?? 'unknown',
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
buildSummary(type, total, pii, offHours, anomalies) {
|
|
113
|
+
return `${type.toUpperCase()} report: ${total} total resolutions, ${pii} PII resource accesses, ${offHours} off-hours accesses, ${anomalies} anomaly events`;
|
|
114
|
+
}
|
|
115
|
+
buildMarkdownReport(report) {
|
|
116
|
+
const lines = [
|
|
117
|
+
`# Agent Identity — ${report.type.toUpperCase()} Compliance Report`,
|
|
118
|
+
`**Period:** ${report.periodFrom} – ${report.periodTo}`,
|
|
119
|
+
`**Generated:** ${report.generatedAt}`,
|
|
120
|
+
`**Total resolutions:** ${report.totalEntries}`,
|
|
121
|
+
'',
|
|
122
|
+
'## Agent Access Summary',
|
|
123
|
+
'| Agent | Resolutions | Credentials | Resources | First Seen | Last Seen |',
|
|
124
|
+
'|-------|-------------|-------------|-----------|------------|-----------|',
|
|
125
|
+
...report.agentAccessSummary.map((a) => `| ${a.userId} | ${a.resolutionCount} | ${a.credentialIds.length} | ${a.resourceIds.length} | ${a.firstSeen.slice(0, 10)} | ${a.lastSeen.slice(0, 10)} |`),
|
|
126
|
+
'',
|
|
127
|
+
`## PII Resource Access (${report.piiResourceAccess.length} events)`,
|
|
128
|
+
report.piiResourceAccess.length === 0 ? '_None_' : report.piiResourceAccess.map((e) => `- ${e.timestamp} | ${e.userId} | ${e.action} | ${e.resourceId}`).join('\n'),
|
|
129
|
+
'',
|
|
130
|
+
`## Off-Hours Access (${report.offHoursAccess.length} events)`,
|
|
131
|
+
report.offHoursAccess.length === 0 ? '_None_' : report.offHoursAccess.map((e) => `- ${e.timestamp} | ${e.userId} | ${e.action} | ${e.resourceId}`).join('\n'),
|
|
132
|
+
'',
|
|
133
|
+
`## Credential Rotation History (${report.credentialRotationHistory.length} rotations)`,
|
|
134
|
+
report.credentialRotationHistory.length === 0 ? '_None_' : report.credentialRotationHistory.map((r) => `- ${r.rotatedAt} | ${r.credentialId} | triggered by ${r.triggeredBy}`).join('\n'),
|
|
135
|
+
'',
|
|
136
|
+
`## Anomaly Events (${report.anomalyEvents.length} events)`,
|
|
137
|
+
report.anomalyEvents.length === 0 ? '_None_' : report.anomalyEvents.map((a) => `- ${a.timestamp} | ${a.userId} | ${a.credentialId} | ${a.signal} (${a.severity})`).join('\n'),
|
|
138
|
+
];
|
|
139
|
+
return lines.join('\n');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
exports.ComplianceReportGenerator = ComplianceReportGenerator;
|
|
143
|
+
/**
|
|
144
|
+
* Computes the SHA-256 hash for a single audit log entry.
|
|
145
|
+
*
|
|
146
|
+
* The hash input is:
|
|
147
|
+
* SHA256( JSON.stringify(coreFields) + prevHash )
|
|
148
|
+
*
|
|
149
|
+
* where coreFields are the entry's own data fields (excluding hash/prevHash
|
|
150
|
+
* themselves) sorted by key for deterministic serialisation.
|
|
151
|
+
*/
|
|
152
|
+
function computeEntryHash(entry, prevHash) {
|
|
153
|
+
// Exclude the chain fields from the payload so re-verification is stable
|
|
154
|
+
const { hash: _h, prevHash: _p, ...coreFields } = entry;
|
|
155
|
+
void _h;
|
|
156
|
+
void _p;
|
|
157
|
+
const sortedKeys = Object.keys(coreFields).sort();
|
|
158
|
+
const payload = {};
|
|
159
|
+
for (const k of sortedKeys) {
|
|
160
|
+
payload[k] = coreFields[k];
|
|
161
|
+
}
|
|
162
|
+
return (0, node_crypto_1.createHash)('sha256')
|
|
163
|
+
.update(JSON.stringify(payload) + prevHash)
|
|
164
|
+
.digest('hex');
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* HashChainAuditLogger wraps any existing AuditLogger and appends
|
|
168
|
+
* `hash` and `prevHash` fields to every entry before forwarding to
|
|
169
|
+
* the underlying sink.
|
|
170
|
+
*
|
|
171
|
+
* Each entry's hash covers its own data + the previous entry's hash,
|
|
172
|
+
* forming a SHA-256 linked list. Any retroactive modification to an
|
|
173
|
+
* entry breaks the chain from that point forward — detectable by
|
|
174
|
+
* ChainVerifier.verify() in O(n) time.
|
|
175
|
+
*
|
|
176
|
+
* Usage:
|
|
177
|
+
* const base = new ConsoleAuditLogger();
|
|
178
|
+
* const chained = new HashChainAuditLogger(base);
|
|
179
|
+
* const router = createRouter(credentials, rules, chained);
|
|
180
|
+
*
|
|
181
|
+
* The underlying sink receives ChainedAuditLogEntry objects. If it
|
|
182
|
+
* serialises to JSONL (one JSON object per line), that file can be
|
|
183
|
+
* verified offline with:
|
|
184
|
+
* agent-identity audit verify --file ./audit.jsonl
|
|
185
|
+
*/
|
|
186
|
+
class HashChainAuditLogger {
|
|
187
|
+
constructor(sink) {
|
|
188
|
+
this.sink = sink;
|
|
189
|
+
this.prevHash = '';
|
|
190
|
+
}
|
|
191
|
+
log(entry) {
|
|
192
|
+
const hash = computeEntryHash(entry, this.prevHash);
|
|
193
|
+
const chained = {
|
|
194
|
+
...entry,
|
|
195
|
+
prevHash: this.prevHash,
|
|
196
|
+
hash,
|
|
197
|
+
};
|
|
198
|
+
this.prevHash = hash;
|
|
199
|
+
this.sink.log(chained);
|
|
200
|
+
}
|
|
201
|
+
/** Returns the hash of the most recently logged entry (the current chain tip) */
|
|
202
|
+
get currentHash() {
|
|
203
|
+
return this.prevHash;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.HashChainAuditLogger = HashChainAuditLogger;
|
|
207
|
+
/**
|
|
208
|
+
* Verifies a sequence of ChainedAuditLogEntry objects.
|
|
209
|
+
*
|
|
210
|
+
* Replays the SHA-256 chain from the beginning. The first entry must
|
|
211
|
+
* have prevHash === '' (empty string). Every subsequent entry's hash
|
|
212
|
+
* must equal SHA256(sortedEntryData + prevEntry.hash).
|
|
213
|
+
*
|
|
214
|
+
* Any single field modification in any entry will break the chain
|
|
215
|
+
* from that entry onward.
|
|
216
|
+
*/
|
|
217
|
+
class ChainVerifier {
|
|
218
|
+
/**
|
|
219
|
+
* Verify an in-memory array of entries.
|
|
220
|
+
*
|
|
221
|
+
* @param entries - Array of entries parsed from an audit log
|
|
222
|
+
*/
|
|
223
|
+
static verify(entries) {
|
|
224
|
+
if (entries.length === 0) {
|
|
225
|
+
return { intact: false, entryCount: 0, rootHash: null, brokenAt: null, brokenReason: 'Log is empty — nothing to verify' };
|
|
226
|
+
}
|
|
227
|
+
let prevHash = '';
|
|
228
|
+
for (let i = 0; i < entries.length; i++) {
|
|
229
|
+
const entry = entries[i];
|
|
230
|
+
// Check the recorded prevHash matches our running chain
|
|
231
|
+
if (entry.prevHash !== prevHash) {
|
|
232
|
+
return {
|
|
233
|
+
intact: false,
|
|
234
|
+
entryCount: entries.length,
|
|
235
|
+
rootHash: entries[i - 1]?.hash ?? null,
|
|
236
|
+
brokenAt: i,
|
|
237
|
+
brokenReason: `Entry ${i}: prevHash mismatch — expected ${prevHash.slice(0, 16)}… got ${entry.prevHash.slice(0, 16)}…`,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
// Recompute the hash and check it matches what was recorded
|
|
241
|
+
const expected = computeEntryHash(entry, prevHash);
|
|
242
|
+
if (entry.hash !== expected) {
|
|
243
|
+
return {
|
|
244
|
+
intact: false,
|
|
245
|
+
entryCount: entries.length,
|
|
246
|
+
rootHash: entries[i - 1]?.hash ?? null,
|
|
247
|
+
brokenAt: i,
|
|
248
|
+
brokenReason: `Entry ${i}: hash mismatch — entry data appears to have been modified`,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
prevHash = entry.hash;
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
intact: true,
|
|
255
|
+
entryCount: entries.length,
|
|
256
|
+
rootHash: prevHash,
|
|
257
|
+
brokenAt: null,
|
|
258
|
+
brokenReason: null,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Parse a JSONL string (one JSON object per line) and verify the chain.
|
|
263
|
+
* Blank lines and lines that fail JSON.parse are skipped with a warning.
|
|
264
|
+
*
|
|
265
|
+
* @param jsonl - Full JSONL file content as a string
|
|
266
|
+
*/
|
|
267
|
+
static verifyJsonl(jsonl) {
|
|
268
|
+
const entries = [];
|
|
269
|
+
const lines = jsonl.split('\n');
|
|
270
|
+
for (let i = 0; i < lines.length; i++) {
|
|
271
|
+
const line = lines[i].trim();
|
|
272
|
+
if (!line)
|
|
273
|
+
continue;
|
|
274
|
+
try {
|
|
275
|
+
entries.push(JSON.parse(line));
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
// Non-JSON line — treat as a chain break
|
|
279
|
+
return {
|
|
280
|
+
intact: false,
|
|
281
|
+
entryCount: entries.length,
|
|
282
|
+
rootHash: entries[entries.length - 1]?.hash ?? null,
|
|
283
|
+
brokenAt: entries.length,
|
|
284
|
+
brokenReason: `Line ${i + 1}: failed to parse as JSON — log file may be corrupted`,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return ChainVerifier.verify(entries);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
exports.ChainVerifier = ChainVerifier;
|
|
292
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/** In-memory store for testing — populate with entries from your audit sink */
|
|
3
|
+
export class MemoryReportStore {
|
|
4
|
+
constructor(entries) {
|
|
5
|
+
this.entries = entries;
|
|
6
|
+
}
|
|
7
|
+
async queryEntries(from, to) {
|
|
8
|
+
const start = new Date(from).getTime();
|
|
9
|
+
const end = new Date(to).getTime();
|
|
10
|
+
return this.entries.filter((e) => {
|
|
11
|
+
const t = new Date(e.timestamp).getTime();
|
|
12
|
+
return t >= start && t <= end;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// ─── Business hours helper ───────────────────────────────────────────────────────
|
|
17
|
+
function isOffHours(timestamp, startHour = 9, endHour = 18) {
|
|
18
|
+
const d = new Date(timestamp);
|
|
19
|
+
const hour = d.getUTCHours();
|
|
20
|
+
const day = d.getUTCDay(); // 0 = Sun, 6 = Sat
|
|
21
|
+
if (day === 0 || day === 6)
|
|
22
|
+
return true; // weekends
|
|
23
|
+
return hour < startHour || hour >= endHour;
|
|
24
|
+
}
|
|
25
|
+
export class ComplianceReportGenerator {
|
|
26
|
+
constructor(config) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
this.piiTags = config.piiTags ?? ['pii', 'phi', 'personal'];
|
|
29
|
+
this.bhStart = config.businessHoursStart ?? 9;
|
|
30
|
+
this.bhEnd = config.businessHoursEnd ?? 18;
|
|
31
|
+
}
|
|
32
|
+
async generate(request) {
|
|
33
|
+
const allEntries = await this.config.store.queryEntries(request.from, request.to);
|
|
34
|
+
// Filter by agent IDs if specified
|
|
35
|
+
const entries = request.agentIds?.length
|
|
36
|
+
? allEntries.filter((e) => request.agentIds.includes(e.userId))
|
|
37
|
+
: allEntries;
|
|
38
|
+
const agentAccessSummary = this.buildAgentAccessSummary(entries);
|
|
39
|
+
const piiResourceAccess = this.findPiiAccess(entries);
|
|
40
|
+
const offHoursAccess = this.findOffHoursAccess(entries);
|
|
41
|
+
const credentialRotationHistory = this.findRotationEvents(entries);
|
|
42
|
+
const anomalyEvents = this.findAnomalyEvents(entries);
|
|
43
|
+
const report = {
|
|
44
|
+
type: request.type,
|
|
45
|
+
generatedAt: new Date().toISOString(),
|
|
46
|
+
periodFrom: request.from,
|
|
47
|
+
periodTo: request.to,
|
|
48
|
+
agentAccessSummary,
|
|
49
|
+
piiResourceAccess,
|
|
50
|
+
offHoursAccess,
|
|
51
|
+
credentialRotationHistory,
|
|
52
|
+
anomalyEvents,
|
|
53
|
+
totalEntries: entries.length,
|
|
54
|
+
summary: this.buildSummary(request.type, entries.length, piiResourceAccess.length, offHoursAccess.length, anomalyEvents.length),
|
|
55
|
+
};
|
|
56
|
+
if (request.format === 'markdown') {
|
|
57
|
+
return { ...report, summary: this.buildMarkdownReport(report) };
|
|
58
|
+
}
|
|
59
|
+
return report;
|
|
60
|
+
}
|
|
61
|
+
buildAgentAccessSummary(entries) {
|
|
62
|
+
const map = new Map();
|
|
63
|
+
for (const e of entries) {
|
|
64
|
+
if (e.action.startsWith('credential.'))
|
|
65
|
+
continue; // system events
|
|
66
|
+
let s = map.get(e.userId);
|
|
67
|
+
if (!s) {
|
|
68
|
+
s = { userId: e.userId, credentialIds: [], resourceIds: [], actionCounts: {}, resolutionCount: 0, firstSeen: e.timestamp, lastSeen: e.timestamp };
|
|
69
|
+
map.set(e.userId, s);
|
|
70
|
+
}
|
|
71
|
+
if (!s.credentialIds.includes(e.credentialId))
|
|
72
|
+
s.credentialIds.push(e.credentialId);
|
|
73
|
+
if (!s.resourceIds.includes(e.resourceId))
|
|
74
|
+
s.resourceIds.push(e.resourceId);
|
|
75
|
+
s.actionCounts[e.action] = (s.actionCounts[e.action] ?? 0) + 1;
|
|
76
|
+
s.resolutionCount += 1;
|
|
77
|
+
if (e.timestamp < s.firstSeen)
|
|
78
|
+
s.firstSeen = e.timestamp;
|
|
79
|
+
if (e.timestamp > s.lastSeen)
|
|
80
|
+
s.lastSeen = e.timestamp;
|
|
81
|
+
}
|
|
82
|
+
return Array.from(map.values()).sort((a, b) => b.resolutionCount - a.resolutionCount);
|
|
83
|
+
}
|
|
84
|
+
findPiiAccess(entries) {
|
|
85
|
+
return entries.filter((e) => this.piiTags.some((tag) => e.resourceId.toLowerCase().includes(tag)));
|
|
86
|
+
}
|
|
87
|
+
findOffHoursAccess(entries) {
|
|
88
|
+
return entries
|
|
89
|
+
.filter((e) => !e.action.startsWith('credential.') && isOffHours(e.timestamp, this.bhStart, this.bhEnd))
|
|
90
|
+
.map((e) => ({ timestamp: e.timestamp, userId: e.userId, action: e.action, resourceId: e.resourceId, credentialId: e.credentialId }));
|
|
91
|
+
}
|
|
92
|
+
findRotationEvents(entries) {
|
|
93
|
+
return entries
|
|
94
|
+
.filter((e) => e.action === 'credential.rotated')
|
|
95
|
+
.map((e) => ({ credentialId: e.credentialId, rotatedAt: e.timestamp, triggeredBy: e.userId }));
|
|
96
|
+
}
|
|
97
|
+
findAnomalyEvents(entries) {
|
|
98
|
+
return entries
|
|
99
|
+
.filter((e) => e.action === 'credential.anomaly')
|
|
100
|
+
.map((e) => ({
|
|
101
|
+
timestamp: e.timestamp,
|
|
102
|
+
userId: e.userId,
|
|
103
|
+
credentialId: e.credentialId,
|
|
104
|
+
signal: e.signal ?? 'unknown',
|
|
105
|
+
severity: e.severity ?? 'unknown',
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
buildSummary(type, total, pii, offHours, anomalies) {
|
|
109
|
+
return `${type.toUpperCase()} report: ${total} total resolutions, ${pii} PII resource accesses, ${offHours} off-hours accesses, ${anomalies} anomaly events`;
|
|
110
|
+
}
|
|
111
|
+
buildMarkdownReport(report) {
|
|
112
|
+
const lines = [
|
|
113
|
+
`# Agent Identity — ${report.type.toUpperCase()} Compliance Report`,
|
|
114
|
+
`**Period:** ${report.periodFrom} – ${report.periodTo}`,
|
|
115
|
+
`**Generated:** ${report.generatedAt}`,
|
|
116
|
+
`**Total resolutions:** ${report.totalEntries}`,
|
|
117
|
+
'',
|
|
118
|
+
'## Agent Access Summary',
|
|
119
|
+
'| Agent | Resolutions | Credentials | Resources | First Seen | Last Seen |',
|
|
120
|
+
'|-------|-------------|-------------|-----------|------------|-----------|',
|
|
121
|
+
...report.agentAccessSummary.map((a) => `| ${a.userId} | ${a.resolutionCount} | ${a.credentialIds.length} | ${a.resourceIds.length} | ${a.firstSeen.slice(0, 10)} | ${a.lastSeen.slice(0, 10)} |`),
|
|
122
|
+
'',
|
|
123
|
+
`## PII Resource Access (${report.piiResourceAccess.length} events)`,
|
|
124
|
+
report.piiResourceAccess.length === 0 ? '_None_' : report.piiResourceAccess.map((e) => `- ${e.timestamp} | ${e.userId} | ${e.action} | ${e.resourceId}`).join('\n'),
|
|
125
|
+
'',
|
|
126
|
+
`## Off-Hours Access (${report.offHoursAccess.length} events)`,
|
|
127
|
+
report.offHoursAccess.length === 0 ? '_None_' : report.offHoursAccess.map((e) => `- ${e.timestamp} | ${e.userId} | ${e.action} | ${e.resourceId}`).join('\n'),
|
|
128
|
+
'',
|
|
129
|
+
`## Credential Rotation History (${report.credentialRotationHistory.length} rotations)`,
|
|
130
|
+
report.credentialRotationHistory.length === 0 ? '_None_' : report.credentialRotationHistory.map((r) => `- ${r.rotatedAt} | ${r.credentialId} | triggered by ${r.triggeredBy}`).join('\n'),
|
|
131
|
+
'',
|
|
132
|
+
`## Anomaly Events (${report.anomalyEvents.length} events)`,
|
|
133
|
+
report.anomalyEvents.length === 0 ? '_None_' : report.anomalyEvents.map((a) => `- ${a.timestamp} | ${a.userId} | ${a.credentialId} | ${a.signal} (${a.severity})`).join('\n'),
|
|
134
|
+
];
|
|
135
|
+
return lines.join('\n');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Computes the SHA-256 hash for a single audit log entry.
|
|
140
|
+
*
|
|
141
|
+
* The hash input is:
|
|
142
|
+
* SHA256( JSON.stringify(coreFields) + prevHash )
|
|
143
|
+
*
|
|
144
|
+
* where coreFields are the entry's own data fields (excluding hash/prevHash
|
|
145
|
+
* themselves) sorted by key for deterministic serialisation.
|
|
146
|
+
*/
|
|
147
|
+
function computeEntryHash(entry, prevHash) {
|
|
148
|
+
// Exclude the chain fields from the payload so re-verification is stable
|
|
149
|
+
const { hash: _h, prevHash: _p, ...coreFields } = entry;
|
|
150
|
+
void _h;
|
|
151
|
+
void _p;
|
|
152
|
+
const sortedKeys = Object.keys(coreFields).sort();
|
|
153
|
+
const payload = {};
|
|
154
|
+
for (const k of sortedKeys) {
|
|
155
|
+
payload[k] = coreFields[k];
|
|
156
|
+
}
|
|
157
|
+
return createHash('sha256')
|
|
158
|
+
.update(JSON.stringify(payload) + prevHash)
|
|
159
|
+
.digest('hex');
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* HashChainAuditLogger wraps any existing AuditLogger and appends
|
|
163
|
+
* `hash` and `prevHash` fields to every entry before forwarding to
|
|
164
|
+
* the underlying sink.
|
|
165
|
+
*
|
|
166
|
+
* Each entry's hash covers its own data + the previous entry's hash,
|
|
167
|
+
* forming a SHA-256 linked list. Any retroactive modification to an
|
|
168
|
+
* entry breaks the chain from that point forward — detectable by
|
|
169
|
+
* ChainVerifier.verify() in O(n) time.
|
|
170
|
+
*
|
|
171
|
+
* Usage:
|
|
172
|
+
* const base = new ConsoleAuditLogger();
|
|
173
|
+
* const chained = new HashChainAuditLogger(base);
|
|
174
|
+
* const router = createRouter(credentials, rules, chained);
|
|
175
|
+
*
|
|
176
|
+
* The underlying sink receives ChainedAuditLogEntry objects. If it
|
|
177
|
+
* serialises to JSONL (one JSON object per line), that file can be
|
|
178
|
+
* verified offline with:
|
|
179
|
+
* agent-identity audit verify --file ./audit.jsonl
|
|
180
|
+
*/
|
|
181
|
+
export class HashChainAuditLogger {
|
|
182
|
+
constructor(sink) {
|
|
183
|
+
this.sink = sink;
|
|
184
|
+
this.prevHash = '';
|
|
185
|
+
}
|
|
186
|
+
log(entry) {
|
|
187
|
+
const hash = computeEntryHash(entry, this.prevHash);
|
|
188
|
+
const chained = {
|
|
189
|
+
...entry,
|
|
190
|
+
prevHash: this.prevHash,
|
|
191
|
+
hash,
|
|
192
|
+
};
|
|
193
|
+
this.prevHash = hash;
|
|
194
|
+
this.sink.log(chained);
|
|
195
|
+
}
|
|
196
|
+
/** Returns the hash of the most recently logged entry (the current chain tip) */
|
|
197
|
+
get currentHash() {
|
|
198
|
+
return this.prevHash;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Verifies a sequence of ChainedAuditLogEntry objects.
|
|
203
|
+
*
|
|
204
|
+
* Replays the SHA-256 chain from the beginning. The first entry must
|
|
205
|
+
* have prevHash === '' (empty string). Every subsequent entry's hash
|
|
206
|
+
* must equal SHA256(sortedEntryData + prevEntry.hash).
|
|
207
|
+
*
|
|
208
|
+
* Any single field modification in any entry will break the chain
|
|
209
|
+
* from that entry onward.
|
|
210
|
+
*/
|
|
211
|
+
export class ChainVerifier {
|
|
212
|
+
/**
|
|
213
|
+
* Verify an in-memory array of entries.
|
|
214
|
+
*
|
|
215
|
+
* @param entries - Array of entries parsed from an audit log
|
|
216
|
+
*/
|
|
217
|
+
static verify(entries) {
|
|
218
|
+
if (entries.length === 0) {
|
|
219
|
+
return { intact: false, entryCount: 0, rootHash: null, brokenAt: null, brokenReason: 'Log is empty — nothing to verify' };
|
|
220
|
+
}
|
|
221
|
+
let prevHash = '';
|
|
222
|
+
for (let i = 0; i < entries.length; i++) {
|
|
223
|
+
const entry = entries[i];
|
|
224
|
+
// Check the recorded prevHash matches our running chain
|
|
225
|
+
if (entry.prevHash !== prevHash) {
|
|
226
|
+
return {
|
|
227
|
+
intact: false,
|
|
228
|
+
entryCount: entries.length,
|
|
229
|
+
rootHash: entries[i - 1]?.hash ?? null,
|
|
230
|
+
brokenAt: i,
|
|
231
|
+
brokenReason: `Entry ${i}: prevHash mismatch — expected ${prevHash.slice(0, 16)}… got ${entry.prevHash.slice(0, 16)}…`,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
// Recompute the hash and check it matches what was recorded
|
|
235
|
+
const expected = computeEntryHash(entry, prevHash);
|
|
236
|
+
if (entry.hash !== expected) {
|
|
237
|
+
return {
|
|
238
|
+
intact: false,
|
|
239
|
+
entryCount: entries.length,
|
|
240
|
+
rootHash: entries[i - 1]?.hash ?? null,
|
|
241
|
+
brokenAt: i,
|
|
242
|
+
brokenReason: `Entry ${i}: hash mismatch — entry data appears to have been modified`,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
prevHash = entry.hash;
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
intact: true,
|
|
249
|
+
entryCount: entries.length,
|
|
250
|
+
rootHash: prevHash,
|
|
251
|
+
brokenAt: null,
|
|
252
|
+
brokenReason: null,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Parse a JSONL string (one JSON object per line) and verify the chain.
|
|
257
|
+
* Blank lines and lines that fail JSON.parse are skipped with a warning.
|
|
258
|
+
*
|
|
259
|
+
* @param jsonl - Full JSONL file content as a string
|
|
260
|
+
*/
|
|
261
|
+
static verifyJsonl(jsonl) {
|
|
262
|
+
const entries = [];
|
|
263
|
+
const lines = jsonl.split('\n');
|
|
264
|
+
for (let i = 0; i < lines.length; i++) {
|
|
265
|
+
const line = lines[i].trim();
|
|
266
|
+
if (!line)
|
|
267
|
+
continue;
|
|
268
|
+
try {
|
|
269
|
+
entries.push(JSON.parse(line));
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
// Non-JSON line — treat as a chain break
|
|
273
|
+
return {
|
|
274
|
+
intact: false,
|
|
275
|
+
entryCount: entries.length,
|
|
276
|
+
rootHash: entries[entries.length - 1]?.hash ?? null,
|
|
277
|
+
brokenAt: entries.length,
|
|
278
|
+
brokenReason: `Line ${i + 1}: failed to parse as JSON — log file may be corrupted`,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return ChainVerifier.verify(entries);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @datacules/agent-identity-compliance
|
|
3
|
+
*
|
|
4
|
+
* Automated compliance report generation from agent-identity audit logs.
|
|
5
|
+
* Answers regulatory questions directly — no custom queries needed.
|
|
6
|
+
*
|
|
7
|
+
* Supported report types:
|
|
8
|
+
* soc2 — SOC 2 CC6 Logical and Physical Access Controls
|
|
9
|
+
* gdpr — GDPR Article 30 Records of Processing Activities
|
|
10
|
+
* hipaa — HIPAA §164.312 Access Controls
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* const generator = new ComplianceReportGenerator({ store });
|
|
14
|
+
* const report = await generator.generate({
|
|
15
|
+
* type: 'soc2',
|
|
16
|
+
* from: '2026-01-01T00:00:00Z',
|
|
17
|
+
* to: '2026-03-31T23:59:59Z',
|
|
18
|
+
* });
|
|
19
|
+
*/
|
|
20
|
+
import type { AuditLogEntry, AuditLogger } from '@datacules/agent-identity';
|
|
21
|
+
export type ReportType = 'soc2' | 'gdpr' | 'hipaa' | 'custom';
|
|
22
|
+
export interface ReportRequest {
|
|
23
|
+
type: ReportType;
|
|
24
|
+
/** ISO 8601 start of the reporting period */
|
|
25
|
+
from: string;
|
|
26
|
+
/** ISO 8601 end of the reporting period */
|
|
27
|
+
to: string;
|
|
28
|
+
/** Only include entries for these agent IDs (optional — all agents if omitted) */
|
|
29
|
+
agentIds?: string[];
|
|
30
|
+
/** Only include entries where resourceId tags include these tags (optional) */
|
|
31
|
+
resourceTags?: string[];
|
|
32
|
+
format?: 'json' | 'markdown';
|
|
33
|
+
}
|
|
34
|
+
export interface AgentAccessSummary {
|
|
35
|
+
userId: string;
|
|
36
|
+
credentialIds: string[];
|
|
37
|
+
resourceIds: string[];
|
|
38
|
+
actionCounts: Record<string, number>;
|
|
39
|
+
resolutionCount: number;
|
|
40
|
+
firstSeen: string;
|
|
41
|
+
lastSeen: string;
|
|
42
|
+
}
|
|
43
|
+
export interface CredentialRotationEntry {
|
|
44
|
+
credentialId: string;
|
|
45
|
+
rotatedAt: string;
|
|
46
|
+
triggeredBy: string;
|
|
47
|
+
}
|
|
48
|
+
export interface AnomalyEventEntry {
|
|
49
|
+
timestamp: string;
|
|
50
|
+
userId: string;
|
|
51
|
+
credentialId: string;
|
|
52
|
+
signal: string;
|
|
53
|
+
severity: string;
|
|
54
|
+
}
|
|
55
|
+
export interface OffHoursEntry {
|
|
56
|
+
timestamp: string;
|
|
57
|
+
userId: string;
|
|
58
|
+
action: string;
|
|
59
|
+
resourceId: string;
|
|
60
|
+
credentialId: string;
|
|
61
|
+
}
|
|
62
|
+
export interface ComplianceReport {
|
|
63
|
+
type: ReportType;
|
|
64
|
+
generatedAt: string;
|
|
65
|
+
periodFrom: string;
|
|
66
|
+
periodTo: string;
|
|
67
|
+
agentAccessSummary: AgentAccessSummary[];
|
|
68
|
+
piiResourceAccess: AuditLogEntry[];
|
|
69
|
+
offHoursAccess: OffHoursEntry[];
|
|
70
|
+
credentialRotationHistory: CredentialRotationEntry[];
|
|
71
|
+
anomalyEvents: AnomalyEventEntry[];
|
|
72
|
+
totalEntries: number;
|
|
73
|
+
summary: string;
|
|
74
|
+
}
|
|
75
|
+
export interface ReportStore {
|
|
76
|
+
queryEntries(from: string, to: string): Promise<AuditLogEntry[]>;
|
|
77
|
+
}
|
|
78
|
+
/** In-memory store for testing — populate with entries from your audit sink */
|
|
79
|
+
export declare class MemoryReportStore implements ReportStore {
|
|
80
|
+
private readonly entries;
|
|
81
|
+
constructor(entries: AuditLogEntry[]);
|
|
82
|
+
queryEntries(from: string, to: string): Promise<AuditLogEntry[]>;
|
|
83
|
+
}
|
|
84
|
+
export interface ComplianceReportGeneratorConfig {
|
|
85
|
+
store: ReportStore;
|
|
86
|
+
/** Tags that identify PII resources (default: ['pii', 'phi', 'personal']) */
|
|
87
|
+
piiTags?: string[];
|
|
88
|
+
/** Business hours start (UTC, default: 9) */
|
|
89
|
+
businessHoursStart?: number;
|
|
90
|
+
/** Business hours end (UTC, default: 18) */
|
|
91
|
+
businessHoursEnd?: number;
|
|
92
|
+
}
|
|
93
|
+
export declare class ComplianceReportGenerator {
|
|
94
|
+
private readonly config;
|
|
95
|
+
private readonly piiTags;
|
|
96
|
+
private readonly bhStart;
|
|
97
|
+
private readonly bhEnd;
|
|
98
|
+
constructor(config: ComplianceReportGeneratorConfig);
|
|
99
|
+
generate(request: ReportRequest): Promise<ComplianceReport>;
|
|
100
|
+
private buildAgentAccessSummary;
|
|
101
|
+
private findPiiAccess;
|
|
102
|
+
private findOffHoursAccess;
|
|
103
|
+
private findRotationEvents;
|
|
104
|
+
private findAnomalyEvents;
|
|
105
|
+
private buildSummary;
|
|
106
|
+
private buildMarkdownReport;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A chained audit log entry — extends the base AuditLogEntry with two
|
|
110
|
+
* additional fields that form the tamper-evident SHA-256 chain.
|
|
111
|
+
*/
|
|
112
|
+
export interface ChainedAuditLogEntry extends AuditLogEntry {
|
|
113
|
+
/** SHA-256 hash of (serialised entry data + prevHash) */
|
|
114
|
+
hash: string;
|
|
115
|
+
/** Hash of the immediately preceding entry, or '' for the first entry */
|
|
116
|
+
prevHash: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Result returned by ChainVerifier.verify()
|
|
120
|
+
*/
|
|
121
|
+
export interface VerificationResult {
|
|
122
|
+
/** true only if every entry's hash recomputes correctly */
|
|
123
|
+
intact: boolean;
|
|
124
|
+
/** Number of entries verified */
|
|
125
|
+
entryCount: number;
|
|
126
|
+
/** SHA-256 hash of the last valid entry (the chain root for anchoring) */
|
|
127
|
+
rootHash: string | null;
|
|
128
|
+
/** Index of the first broken link (null if intact) */
|
|
129
|
+
brokenAt: number | null;
|
|
130
|
+
/** Human-readable reason for breakage (null if intact) */
|
|
131
|
+
brokenReason: string | null;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* HashChainAuditLogger wraps any existing AuditLogger and appends
|
|
135
|
+
* `hash` and `prevHash` fields to every entry before forwarding to
|
|
136
|
+
* the underlying sink.
|
|
137
|
+
*
|
|
138
|
+
* Each entry's hash covers its own data + the previous entry's hash,
|
|
139
|
+
* forming a SHA-256 linked list. Any retroactive modification to an
|
|
140
|
+
* entry breaks the chain from that point forward — detectable by
|
|
141
|
+
* ChainVerifier.verify() in O(n) time.
|
|
142
|
+
*
|
|
143
|
+
* Usage:
|
|
144
|
+
* const base = new ConsoleAuditLogger();
|
|
145
|
+
* const chained = new HashChainAuditLogger(base);
|
|
146
|
+
* const router = createRouter(credentials, rules, chained);
|
|
147
|
+
*
|
|
148
|
+
* The underlying sink receives ChainedAuditLogEntry objects. If it
|
|
149
|
+
* serialises to JSONL (one JSON object per line), that file can be
|
|
150
|
+
* verified offline with:
|
|
151
|
+
* agent-identity audit verify --file ./audit.jsonl
|
|
152
|
+
*/
|
|
153
|
+
export declare class HashChainAuditLogger implements AuditLogger {
|
|
154
|
+
private readonly sink;
|
|
155
|
+
private prevHash;
|
|
156
|
+
constructor(sink: AuditLogger);
|
|
157
|
+
log(entry: AuditLogEntry): void;
|
|
158
|
+
/** Returns the hash of the most recently logged entry (the current chain tip) */
|
|
159
|
+
get currentHash(): string;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Verifies a sequence of ChainedAuditLogEntry objects.
|
|
163
|
+
*
|
|
164
|
+
* Replays the SHA-256 chain from the beginning. The first entry must
|
|
165
|
+
* have prevHash === '' (empty string). Every subsequent entry's hash
|
|
166
|
+
* must equal SHA256(sortedEntryData + prevEntry.hash).
|
|
167
|
+
*
|
|
168
|
+
* Any single field modification in any entry will break the chain
|
|
169
|
+
* from that entry onward.
|
|
170
|
+
*/
|
|
171
|
+
export declare class ChainVerifier {
|
|
172
|
+
/**
|
|
173
|
+
* Verify an in-memory array of entries.
|
|
174
|
+
*
|
|
175
|
+
* @param entries - Array of entries parsed from an audit log
|
|
176
|
+
*/
|
|
177
|
+
static verify(entries: ChainedAuditLogEntry[]): VerificationResult;
|
|
178
|
+
/**
|
|
179
|
+
* Parse a JSONL string (one JSON object per line) and verify the chain.
|
|
180
|
+
* Blank lines and lines that fail JSON.parse are skipped with a warning.
|
|
181
|
+
*
|
|
182
|
+
* @param jsonl - Full JSONL file content as a string
|
|
183
|
+
*/
|
|
184
|
+
static verifyJsonl(jsonl: string): VerificationResult;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datacules/agent-identity-compliance",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Compliance report generator + tamper-evident audit log for @datacules/agent-identity — SOC 2, GDPR, HIPAA reports, SHA-256 chain verification CLI",
|
|
6
6
|
"author": "Datacules LLC",
|
|
7
|
-
"license": "
|
|
7
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "https://github.com/hvrcharon1/agent-identity.git",
|
|
@@ -26,10 +26,11 @@
|
|
|
26
26
|
"files": [
|
|
27
27
|
"dist",
|
|
28
28
|
"bin",
|
|
29
|
-
"README.md"
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE"
|
|
30
31
|
],
|
|
31
32
|
"scripts": {
|
|
32
|
-
"build": "tsc -p tsconfig.build.json",
|
|
33
|
+
"build": "tsc -p tsconfig.build.json && tsc -p tsconfig.cjs.json",
|
|
33
34
|
"type-check": "tsc --noEmit",
|
|
34
35
|
"lint": "eslint src --ext .ts"
|
|
35
36
|
},
|
|
@@ -38,6 +39,17 @@
|
|
|
38
39
|
"typescript": "^5"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
|
-
"@datacules/agent-identity": "^0.
|
|
42
|
-
}
|
|
42
|
+
"@datacules/agent-identity": "^0.11.1"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"agent-identity",
|
|
46
|
+
"compliance",
|
|
47
|
+
"soc2",
|
|
48
|
+
"gdpr",
|
|
49
|
+
"hipaa",
|
|
50
|
+
"audit",
|
|
51
|
+
"hash-chain",
|
|
52
|
+
"ai-agents",
|
|
53
|
+
"datacules"
|
|
54
|
+
]
|
|
43
55
|
}
|