Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default [
...nx.configs['flat/typescript'],
...nx.configs['flat/javascript'],
{
ignores: ['**/dist'],
ignores: ['**/dist', '**/*.spec.ts', '**/__tests__/**'],
},
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
Expand Down
16 changes: 16 additions & 0 deletions libs/ast-guard/src/transforms/concat.transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,24 @@ export function transformTemplateLiterals(ast: acorn.Node, config: ConcatTransfo

let transformedCount = 0;

// Track which TemplateLiteral nodes are part of TaggedTemplateExpressions
// We should NOT transform these as they need to pass the raw template to the tag function
const taggedTemplates = new Set<acorn.Node>();

// First pass: identify tagged template literals
walk.simple(ast as any, {
TaggedTemplateExpression: (node: any) => {
taggedTemplates.add(node.quasi);
},
});

walk.simple(ast as any, {
TemplateLiteral: (node: any) => {
// Skip tagged templates - they pass templates directly to the tag function
if (taggedTemplates.has(node)) {
return;
}

// Only transform templates with expressions
if (node.expressions.length === 0) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Array[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -50,8 +50,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Object[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -62,8 +62,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Math[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -75,8 +75,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return JSON[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -87,8 +87,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return String[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -107,8 +107,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return myGlobal[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});
});
Expand All @@ -122,8 +122,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Number[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -135,8 +135,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Date[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});
});
Expand Down Expand Up @@ -174,8 +174,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return proto ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -191,8 +191,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Ctor ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand Down Expand Up @@ -244,8 +244,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Ctor ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand Down Expand Up @@ -278,8 +278,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Math[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -296,8 +296,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return JSON[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -309,8 +309,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return String[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -321,8 +321,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Number[key] ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});
});
Expand All @@ -336,8 +336,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Ctor ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -349,8 +349,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Ctor ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});
});
Expand Down Expand Up @@ -383,8 +383,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return Ctor ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});
});
Expand Down Expand Up @@ -418,8 +418,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return promiseCtor ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand All @@ -436,8 +436,8 @@ describe('Constructor Obfuscation Attack Vectors', () => {
return FunctionCtor ? 'escaped' : 'blocked';
`;
const result = await enclave.run(code);
expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
enclave.dispose();
});

Expand Down
43 changes: 16 additions & 27 deletions libs/enclave-vm/src/__tests__/double-vm.security.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,7 @@ describe('Double VM Security Layer', () => {
});

// Try to escape via computed property constructor access on tool result
// This bypasses AST validation but is caught by secure proxy
// This bypasses AST validation but is caught by secure proxy which throws an error
const result = await enclave.run(`
async function __ag_main() {
const result = await callTool('data:get', {});
Expand All @@ -1014,8 +1014,8 @@ describe('Double VM Security Layer', () => {
}
`);

expect(result.success).toBe(true);
expect(result.value).toBe('undefined'); // Constructor blocked by secure proxy
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
});

it('should block prototype access on tool results via computed property', async () => {
Expand All @@ -1038,8 +1038,8 @@ describe('Double VM Security Layer', () => {
}
`);

expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
});

it('should block Promise constructor access via computed property', async () => {
Expand All @@ -1061,8 +1061,8 @@ describe('Double VM Security Layer', () => {
}
`);

expect(result.success).toBe(true);
expect(result.value).toBe('undefined');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
});

it('should block access to host Function via deeply computed property attacks', async () => {
Expand All @@ -1077,30 +1077,19 @@ describe('Double VM Security Layer', () => {
});

// Try deeply computed property attack on nested objects
// Now that blocked properties throw, accessing them will fail immediately
const result = await enclave.run(`
async function __ag_main() {
const result = await callTool('data:get', {});
// Try multiple computed property variations
const props = ['const' + 'ructor', '__pro' + 'to__', 'proto' + 'type'];
const found = [];

for (const prop of props) {
// Check on result object
if (result[prop] !== undefined) {
found.push('result.' + prop);
}
// Check on nested object
if (result.nested && result.nested[prop] !== undefined) {
found.push('nested.' + prop);
}
}

return found.length === 0 ? 'all-blocked' : found.join(',');
// Try computed property variations - first one should throw
const prop = 'const' + 'ructor';
const ctor = result[prop];
return ctor;
}
`);

expect(result.success).toBe(true);
expect(result.value).toBe('all-blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
});

it('should prevent chained escapes through multiple tool calls', async () => {
Expand Down Expand Up @@ -1144,8 +1133,8 @@ describe('Double VM Security Layer', () => {
}
`);

expect(result.success).toBe(true);
expect(result.value).toBe('blocked');
expect(result.success).toBe(false);
expect(result.error?.message).toContain('Security violation');
});
});

Expand Down
Loading
Loading