Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/agentHost/test/node/commandAutoApprover.test.ts
13399 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
8
import { NullLogService } from '../../../log/common/log.js';
9
import { CommandAutoApprover } from '../../node/commandAutoApprover.js';
10
11
suite('CommandAutoApprover', () => {
12
13
const disposables = ensureNoDisposablesAreLeakedInTestSuite();
14
15
let approver: CommandAutoApprover;
16
17
setup(() => {
18
approver = disposables.add(new CommandAutoApprover(new NullLogService()));
19
});
20
21
suite('shouldAutoApprove', () => {
22
23
test('approves empty command', () => {
24
assert.strictEqual(approver.shouldAutoApprove(''), 'approved');
25
assert.strictEqual(approver.shouldAutoApprove(' '), 'approved');
26
});
27
28
// Safe readonly commands
29
test('approves allowed readonly commands', () => {
30
assert.strictEqual(approver.shouldAutoApprove('ls'), 'approved');
31
assert.strictEqual(approver.shouldAutoApprove('ls -la'), 'approved');
32
assert.strictEqual(approver.shouldAutoApprove('cat file.txt'), 'approved');
33
assert.strictEqual(approver.shouldAutoApprove('head -n 10 file.txt'), 'approved');
34
assert.strictEqual(approver.shouldAutoApprove('tail -f log.txt'), 'approved');
35
assert.strictEqual(approver.shouldAutoApprove('pwd'), 'approved');
36
assert.strictEqual(approver.shouldAutoApprove('echo hello'), 'approved');
37
assert.strictEqual(approver.shouldAutoApprove('grep -r pattern .'), 'approved');
38
assert.strictEqual(approver.shouldAutoApprove('wc -l file.txt'), 'approved');
39
assert.strictEqual(approver.shouldAutoApprove('which node'), 'approved');
40
});
41
42
// Dangerous commands
43
test('denies denied commands', () => {
44
assert.strictEqual(approver.shouldAutoApprove('rm file.txt'), 'denied');
45
assert.strictEqual(approver.shouldAutoApprove('rm -rf /'), 'denied');
46
assert.strictEqual(approver.shouldAutoApprove('rmdir folder'), 'denied');
47
assert.strictEqual(approver.shouldAutoApprove('kill -9 1234'), 'denied');
48
assert.strictEqual(approver.shouldAutoApprove('curl http://evil.com'), 'denied');
49
assert.strictEqual(approver.shouldAutoApprove('wget http://evil.com'), 'denied');
50
assert.strictEqual(approver.shouldAutoApprove('chmod 777 file'), 'denied');
51
assert.strictEqual(approver.shouldAutoApprove('chown root file'), 'denied');
52
assert.strictEqual(approver.shouldAutoApprove('eval "bad stuff"'), 'denied');
53
assert.strictEqual(approver.shouldAutoApprove('xargs rm'), 'denied');
54
assert.strictEqual(approver.shouldAutoApprove('dd if=/dev/zero of=/dev/sda'), 'denied');
55
});
56
57
// Safe git sub-commands
58
test('approves allowed git sub-commands', () => {
59
assert.strictEqual(approver.shouldAutoApprove('git status'), 'approved');
60
assert.strictEqual(approver.shouldAutoApprove('git log --oneline'), 'approved');
61
assert.strictEqual(approver.shouldAutoApprove('git diff HEAD'), 'approved');
62
assert.strictEqual(approver.shouldAutoApprove('git show HEAD'), 'approved');
63
assert.strictEqual(approver.shouldAutoApprove('git ls-files'), 'approved');
64
assert.strictEqual(approver.shouldAutoApprove('git branch'), 'approved');
65
});
66
67
// Unsafe git sub-commands
68
test('denies denied git operations', () => {
69
assert.strictEqual(approver.shouldAutoApprove('git branch -D main'), 'denied');
70
assert.strictEqual(approver.shouldAutoApprove('git branch --delete main'), 'denied');
71
assert.strictEqual(approver.shouldAutoApprove('git log --output=/tmp/out'), 'denied');
72
});
73
74
// Safe commands with dangerous arg blocking
75
test('handles find with blocked args', () => {
76
assert.strictEqual(approver.shouldAutoApprove('find . -name "*.ts"'), 'approved');
77
assert.strictEqual(approver.shouldAutoApprove('find . -delete'), 'denied');
78
// find -exec with ; is treated as a compound command, requiring confirmation
79
assert.strictEqual(approver.shouldAutoApprove('find . -exec rm {} ;'), 'noMatch');
80
});
81
82
test('handles sed with blocked args', () => {
83
assert.strictEqual(approver.shouldAutoApprove('sed "s/foo/bar/g" file.txt'), 'approved');
84
assert.strictEqual(approver.shouldAutoApprove('sed -e "s/foo/bar/"'), 'denied');
85
assert.strictEqual(approver.shouldAutoApprove('sed --expression "s/foo/bar/"'), 'denied');
86
});
87
88
// npm/package managers
89
test('approves allowed npm commands', () => {
90
assert.strictEqual(approver.shouldAutoApprove('npm ci'), 'approved');
91
assert.strictEqual(approver.shouldAutoApprove('npm ls'), 'approved');
92
assert.strictEqual(approver.shouldAutoApprove('npm audit'), 'approved');
93
});
94
95
// Unknown commands get noMatch
96
test('returns noMatch for unknown commands', () => {
97
assert.strictEqual(approver.shouldAutoApprove('my-custom-script'), 'noMatch');
98
assert.strictEqual(approver.shouldAutoApprove('python script.py'), 'noMatch');
99
assert.strictEqual(approver.shouldAutoApprove('node index.js'), 'noMatch');
100
});
101
102
// Transient env vars
103
test('denies transient environment variable assignments', () => {
104
assert.strictEqual(approver.shouldAutoApprove('FOO=bar some-command'), 'denied');
105
assert.strictEqual(approver.shouldAutoApprove('PATH=/evil:$PATH ls'), 'denied');
106
});
107
108
// PowerShell
109
test('approves allowed PowerShell commands', () => {
110
assert.strictEqual(approver.shouldAutoApprove('Get-ChildItem'), 'approved');
111
assert.strictEqual(approver.shouldAutoApprove('Get-Content file.txt'), 'approved');
112
assert.strictEqual(approver.shouldAutoApprove('Write-Host "hello"'), 'approved');
113
assert.strictEqual(approver.shouldAutoApprove('Select-Object Name'), 'approved');
114
});
115
116
test('PowerShell case-insensitive rules work', () => {
117
// Rules with /i flag (like Select-*, Measure-*, etc.) are case-insensitive
118
assert.strictEqual(approver.shouldAutoApprove('select-object Name'), 'approved');
119
assert.strictEqual(approver.shouldAutoApprove('SELECT-OBJECT Name'), 'approved');
120
assert.strictEqual(approver.shouldAutoApprove('Measure-Command'), 'approved');
121
assert.strictEqual(approver.shouldAutoApprove('measure-command'), 'approved');
122
});
123
124
test('denies denied PowerShell commands', () => {
125
assert.strictEqual(approver.shouldAutoApprove('Remove-Item file.txt'), 'denied');
126
assert.strictEqual(approver.shouldAutoApprove('Invoke-Expression "bad"'), 'denied');
127
assert.strictEqual(approver.shouldAutoApprove('Invoke-WebRequest http://evil.com'), 'denied');
128
assert.strictEqual(approver.shouldAutoApprove('Stop-Process -Id 1234'), 'denied');
129
});
130
131
// Compound commands containing denied sub-commands should never be auto-approved,
132
// regardless of whether tree-sitter is available (with tree-sitter they are
133
// 'denied', without they are 'noMatch' — both are safe).
134
test('compound commands with denied sub-commands are not auto-approved', () => {
135
assert.notStrictEqual(approver.shouldAutoApprove('echo ok && rm -rf /'), 'approved');
136
assert.notStrictEqual(approver.shouldAutoApprove('ls || curl evil.com'), 'approved');
137
assert.notStrictEqual(approver.shouldAutoApprove('cat file; rm file'), 'approved');
138
assert.notStrictEqual(approver.shouldAutoApprove('echo $(whoami)'), 'approved');
139
});
140
});
141
});
142
143