fix: use sha256 descriptor digest and keep operator errors fully sanitized
This commit is contained in:
@@ -41,10 +41,9 @@ function boundedJson(result: WorkspaceOperationResult): string {
|
|||||||
return encoded;
|
return encoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizeStderr(error: unknown): string {
|
function sanitizeStderr(_error: unknown): string {
|
||||||
const detail = error instanceof Error ? error.message : String(error);
|
// Never return raw exception text: it may embed endpoints, tokens, or SQL.
|
||||||
const safe = detail.replace(/[\r\n]+/g, " ").slice(0, 512);
|
return "workspace maintenance failed\n";
|
||||||
return safe.length > 0 ? `workspace maintenance failed: ${safe}\n` : "workspace maintenance failed\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRequest(command: string, stdin: string): Record<string, unknown> {
|
function parseRequest(command: string, stdin: string): Record<string, unknown> {
|
||||||
|
|||||||
@@ -129,8 +129,7 @@ export class WorkspacePreprocessingService {
|
|||||||
{ kind: "runtime_config", digest: runtime.configLease.configDigest },
|
{ kind: "runtime_config", digest: runtime.configLease.configDigest },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch {
|
||||||
const detail = error instanceof Error ? error.message : String(error);
|
|
||||||
return {
|
return {
|
||||||
schemaVersion: 1,
|
schemaVersion: 1,
|
||||||
status: "failed",
|
status: "failed",
|
||||||
@@ -140,7 +139,6 @@ export class WorkspacePreprocessingService {
|
|||||||
descriptorBlob: "",
|
descriptorBlob: "",
|
||||||
operation: "inspect",
|
operation: "inspect",
|
||||||
completedStages: [],
|
completedStages: [],
|
||||||
warnings: detail.length > 0 ? [detail.slice(0, 512)] : undefined,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,7 +352,8 @@ export async function renderActiveWorkspaceRuntime(options: {
|
|||||||
const repository = new GitWorkspaceRepository(options.registryConfig);
|
const repository = new GitWorkspaceRepository(options.registryConfig);
|
||||||
await repository.ensureLayout();
|
await repository.ensureLayout();
|
||||||
const snapshotPath = options.registry.snapshotPath(revision.commit, revision.id);
|
const snapshotPath = options.registry.snapshotPath(revision.commit, revision.id);
|
||||||
const workspace = parseWorkspaceYaml(await readFileAsync(snapshotPath, "utf8"));
|
const descriptorSource = await readFileAsync(snapshotPath, "utf8");
|
||||||
|
const workspace = parseWorkspaceYaml(descriptorSource);
|
||||||
const rendered = renderWorkspaceRuntimeFromWorkspace({
|
const rendered = renderWorkspaceRuntimeFromWorkspace({
|
||||||
workspace,
|
workspace,
|
||||||
workspaceId: revision.id,
|
workspaceId: revision.id,
|
||||||
@@ -367,7 +368,7 @@ export async function renderActiveWorkspaceRuntime(options: {
|
|||||||
return {
|
return {
|
||||||
...rendered,
|
...rendered,
|
||||||
snapshotPath,
|
snapshotPath,
|
||||||
descriptorBlob: revision.blob,
|
descriptorBlob: `sha256:${createHash("sha256").update(descriptorSource).digest("hex")}`,
|
||||||
catalogBlob: (await repository.catalogBlob(revision.commit)).trim(),
|
catalogBlob: (await repository.catalogBlob(revision.commit)).trim(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ test("active workspace rendering is byte-identical to direct snapshot rendering"
|
|||||||
|
|
||||||
expect(active.renderedConfig).toBe(direct.renderedConfig);
|
expect(active.renderedConfig).toBe(direct.renderedConfig);
|
||||||
expect(active.workspaceRevision).toBe(f.revision.commit);
|
expect(active.workspaceRevision).toBe(f.revision.commit);
|
||||||
expect(active.descriptorBlob).toBe(f.revision.blob);
|
expect(active.descriptorBlob).toMatch(/^sha256:[0-9a-f]{64}$/);
|
||||||
expect(active.catalogBlob).toMatch(/^[0-9a-f]{40}$/);
|
expect(active.catalogBlob).toMatch(/^[0-9a-f]{40}$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user