@eyeauras/insight-cli 0.5.4 → 0.5.5
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/.cli-package-build.json +1 -1
- package/SKILL.md +1 -1
- package/dist/src/analysis-storage.d.ts +2 -1
- package/dist/src/analysis-storage.js +71 -7
- package/dist/src/context-diagnostics.d.ts +1 -1
- package/dist/src/context-diagnostics.js +1 -1
- package/dist/src/contexts.d.ts +1 -0
- package/dist/src/contexts.js +29 -3
- package/dist/src/ghidra-processes.js +6 -1
- package/dist/src/tool-runtime/java-arguments.js +2 -2
- package/dist/src/tool-runtime/native-process-proof.d.ts +2 -0
- package/dist/src/tool-runtime/native-process-proof.js +26 -0
- package/dist/src/tool-runtime/run.js +3 -3
- package/dist/src/tool-runtime/store.d.ts +2 -0
- package/dist/src/tool-runtime/store.js +26 -8
- package/dist/src/tool-runtime/supervisor.js +4 -4
- package/package.json +1 -1
- package/references/recovery.md +17 -0
- package/references/storage.md +11 -0
- package/runtime/native/process-identity-darwin-arm64 +0 -0
- package/runtime/native/process-identity-darwin.c +47 -0
- package/runtime/native/process-identity.provenance.json +18 -0
- package/skill-bundle/insight-artifacts/SKILL.md +1 -1
- package/skill-bundle/insight-artifacts/references/recovery.md +17 -0
- package/skill-bundle/insight-artifacts/references/storage.md +11 -0
- package/skill-bundle/insight-binary/SKILL.md +1 -1
- package/skill-bundle/insight-capture/SKILL.md +1 -1
- package/skill-bundle/insight-cli/SKILL.md +1 -1
- package/skill-bundle/insight-cli/references/recovery.md +17 -0
- package/skill-bundle/insight-cli/references/storage.md +11 -0
- package/skill-bundle/insight-die/SKILL.md +1 -1
- package/skill-bundle/insight-dnspy/SKILL.md +1 -1
- package/skill-bundle/insight-dotnet-dump/SKILL.md +1 -1
- package/skill-bundle/insight-dotnet-stack/SKILL.md +1 -1
- package/skill-bundle/insight-dotnet-trace/SKILL.md +1 -1
- package/skill-bundle/insight-frida/SKILL.md +1 -1
- package/skill-bundle/insight-ghidra/SKILL.md +4 -1
- package/skill-bundle/insight-ghidra/references/recovery.md +17 -0
- package/skill-bundle/insight-ghidra/references/storage.md +11 -0
- package/skill-bundle/insight-ilspy/SKILL.md +1 -1
- package/skill-bundle/insight-interop/SKILL.md +1 -1
- package/skill-bundle/insight-lldb/SKILL.md +1 -1
- package/skill-bundle/insight-llvm/SKILL.md +1 -1
- package/skill-bundle/insight-managed-heap/SKILL.md +1 -1
- package/skill-bundle/insight-managed-metadata/SKILL.md +1 -1
- package/skill-bundle/insight-mitmproxy/SKILL.md +1 -1
- package/skill-bundle/insight-overview/SKILL.md +1 -1
- package/skill-bundle/insight-procmon/SKILL.md +1 -1
- package/skill-bundle/insight-re/SKILL.md +1 -1
- package/skill-bundle/insight-re/references/recovery.md +17 -0
- package/skill-bundle/insight-re/references/storage.md +11 -0
- package/skill-bundle/insight-sevenzip/SKILL.md +1 -1
- package/skill-bundle/insight-strace/SKILL.md +1 -1
- package/skill-bundle/insight-tshark/SKILL.md +1 -1
- package/skill-bundle/insight-windbg/SKILL.md +1 -1
- package/skill-bundle/manifest.json +1 -1
- package/skills/insight-artifacts/SKILL.md +1 -1
- package/skills/insight-artifacts/references/recovery.md +17 -0
- package/skills/insight-artifacts/references/storage.md +11 -0
- package/skills/insight-binary/SKILL.md +1 -1
- package/skills/insight-capture/SKILL.md +1 -1
- package/skills/insight-die/SKILL.md +1 -1
- package/skills/insight-dnspy/SKILL.md +1 -1
- package/skills/insight-dotnet-dump/SKILL.md +1 -1
- package/skills/insight-dotnet-stack/SKILL.md +1 -1
- package/skills/insight-dotnet-trace/SKILL.md +1 -1
- package/skills/insight-frida/SKILL.md +1 -1
- package/skills/insight-ghidra/SKILL.md +4 -1
- package/skills/insight-ghidra/references/recovery.md +17 -0
- package/skills/insight-ghidra/references/storage.md +11 -0
- package/skills/insight-ilspy/SKILL.md +1 -1
- package/skills/insight-interop/SKILL.md +1 -1
- package/skills/insight-lldb/SKILL.md +1 -1
- package/skills/insight-llvm/SKILL.md +1 -1
- package/skills/insight-managed-heap/SKILL.md +1 -1
- package/skills/insight-managed-metadata/SKILL.md +1 -1
- package/skills/insight-mitmproxy/SKILL.md +1 -1
- package/skills/insight-overview/SKILL.md +1 -1
- package/skills/insight-procmon/SKILL.md +1 -1
- package/skills/insight-re/SKILL.md +1 -1
- package/skills/insight-re/references/recovery.md +17 -0
- package/skills/insight-re/references/storage.md +11 -0
- package/skills/insight-sevenzip/SKILL.md +1 -1
- package/skills/insight-strace/SKILL.md +1 -1
- package/skills/insight-tshark/SKILL.md +1 -1
- package/skills/insight-windbg/SKILL.md +1 -1
package/.cli-package-build.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":1,"artifact":"
|
|
1
|
+
{"version":1,"artifact":"338d2246eeab5c3a4de372ad6e77d505601a39dad11aea43d3627988afd14ba2"}
|
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: insight-cli
|
|
|
3
3
|
description: Choose and prepare reverse-engineering capabilities from the insight-cli toolbox for binary, managed-code and runtime investigations. Use to discover the appropriate tool or its packaged skill before installing native runtimes.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# insight-cli 0.5.
|
|
6
|
+
# insight-cli 0.5.5
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -115,7 +115,8 @@ export declare class AnalysisStorage {
|
|
|
115
115
|
}>;
|
|
116
116
|
private checkCopy;
|
|
117
117
|
private continue;
|
|
118
|
-
|
|
118
|
+
private repairBinding;
|
|
119
|
+
resume(c: CommandContext, repairProfile?: boolean): Promise<{
|
|
119
120
|
state: "rolled_back";
|
|
120
121
|
id: string;
|
|
121
122
|
directory: string;
|
|
@@ -251,8 +251,14 @@ export class AnalysisStorage {
|
|
|
251
251
|
fail('busy', 'Finish active commands before storage migration.');
|
|
252
252
|
this.changing.add(key);
|
|
253
253
|
try {
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
let processes, native;
|
|
255
|
+
try {
|
|
256
|
+
processes = (await listProcesses(key)).filter(p => !['exited', 'failed'].includes(p.state));
|
|
257
|
+
native = await runtimeProcesses(key);
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
throw new CliError('Cannot verify storage process inventory. Inspect storage show and preserve legacy compatibility paths before retrying.', { code: 'storage.inventory', cause: error, next: [['storage', 'show']] });
|
|
261
|
+
}
|
|
256
262
|
if (processes.length || native.length)
|
|
257
263
|
throw new CliError('Close the profile contexts and stop its owned server/processes before migration. Unverified lifetimes also block copying.', { code: 'storage.busy', result: { processes, native }, next: [['tools', 'processes']] });
|
|
258
264
|
await this.release(c);
|
|
@@ -354,8 +360,14 @@ export class AnalysisStorage {
|
|
|
354
360
|
return this.restore(c, j);
|
|
355
361
|
if (j.state === 'cleaning')
|
|
356
362
|
return this.finishCleanup(c, j);
|
|
357
|
-
if (terminal(j.state) && !(j.automatic && j.state === 'committed'))
|
|
358
|
-
|
|
363
|
+
if (terminal(j.state) && !(j.automatic && j.state === 'committed')) {
|
|
364
|
+
const selected = { ...c, profile: await this.store(c).get(c.profile.name) };
|
|
365
|
+
await this.validate(selected);
|
|
366
|
+
const status = await this.show(selected);
|
|
367
|
+
if (status.availability !== 'available')
|
|
368
|
+
throw new CliError('Completed migration storage is unavailable. Inspect storage show before retrying.', { code: 'storage.unavailable', next: [['storage', 'show']] });
|
|
369
|
+
return status;
|
|
370
|
+
}
|
|
359
371
|
if (j.makeDefault && !j.defaultApplied) {
|
|
360
372
|
const base = dirname(dirname(j.destination));
|
|
361
373
|
if (await canonicalFuture(base) !== base)
|
|
@@ -535,8 +547,60 @@ export class AnalysisStorage {
|
|
|
535
547
|
}
|
|
536
548
|
return { state: j.state, id: j.id, directory: j.destination, manifest: j.manifest, sourceRetained: true, defaultBaseDirectory: storageDefault(c)?.base ?? null, installationDefault: j.makeDefault ? (j.defaultSuperseded ? 'superseded' : 'configured') : 'unchanged', rollback: 'storage rollback', cleanup: 'storage cleanup' };
|
|
537
549
|
}
|
|
538
|
-
async
|
|
539
|
-
|
|
550
|
+
async repairBinding(c, j) {
|
|
551
|
+
if (!['committed', 'cleaned'].includes(j.state))
|
|
552
|
+
fail('repair', 'Profile repair requires a completed migration. Resume the pending transaction without --repair-profile.');
|
|
553
|
+
if (await directoryIdentity(j.source) !== j.sourceIdentity || await canonicalFuture(j.destination) !== j.destination || !j.targetIdentity || await directoryIdentity(j.destination) !== j.targetIdentity)
|
|
554
|
+
fail('binding', 'Migration source or destination identity changed. Preserve both copies; profile repair was refused.');
|
|
555
|
+
if (await directoryIdentity(j.destinationAnchor) !== j.destinationIdentity)
|
|
556
|
+
fail('volume', 'Migration destination volume changed. Profile repair was refused.');
|
|
557
|
+
if (j.defaultBinding)
|
|
558
|
+
validateStorageDefault(c, j.defaultBinding);
|
|
559
|
+
if (j.defaultBaseIdentity && await directoryIdentity(dirname(dirname(j.destination))) !== j.defaultBaseIdentity)
|
|
560
|
+
fail('volume', 'Storage base changed. Profile repair was refused.');
|
|
561
|
+
const marker = await readJson(join(j.destination, '.insight-analysis.json'));
|
|
562
|
+
if (marker?.schema !== 1 || marker.id !== j.id || marker.binding !== j.binding || marker.profile !== j.profile)
|
|
563
|
+
fail('binding', 'Destination ownership does not match the retained migration. Profile repair was refused.');
|
|
564
|
+
for (const tree of analysisTrees) {
|
|
565
|
+
const target = join(j.destination, tree), alias = join(j.source, tree);
|
|
566
|
+
try {
|
|
567
|
+
if (await canonicalFuture(target) !== target || !(await lstat(target)).isDirectory() || !(await lstat(alias)).isSymbolicLink() || await realpath(alias) !== target)
|
|
568
|
+
fail('aliases', 'A migration compatibility path changed. Preserve the conflicting path and restore its recorded destination before profile repair.');
|
|
569
|
+
}
|
|
570
|
+
catch (error) {
|
|
571
|
+
if (error instanceof CliError)
|
|
572
|
+
throw error;
|
|
573
|
+
throw new CliError('Cannot verify a migration compatibility path. Preserve both copies before profile repair.', { code: 'storage.aliases', cause: error });
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
const setting = c.profile.values.analysisDirectory;
|
|
577
|
+
if (setting !== undefined && setting !== null && setting !== j.previous && setting !== j.source && setting !== j.destination)
|
|
578
|
+
fail('configuration', 'The profile selects a different storage location. Preserve it; automatic profile repair was refused.');
|
|
579
|
+
}
|
|
580
|
+
async resume(c, repairProfile = false) {
|
|
581
|
+
const j = await this.journal(c, true);
|
|
582
|
+
if (!j)
|
|
583
|
+
fail('missing', 'No migration journal exists.');
|
|
584
|
+
// Diagnose a terminal profile mismatch before scanning legacy native paths.
|
|
585
|
+
if (['committed', 'cleaned'].includes(j.state)) {
|
|
586
|
+
if (repairProfile)
|
|
587
|
+
await this.repairBinding(c, j);
|
|
588
|
+
else if (analysisDirectory(c) !== j.destination)
|
|
589
|
+
throw new CliError('Factory profile and completed migration differ. Verify and restore the profile with storage resume --repair-profile.', { code: 'storage.configuration', next: [['storage', 'resume', '--repair-profile']] });
|
|
590
|
+
}
|
|
591
|
+
else if (repairProfile)
|
|
592
|
+
fail('repair', 'Profile repair requires a completed migration. Resume without --repair-profile.');
|
|
593
|
+
return this.exclusive(c, async () => {
|
|
594
|
+
if (repairProfile) {
|
|
595
|
+
c = { ...c, profile: await this.store(c).get(c.profile.name) };
|
|
596
|
+
await this.repairBinding(c, j);
|
|
597
|
+
await this.store(c).set(c.profile.name, { analysisDirectory: j.destination });
|
|
598
|
+
await sync(join(c.appArguments.RoamingAppDataDirectory, 'profiles.json'));
|
|
599
|
+
c = { ...c, profile: await this.store(c).get(c.profile.name) };
|
|
600
|
+
}
|
|
601
|
+
return this.continue(c, j);
|
|
602
|
+
});
|
|
603
|
+
}
|
|
540
604
|
async rollback(c) {
|
|
541
605
|
return this.exclusive(c, async () => {
|
|
542
606
|
const j = await this.journal(c, true);
|
|
@@ -650,7 +714,7 @@ export class AnalysisStorage {
|
|
|
650
714
|
command('default', 'Set the installation storage base for new profiles; preserve existing data locations', ({ options: o }, c) => this.setDefault(c, o.directory), { permission: Permission.Update, options: [directory] }),
|
|
651
715
|
command('configure', 'Choose storage for an empty profile and subsequent new profiles', ({ options: o }, c) => this.migrate(c, o.directory, false, true, Boolean(o.profileOnly)), { permission: Permission.Update, options: [directory, profileOnly] }),
|
|
652
716
|
command('migrate', 'Copy and verify all analysis data, retain source, then switch storage', ({ options: o }, c) => this.migrate(c, o.directory, Boolean(o.dryRun), false, Boolean(o.profileOnly)), { permission: Permission.Update, options: [directory, profileOnly, { flags: '--dry-run', description: 'Read-only path, capacity and quiescence preflight; create no migration' }] }),
|
|
653
|
-
command('resume', 'Resume
|
|
717
|
+
command('resume', 'Resume migration, or explicitly repair its completed profile binding', ({ options: o }, c) => this.resume(c, Boolean(o.repairProfile)), { permission: Permission.Update, options: [{ flags: '--repair-profile', description: 'Restore a completed migration setting after verifying destination ownership and compatibility paths' }] }),
|
|
654
718
|
command('rollback', 'Restore retained source; refuse after any destination content changes', (_i, c) => this.rollback(c), { permission: Permission.Update }),
|
|
655
719
|
command('cleanup', 'Delete verified retained source copies; retain compatibility paths permanently', (_i, c) => this.cleanup(c), { permission: 'Destructive' }),
|
|
656
720
|
]);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type CommandContext, type CommandDefinition } from '@eyeauras/cli-factory';
|
|
2
2
|
import { type Research } from './research.js';
|
|
3
3
|
import type { Workspace, Scope } from './workspace.js';
|
|
4
|
-
export type StopReason = 'explicit_close' | 'view_replaced' | 'mode_changed' | 'owner_dispose' | 'startup_failed' | 'temporary_complete' | 'legacy_cleanup' | 'unknown';
|
|
4
|
+
export type StopReason = 'explicit_close' | 'view_replaced' | 'mode_changed' | 'owner_dispose' | 'startup_failed' | 'temporary_complete' | 'legacy_cleanup' | 'backend_unresponsive' | 'unknown';
|
|
5
5
|
type Event = {
|
|
6
6
|
at: string;
|
|
7
7
|
event: 'start' | 'restore' | 'stop';
|
|
@@ -7,7 +7,7 @@ import { object, fail, stable } from './model.js';
|
|
|
7
7
|
import { selectedOptions } from './arguments.js';
|
|
8
8
|
const writes = new Map();
|
|
9
9
|
const journal = (workspace, context) => join(workspace.directory, 'diagnostics', 'lifecycle', context + '.json');
|
|
10
|
-
const reasons = new Set(['explicit_close', 'view_replaced', 'mode_changed', 'owner_dispose', 'startup_failed', 'temporary_complete', 'legacy_cleanup', 'unknown', 'native_launch', 'saved_snapshot']);
|
|
10
|
+
const reasons = new Set(['explicit_close', 'view_replaced', 'mode_changed', 'owner_dispose', 'startup_failed', 'temporary_complete', 'legacy_cleanup', 'unknown', 'native_launch', 'saved_snapshot', 'backend_unresponsive']);
|
|
11
11
|
async function events(path) {
|
|
12
12
|
try {
|
|
13
13
|
const rows = JSON.parse(await readFile(path, 'utf8'));
|
package/dist/src/contexts.d.ts
CHANGED
package/dist/src/contexts.js
CHANGED
|
@@ -429,7 +429,7 @@ export class GhidraContexts {
|
|
|
429
429
|
}
|
|
430
430
|
async open(scope, request) {
|
|
431
431
|
const context = this.workspace.context(scope), target = this.workspace.target(scope.target);
|
|
432
|
-
if (this.workspace.unresolved(scope).some(intent => intent.id !== request))
|
|
432
|
+
if (this.workspace.unresolved(scope).some(intent => intent.id !== request && intent.operation !== 'ghidra.open'))
|
|
433
433
|
fail('blocked', 'This context retains an unresolved operation; reconcile its recorded outcome before continuing.');
|
|
434
434
|
let binding;
|
|
435
435
|
try {
|
|
@@ -458,8 +458,15 @@ export class GhidraContexts {
|
|
|
458
458
|
if (binding && !stopped && alive(binding.pid)) {
|
|
459
459
|
if (binding.context !== context.id)
|
|
460
460
|
fail('binding', 'Native process belongs to a different working context.');
|
|
461
|
-
|
|
462
|
-
|
|
461
|
+
try {
|
|
462
|
+
await this.verified(binding, target);
|
|
463
|
+
return context;
|
|
464
|
+
}
|
|
465
|
+
catch (error) {
|
|
466
|
+
if (!await this.retireUnresponsive(scope, binding, error))
|
|
467
|
+
throw error;
|
|
468
|
+
stopped = true;
|
|
469
|
+
}
|
|
463
470
|
}
|
|
464
471
|
if (binding?.mode === 'gui')
|
|
465
472
|
await this.debuggerReleased(binding);
|
|
@@ -486,6 +493,25 @@ export class GhidraContexts {
|
|
|
486
493
|
return this.workspace.saveContext(scope, context.generation, { state: 'ready', baseRevision: model.sharedRevision,
|
|
487
494
|
baseSnapshot: snapshot, workingSnapshot: snapshot });
|
|
488
495
|
}
|
|
496
|
+
async retireUnresponsive(scope, binding, error) {
|
|
497
|
+
const failure = error;
|
|
498
|
+
if (!['coordinator.timeout', 'coordinator.transport'].includes(failure.code ?? '') || failure.result?.operation !== '/mcp/instance_info' ||
|
|
499
|
+
binding.mode !== 'headless' || !binding.toolProcessId)
|
|
500
|
+
return false;
|
|
501
|
+
const context = this.workspace.context(scope);
|
|
502
|
+
if (!context.workingSnapshot || context.debugger?.connected || this.workspace.activePublication(scope.target) ||
|
|
503
|
+
this.workspace.unresolved(scope).some(intent => intent.operation !== 'ghidra.open'))
|
|
504
|
+
return false;
|
|
505
|
+
// Verify acknowledged bytes before retiring anything. Unknown mutations stay blocked.
|
|
506
|
+
await this.workspace.artifacts.verify(context.workingSnapshot);
|
|
507
|
+
const row = await processStatus(this.c.appArguments.AppDataDirectory, binding.toolProcessId, r => verifyGhidraProcess(r, this.c));
|
|
508
|
+
if (row.state !== 'running' || row.pid !== binding.pid || row.tool !== 'ghidra' || row.role !== 'context' || row.session !== scope.session ||
|
|
509
|
+
row.target !== scope.target || row.installation !== binding.runtimeFingerprint || row.metadata?.contextRoot !== binding.root)
|
|
510
|
+
return false;
|
|
511
|
+
await terminateProcess(this.c.appArguments.AppDataDirectory, binding.toolProcessId, true, r => verifyGhidraProcess(r, this.c));
|
|
512
|
+
await this.stopRecord({ binding, http: new HttpBridge(binding.endpoint) }, false, true, 'backend_unresponsive');
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
489
515
|
async selected(scope) {
|
|
490
516
|
const context = this.workspace.context(scope);
|
|
491
517
|
const binding = JSON.parse(await readFile(join(this.directory(), 'context', context.id, 'current.json'), 'utf8'));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nativeProcessProof } from './tool-runtime/native-process-proof.js';
|
|
1
2
|
import { sameDirectory, ownedDirectory, projectPathWithin } from './directory-identity.js';
|
|
2
3
|
import { runtimeSelection } from './runtime-selection.js';
|
|
3
4
|
import { join, resolve, relative, sep, dirname } from 'node:path';
|
|
@@ -52,7 +53,11 @@ export async function verifyGhidraProcess(r, c) {
|
|
|
52
53
|
const root = await ownedPath(c, r.metadata.contextRoot), binding = JSON.parse(await readFile(join(root, 'process.json'), 'utf8'));
|
|
53
54
|
if (!sameDirectory(binding.root, root) || binding.pid !== r.pid || binding.runtimeFingerprint !== r.installation || binding.target !== r.target)
|
|
54
55
|
return false;
|
|
55
|
-
const token = await c.secrets.require(binding.tokenSecret)
|
|
56
|
+
const token = await c.secrets.require(binding.tokenSecret);
|
|
57
|
+
// A headless JVM may be unable to serve HTTP while the kernel still proves its token, executable and private context root.
|
|
58
|
+
if (binding.mode === 'headless' && await nativeProcessProof(r.pid, r.executable, root, token))
|
|
59
|
+
return true;
|
|
60
|
+
const info = await new HttpBridge(binding.endpoint, c.fetch, undefined, token, 1500).instance();
|
|
56
61
|
return info.pid === r.pid && info.tcp_port === Number(new URL(binding.endpoint).port) && projectPathWithin(info.project_path, binding.root);
|
|
57
62
|
}
|
|
58
63
|
return undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writeFile } from 'node:fs/promises';
|
|
1
|
+
import { writeFile, realpath } from 'node:fs/promises';
|
|
2
2
|
import { join, win32 } from 'node:path';
|
|
3
3
|
import { execFile } from 'node:child_process';
|
|
4
4
|
import { promisify } from 'node:util';
|
|
@@ -36,7 +36,7 @@ export async function javaClasspathArguments(root, tool, executable, args, platf
|
|
|
36
36
|
const index = args.findIndex(arg => arg === '-cp' || arg === '-classpath' || arg === '--class-path');
|
|
37
37
|
if (index < 0 || args[index + 1] === undefined)
|
|
38
38
|
return args;
|
|
39
|
-
const path = join(root, 'java-classpath.args');
|
|
39
|
+
const path = join(await realpath(root), 'java-classpath.args');
|
|
40
40
|
const content = '-classpath\n' + quoteJavaArgument(args[index + 1]) + '\n';
|
|
41
41
|
const bytes = /^java\.exe$/i.test(win32.basename(executable)) && process.platform === 'win32' ? await encodeJavaLauncherFile(content) : Buffer.from(content, 'utf8');
|
|
42
42
|
await writeFile(path, bytes, { flag: 'wx', mode: 0o600 });
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { readFile, realpath } from 'node:fs/promises';
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
import { createHash, timingSafeEqual } from 'node:crypto';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const exec = promisify(execFile);
|
|
7
|
+
/** Independent kernel observation; no request enters the possibly wedged JVM. */
|
|
8
|
+
export async function nativeProcessProof(pid, executable, cwd, token) {
|
|
9
|
+
if (process.platform !== 'darwin' || !Number.isSafeInteger(pid) || pid < 1)
|
|
10
|
+
return false;
|
|
11
|
+
try {
|
|
12
|
+
const base = new URL('../../../runtime/native/', import.meta.url), path = fileURLToPath(new URL('process-identity-darwin-' + process.arch, base));
|
|
13
|
+
const provenance = JSON.parse(await readFile(new URL('process-identity.provenance.json', base), 'utf8'));
|
|
14
|
+
if (provenance.platform !== 'darwin' || provenance.architecture !== process.arch || createHash('sha256').update(await readFile(path)).digest('hex') !== provenance.sha256)
|
|
15
|
+
return false;
|
|
16
|
+
const result = await exec(path, [String(pid)], { timeout: 3000, maxBuffer: 16384, env: { PATH: '/usr/bin:/bin' } }), row = JSON.parse(result.stdout);
|
|
17
|
+
if (row.pid !== pid || row.uid !== process.geteuid?.() || !Number.isSafeInteger(row.birthSeconds) || row.birthSeconds <= 0 || !Number.isSafeInteger(row.birthMicroseconds) || row.birthMicroseconds < 0 || row.birthMicroseconds >= 1000000 || typeof row.tokenSha256 !== 'string' || !/^[a-f0-9]{64}$/.test(row.tokenSha256))
|
|
18
|
+
return false;
|
|
19
|
+
if (await realpath(row.executable) !== await realpath(executable) || await realpath(row.root) !== await realpath(cwd))
|
|
20
|
+
return false;
|
|
21
|
+
return timingSafeEqual(Buffer.from(row.tokenSha256, 'hex'), createHash('sha256').update(token).digest());
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
2
|
import { join, isAbsolute, resolve } from 'node:path';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { performance } from 'node:perf_hooks';
|
|
5
5
|
import { CliError } from '@eyeauras/cli-factory';
|
|
6
|
-
import { atomicWrite, withToolUse, toolRoot, readJson } from './store.js';
|
|
6
|
+
import { atomicWrite, withToolUse, toolRoot, readJson, prepareRunRoot } from './store.js';
|
|
7
7
|
import { launchProcess } from './supervisor.js';
|
|
8
8
|
import { digest } from '../model.js';
|
|
9
9
|
import { deadlineLaunch } from './deadline.js';
|
|
@@ -23,7 +23,7 @@ export async function runTool(c, spec) {
|
|
|
23
23
|
const started = performance.now(), limit = spec.maxOutputBytes ?? 4 * 1024 ** 2;
|
|
24
24
|
let child, outcome, truncated = false, bytes = 0;
|
|
25
25
|
const stdout = [], stderr = [], record = { schema: 1, id, tool: spec.tool, role: spec.role, installation: spec.installation ?? 'probe', state: 'starting', executable: spec.executable, createdAt: new Date().toISOString(), root, ownerPid: process.pid, profileBinding: digest(resolve(directory)), gated: process.platform !== 'win32' && typeof process.execve === 'function', phase: 'reserved', ...(spec.session ? { session: spec.session } : {}), ...(spec.target ? { target: spec.target } : {}) };
|
|
26
|
-
const lease = await withToolUse(directory, spec.tool, async () => { await
|
|
26
|
+
const lease = await withToolUse(directory, spec.tool, async () => { await prepareRunRoot(directory, id); }, c.signal);
|
|
27
27
|
const environment = { PATH: c.environment.PATH ?? process.env.PATH, HOME: root, TMPDIR: root, ...(process.platform === 'win32' ? { SystemRoot: process.env.SystemRoot, WINDIR: process.env.WINDIR, ComSpec: process.env.ComSpec, TEMP: root, TMP: root, USERPROFILE: root, LOCALAPPDATA: root, APPDATA: root } : {}), ...spec.environment };
|
|
28
28
|
const secrets = Object.entries(environment).filter(([key, value]) => /TOKEN|PASSWORD|SECRET|KEY/i.test(key) && value && value.length >= 4).map(([, v]) => v);
|
|
29
29
|
const redact = (value) => secrets.reduce((text, secret) => text.split(secret).join('[redacted]'), value);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export declare function atomicWrite(path: string, value: unknown): Promise<void>;
|
|
2
|
+
/** Create new run folders through the physical runtime, retaining logical receipt identity. */
|
|
3
|
+
export declare function prepareRunRoot(directory: string, id: string): Promise<void>;
|
|
2
4
|
export declare function readJson<T>(path: string): Promise<T | undefined>;
|
|
3
5
|
export declare function toolRoot(directory: string, tool: string): string;
|
|
4
6
|
/** CLI clients already share Factory's elected owner. Do not create another IPC/lease protocol. */
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { mkdir, open, rename, rm, readFile, readdir, lstat, realpath } from 'node:fs/promises';
|
|
2
|
-
import { createReadStream } from 'node:fs';
|
|
3
|
-
import { dirname, join, resolve, relative, sep } from 'node:path';
|
|
2
|
+
import { createReadStream, existsSync } from 'node:fs';
|
|
3
|
+
import { dirname, basename, join, resolve, relative, sep } from 'node:path';
|
|
4
4
|
import { randomUUID, createHash } from 'node:crypto';
|
|
5
5
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
6
6
|
import { CliError } from '@eyeauras/cli-factory';
|
|
7
7
|
import { fail } from '../model.js';
|
|
8
8
|
export async function atomicWrite(path, value) {
|
|
9
|
-
await
|
|
10
|
-
|
|
9
|
+
const parent = await physicalDirectory(dirname(path)), target = join(parent, basename(path));
|
|
10
|
+
await mkdir(parent, { recursive: true, mode: 0o700 });
|
|
11
|
+
const temporary = target + '.new-' + randomUUID(), file = await open(temporary, 'wx', 0o600);
|
|
11
12
|
try {
|
|
12
13
|
await file.writeFile(JSON.stringify(value, null, 2) + '\n');
|
|
13
14
|
await file.sync();
|
|
@@ -15,17 +16,34 @@ export async function atomicWrite(path, value) {
|
|
|
15
16
|
finally {
|
|
16
17
|
await file.close();
|
|
17
18
|
}
|
|
18
|
-
await rename(temporary,
|
|
19
|
+
await rename(temporary, target);
|
|
19
20
|
if (process.platform !== 'win32') {
|
|
20
|
-
const
|
|
21
|
+
const handle = await open(parent, 'r');
|
|
21
22
|
try {
|
|
22
|
-
await
|
|
23
|
+
await handle.sync();
|
|
23
24
|
}
|
|
24
25
|
finally {
|
|
25
|
-
await
|
|
26
|
+
await handle.close();
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
}
|
|
30
|
+
async function physicalDirectory(path) {
|
|
31
|
+
try {
|
|
32
|
+
return await realpath(path);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error.code !== 'ENOENT' || dirname(path) === path)
|
|
36
|
+
throw error;
|
|
37
|
+
return join(await physicalDirectory(dirname(path)), basename(path));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Create new run folders through the physical runtime, retaining logical receipt identity. */
|
|
41
|
+
export async function prepareRunRoot(directory, id) {
|
|
42
|
+
const runtime = join(directory, 'runtime');
|
|
43
|
+
if (!existsSync(runtime))
|
|
44
|
+
await mkdir(runtime, { recursive: true, mode: 0o700 });
|
|
45
|
+
await mkdir(join(await realpath(runtime), 'runs', id), { recursive: true, mode: 0o700 });
|
|
46
|
+
}
|
|
29
47
|
export async function readJson(path) { try {
|
|
30
48
|
return JSON.parse(await readFile(path, 'utf8'));
|
|
31
49
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { sameDirectory } from '../directory-identity.js';
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
|
-
import { access,
|
|
3
|
+
import { access, readdir } from 'node:fs/promises';
|
|
4
4
|
import { constants } from 'node:fs';
|
|
5
5
|
import { join, isAbsolute, resolve } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { randomUUID } from 'node:crypto';
|
|
8
8
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
9
9
|
import { CliError } from '@eyeauras/cli-factory';
|
|
10
|
-
import { atomicWrite, readJson, toolRoot, withToolLock } from './store.js';
|
|
10
|
+
import { atomicWrite, readJson, toolRoot, withToolLock, prepareRunRoot } from './store.js';
|
|
11
11
|
import { processIdentity } from './identity.js';
|
|
12
12
|
import { javaClasspathArguments } from './java-arguments.js';
|
|
13
13
|
import { digest } from '../model.js';
|
|
@@ -53,7 +53,7 @@ export async function launchProcess(directory, spec, executable, args, options =
|
|
|
53
53
|
if (!/^[a-f0-9-]{36}$/.test(id))
|
|
54
54
|
throw Error('Invalid process identity');
|
|
55
55
|
const root = join(directory, 'runtime/runs', id), path = join(directory, 'runtime/processes', id + '.json');
|
|
56
|
-
await
|
|
56
|
+
await prepareRunRoot(directory, id);
|
|
57
57
|
const gated = process.platform !== 'win32' && typeof process.execve === 'function';
|
|
58
58
|
const record = { schema: 1, id, ...spec, state: 'starting', executable, root, ownerPid: process.pid, profileBinding: digest(resolve(directory)), gated, phase: 'reserved', createdAt: new Date().toISOString() };
|
|
59
59
|
await atomicWrite(path, record);
|
|
@@ -273,7 +273,7 @@ export async function registerExistingProcess(directory, spec, executable, pid,
|
|
|
273
273
|
const previous = await readJson(path);
|
|
274
274
|
if (previous)
|
|
275
275
|
return id;
|
|
276
|
-
await
|
|
276
|
+
await prepareRunRoot(directory, id);
|
|
277
277
|
await atomicWrite(path, record);
|
|
278
278
|
return id;
|
|
279
279
|
}
|
package/package.json
CHANGED
package/references/recovery.md
CHANGED
|
@@ -24,6 +24,23 @@ establishes that the old owned bootstrap worker has ended, then uses an authorit
|
|
|
24
24
|
probe. An absent canonical item permits private preparation; an existing first version requires
|
|
25
25
|
independent readback. The old project, intent and original session/target remain preserved.
|
|
26
26
|
|
|
27
|
+
## Unresponsive headless backend
|
|
28
|
+
|
|
29
|
+
Continue the same session/target with ordinary `ghidra open` or a research read. When the
|
|
30
|
+
backend fails its identity preflight, the facade verifies the acknowledged private snapshot
|
|
31
|
+
and independently authenticates the exact worker before retiring it and restoring that snapshot.
|
|
32
|
+
This automatic retirement is limited to headless contexts without uncertain mutations, active
|
|
33
|
+
publication or a connected debugger. A failed health preflight is never permission to repeat a
|
|
34
|
+
script, save or publication. Journals, original projects and acknowledged pending edits remain.
|
|
35
|
+
Lifecycle diagnostics record `backend_unresponsive` followed by snapshot restoration.
|
|
36
|
+
|
|
37
|
+
On macOS ARM64 the packaged read-only kernel helper checks the same-user process, executable,
|
|
38
|
+
private root and digest of its unique context token without entering the JVM. It emits no token,
|
|
39
|
+
argument list or environment values. PID birth and profile bindings remain independently checked.
|
|
40
|
+
A missing/unreadable/mismatched proof stays blocked; never kill by port, basename or guessed PID.
|
|
41
|
+
Other platforms retain their existing ownership authentication; this does not claim equivalent
|
|
42
|
+
hang recovery where no independent proof is available. No compiler is required on installation.
|
|
43
|
+
|
|
27
44
|
## Interrupted save or close
|
|
28
45
|
|
|
29
46
|
After restoring profile storage, inspect the selected session and target with
|
package/references/storage.md
CHANGED
|
@@ -84,6 +84,17 @@ those links or their profile/source ancestors manually after migrating.
|
|
|
84
84
|
|
|
85
85
|
## Interruptions, uncertainty and retention
|
|
86
86
|
|
|
87
|
+
If a completed migration no longer agrees with the Factory profile setting, ordinary
|
|
88
|
+
commands and `storage resume` fail. After inspecting `storage show`, explicitly restore
|
|
89
|
+
the setting with `storage resume --repair-profile --json`. This Update operation verifies
|
|
90
|
+
the journal, source/destination identities, destination ownership and every compatibility
|
|
91
|
+
link before changing only `analysisDirectory`. Later destination research is preserved;
|
|
92
|
+
the old migration manifest is not used to roll it back. A different selected location,
|
|
93
|
+
missing volume or replaced compatibility path blocks repair. Preserve conflicting paths
|
|
94
|
+
and restore the recorded links before retrying; repair never deletes or merges them.
|
|
95
|
+
Process-inventory failures are reported as `storage.inventory` with the original cause
|
|
96
|
+
available under `--verbose`. A terminal unavailable migration is never a successful resume.
|
|
97
|
+
|
|
87
98
|
```sh
|
|
88
99
|
insight-cli storage resume --json
|
|
89
100
|
insight-cli storage rollback --json
|
|
Binary file
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* MIT; first-party read-only process proof. Never prints argv or environment values. */
|
|
2
|
+
#include <libproc.h>
|
|
3
|
+
#include <sys/sysctl.h>
|
|
4
|
+
#include <sys/proc_info.h>
|
|
5
|
+
#include <sys/proc.h>
|
|
6
|
+
#include <CommonCrypto/CommonDigest.h>
|
|
7
|
+
#include <unistd.h>
|
|
8
|
+
#include <stdio.h>
|
|
9
|
+
#include <stdlib.h>
|
|
10
|
+
#include <string.h>
|
|
11
|
+
#include <errno.h>
|
|
12
|
+
static void json(const char *s) {
|
|
13
|
+
putchar('"'); for(const unsigned char *p=(const unsigned char *)s;*p;p++) {
|
|
14
|
+
if(*p=='"'||*p=='\\')printf("\\%c",*p); else if(*p<32)printf("\\u%04x",*p);else putchar(*p);
|
|
15
|
+
} putchar('"');
|
|
16
|
+
}
|
|
17
|
+
int main(int argc,char **argv) {
|
|
18
|
+
if(argc==2&&!strcmp(argv[1],"--version")){puts("insight-process-identity 1");return 0;}
|
|
19
|
+
char *end=NULL;errno=0;long value=argc==2?strtol(argv[1],&end,10):0;
|
|
20
|
+
if(errno||!end||*end||value<1||value>2147483647)return 64;
|
|
21
|
+
int pid=(int)value;struct proc_bsdinfo before,after;char executable[PROC_PIDPATHINFO_MAXSIZE];
|
|
22
|
+
if(proc_pidinfo(pid,PROC_PIDTBSDINFO,0,&before,sizeof before)!=sizeof before||before.pbi_uid!=geteuid()||before.pbi_status==SZOMB)return 11;
|
|
23
|
+
memset(executable,0,sizeof executable);int has_path=proc_pidpath(pid,executable,sizeof executable)>0;
|
|
24
|
+
char owned_root[PROC_PIDPATHINFO_MAXSIZE]={0};
|
|
25
|
+
int mib[3]={CTL_KERN,KERN_PROCARGS2,pid};size_t size=0;
|
|
26
|
+
if(sysctl(mib,3,NULL,&size,NULL,0)||size<sizeof(int)||size>2*1024*1024)return 13;
|
|
27
|
+
char *data=calloc(size+1,1);if(!data)return 70;
|
|
28
|
+
if(sysctl(mib,3,data,&size,NULL,0)){free(data);return 14;}
|
|
29
|
+
int nargs;memcpy(&nargs,data,sizeof nargs);if(nargs<1||nargs>65536){free(data);return 1;}
|
|
30
|
+
char *p=data+sizeof(int),*limit=data+size;size_t executable_length=strnlen(p,(size_t)(limit-p));
|
|
31
|
+
if(executable_length>=sizeof executable||p+executable_length>=limit){free(data);return 1;}
|
|
32
|
+
if(!has_path)memcpy(executable,p,executable_length+1);
|
|
33
|
+
while(p<limit&&*p)p++;while(p<limit&&!*p)p++;
|
|
34
|
+
for(int i=0;i<nargs&&p<limit;i++){while(p<limit&&*p)p++;if(p<limit)p++;}
|
|
35
|
+
unsigned char digest[CC_SHA256_DIGEST_LENGTH];int found=0;const char key[]="GHIDRA_MCP_AUTH_TOKEN=";
|
|
36
|
+
while(p<limit&&!*p)p++;
|
|
37
|
+
while(p<limit&&*p){size_t n=strnlen(p,(size_t)(limit-p));
|
|
38
|
+
if(n>sizeof(key)-1&&!memcmp(p,key,sizeof(key)-1)){if(found){free(data);return 1;}CC_SHA256(p+sizeof(key)-1,(CC_LONG)(n-sizeof(key)+1),digest);found=1;}
|
|
39
|
+
const char root_key[]="GHIDRA_MCP_FILE_ROOT=";
|
|
40
|
+
if(n>sizeof(root_key)-1&&!memcmp(p,root_key,sizeof(root_key)-1)){size_t length=n-sizeof(root_key)+1;if(owned_root[0]||length>=sizeof owned_root){free(data);return 1;}memcpy(owned_root,p+sizeof(root_key)-1,length);}
|
|
41
|
+
p+=n+1;
|
|
42
|
+
}
|
|
43
|
+
memset(data,0,size);free(data);
|
|
44
|
+
if(!found||!owned_root[0]||proc_pidinfo(pid,PROC_PIDTBSDINFO,0,&after,sizeof after)!=sizeof after||before.pbi_start_tvsec!=after.pbi_start_tvsec||before.pbi_start_tvusec!=after.pbi_start_tvusec||before.pbi_uid!=after.pbi_uid)return 15;
|
|
45
|
+
printf("{\"pid\":%d,\"uid\":%u,\"birthSeconds\":%llu,\"birthMicroseconds\":%llu,\"executable\":",pid,before.pbi_uid,before.pbi_start_tvsec,before.pbi_start_tvusec);json(executable);
|
|
46
|
+
printf(",\"root\":");json(owned_root);printf(",\"tokenSha256\":\"");for(size_t i=0;i<sizeof digest;i++)printf("%02x",digest[i]);puts("\"}");return 0;
|
|
47
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": 1,
|
|
3
|
+
"license": "MIT",
|
|
4
|
+
"platform": "darwin",
|
|
5
|
+
"architecture": "arm64",
|
|
6
|
+
"compiler": "Apple clang version 17.0.0 (clang-1700.3.19.1)\nTarget: arm64-apple-darwin25.6.0\nThread model: posix\nInstalledDir: /Library/Developer/CommandLineTools/usr/bin",
|
|
7
|
+
"sourceSha256": "f147da90168123783593f06461c78aea9e4b6ac326b7bdd003da1b0765a06265",
|
|
8
|
+
"sha256": "8d7a15c925ba49b8bea5280c94c03c5a9e39ac33f408ad6dcc068af095e796f4",
|
|
9
|
+
"flags": [
|
|
10
|
+
"-arch",
|
|
11
|
+
"arm64",
|
|
12
|
+
"-mmacosx-version-min=13.0",
|
|
13
|
+
"-O2",
|
|
14
|
+
"-g0",
|
|
15
|
+
"-Werror",
|
|
16
|
+
"-Wno-deprecated-declarations"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -3,7 +3,7 @@ name: insight-artifacts
|
|
|
3
3
|
description: Retain a completed reverse-engineering evidence file or directory as an ordinary checkpoint using insight-cli artifacts save. Use after Ghidra or any external tool; preserve existing project formats and source of truth.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight Artifacts 0.5.
|
|
6
|
+
# Insight Artifacts 0.5.5
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -24,6 +24,23 @@ establishes that the old owned bootstrap worker has ended, then uses an authorit
|
|
|
24
24
|
probe. An absent canonical item permits private preparation; an existing first version requires
|
|
25
25
|
independent readback. The old project, intent and original session/target remain preserved.
|
|
26
26
|
|
|
27
|
+
## Unresponsive headless backend
|
|
28
|
+
|
|
29
|
+
Continue the same session/target with ordinary `ghidra open` or a research read. When the
|
|
30
|
+
backend fails its identity preflight, the facade verifies the acknowledged private snapshot
|
|
31
|
+
and independently authenticates the exact worker before retiring it and restoring that snapshot.
|
|
32
|
+
This automatic retirement is limited to headless contexts without uncertain mutations, active
|
|
33
|
+
publication or a connected debugger. A failed health preflight is never permission to repeat a
|
|
34
|
+
script, save or publication. Journals, original projects and acknowledged pending edits remain.
|
|
35
|
+
Lifecycle diagnostics record `backend_unresponsive` followed by snapshot restoration.
|
|
36
|
+
|
|
37
|
+
On macOS ARM64 the packaged read-only kernel helper checks the same-user process, executable,
|
|
38
|
+
private root and digest of its unique context token without entering the JVM. It emits no token,
|
|
39
|
+
argument list or environment values. PID birth and profile bindings remain independently checked.
|
|
40
|
+
A missing/unreadable/mismatched proof stays blocked; never kill by port, basename or guessed PID.
|
|
41
|
+
Other platforms retain their existing ownership authentication; this does not claim equivalent
|
|
42
|
+
hang recovery where no independent proof is available. No compiler is required on installation.
|
|
43
|
+
|
|
27
44
|
## Interrupted save or close
|
|
28
45
|
|
|
29
46
|
After restoring profile storage, inspect the selected session and target with
|
|
@@ -84,6 +84,17 @@ those links or their profile/source ancestors manually after migrating.
|
|
|
84
84
|
|
|
85
85
|
## Interruptions, uncertainty and retention
|
|
86
86
|
|
|
87
|
+
If a completed migration no longer agrees with the Factory profile setting, ordinary
|
|
88
|
+
commands and `storage resume` fail. After inspecting `storage show`, explicitly restore
|
|
89
|
+
the setting with `storage resume --repair-profile --json`. This Update operation verifies
|
|
90
|
+
the journal, source/destination identities, destination ownership and every compatibility
|
|
91
|
+
link before changing only `analysisDirectory`. Later destination research is preserved;
|
|
92
|
+
the old migration manifest is not used to roll it back. A different selected location,
|
|
93
|
+
missing volume or replaced compatibility path blocks repair. Preserve conflicting paths
|
|
94
|
+
and restore the recorded links before retrying; repair never deletes or merges them.
|
|
95
|
+
Process-inventory failures are reported as `storage.inventory` with the original cause
|
|
96
|
+
available under `--verbose`. A terminal unavailable migration is never a successful resume.
|
|
97
|
+
|
|
87
98
|
```sh
|
|
88
99
|
insight-cli storage resume --json
|
|
89
100
|
insight-cli storage rollback --json
|
|
@@ -3,7 +3,7 @@ name: insight-binary
|
|
|
3
3
|
description: Query structured PE, ELF and thin Mach-O headers, sections, imports and exports, or make an explicitly guarded new PE subsystem copy through insight-cli binary. Use for scoped binary structure, linkage and copy-only header edits without target execution.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight Binary 0.5.
|
|
6
|
+
# Insight Binary 0.5.5
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -3,7 +3,7 @@ name: insight-capture
|
|
|
3
3
|
description: Capture one explicitly authorized native program run, exact stdout/stderr and declared file snapshots through insight-cli capture, then inspect or compare retained reports without replay. Use for controlled input/output research, preserving original files and interrupted or unknown outcomes.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Insight Capture 0.5.
|
|
6
|
+
# Insight Capture 0.5.5
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|
|
@@ -3,7 +3,7 @@ name: insight-cli
|
|
|
3
3
|
description: Choose and prepare reverse-engineering capabilities from the insight-cli toolbox for binary, managed-code and runtime investigations. Use to discover the appropriate tool or its packaged skill before installing native runtimes.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# insight-cli 0.5.
|
|
6
|
+
# insight-cli 0.5.5
|
|
7
7
|
|
|
8
8
|
If local help/status reports AppData or IPC access denied, retry that **read-only probe**
|
|
9
9
|
with the agent host's normal permission escalation, then use that route for later authorized
|