Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/inlineEdits/vscode-node/components/test/inlineEditDebugComponent.spec.ts
13406 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 { expect, suite, test } from 'vitest';
7
import { LogEntry } from '../../../../../platform/workspaceRecorder/common/workspaceLog';
8
import { filterLogForSensitiveFiles } from '../inlineEditDebugComponent';
9
10
suite('filter recording for sensitive files', () => {
11
test('should filter out settings.json files', () => {
12
const log: LogEntry[] = [
13
{
14
documentType: '[email protected]',
15
kind: 'header',
16
repoRootUri: 'file:///path/to/repo',
17
time: 1733253792609,
18
uuid: '233d78f2-202a-4d3e-9b90-0f1acc058125'
19
},
20
{
21
kind: 'documentEncountered',
22
id: 1,
23
relativePath: 'package.json',
24
time: 1733253735332
25
},
26
{
27
kind: 'documentEncountered',
28
id: 2,
29
relativePath: '.vscode/settings.json',
30
time: 1733253735340
31
},
32
{
33
kind: 'setContent',
34
id: 1,
35
v: 1,
36
content: '{ "name": "example" }',
37
time: 1733253735332
38
},
39
{
40
kind: 'setContent',
41
id: 2,
42
v: 1,
43
content: '{ "sensitive": "data" }',
44
time: 1733253735340
45
}
46
];
47
48
const result = filterLogForSensitiveFiles(log);
49
50
expect(result).toMatchInlineSnapshot(`
51
[
52
{
53
"documentType": "[email protected]",
54
"kind": "header",
55
"repoRootUri": "file:///path/to/repo",
56
"time": 1733253792609,
57
"uuid": "233d78f2-202a-4d3e-9b90-0f1acc058125",
58
},
59
{
60
"id": 1,
61
"kind": "documentEncountered",
62
"relativePath": "package.json",
63
"time": 1733253735332,
64
},
65
{
66
"content": "{ "name": "example" }",
67
"id": 1,
68
"kind": "setContent",
69
"time": 1733253735332,
70
"v": 1,
71
},
72
]
73
`);
74
});
75
76
test('should filter out .env files and variants', () => {
77
const log: LogEntry[] = [
78
{ documentType: '[email protected]', kind: 'header', repoRootUri: 'file:///repo', time: 0, uuid: 'test' },
79
{ kind: 'documentEncountered', id: 1, relativePath: '.env', time: 0 },
80
{ kind: 'documentEncountered', id: 2, relativePath: '.env.local', time: 0 },
81
{ kind: 'documentEncountered', id: 3, relativePath: '.env.production', time: 0 },
82
{ kind: 'documentEncountered', id: 4, relativePath: 'app.env', time: 0 },
83
{ kind: 'documentEncountered', id: 5, relativePath: 'src/index.ts', time: 0 },
84
{ kind: 'setContent', id: 1, v: 1, content: 'SECRET=xyz', time: 0 },
85
{ kind: 'setContent', id: 5, v: 1, content: 'console.log(\'hello\')', time: 0 },
86
];
87
88
const result = filterLogForSensitiveFiles(log);
89
90
// Only header and src/index.ts should remain
91
expect(result.filter(e => e.kind === 'documentEncountered')).toHaveLength(1);
92
expect(result.find(e => e.kind === 'documentEncountered')).toMatchObject({
93
relativePath: 'src/index.ts'
94
});
95
});
96
97
test('should filter out private key files', () => {
98
const log: LogEntry[] = [
99
{ documentType: '[email protected]', kind: 'header', repoRootUri: 'file:///repo', time: 0, uuid: 'test' },
100
{ kind: 'documentEncountered', id: 1, relativePath: 'server.pem', time: 0 },
101
{ kind: 'documentEncountered', id: 2, relativePath: 'private.key', time: 0 },
102
{ kind: 'documentEncountered', id: 3, relativePath: 'cert.p12', time: 0 },
103
{ kind: 'documentEncountered', id: 4, relativePath: 'cert.pfx', time: 0 },
104
{ kind: 'documentEncountered', id: 5, relativePath: 'readme.md', time: 0 },
105
];
106
107
const result = filterLogForSensitiveFiles(log);
108
109
expect(result.filter(e => e.kind === 'documentEncountered')).toHaveLength(1);
110
expect(result.find(e => e.kind === 'documentEncountered')).toMatchObject({
111
relativePath: 'readme.md'
112
});
113
});
114
115
test('should filter out files in sensitive directories', () => {
116
const log: LogEntry[] = [
117
{ documentType: '[email protected]', kind: 'header', repoRootUri: 'file:///repo', time: 0, uuid: 'test' },
118
{ kind: 'documentEncountered', id: 1, relativePath: '.aws/credentials', time: 0 },
119
{ kind: 'documentEncountered', id: 2, relativePath: '.ssh/id_rsa', time: 0 },
120
{ kind: 'documentEncountered', id: 3, relativePath: '.gnupg/private-keys-v1.d/key.gpg', time: 0 },
121
{ kind: 'documentEncountered', id: 4, relativePath: '.docker/config.json', time: 0 },
122
{ kind: 'documentEncountered', id: 5, relativePath: 'src/aws/client.ts', time: 0 },
123
];
124
125
const result = filterLogForSensitiveFiles(log);
126
127
// Only src/aws/client.ts should remain (it's not in .aws directory)
128
expect(result.filter(e => e.kind === 'documentEncountered')).toHaveLength(1);
129
expect(result.find(e => e.kind === 'documentEncountered')).toMatchObject({
130
relativePath: 'src/aws/client.ts'
131
});
132
});
133
134
test('should filter out files with sensitive name patterns', () => {
135
const log: LogEntry[] = [
136
{ documentType: '[email protected]', kind: 'header', repoRootUri: 'file:///repo', time: 0, uuid: 'test' },
137
{ kind: 'documentEncountered', id: 1, relativePath: 'id_rsa', time: 0 },
138
{ kind: 'documentEncountered', id: 2, relativePath: 'id_ed25519.pub', time: 0 },
139
{ kind: 'documentEncountered', id: 3, relativePath: 'app.secret.yaml', time: 0 },
140
{ kind: 'documentEncountered', id: 4, relativePath: 'normal_file.ts', time: 0 },
141
];
142
143
const result = filterLogForSensitiveFiles(log);
144
145
expect(result.filter(e => e.kind === 'documentEncountered')).toHaveLength(1);
146
expect(result.find(e => e.kind === 'documentEncountered')).toMatchObject({
147
relativePath: 'normal_file.ts'
148
});
149
});
150
151
test('should filter exact password/token data files but not code files with those words', () => {
152
const log: LogEntry[] = [
153
{ documentType: '[email protected]', kind: 'header', repoRootUri: 'file:///repo', time: 0, uuid: 'test' },
154
// These should be filtered (exact sensitive data files)
155
{ kind: 'documentEncountered', id: 1, relativePath: 'password.txt', time: 0 },
156
{ kind: 'documentEncountered', id: 2, relativePath: 'passwords.json', time: 0 },
157
{ kind: 'documentEncountered', id: 3, relativePath: 'token.json', time: 0 },
158
{ kind: 'documentEncountered', id: 4, relativePath: 'tokens.txt', time: 0 },
159
// These should NOT be filtered (code files that deal with passwords/tokens)
160
{ kind: 'documentEncountered', id: 5, relativePath: 'passwordValidator.ts', time: 0 },
161
{ kind: 'documentEncountered', id: 6, relativePath: 'tokenAnalyzer.ts', time: 0 },
162
{ kind: 'documentEncountered', id: 7, relativePath: 'auth/refreshToken.service.ts', time: 0 },
163
{ kind: 'documentEncountered', id: 8, relativePath: 'utils/passwordStrength.ts', time: 0 },
164
];
165
166
const result = filterLogForSensitiveFiles(log);
167
168
const remainingPaths = result
169
.filter((e): e is LogEntry & { kind: 'documentEncountered' } => e.kind === 'documentEncountered')
170
.map(e => e.relativePath);
171
172
// Only code files should remain
173
expect(remainingPaths).toEqual([
174
'passwordValidator.ts',
175
'tokenAnalyzer.ts',
176
'auth/refreshToken.service.ts',
177
'utils/passwordStrength.ts',
178
]);
179
});
180
181
test('should filter out other sensitive config files', () => {
182
const log: LogEntry[] = [
183
{ documentType: '[email protected]', kind: 'header', repoRootUri: 'file:///repo', time: 0, uuid: 'test' },
184
{ kind: 'documentEncountered', id: 1, relativePath: '.vscode/launch.json', time: 0 },
185
{ kind: 'documentEncountered', id: 2, relativePath: '.npmrc', time: 0 },
186
{ kind: 'documentEncountered', id: 3, relativePath: '.gitconfig', time: 0 },
187
{ kind: 'documentEncountered', id: 4, relativePath: 'credentials.json', time: 0 },
188
{ kind: 'documentEncountered', id: 5, relativePath: 'tsconfig.json', time: 0 },
189
];
190
191
const result = filterLogForSensitiveFiles(log);
192
193
// Only tsconfig.json should remain
194
expect(result.filter(e => e.kind === 'documentEncountered')).toHaveLength(1);
195
expect(result.find(e => e.kind === 'documentEncountered')).toMatchObject({
196
relativePath: 'tsconfig.json'
197
});
198
});
199
200
test('should handle Windows-style backslash paths', () => {
201
// Windows paths with backslashes are normalized to forward slashes before processing
202
// This ensures the function works correctly even when processing logs recorded on Windows
203
const log: LogEntry[] = [
204
{ documentType: '[email protected]', kind: 'header', repoRootUri: 'file:///repo', time: 0, uuid: 'test' },
205
{ kind: 'documentEncountered', id: 1, relativePath: '.vscode\\settings.json', time: 0 },
206
{ kind: 'documentEncountered', id: 2, relativePath: 'src\\index.ts', time: 0 },
207
{ kind: 'documentEncountered', id: 3, relativePath: 'config\\.env.local', time: 0 },
208
{ kind: 'documentEncountered', id: 4, relativePath: '.aws\\credentials', time: 0 },
209
];
210
211
const result = filterLogForSensitiveFiles(log);
212
213
// Only src\index.ts should remain - others are sensitive
214
expect(result.filter(e => e.kind === 'documentEncountered')).toHaveLength(1);
215
expect(result.find(e => e.kind === 'documentEncountered')).toMatchObject({
216
relativePath: 'src\\index.ts'
217
});
218
});
219
220
test('should preserve non-document log entries', () => {
221
const log: LogEntry[] = [
222
{ documentType: '[email protected]', kind: 'header', repoRootUri: 'file:///repo', time: 0, uuid: 'test' },
223
{ kind: 'meta', data: { key: 'value' } },
224
{ kind: 'applicationStart', time: 0 },
225
{ kind: 'event', time: 0, data: {} },
226
{ kind: 'bookmark', time: 0 },
227
];
228
229
const result = filterLogForSensitiveFiles(log);
230
231
expect(result).toHaveLength(5);
232
});
233
234
});
235
236