Tighten opaque plugin content boundaries
This commit is contained in:
@@ -171,76 +171,6 @@ function unsafeGameClientBridgeCommandTypeReason(value: string): string | undefi
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function unsafeGameClientBridgePayloadKey(value: string): boolean {
|
||||
const tokens = identifierTokens(value);
|
||||
const compact = tokens.join("");
|
||||
return ["sql", "rawsql", "sqltext", "sqlstatement", "dsn", "hostpath", "socket", "credential", "accesstoken"].includes(compact);
|
||||
}
|
||||
|
||||
function unsafeBridgeSchemaFieldReason(fieldName: string): string | undefined {
|
||||
const tokens = identifierTokens(fieldName);
|
||||
const compact = tokens.join("");
|
||||
const generalReason = unsafeFieldReason(fieldName);
|
||||
if (generalReason) {
|
||||
return generalReason;
|
||||
}
|
||||
if ((tokens.includes("sql") || tokens.includes("query")) && tokens.includes("template") && (tokens.includes("key") || tokens.includes("ref"))) {
|
||||
return undefined;
|
||||
}
|
||||
if (["sql", "rawsql", "sqltext", "sqlquery", "sqlstatement", "rawquery", "statement"].includes(compact)) {
|
||||
return "arbitrary SQL field is not allowed";
|
||||
}
|
||||
if (["shell", "shellcommand", "shellscript", "script", "scriptbody", "terminalcommand", "commandline", "powershell"].includes(compact)) {
|
||||
return "arbitrary shell or script field is not allowed";
|
||||
}
|
||||
if (["hostpath", "rawpath", "absolutepath", "filesystempath"].includes(compact)) {
|
||||
return "raw host path field is not allowed";
|
||||
}
|
||||
if (["runcapability", "executorcapability", "runendpoint", "runsocket", "directrun"].includes(compact)) {
|
||||
return "unsafe executor capability or direct Run field is not allowed";
|
||||
}
|
||||
if (tokens.some((token) => ["socket", "password", "credential", "secret", "token", "dsn"].includes(token))) {
|
||||
return "direct socket or raw credential field is not allowed";
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function unsafeBridgeSchemaStringReasons(value: string): string[] {
|
||||
const reasons = [...unsafeStringReasons(value)];
|
||||
const trimmed = value.trim();
|
||||
const fieldReason = unsafeBridgeSchemaFieldReason(trimmed);
|
||||
if (fieldReason) {
|
||||
reasons.push(fieldReason);
|
||||
}
|
||||
if (/\bselect\b[\s\S]{0,240}\bfrom\b/i.test(trimmed) || /\b(?:insert\s+into|update\s+[a-z0-9_.]+\s+set|delete\s+from|drop\s+table|alter\s+table|create\s+table|attach\s+database|pragma\s+[a-z0-9_]+)/i.test(trimmed)) {
|
||||
reasons.push("arbitrary SQL content is not allowed");
|
||||
}
|
||||
if (/^\s*(?:sh|bash|zsh|powershell|pwsh)\s+-[a-z]*c\b/i.test(trimmed) || /^\s*cmd(?:\.exe)?\s+\/c\b/i.test(trimmed)) {
|
||||
reasons.push("arbitrary shell content is not allowed");
|
||||
}
|
||||
if (/^(?:run|executor|shell|script|terminal)\.(?:socket|endpoint|exec|execute|command)$/i.test(trimmed)) {
|
||||
reasons.push("unsafe executor capability is not allowed");
|
||||
}
|
||||
return [...new Set(reasons)];
|
||||
}
|
||||
|
||||
function scanUnsafeBridgeSchema(value: unknown, location: string): string[] {
|
||||
if (typeof value === "string") {
|
||||
return unsafeBridgeSchemaStringReasons(value).map((reason) => `${location}: ${reason}`);
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.flatMap((item, index) => scanUnsafeBridgeSchema(item, `${location}[${index}]`));
|
||||
}
|
||||
if (typeof value === "object" && value !== null) {
|
||||
return Object.entries(value).flatMap(([key, child]) => {
|
||||
const keyReason = unsafeBridgeSchemaFieldReason(key);
|
||||
const keyErrors = keyReason ? [`${location}.${key}: ${keyReason}`] : [];
|
||||
return [...keyErrors, ...scanUnsafeBridgeSchema(child, `${location}.${key}`)];
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function validateBoundedBridgeSchema(value: unknown, location: string): string[] {
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
||||
return [`${location}: bridge schema root must be an object schema`];
|
||||
@@ -790,7 +720,6 @@ function validateGameClientBridgeSchemaFiles(manifest: unknown, manifestDir: str
|
||||
const message = error instanceof Error ? error.message : "invalid JSON Schema";
|
||||
errors.push(`${declaration.location}: bridge schema is invalid: ${message}`);
|
||||
}
|
||||
errors.push(...scanUnsafeBridgeSchema(schema, `${declaration.location}.schema`));
|
||||
errors.push(...validateBoundedBridgeSchema(schema, `${declaration.location}.schema`));
|
||||
}
|
||||
return errors;
|
||||
|
||||
Reference in New Issue
Block a user