Menjalankan kode dinamis dari antarmuka web atau multi-tenant runner membuka celah keamanan Arbitrary Code Execution (ACE) dan konsumsi resource tak terkendali. Pada runner Fusion Lang (fusion-lang.org), mengevaluasi kode tanpa proteksi memungkinkan attacker memanggil native binding, mengakses memory sistem, atau melakukan eksfiltrasi data via network I/O. Solusinya adalah membangun arsitektur pertahanan bertingkat (defense-in-depth) yang mencakup token boundary validation, AST tree-walking filtering, dan sandbox policy enforcement.
Vektor Serangan pada Runner Fusion Lang
Kode Fusion dikompilasi atau ditranspilasi melalui tahapan Source → Lexer/Tokens → AST → Target Execution. Ancaman utama meliputi:
- Eksfiltrasi Lingkungan Runtime: Penggunaan instruksi sistem level rendah atau manipulasi binding eksternal untuk mengakses environment variable atau filesystem host.
- Denial of Service (DoS): Infinite loops, allocation recursion, atau memory exhaustion yang membekukan instance runner.
- AST Prototype Poisoning / Metaprogramming Bypass: Memanfaatkan refleksi dinamis untuk keluar dari scope terisolasi.
Arsitektur Hardening: AST Filtering & Runtime Policy
Proteksi efektif tidak bergantung pada regex sanitasi string mentah, melainkan verifikasi struktur semantik pada level AST sebelum kode masuk fase translasi/evaluasi. Runtime harus menerapkan prinsip default-deny: semua node AST ditolak kecuali jika terdaftar secara eksplisit pada allowlist policy runner.
1. Validasi Token dan Token Depth
Sebelum AST dibentuk, parser harus membatasi ukuran input mentah, kedalaman parsing token (mencegah stack overflow parser DoS), dan menolak token identifier bernilai primitif terlarang (misal system, process, __ffi, io).
2. Sanitasi Node AST
Transformer AST memeriksa setiap node ekspresi. Jika node bertipe CallExpression memanggil method atau modul di luar namespace terisolasi yang diizinkan (misal math functions dasar), node tersebut ditolak saat waktu kompilasi (compile-time abort).
Implementasi Sandbox Execution Policy Backend
Berikut adalah implementasi controller runner aman menggunakan TypeScript/JavaScript runtime yang mengisolasi AST Fusion dan menegakkan runtime execution boundary.
interface ASTNode {
type: string;
name?: string;
callee?: ASTNode;
arguments?: ASTNode[];
body?: ASTNode[];
}
class SecurityViolationError extends Error {
constructor(message: string) {
super(`[Security Policy Violation] ${message}`);
this.name = "SecurityViolationError";
}
}
class FusionASTPolicy {
// Allowlist node AST dan fungsi yang diizinkan
private static readonly ALLOWED_NODE_TYPES = new Set([
"Program",
"VariableDeclaration",
"BinaryExpression",
"Literal",
"Identifier",
"CallExpression",
"BlockStatement",
"ReturnStatement",
]);
private static readonly ALLOWED_CALLS = new Set([
"print",
"Math.abs",
"Math.max",
"Math.min",
"String.length",
]);
// Verifikasi recursive pada seluruh tree AST
public static validate(node: ASTNode | null | undefined, depth: number = 0): void {
if (!node) return;
if (depth > 50) {
throw new SecurityViolationError("Maximum AST depth exceeded. Potential parser exhaustion.");
}
if (!this.ALLOWED_NODE_TYPES.has(node.type)) {
throw new SecurityViolationError(`Forbidden node type detected: ${node.type}`);
}
if (node.type === "CallExpression") {
const target = node.callee?.name || "";
if (!this.ALLOWED_CALLS.has(target)) {
throw new SecurityViolationError(`Unauthorized call invocation: ${target}()`);
}
}
// Traversal anak AST
if (node.arguments) {
for (const arg of node.arguments) this.validate(arg, depth + 1);
}
if (node.body) {
for (const child of node.body) this.validate(child, depth + 1);
}
}
}Isolasi Eksekusi: Memory dan Resource Limit
Validasi AST menangani integritas logika statis, namun runtime tetap memerlukan limitasi resource fisik pada backend hosting runner:
- Virtual Memory Bounds: Batasi heap memory runner (misal
--max-old-space-size=64atau kontrol via Linux cgroupsmemory.max). - Wall-clock Execution Timeout: Pasang hard interrupt menggunakan worker thread atau subprocess SIGKILL jika waktu pemrosesan melampaui batas wajar (misal 1000ms).
- System Call Interception: Jika Fusion dikompilasi ke native binary atau target LLVM, gunakan
seccomp-bpfpada Linux runner untuk memblokir syscall network (sys_socket,sys_connect) dan file mutation.
Verifikasi Keamanan: Runnable Assert Test
Kode pengujian unit assert berikut memvalidasi bahwa AST parser dan filter secara tepat memotong injeksi instruksi berbahaya sebelum runner mengeksekusi instruksi payload.
// Verifikasi runnable tanpa framework testing eksternal
function runSecurityAssertions() {
const validAST: ASTNode = {
type: "Program",
body: [
{
type: "CallExpression",
callee: { type: "Identifier", name: "print" },
arguments: [{ type: "Literal", name: "Hello Sandbox" }],
},
],
};
const maliciousSyscallAST: ASTNode = {
type: "Program",
body: [
{
type: "CallExpression",
callee: { type: "Identifier", name: "system.execute" },
arguments: [{ type: "Literal", name: "cat /etc/passwd" }],
},
],
};
const deepRecursionAST: ASTNode = { type: "Program", body: [] };
let current = deepRecursionAST;
for (let i = 0; i < 55; i++) {
const next = { type: "BlockStatement", body: [] };
current.body = [next];
current = next;
}
// Test 1: Program aman harus lolos validasi
try {
FusionASTPolicy.validate(validAST);
console.log("PASS: Valid AST allowed.");
} catch (err) {
throw new Error("FAIL: Valid AST was incorrectly rejected.");
}
// Test 2: Unsafe call harus memicu SecurityViolationError
let caughtMalicious = false;
try {
FusionASTPolicy.validate(maliciousSyscallAST);
} catch (err) {
if (err instanceof SecurityViolationError) {
caughtMalicious = true;
console.log("PASS: Malicious call successfully blocked.");
}
}
if (!caughtMalicious) {
throw new Error("CRITICAL FAIL: Malicious syscall bypassed AST policy.");
}
// Test 3: AST depth limit harus mencegah depth-exhaustion DoS
let caughtDepthLimit = false;
try {
FusionASTPolicy.validate(deepRecursionAST);
} catch (err) {
if (err instanceof SecurityViolationError) {
caughtDepthLimit = true;
console.log("PASS: AST depth limit enforced.");
}
}
if (!caughtDepthLimit) {
throw new Error("CRITICAL FAIL: Deeply nested AST bypassed recursion guard.");
}
}
runSecurityAssertions();Troubleshooting dan Pitfall Implementasi
- Indirect Invocation: Pengembang sering mengabaikan akses objek dinamis seperti
this["sys" + "tem"]. Jika target runtime mendukung computed member access, blokir nodeMemberExpressionyang bersifat computed tanpa validasi nama statis. - Bypass Lewat Exception: Blok
try-catchyang menangani fatal error runner dapat menutupi kegagalan resource limits. Pastikan hard timeout runner memicu terminating signal (SIGKILL) ke worker, bukan melempar internal JavaScript exception yang dapat di-catch oleh kode pengguna.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!