mirror of
https://github.com/morluto/rea
synced 2026-10-28 23:11:11 -04:00
bd4f5e9c19
Retain bounded FAT and offset-tagged PE observations through archive hashing, classification, and content nodes. Preserve PyInstaller member paths according to the observed carrier dialect and share APK v1 signing-name semantics. Read SQLite's implicit sqlite_schema table without inventing a stored schema declaration. Distinguish unsupported ELF numbering and decoded-name admission from malformed data and fixed scan allowances. Preserve Sigrok basenames and classify matching explicit input-format refusals at their owning boundary. Validation: 554 distinct tests passed, 3 skipped across serial focused runs; build, typecheck/lint, docs, formatting, and discovery (2141 files) passed. Sigrok command-boundary simulators do not establish real-tool decoding. Small repeated public PyInstaller CLI runs had equivalent inventory facts; warm median elapsed 0.5431s before / 0.5426s after, with no speed claim. Fixes #6733 Fixes #6741 Fixes #6722 Fixes #6732 Fixes #6736 Fixes #6734 Fixes #6764 Fixes #6766 Refs #6739
129 lines
4.6 KiB
TypeScript
129 lines
4.6 KiB
TypeScript
import type { ExecutionOptions } from "../AnalysisProvider.js";
|
|
import type { SqliteDatabasePort } from "./SqliteDatabasePort.js";
|
|
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
|
|
import {
|
|
AnalysisCancelledError,
|
|
AnalysisOutputError,
|
|
} from "../../domain/analysisErrorCore.js";
|
|
import {
|
|
createEvidence,
|
|
type Evidence,
|
|
type EvidenceSubjectTarget,
|
|
} from "../../domain/evidence.js";
|
|
import {
|
|
inspectSqliteDatabaseInputSchema,
|
|
sqliteDatabaseSchema,
|
|
type InspectSqliteDatabaseInput,
|
|
type SqliteDatabase,
|
|
SQLITE_ROW_LIMIT_DEFAULT,
|
|
DEFAULT_SQLITE_WORKER_TIMEOUT_MS,
|
|
sqliteSchemaHasObjectDeclaration,
|
|
sqliteSchemaScope,
|
|
sqliteInspectionParameters,
|
|
} from "../../domain/sqlite/sqliteDatabase.js";
|
|
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
|
|
import { err, ok, type Result } from "../../domain/result.js";
|
|
import { jsonValueSchema } from "../../domain/jsonValue.js";
|
|
|
|
const OPERATION = "inspect_sqlite_database";
|
|
|
|
const matchesSelectedSchema = (
|
|
schema: SqliteDatabase["schema"],
|
|
table: string,
|
|
): boolean =>
|
|
sqliteSchemaHasObjectDeclaration(table)
|
|
? schema.tables.length === 1 &&
|
|
schema.tables[0]?.name === table &&
|
|
schema.indexes.every((index) => index.table === table) &&
|
|
schema.triggers.every((trigger) => trigger.table === table) &&
|
|
schema.views.length === 0
|
|
: schema.tables.length === 0 &&
|
|
schema.indexes.length === 0 &&
|
|
schema.views.length === 0 &&
|
|
schema.triggers.length === 0;
|
|
|
|
const matchesSelectedDatabase = (
|
|
report: SqliteDatabase,
|
|
input: InspectSqliteDatabaseInput,
|
|
subject: EvidenceSubjectTarget,
|
|
): boolean =>
|
|
report.artifact.path === input.path &&
|
|
subject.path === input.path &&
|
|
subject.sha256 === report.artifact.sha256 &&
|
|
subject.format === "file" &&
|
|
// Fresh executions must report known settings; only historical parsing permits omission.
|
|
report.worker_timeout_ms ===
|
|
(input.worker_timeout_ms ?? DEFAULT_SQLITE_WORKER_TIMEOUT_MS) &&
|
|
(report.wal === null || report.wal.path === `${input.path}-wal`) &&
|
|
report.schema.scope === sqliteSchemaScope(input) &&
|
|
(report.schema.scope !== "selected_table" ||
|
|
(input.table !== undefined &&
|
|
matchesSelectedSchema(report.schema, input.table))) &&
|
|
(input.table === undefined
|
|
? report.rows === null
|
|
: report.rows?.table === input.table &&
|
|
report.rows.row_limit === (input.row_limit ?? SQLITE_ROW_LIMIT_DEFAULT) &&
|
|
(input.columns === undefined ||
|
|
(report.rows.columns.length === input.columns.length &&
|
|
report.rows.columns.every(
|
|
(name, index) => name === input.columns?.[index],
|
|
))));
|
|
|
|
/** Shared snapshot inspection and caller-selection validation for CLI and MCP. */
|
|
export class SqliteDatabaseService {
|
|
constructor(readonly provider: SqliteDatabasePort) {}
|
|
|
|
/** Await provider shutdown, retaining failed cleanup owners for retry. */
|
|
close(): Promise<void> {
|
|
return this.provider.close?.() ?? Promise.resolve();
|
|
}
|
|
|
|
/** Inspect an explicit database snapshot and preserve its original database/WAL identity. */
|
|
async inspect(
|
|
rawInput: unknown,
|
|
options?: ExecutionOptions,
|
|
): Promise<Result<Evidence, AnalysisError>> {
|
|
if (options?.signal?.aborted)
|
|
return err(
|
|
new AnalysisCancelledError(OPERATION, {
|
|
cause: options.signal.reason,
|
|
}),
|
|
);
|
|
const input = inspectSqliteDatabaseInputSchema.safeParse(rawInput);
|
|
if (!input.success)
|
|
return err(
|
|
analysisInputErrorFromIssues(OPERATION, input.error.issues, rawInput, {
|
|
cause: input.error,
|
|
}),
|
|
);
|
|
const inspected = await this.provider.inspect(input.data, options);
|
|
if (!inspected.ok) return inspected;
|
|
const execution = inspected.value;
|
|
const report = sqliteDatabaseSchema.safeParse(execution.result);
|
|
if (
|
|
!report.success ||
|
|
execution.subject == null ||
|
|
!matchesSelectedDatabase(report.data, input.data, execution.subject)
|
|
)
|
|
return err(
|
|
new AnalysisOutputError(
|
|
OPERATION,
|
|
"SQLite adapter returned malformed data or changed the selected database identity/table/row limit/worker budget.",
|
|
),
|
|
);
|
|
return ok(
|
|
createEvidence(execution.subject, execution.provider, {
|
|
operation: OPERATION,
|
|
parameters: sqliteInspectionParameters(input.data),
|
|
result: jsonValueSchema.parse(report.data),
|
|
...(execution.rawResult === null
|
|
? {}
|
|
: { rawResult: execution.rawResult }),
|
|
confidence: "observed",
|
|
locations: execution.locations,
|
|
limitations: execution.limitations,
|
|
}),
|
|
);
|
|
}
|
|
}
|