opena2a-cli 0.1.1 → 0.1.2
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/README.md +2 -2
- package/dist/adapters/registry.js +1 -1
- package/dist/adapters/registry.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +78 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/protect.d.ts +2 -0
- package/dist/commands/protect.d.ts.map +1 -1
- package/dist/commands/protect.js +56 -10
- package/dist/commands/protect.js.map +1 -1
- package/dist/commands/runtime.d.ts +1 -1
- package/dist/commands/runtime.js +5 -5
- package/dist/commands/runtime.js.map +1 -1
- package/dist/commands/self-register.js +6 -6
- package/dist/commands/self-register.js.map +1 -1
- package/dist/commands/shield.d.ts +36 -0
- package/dist/commands/shield.d.ts.map +1 -0
- package/dist/commands/shield.js +834 -0
- package/dist/commands/shield.js.map +1 -0
- package/dist/commands/verify.js +1 -1
- package/dist/commands/verify.js.map +1 -1
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -1
- package/dist/shield/detect.d.ts +18 -0
- package/dist/shield/detect.d.ts.map +1 -0
- package/dist/shield/detect.js +402 -0
- package/dist/shield/detect.js.map +1 -0
- package/dist/shield/events.d.ts +65 -0
- package/dist/shield/events.d.ts.map +1 -0
- package/dist/shield/events.js +342 -0
- package/dist/shield/events.js.map +1 -0
- package/dist/shield/init.d.ts +22 -0
- package/dist/shield/init.d.ts.map +1 -0
- package/dist/shield/init.js +290 -0
- package/dist/shield/init.js.map +1 -0
- package/dist/shield/integrity.d.ts +75 -0
- package/dist/shield/integrity.d.ts.map +1 -0
- package/dist/shield/integrity.js +435 -0
- package/dist/shield/integrity.js.map +1 -0
- package/dist/shield/llm-backend.d.ts +36 -0
- package/dist/shield/llm-backend.d.ts.map +1 -0
- package/dist/shield/llm-backend.js +145 -0
- package/dist/shield/llm-backend.js.map +1 -0
- package/dist/shield/llm.d.ts +116 -0
- package/dist/shield/llm.d.ts.map +1 -0
- package/dist/shield/llm.js +536 -0
- package/dist/shield/llm.js.map +1 -0
- package/dist/shield/policy.d.ts +70 -0
- package/dist/shield/policy.d.ts.map +1 -0
- package/dist/shield/policy.js +399 -0
- package/dist/shield/policy.js.map +1 -0
- package/dist/shield/session.d.ts +63 -0
- package/dist/shield/session.d.ts.map +1 -0
- package/dist/shield/session.js +242 -0
- package/dist/shield/session.js.map +1 -0
- package/dist/shield/signing.d.ts +41 -0
- package/dist/shield/signing.d.ts.map +1 -0
- package/dist/shield/signing.js +161 -0
- package/dist/shield/signing.js.map +1 -0
- package/dist/shield/status.d.ts +4 -0
- package/dist/shield/status.d.ts.map +1 -0
- package/dist/shield/status.js +241 -0
- package/dist/shield/status.js.map +1 -0
- package/dist/shield/types.d.ts +398 -0
- package/dist/shield/types.d.ts.map +1 -0
- package/dist/shield/types.js +31 -0
- package/dist/shield/types.js.map +1 -0
- package/dist/util/drift-liveness.d.ts +37 -0
- package/dist/util/drift-liveness.d.ts.map +1 -0
- package/dist/util/drift-liveness.js +114 -0
- package/dist/util/drift-liveness.js.map +1 -0
- package/dist/util/drift-verification.d.ts +60 -0
- package/dist/util/drift-verification.d.ts.map +1 -0
- package/dist/util/drift-verification.js +457 -0
- package/dist/util/drift-verification.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shield tamper-evident event system.
|
|
4
|
+
*
|
|
5
|
+
* Events are stored as newline-delimited JSON (JSONL) with SHA-256 hash
|
|
6
|
+
* chains. Each event references the hash of the previous event, forming
|
|
7
|
+
* an append-only tamper-evident log. The very first event in the chain
|
|
8
|
+
* uses SHA-256("genesis") as its prevHash.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.GENESIS_HASH = void 0;
|
|
12
|
+
exports.uuidv7 = uuidv7;
|
|
13
|
+
exports.getShieldDir = getShieldDir;
|
|
14
|
+
exports.getEventsPath = getEventsPath;
|
|
15
|
+
exports.writeEvent = writeEvent;
|
|
16
|
+
exports.readEvents = readEvents;
|
|
17
|
+
exports.verifyEventChain = verifyEventChain;
|
|
18
|
+
const node_crypto_1 = require("node:crypto");
|
|
19
|
+
const node_fs_1 = require("node:fs");
|
|
20
|
+
const node_os_1 = require("node:os");
|
|
21
|
+
const node_path_1 = require("node:path");
|
|
22
|
+
const types_js_1 = require("./types.js");
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// UUIDv7 (RFC 9562)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* Generate a UUIDv7 (time-sortable) per RFC 9562.
|
|
28
|
+
*
|
|
29
|
+
* Layout (128 bits):
|
|
30
|
+
* 48 bits - unix_ts_ms
|
|
31
|
+
* 4 bits - version (0b0111)
|
|
32
|
+
* 12 bits - rand_a
|
|
33
|
+
* 2 bits - variant (0b10)
|
|
34
|
+
* 62 bits - rand_b
|
|
35
|
+
*/
|
|
36
|
+
function uuidv7() {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
const rand = (0, node_crypto_1.randomBytes)(10); // 80 random bits; we use 74
|
|
39
|
+
// Bytes 0-5: 48-bit unix timestamp in milliseconds (big-endian)
|
|
40
|
+
const buf = Buffer.alloc(16);
|
|
41
|
+
buf[0] = (now / 2 ** 40) & 0xff;
|
|
42
|
+
buf[1] = (now / 2 ** 32) & 0xff;
|
|
43
|
+
buf[2] = (now / 2 ** 24) & 0xff;
|
|
44
|
+
buf[3] = (now / 2 ** 16) & 0xff;
|
|
45
|
+
buf[4] = (now / 2 ** 8) & 0xff;
|
|
46
|
+
buf[5] = now & 0xff;
|
|
47
|
+
// Bytes 6-7: version (4 bits = 0111) + rand_a (12 bits)
|
|
48
|
+
buf[6] = 0x70 | (rand[0] & 0x0f);
|
|
49
|
+
buf[7] = rand[1];
|
|
50
|
+
// Bytes 8-15: variant (2 bits = 10) + rand_b (62 bits)
|
|
51
|
+
buf[8] = 0x80 | (rand[2] & 0x3f);
|
|
52
|
+
buf[9] = rand[3];
|
|
53
|
+
buf[10] = rand[4];
|
|
54
|
+
buf[11] = rand[5];
|
|
55
|
+
buf[12] = rand[6];
|
|
56
|
+
buf[13] = rand[7];
|
|
57
|
+
buf[14] = rand[8];
|
|
58
|
+
buf[15] = rand[9];
|
|
59
|
+
const hex = buf.toString('hex');
|
|
60
|
+
return [
|
|
61
|
+
hex.slice(0, 8),
|
|
62
|
+
hex.slice(8, 12),
|
|
63
|
+
hex.slice(12, 16),
|
|
64
|
+
hex.slice(16, 20),
|
|
65
|
+
hex.slice(20, 32),
|
|
66
|
+
].join('-');
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Directory helpers
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
/** Return the absolute path to the Shield data directory (~/.opena2a/shield). */
|
|
72
|
+
function getShieldDir() {
|
|
73
|
+
const dir = (0, node_path_1.join)((0, node_os_1.homedir)(), '.opena2a', 'shield');
|
|
74
|
+
if (!(0, node_fs_1.existsSync)(dir)) {
|
|
75
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true, mode: 0o700 });
|
|
76
|
+
}
|
|
77
|
+
return dir;
|
|
78
|
+
}
|
|
79
|
+
/** Return the absolute path to the events JSONL file. */
|
|
80
|
+
function getEventsPath() {
|
|
81
|
+
return (0, node_path_1.join)(getShieldDir(), types_js_1.SHIELD_EVENTS_FILE);
|
|
82
|
+
}
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Hashing helpers
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
exports.GENESIS_HASH = (0, node_crypto_1.createHash)('sha256').update('genesis').digest('hex');
|
|
87
|
+
/** Compute SHA-256 hex digest of a string. */
|
|
88
|
+
function sha256(data) {
|
|
89
|
+
return (0, node_crypto_1.createHash)('sha256').update(data).digest('hex');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Read the last non-empty line from a file. Returns null if the file
|
|
93
|
+
* does not exist or is empty.
|
|
94
|
+
*/
|
|
95
|
+
function readLastLine(filePath) {
|
|
96
|
+
if (!(0, node_fs_1.existsSync)(filePath))
|
|
97
|
+
return null;
|
|
98
|
+
let content;
|
|
99
|
+
try {
|
|
100
|
+
content = (0, node_fs_1.readFileSync)(filePath, 'utf-8');
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const lines = content.split('\n');
|
|
106
|
+
// Walk backwards to find the last non-empty line
|
|
107
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
108
|
+
const line = lines[i].trim();
|
|
109
|
+
if (line.length > 0)
|
|
110
|
+
return line;
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Extract the prevHash for the next event by reading the eventHash
|
|
116
|
+
* of the last event in the chain. Returns the genesis hash if the
|
|
117
|
+
* file is empty or missing.
|
|
118
|
+
*/
|
|
119
|
+
function getPrevHash(eventsPath) {
|
|
120
|
+
const lastLine = readLastLine(eventsPath);
|
|
121
|
+
if (!lastLine)
|
|
122
|
+
return exports.GENESIS_HASH;
|
|
123
|
+
try {
|
|
124
|
+
const parsed = JSON.parse(lastLine);
|
|
125
|
+
if (parsed.eventHash && typeof parsed.eventHash === 'string') {
|
|
126
|
+
return parsed.eventHash;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Corrupted last line -- fall through to genesis
|
|
131
|
+
}
|
|
132
|
+
return exports.GENESIS_HASH;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Rotate the events file if it exceeds MAX_EVENTS_FILE_SIZE.
|
|
136
|
+
* The current file is renamed with a timestamp suffix, and a fresh
|
|
137
|
+
* file is started.
|
|
138
|
+
*/
|
|
139
|
+
function rotateIfNeeded(eventsPath) {
|
|
140
|
+
if (!(0, node_fs_1.existsSync)(eventsPath))
|
|
141
|
+
return;
|
|
142
|
+
let size;
|
|
143
|
+
try {
|
|
144
|
+
size = (0, node_fs_1.statSync)(eventsPath).size;
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (size <= types_js_1.MAX_EVENTS_FILE_SIZE)
|
|
150
|
+
return;
|
|
151
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
152
|
+
const rotatedPath = eventsPath.replace(/\.jsonl$/, `-${timestamp}.jsonl`);
|
|
153
|
+
(0, node_fs_1.renameSync)(eventsPath, rotatedPath);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Write a new event to the tamper-evident log.
|
|
157
|
+
*
|
|
158
|
+
* The caller provides all event fields except id, timestamp, version,
|
|
159
|
+
* prevHash, and eventHash -- those are generated automatically.
|
|
160
|
+
*/
|
|
161
|
+
function writeEvent(partial) {
|
|
162
|
+
const eventsPath = getEventsPath();
|
|
163
|
+
// Rotate before writing if the file is oversized
|
|
164
|
+
rotateIfNeeded(eventsPath);
|
|
165
|
+
const prevHash = getPrevHash(eventsPath);
|
|
166
|
+
// Build the event without the final eventHash
|
|
167
|
+
const event = {
|
|
168
|
+
id: uuidv7(),
|
|
169
|
+
timestamp: new Date().toISOString(),
|
|
170
|
+
version: 1,
|
|
171
|
+
...partial,
|
|
172
|
+
prevHash,
|
|
173
|
+
};
|
|
174
|
+
// Compute the hash over the event (without the eventHash field itself)
|
|
175
|
+
const hashInput = JSON.stringify(event);
|
|
176
|
+
const eventHash = sha256(hashInput);
|
|
177
|
+
const fullEvent = {
|
|
178
|
+
...event,
|
|
179
|
+
eventHash,
|
|
180
|
+
};
|
|
181
|
+
const line = JSON.stringify(fullEvent) + '\n';
|
|
182
|
+
// Ensure the shield directory exists (getEventsPath already calls getShieldDir)
|
|
183
|
+
(0, node_fs_1.appendFileSync)(eventsPath, line, { encoding: 'utf-8', mode: 0o600 });
|
|
184
|
+
// Ensure restrictive permissions on the events file
|
|
185
|
+
try {
|
|
186
|
+
(0, node_fs_1.chmodSync)(eventsPath, 0o600);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// Best-effort; appendFileSync already set mode on creation
|
|
190
|
+
}
|
|
191
|
+
return fullEvent;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Parse a relative time string into a Date.
|
|
195
|
+
*
|
|
196
|
+
* Supported formats:
|
|
197
|
+
* "7d" - 7 days ago
|
|
198
|
+
* "1w" - 1 week ago
|
|
199
|
+
* "2w" - 2 weeks ago
|
|
200
|
+
* "1m" - 1 month ago (30 days)
|
|
201
|
+
* "3m" - 3 months ago (90 days)
|
|
202
|
+
*
|
|
203
|
+
* If the string is not a relative format, it is parsed as ISO 8601.
|
|
204
|
+
* Returns null if parsing fails entirely.
|
|
205
|
+
*/
|
|
206
|
+
function parseSince(since) {
|
|
207
|
+
const relativeMatch = since.match(/^(\d+)([dwm])$/);
|
|
208
|
+
if (relativeMatch) {
|
|
209
|
+
const amount = parseInt(relativeMatch[1], 10);
|
|
210
|
+
const unit = relativeMatch[2];
|
|
211
|
+
const now = Date.now();
|
|
212
|
+
let ms;
|
|
213
|
+
switch (unit) {
|
|
214
|
+
case 'd':
|
|
215
|
+
ms = amount * 24 * 60 * 60 * 1000;
|
|
216
|
+
break;
|
|
217
|
+
case 'w':
|
|
218
|
+
ms = amount * 7 * 24 * 60 * 60 * 1000;
|
|
219
|
+
break;
|
|
220
|
+
case 'm':
|
|
221
|
+
ms = amount * 30 * 24 * 60 * 60 * 1000;
|
|
222
|
+
break;
|
|
223
|
+
default:
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
return new Date(now - ms);
|
|
227
|
+
}
|
|
228
|
+
// Try ISO 8601
|
|
229
|
+
const d = new Date(since);
|
|
230
|
+
if (isNaN(d.getTime()))
|
|
231
|
+
return null;
|
|
232
|
+
return d;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Read events from the JSONL log file, applying optional filters.
|
|
236
|
+
*
|
|
237
|
+
* Returns events in newest-first order. Corrupted JSON lines are
|
|
238
|
+
* silently skipped.
|
|
239
|
+
*/
|
|
240
|
+
function readEvents(filters = {}) {
|
|
241
|
+
const eventsPath = getEventsPath();
|
|
242
|
+
if (!(0, node_fs_1.existsSync)(eventsPath))
|
|
243
|
+
return [];
|
|
244
|
+
let content;
|
|
245
|
+
try {
|
|
246
|
+
content = (0, node_fs_1.readFileSync)(eventsPath, 'utf-8');
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
251
|
+
const lines = content.split('\n');
|
|
252
|
+
const events = [];
|
|
253
|
+
for (const line of lines) {
|
|
254
|
+
const trimmed = line.trim();
|
|
255
|
+
if (trimmed.length === 0)
|
|
256
|
+
continue;
|
|
257
|
+
try {
|
|
258
|
+
const event = JSON.parse(trimmed);
|
|
259
|
+
events.push(event);
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// Skip corrupted lines
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Apply filters
|
|
267
|
+
let filtered = events;
|
|
268
|
+
if (filters.source) {
|
|
269
|
+
const src = filters.source;
|
|
270
|
+
filtered = filtered.filter(e => e.source === src);
|
|
271
|
+
}
|
|
272
|
+
if (filters.severity) {
|
|
273
|
+
const sev = filters.severity;
|
|
274
|
+
filtered = filtered.filter(e => e.severity === sev);
|
|
275
|
+
}
|
|
276
|
+
if (filters.agent) {
|
|
277
|
+
const agent = filters.agent;
|
|
278
|
+
filtered = filtered.filter(e => e.agent === agent);
|
|
279
|
+
}
|
|
280
|
+
if (filters.category) {
|
|
281
|
+
const cat = filters.category;
|
|
282
|
+
filtered = filtered.filter(e => e.category === cat);
|
|
283
|
+
}
|
|
284
|
+
if (filters.since) {
|
|
285
|
+
const sinceDate = parseSince(filters.since);
|
|
286
|
+
if (sinceDate) {
|
|
287
|
+
const sinceMs = sinceDate.getTime();
|
|
288
|
+
filtered = filtered.filter(e => {
|
|
289
|
+
const eventTime = new Date(e.timestamp).getTime();
|
|
290
|
+
return eventTime >= sinceMs;
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Newest-first (reverse chronological order)
|
|
295
|
+
filtered.reverse();
|
|
296
|
+
// Apply count limit after reversing
|
|
297
|
+
if (filters.count !== undefined && filters.count > 0) {
|
|
298
|
+
filtered = filtered.slice(0, filters.count);
|
|
299
|
+
}
|
|
300
|
+
return filtered;
|
|
301
|
+
}
|
|
302
|
+
// ---------------------------------------------------------------------------
|
|
303
|
+
// verifyEventChain
|
|
304
|
+
// ---------------------------------------------------------------------------
|
|
305
|
+
/**
|
|
306
|
+
* Verify the integrity of a hash chain.
|
|
307
|
+
*
|
|
308
|
+
* Events must be provided in chronological order (oldest first).
|
|
309
|
+
* The first event's prevHash must equal SHA-256("genesis").
|
|
310
|
+
*
|
|
311
|
+
* Returns { valid: true, brokenAt: null } if the chain is intact,
|
|
312
|
+
* or { valid: false, brokenAt: <index> } pointing to the first
|
|
313
|
+
* event where the chain breaks.
|
|
314
|
+
*/
|
|
315
|
+
function verifyEventChain(events) {
|
|
316
|
+
if (events.length === 0) {
|
|
317
|
+
return { valid: true, brokenAt: null };
|
|
318
|
+
}
|
|
319
|
+
for (let i = 0; i < events.length; i++) {
|
|
320
|
+
const event = events[i];
|
|
321
|
+
// 1. Verify prevHash links to the previous event (or genesis)
|
|
322
|
+
if (i === 0) {
|
|
323
|
+
if (event.prevHash !== exports.GENESIS_HASH) {
|
|
324
|
+
return { valid: false, brokenAt: 0 };
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
if (event.prevHash !== events[i - 1].eventHash) {
|
|
329
|
+
return { valid: false, brokenAt: i };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// 2. Verify the eventHash matches the event content
|
|
333
|
+
// Reconstruct the event without eventHash and compute the hash
|
|
334
|
+
const { eventHash: _storedHash, ...rest } = event;
|
|
335
|
+
const computedHash = sha256(JSON.stringify(rest));
|
|
336
|
+
if (computedHash !== event.eventHash) {
|
|
337
|
+
return { valid: false, brokenAt: i };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return { valid: true, brokenAt: null };
|
|
341
|
+
}
|
|
342
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/shield/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { EnvironmentScan, ShieldPolicy } from './types.js';
|
|
2
|
+
interface InitResult {
|
|
3
|
+
scan: EnvironmentScan;
|
|
4
|
+
policy: ShieldPolicy;
|
|
5
|
+
shellHookInstalled: boolean;
|
|
6
|
+
policyPath: string;
|
|
7
|
+
steps: {
|
|
8
|
+
name: string;
|
|
9
|
+
status: 'done' | 'skipped' | 'warn';
|
|
10
|
+
}[];
|
|
11
|
+
}
|
|
12
|
+
export declare function shieldInit(options: {
|
|
13
|
+
targetDir?: string;
|
|
14
|
+
ci?: boolean;
|
|
15
|
+
format?: string;
|
|
16
|
+
verbose?: boolean;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
exitCode: number;
|
|
19
|
+
result: InitResult;
|
|
20
|
+
}>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/shield/init.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EAKb,MAAM,YAAY,CAAC;AAUpB,UAAU,UAAU;IAClB,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,YAAY,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;KAAE,EAAE,CAAC;CAChE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC,CAoQpD"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.shieldInit = shieldInit;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const node_os_1 = require("node:os");
|
|
7
|
+
const types_js_1 = require("./types.js");
|
|
8
|
+
const detect_js_1 = require("./detect.js");
|
|
9
|
+
const policy_js_1 = require("./policy.js");
|
|
10
|
+
const events_js_1 = require("./events.js");
|
|
11
|
+
const integrity_js_1 = require("./integrity.js");
|
|
12
|
+
const signing_js_1 = require("./signing.js");
|
|
13
|
+
const colors_js_1 = require("../util/colors.js");
|
|
14
|
+
const spinner_js_1 = require("../util/spinner.js");
|
|
15
|
+
async function shieldInit(options) {
|
|
16
|
+
const targetDir = options.targetDir ?? process.cwd();
|
|
17
|
+
const ci = options.ci ?? false;
|
|
18
|
+
const format = options.format ?? 'text';
|
|
19
|
+
const isText = format === 'text' && !ci;
|
|
20
|
+
const steps = [];
|
|
21
|
+
// --- Step 1: Environment Detection ---
|
|
22
|
+
if (isText)
|
|
23
|
+
process.stdout.write((0, colors_js_1.bold)('\nShield Init\n\n'));
|
|
24
|
+
const spinner = isText ? new spinner_js_1.Spinner('Scanning environment...') : null;
|
|
25
|
+
spinner?.start();
|
|
26
|
+
const scan = (0, detect_js_1.detectEnvironment)(targetDir);
|
|
27
|
+
spinner?.stop();
|
|
28
|
+
steps.push({ name: 'Environment scan', status: 'done' });
|
|
29
|
+
if (isText) {
|
|
30
|
+
process.stdout.write((0, colors_js_1.bold)('Step 1: Environment Detection\n'));
|
|
31
|
+
// CLIs
|
|
32
|
+
if (scan.clis.length > 0) {
|
|
33
|
+
process.stdout.write(` CLIs found: ${scan.clis.map((c) => c.name).join(', ')}\n`);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
process.stdout.write(` No cloud CLIs detected\n`);
|
|
37
|
+
}
|
|
38
|
+
// Assistants
|
|
39
|
+
const activeAssistants = scan.assistants.filter((a) => a.detected);
|
|
40
|
+
if (activeAssistants.length > 0) {
|
|
41
|
+
process.stdout.write(` AI assistants: ${activeAssistants.map((a) => a.name).join(', ')}\n`);
|
|
42
|
+
}
|
|
43
|
+
// MCP servers
|
|
44
|
+
if (scan.mcpServers.length > 0) {
|
|
45
|
+
process.stdout.write(` MCP servers: ${scan.mcpServers.map((s) => s.name).join(', ')}\n`);
|
|
46
|
+
}
|
|
47
|
+
// OAuth sessions
|
|
48
|
+
const activeSessions = scan.oauthSessions.filter((s) => s.hasActiveSession);
|
|
49
|
+
if (activeSessions.length > 0) {
|
|
50
|
+
process.stdout.write((0, colors_js_1.yellow)(` Active OAuth sessions: ${activeSessions.map((s) => s.provider).join(', ')}\n`));
|
|
51
|
+
}
|
|
52
|
+
// Project
|
|
53
|
+
process.stdout.write(` Project: ${scan.projectName ?? 'unknown'} (${scan.projectType})\n`);
|
|
54
|
+
process.stdout.write('\n');
|
|
55
|
+
}
|
|
56
|
+
// --- Step 2: Credential Audit ---
|
|
57
|
+
if (isText)
|
|
58
|
+
process.stdout.write((0, colors_js_1.bold)('Step 2: Credential Audit\n'));
|
|
59
|
+
let credentialFindings = 0;
|
|
60
|
+
try {
|
|
61
|
+
const { quickCredentialScan } = await import('../util/credential-patterns.js');
|
|
62
|
+
const matches = quickCredentialScan(targetDir);
|
|
63
|
+
credentialFindings = matches.length;
|
|
64
|
+
if (isText) {
|
|
65
|
+
if (matches.length === 0) {
|
|
66
|
+
process.stdout.write((0, colors_js_1.green)(' No hardcoded credentials found\n'));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
process.stdout.write((0, colors_js_1.yellow)(` ${matches.length} credential${matches.length !== 1 ? 's' : ''} found\n`));
|
|
70
|
+
for (const m of matches.slice(0, 5)) {
|
|
71
|
+
process.stdout.write(` ${m.severity.toUpperCase()}: ${m.title} in ${m.filePath}:${m.line}\n`);
|
|
72
|
+
}
|
|
73
|
+
if (matches.length > 5) {
|
|
74
|
+
process.stdout.write((0, colors_js_1.dim)(` ... and ${matches.length - 5} more\n`));
|
|
75
|
+
}
|
|
76
|
+
process.stdout.write((0, colors_js_1.dim)(' Run: opena2a protect\n'));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
if (isText)
|
|
82
|
+
process.stdout.write((0, colors_js_1.dim)(' Credential scan skipped (module unavailable)\n'));
|
|
83
|
+
}
|
|
84
|
+
steps.push({ name: 'Credential audit', status: credentialFindings > 0 ? 'warn' : 'done' });
|
|
85
|
+
if (isText)
|
|
86
|
+
process.stdout.write('\n');
|
|
87
|
+
// --- Step 3: Config Integrity Baseline ---
|
|
88
|
+
if (isText)
|
|
89
|
+
process.stdout.write((0, colors_js_1.bold)('Step 3: Config Integrity Baseline\n'));
|
|
90
|
+
try {
|
|
91
|
+
const { guard } = await import('../commands/guard.js');
|
|
92
|
+
await guard({
|
|
93
|
+
subcommand: 'sign',
|
|
94
|
+
targetDir,
|
|
95
|
+
ci: true,
|
|
96
|
+
format: 'json',
|
|
97
|
+
verbose: false,
|
|
98
|
+
});
|
|
99
|
+
steps.push({ name: 'Config signing', status: 'done' });
|
|
100
|
+
if (isText)
|
|
101
|
+
process.stdout.write((0, colors_js_1.green)(' Config files signed as baseline\n'));
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
steps.push({ name: 'Config signing', status: 'skipped' });
|
|
105
|
+
if (isText)
|
|
106
|
+
process.stdout.write((0, colors_js_1.dim)(' Config signing skipped\n'));
|
|
107
|
+
}
|
|
108
|
+
if (isText)
|
|
109
|
+
process.stdout.write('\n');
|
|
110
|
+
// --- Step 4: Generate Policy ---
|
|
111
|
+
if (isText)
|
|
112
|
+
process.stdout.write((0, colors_js_1.bold)('Step 4: Generate Policy\n'));
|
|
113
|
+
const policy = (0, policy_js_1.generatePolicyFromScan)(scan);
|
|
114
|
+
const shieldDir = (0, events_js_1.getShieldDir)();
|
|
115
|
+
const policyPath = (0, node_path_1.join)(shieldDir, types_js_1.SHIELD_POLICY_FILE);
|
|
116
|
+
if ((0, node_fs_1.existsSync)(policyPath) && !ci) {
|
|
117
|
+
if (isText) {
|
|
118
|
+
process.stdout.write((0, colors_js_1.yellow)(' Existing policy found. Preserving current policy.\n'));
|
|
119
|
+
process.stdout.write((0, colors_js_1.dim)(' To regenerate: delete ~/.opena2a/shield/policy.yaml and re-run init\n'));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
(0, policy_js_1.savePolicy)(policy, policyPath);
|
|
124
|
+
(0, integrity_js_1.recordPolicyHash)(policyPath);
|
|
125
|
+
if (isText) {
|
|
126
|
+
process.stdout.write((0, colors_js_1.green)(' Policy generated (adaptive mode)\n'));
|
|
127
|
+
process.stdout.write(` Shield will learn agent behavior before suggesting rules.\n`);
|
|
128
|
+
const denyProcs = policy.default.processes.deny;
|
|
129
|
+
if (denyProcs.length > 0) {
|
|
130
|
+
process.stdout.write(` Recommended blocks: ${denyProcs.join(', ')}\n`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
steps.push({ name: 'Policy generation', status: 'done' });
|
|
135
|
+
if (isText)
|
|
136
|
+
process.stdout.write('\n');
|
|
137
|
+
// --- Step 5: Shell Integration ---
|
|
138
|
+
if (isText)
|
|
139
|
+
process.stdout.write((0, colors_js_1.bold)('Step 5: Shell Integration\n'));
|
|
140
|
+
let shellHookInstalled = false;
|
|
141
|
+
const shell = process.env.SHELL?.includes('zsh') ? 'zsh'
|
|
142
|
+
: process.env.SHELL?.includes('bash') ? 'bash'
|
|
143
|
+
: null;
|
|
144
|
+
if (shell && !ci) {
|
|
145
|
+
const rcFile = shell === 'zsh'
|
|
146
|
+
? (0, node_path_1.join)((0, node_os_1.homedir)(), '.zshrc')
|
|
147
|
+
: (0, node_path_1.join)((0, node_os_1.homedir)(), '.bashrc');
|
|
148
|
+
let rcContent = '';
|
|
149
|
+
if ((0, node_fs_1.existsSync)(rcFile)) {
|
|
150
|
+
try {
|
|
151
|
+
rcContent = (0, node_fs_1.readFileSync)(rcFile, 'utf-8');
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
if (isText)
|
|
155
|
+
process.stdout.write((0, colors_js_1.yellow)(' Cannot read rc file, skipping shell hooks\n'));
|
|
156
|
+
steps.push({ name: 'Shell integration', status: 'skipped' });
|
|
157
|
+
if (isText)
|
|
158
|
+
process.stdout.write('\n');
|
|
159
|
+
// Skip shell integration entirely if we can't read an existing file
|
|
160
|
+
rcContent = '';
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (rcContent.includes('opena2a_shield_preexec') || rcContent.includes('opena2a_shield_debug')) {
|
|
164
|
+
shellHookInstalled = true;
|
|
165
|
+
if (isText)
|
|
166
|
+
process.stdout.write((0, colors_js_1.green)(' Shell hooks already installed\n'));
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
const hookContent = (0, integrity_js_1.getExpectedHookContent)(shell);
|
|
170
|
+
(0, node_fs_1.appendFileSync)(rcFile, '\n' + hookContent + '\n', { mode: 0o600 });
|
|
171
|
+
shellHookInstalled = true;
|
|
172
|
+
if (isText)
|
|
173
|
+
process.stdout.write((0, colors_js_1.green)(` Shell hooks installed in ~/.${shell}rc\n`));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else if (ci) {
|
|
177
|
+
if (isText)
|
|
178
|
+
process.stdout.write((0, colors_js_1.dim)(' Shell hooks skipped (CI mode)\n'));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
if (isText)
|
|
182
|
+
process.stdout.write((0, colors_js_1.dim)(' Shell not detected (zsh or bash required)\n'));
|
|
183
|
+
}
|
|
184
|
+
steps.push({ name: 'Shell integration', status: shellHookInstalled ? 'done' : 'skipped' });
|
|
185
|
+
if (isText)
|
|
186
|
+
process.stdout.write('\n');
|
|
187
|
+
// --- Step 6: ARP Initialization ---
|
|
188
|
+
if (isText)
|
|
189
|
+
process.stdout.write((0, colors_js_1.bold)('Step 6: Runtime Protection\n'));
|
|
190
|
+
try {
|
|
191
|
+
const { runtime } = await import('../commands/runtime.js');
|
|
192
|
+
await runtime({
|
|
193
|
+
subcommand: 'init',
|
|
194
|
+
targetDir,
|
|
195
|
+
ci: true,
|
|
196
|
+
format: 'json',
|
|
197
|
+
verbose: false,
|
|
198
|
+
});
|
|
199
|
+
steps.push({ name: 'ARP init', status: 'done' });
|
|
200
|
+
if (isText)
|
|
201
|
+
process.stdout.write((0, colors_js_1.green)(' ARP config generated\n'));
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
steps.push({ name: 'ARP init', status: 'skipped' });
|
|
205
|
+
if (isText)
|
|
206
|
+
process.stdout.write((0, colors_js_1.dim)(' ARP initialization skipped (hackmyagent not installed)\n'));
|
|
207
|
+
}
|
|
208
|
+
if (isText)
|
|
209
|
+
process.stdout.write('\n');
|
|
210
|
+
// --- Step 7: Browser Guard ---
|
|
211
|
+
if (isText)
|
|
212
|
+
process.stdout.write((0, colors_js_1.bold)('Step 7: Browser Guard\n'));
|
|
213
|
+
const hasBrowserGuard = (0, node_fs_1.existsSync)((0, node_path_1.join)((0, node_os_1.homedir)(), '.config', 'opena2a', 'browser-guard.json')) ||
|
|
214
|
+
(0, node_fs_1.existsSync)((0, node_path_1.join)((0, node_os_1.homedir)(), '.opena2a', 'browser-guard.json'));
|
|
215
|
+
if (hasBrowserGuard) {
|
|
216
|
+
steps.push({ name: 'Browser Guard', status: 'done' });
|
|
217
|
+
if (isText)
|
|
218
|
+
process.stdout.write((0, colors_js_1.green)(' Browser Guard detected\n'));
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
steps.push({ name: 'Browser Guard', status: 'skipped' });
|
|
222
|
+
if (isText) {
|
|
223
|
+
process.stdout.write((0, colors_js_1.dim)(' Browser Guard not installed\n'));
|
|
224
|
+
process.stdout.write((0, colors_js_1.dim)(' Browser session protection is optional.\n'));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (isText)
|
|
228
|
+
process.stdout.write('\n');
|
|
229
|
+
// --- Step 8: Summary ---
|
|
230
|
+
// Save scan results
|
|
231
|
+
const scanPath = (0, node_path_1.join)(shieldDir, types_js_1.SHIELD_SCAN_FILE);
|
|
232
|
+
(0, node_fs_1.writeFileSync)(scanPath, JSON.stringify(scan, null, 2) + '\n', { mode: 0o600 });
|
|
233
|
+
// Sign all Shield artifacts (policy.yaml, scan.json, llm-cache.json)
|
|
234
|
+
(0, signing_js_1.signAllArtifacts)();
|
|
235
|
+
// Write init event
|
|
236
|
+
(0, events_js_1.writeEvent)({
|
|
237
|
+
source: 'shield',
|
|
238
|
+
category: 'shield.init',
|
|
239
|
+
severity: 'info',
|
|
240
|
+
agent: null,
|
|
241
|
+
sessionId: null,
|
|
242
|
+
action: 'shield.init',
|
|
243
|
+
target: targetDir,
|
|
244
|
+
outcome: 'allowed',
|
|
245
|
+
detail: {
|
|
246
|
+
clis: scan.clis.length,
|
|
247
|
+
assistants: scan.assistants.filter((a) => a.detected).length,
|
|
248
|
+
mcpServers: scan.mcpServers.length,
|
|
249
|
+
oauthSessions: scan.oauthSessions.filter((s) => s.hasActiveSession).length,
|
|
250
|
+
credentialFindings,
|
|
251
|
+
shellHookInstalled,
|
|
252
|
+
},
|
|
253
|
+
orgId: null,
|
|
254
|
+
managed: false,
|
|
255
|
+
agentId: null,
|
|
256
|
+
});
|
|
257
|
+
steps.push({ name: 'Summary', status: 'done' });
|
|
258
|
+
if (isText) {
|
|
259
|
+
process.stdout.write((0, colors_js_1.bold)('Step 8: Summary\n'));
|
|
260
|
+
const doneCount = steps.filter(s => s.status === 'done').length;
|
|
261
|
+
const warnCount = steps.filter(s => s.status === 'warn').length;
|
|
262
|
+
process.stdout.write(` ${(0, colors_js_1.green)(`${doneCount} steps completed`)}`);
|
|
263
|
+
if (warnCount > 0)
|
|
264
|
+
process.stdout.write(`, ${(0, colors_js_1.yellow)(`${warnCount} warnings`)}`);
|
|
265
|
+
process.stdout.write('\n');
|
|
266
|
+
process.stdout.write(` Policy: ${policyPath}\n`);
|
|
267
|
+
process.stdout.write(` Events: ${(0, node_path_1.join)(shieldDir, 'events.jsonl')}\n`);
|
|
268
|
+
process.stdout.write('\n');
|
|
269
|
+
process.stdout.write((0, colors_js_1.cyan)(' Shield is now in adaptive mode. It will learn your agent behavior\n'));
|
|
270
|
+
process.stdout.write((0, colors_js_1.cyan)(' and suggest a policy once patterns stabilize.\n'));
|
|
271
|
+
process.stdout.write('\n');
|
|
272
|
+
process.stdout.write((0, colors_js_1.dim)(' View status: opena2a shield status\n'));
|
|
273
|
+
process.stdout.write((0, colors_js_1.dim)(' View events: opena2a shield log\n'));
|
|
274
|
+
process.stdout.write((0, colors_js_1.dim)(' Run check: opena2a shield selfcheck\n'));
|
|
275
|
+
process.stdout.write('\n');
|
|
276
|
+
}
|
|
277
|
+
const result = {
|
|
278
|
+
scan,
|
|
279
|
+
policy,
|
|
280
|
+
shellHookInstalled,
|
|
281
|
+
policyPath,
|
|
282
|
+
steps,
|
|
283
|
+
};
|
|
284
|
+
if (format === 'json' || ci) {
|
|
285
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
286
|
+
}
|
|
287
|
+
const hasFailure = credentialFindings > 0;
|
|
288
|
+
return { exitCode: hasFailure ? 1 : 0, result };
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=init.js.map
|