Tighten opaque plugin content boundaries
This commit is contained in:
@@ -638,18 +638,18 @@ describe("plugin manifest validation", () => {
|
||||
expect(actionErrors.some((error) => error.includes("page must declare remote.access.request"))).toBe(true);
|
||||
});
|
||||
|
||||
it.each(["sqlText", "dsn", "hostPath", "shellCommand", "socketAddress", "accessToken", "credential"])("rejects unsafe query parameter schema field %s", (fieldName) => {
|
||||
it.each(["sqlText", "dsn", "hostPath", "shellCommand", "socketAddress", "accessToken", "credential"])("keeps opaque query parameter schema field %s", (fieldName) => {
|
||||
const errors = validateTemporaryBridgeManifest((_manifest, fixtureDir) => {
|
||||
writeFixtureJSON(fixtureDir, "schemas/bridge/player-by-id.parameters.schema.json", bridgeObjectSchema({ [fieldName]: { type: "string", minLength: 1, maxLength: 120 } }, [fieldName]));
|
||||
});
|
||||
expect(errors.some((error) => error.includes("queryTemplates[0].parameterSchemaRef") && error.includes("not allowed"))).toBe(true);
|
||||
expect(errors.some((error) => error.includes("queryTemplates[0].parameterSchemaRef"))).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects SQL text embedded in a query result schema", () => {
|
||||
it("keeps opaque SQL-looking text embedded in a query result schema", () => {
|
||||
const errors = validateTemporaryBridgeManifest((_manifest, fixtureDir) => {
|
||||
writeFixtureJSON(fixtureDir, "schemas/bridge/player-by-id.result.schema.json", bridgeObjectSchema({ summary: { type: "string", minLength: 1, maxLength: 200, const: "SELECT id FROM players" } }, ["summary"]));
|
||||
});
|
||||
expect(errors.some((error) => error.includes("queryTemplates[0].resultSchemaRef") && error.includes("arbitrary SQL content"))).toBe(true);
|
||||
expect(errors.some((error) => error.includes("queryTemplates[0].resultSchemaRef"))).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects missing bridge schema files end to end", () => {
|
||||
@@ -673,16 +673,13 @@ describe("plugin manifest validation", () => {
|
||||
expect(errors.some((error) => error.includes("payloadSchemaRef") && error.includes("not valid JSON"))).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects dangerous fields and values in payload, result, and snapshot schemas", () => {
|
||||
it("keeps opaque fields and values in payload, result, and snapshot schemas", () => {
|
||||
const errors = validateTemporaryBridgeManifest((_manifest, fixtureDir) => {
|
||||
writeFixtureJSON(fixtureDir, "schemas/bridge/diagnostic-ping.schema.json", bridgeObjectSchema({ sqlText: { type: "string" } }, ["sqlText"]));
|
||||
writeFixtureJSON(fixtureDir, "schemas/bridge/diagnostic-ping-result.schema.json", bridgeObjectSchema({ shellCommand: { type: "string", const: "bash -c whoami" } }, ["shellCommand"]));
|
||||
writeFixtureJSON(fixtureDir, "schemas/bridge/players.schema.json", bridgeObjectSchema({ hostPath: { type: "string" }, mode: { type: "string", const: "run.socket" }, runCapability: { type: "string" } }, ["hostPath", "mode", "runCapability"]));
|
||||
});
|
||||
expect(errors.some((error) => error.includes("payloadSchemaRef") && error.includes("arbitrary SQL field"))).toBe(true);
|
||||
expect(errors.some((error) => error.includes("resultSchemaRef") && error.includes("arbitrary shell"))).toBe(true);
|
||||
expect(errors.some((error) => error.includes("snapshots[0].schemaRef") && error.includes("raw host path"))).toBe(true);
|
||||
expect(errors.some((error) => error.includes("snapshots[0].schemaRef") && error.includes("unsafe executor capability"))).toBe(true);
|
||||
expect(errors.some((error) => error.includes("payloadSchemaRef") || error.includes("resultSchemaRef") || error.includes("snapshots[0].schemaRef"))).toBe(false);
|
||||
});
|
||||
|
||||
it("requires bounded object schemas for every bridge reference", () => {
|
||||
@@ -938,9 +935,21 @@ describe("plugin SDK", () => {
|
||||
storageBehavior: "platform-memory-transfer-session"
|
||||
});
|
||||
expect(reference).toMatchObject({ artifactId: "artifact-1", rangeSupported: true });
|
||||
expect(JSON.stringify(reference)).not.toContain("/Users/");
|
||||
expect(JSON.stringify(reference)).not.toContain("storage://");
|
||||
expect(JSON.stringify(reference)).not.toContain("Bearer ");
|
||||
|
||||
expect(
|
||||
parseArtifactReference({
|
||||
artifactId: "artifact-1",
|
||||
filename: "/Users/operator/password=opaque.bin",
|
||||
contentType: "application/octet-stream",
|
||||
sizeBytes: "64",
|
||||
checksum: "sha256:abc",
|
||||
downloadUrl: "/api/v1/artifacts/artifact-1/content",
|
||||
expiresAt: "2026-07-03T00:15:00Z",
|
||||
rangeSupported: "true",
|
||||
chunkSizeBytes: "1048576",
|
||||
storageBehavior: "plugin-owned opaque storage:// label"
|
||||
})
|
||||
).toMatchObject({ filename: "/Users/operator/password=opaque.bin", storageBehavior: "plugin-owned opaque storage:// label" });
|
||||
|
||||
expect(
|
||||
parseArtifactReference({
|
||||
|
||||
Reference in New Issue
Block a user