aboutsummaryrefslogtreecommitdiff
path: root/tests/drivers/lsp_sandbox.js
blob: 14bc1e37328d269b1dbbcaca929fede33298218c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// opening a file in the editor checks it, and a check runs the file's compile-time code
// (immediate functions). the server must not let that code write files, so a file that
// tries to leaves nothing behind and the server still answers.
// usage: node tests/drivers/lsp_sandbox.js path/to/zclsp
const cp = require('child_process'), fs = require('fs'), os = require('os'), path = require('path');
const target = path.join(os.tmpdir(), 'zc-sandbox-' + process.pid);
try { fs.unlinkSync(target); } catch (e) {}
const src = 'immediate fn evil() { let fd = syscall(1, "' + target + '", 1, 0); syscall(4, fd, 65, 1); syscall(2, fd, 0, 0); return 0; }\nevil\nfn main() { return 0; }\n';
const frame = m => { const b = Buffer.from(JSON.stringify(m)); return Buffer.concat([Buffer.from('Content-Length: ' + b.length + '\r\n\r\n'), b]); };
const input = Buffer.concat([
	frame({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { processId: 1, capabilities: {} } }),
	frame({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri: 'file:///evil.zc', languageId: 'zc', version: 1, text: src } } }),
	frame({ jsonrpc: '2.0', id: 3, method: 'textDocument/semanticTokens/full', params: { textDocument: { uri: 'file:///evil.zc' } } }),
	frame({ jsonrpc: '2.0', method: 'exit' }),
]);
const r = cp.spawnSync(process.argv[2], [], { input, timeout: 20000 });
const wrote = fs.existsSync(target);
if (wrote) fs.unlinkSync(target);
const answered = r.stdout && r.stdout.includes('"id":3');
const ok = !wrote && answered;
console.log((ok ? 'ok   ' : 'FAIL ') + 'lsp sandbox: compile-time code cannot write files' + (ok ? '' : ' (wrote: ' + wrote + ', answered: ' + !!answered + ')'));
process.exit(ok ? 0 : 1);