Skip to content

Commit 21984fd

Browse files
committed
Explicitly enforce project name constraints in StripeTerminal
1 parent cb9bba6 commit 21984fd

File tree

2 files changed

+144
-81
lines changed

2 files changed

+144
-81
lines changed

src/stripeTerminal.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,16 @@ export class StripeTerminal {
5757
private getGlobalCLIFlags(): Array<string> {
5858
const stripeConfig = vscode.workspace.getConfiguration('stripe');
5959

60-
let projectName = stripeConfig.get<string | null>('projectName', null);
60+
const projectName = stripeConfig.get<string | null>('projectName', null);
61+
6162
if (projectName !== null) {
62-
projectName = projectName.replace(/[\\"'`]/g, '');
63+
// Regex to validate project name
64+
const projectNameRegex = /^[a-zA-Z0-9_-\s]+$/;
65+
66+
// Validate project name against the regex
67+
if (!projectNameRegex.test(projectName)) {
68+
throw new Error(`Invalid project name: '${projectName}'. Project names can only contain letters, numbers, spaces, underscores, and hyphens.`);
69+
}
6370
}
6471

6572
const projectNameFlag = projectName ? ['--project-name', projectName] : [];

test/suite/stripeTerminal.test.ts

Lines changed: 135 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -4,85 +4,141 @@ import * as vscode from 'vscode';
44
import {StripeTerminal} from '../../src/stripeTerminal';
55

66
suite('stripeTerminal', function () {
7-
this.timeout(20000);
8-
9-
let sandbox: sinon.SinonSandbox;
10-
11-
const terminalStub = <vscode.Terminal><unknown>{
12-
name: 'Stubbed Terminal',
13-
processId: Promise.resolve(undefined),
14-
creationOptions: {},
15-
exitStatus: undefined,
16-
sendText: (text: string, addNewLine?: boolean) => { },
17-
show: (preserveFocus?: boolean) => { },
18-
hide: () => { },
19-
dispose: () => { },
20-
};
21-
22-
setup(() => {
23-
sandbox = sinon.createSandbox();
24-
});
25-
26-
teardown(() => {
27-
sandbox.restore();
28-
});
29-
30-
['/usr/local/bin/stripe', '/custom/path/to/stripe'].forEach((path) => {
31-
suite(`when the Stripe CLI is installed at ${path}`, () => {
32-
test(`runs command with ${path}`, async () => {
33-
const executeTaskSpy = sandbox.spy(vscode.tasks, 'executeTask');
34-
sandbox.stub(terminalStub, 'sendText');
35-
sandbox
36-
.stub(vscode.window, 'createTerminal')
37-
.returns(terminalStub);
38-
const stripeClientStub = <any>{getCLIPath: () => {}, isAuthenticated: () => true};
39-
sandbox
40-
.stub(stripeClientStub, 'getCLIPath')
41-
.returns(Promise.resolve(path));
42-
43-
const stripeTerminal = new StripeTerminal(stripeClientStub);
44-
await stripeTerminal.execute('listen', ['--forward-to', 'localhost']);
45-
46-
assert.strictEqual(executeTaskSpy.callCount, 1);
47-
assert.deepStrictEqual(executeTaskSpy.args[0], [
48-
new vscode.Task(
49-
{type: 'stripe', command: 'listen'},
50-
vscode.TaskScope.Workspace,
51-
'listen',
52-
'stripe',
53-
new vscode.ShellExecution(path, [
54-
'listen',
55-
'--forward-to',
56-
'localhost',
57-
],
58-
{
59-
shellQuoting: {
60-
escape: {
61-
escapeChar: '\\',
62-
charsToEscape: '&`|"\'',
63-
},
64-
},
65-
}),
66-
),
67-
]);
68-
});
7+
this.timeout(20000);
8+
9+
let sandbox: sinon.SinonSandbox;
10+
11+
const terminalStub = <vscode.Terminal><unknown>{
12+
name: 'Stubbed Terminal',
13+
processId: Promise.resolve(undefined),
14+
creationOptions: {},
15+
exitStatus: undefined,
16+
sendText: (text: string, addNewLine?: boolean) => { },
17+
show: (preserveFocus?: boolean) => { },
18+
hide: () => { },
19+
dispose: () => { },
20+
};
21+
22+
setup(() => {
23+
sandbox = sinon.createSandbox();
6924
});
70-
});
71-
72-
suite('with no Stripe CLI installed', () => {
73-
test('does not run command', async () => {
74-
const sendTextStub = sandbox.stub(terminalStub, 'sendText');
75-
const createTerminalStub = sandbox
76-
.stub(vscode.window, 'createTerminal')
77-
.returns(terminalStub);
78-
const stripeClientStub = <any>{getCLIPath: () => {}, isAuthenticated: () => true};
79-
sandbox.stub(stripeClientStub, 'getCLIPath').returns(null);
80-
81-
const stripeTerminal = new StripeTerminal(stripeClientStub);
82-
await stripeTerminal.execute('listen', ['--forward-to', 'localhost']);
83-
84-
assert.strictEqual(createTerminalStub.callCount, 0);
85-
assert.deepStrictEqual(sendTextStub.callCount, 0);
25+
26+
teardown(() => {
27+
sandbox.restore();
28+
});
29+
30+
['/usr/local/bin/stripe', '/custom/path/to/stripe'].forEach((path) => {
31+
suite(`when the Stripe CLI is installed at ${path}`, () => {
32+
test('runs command with valid project name', async () => {
33+
const executeTaskSpy = sandbox.spy(vscode.tasks, 'executeTask');
34+
sandbox.stub(terminalStub, 'sendText');
35+
sandbox.stub(vscode.window, 'createTerminal').returns(terminalStub);
36+
37+
// Mock the configuration with a valid project name
38+
const stripeClientStub = <any>{
39+
getCLIPath: () => { },
40+
isAuthenticated: () => true,
41+
};
42+
43+
// Mock the getConfiguration function to return an invalid project name
44+
// Mock the getConfiguration function to return a configuration object with a specific project name
45+
sandbox.stub(vscode.workspace, 'getConfiguration').returns({
46+
get: (key: string) => {
47+
if (key === 'projectName') {
48+
return 'Valid_Project-Name'; // or 'Invalid Project Name!' for the invalid test
49+
}
50+
return null; // Return null for any other keys
51+
},
52+
});
53+
54+
sandbox.stub(stripeClientStub, 'getCLIPath').returns(Promise.resolve(path));
55+
56+
const stripeTerminal = new StripeTerminal(stripeClientStub);
57+
await stripeTerminal.execute('listen', ['--forward-to', 'localhost']);
58+
59+
assert.strictEqual(executeTaskSpy.callCount, 1);
60+
assert.deepStrictEqual(executeTaskSpy.args[0], [
61+
new vscode.Task(
62+
{type: 'stripe', command: 'listen'},
63+
vscode.TaskScope.Workspace,
64+
'listen',
65+
'stripe',
66+
new vscode.ShellExecution(path, [
67+
'listen',
68+
'--forward-to',
69+
'localhost',
70+
'--project-name',
71+
'Valid_Project-Name'
72+
],
73+
{
74+
shellQuoting: {
75+
escape: {
76+
escapeChar: '\\',
77+
charsToEscape: '&`|"\'',
78+
},
79+
},
80+
}),
81+
),
82+
]);
83+
});
84+
85+
test('throws error for invalid project name', async () => {
86+
// Mock the configuration with an invalid project name
87+
const stripeClientStub = <any>{
88+
getCLIPath: () => { },
89+
isAuthenticated: () => true,
90+
};
91+
92+
// Mock the getConfiguration function to return an invalid project name
93+
sandbox.stub(vscode.workspace, 'getConfiguration').returns({
94+
get: (key: string) => {
95+
if (key === 'projectName') {
96+
return 'Invalid Project Name!'; // Invalid project name
97+
}
98+
return null;
99+
},
100+
has: function (section: string): boolean {
101+
throw new Error('Function not implemented.');
102+
},
103+
inspect: function <T>(section: string): { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T; workspaceFolderValue?: T; defaultLanguageValue?: T; globalLanguageValue?: T; workspaceLanguageValue?: T; workspaceFolderLanguageValue?: T; languageIds?: string[] } | undefined {
104+
throw new Error('Function not implemented.');
105+
},
106+
update: function (section: string, value: any, configurationTarget?: vscode.ConfigurationTarget | boolean | null, overrideInLanguage?: boolean): Thenable<void> {
107+
throw new Error('Function not implemented.');
108+
}
109+
});
110+
111+
sandbox.stub(vscode.window, 'createTerminal').returns(terminalStub);
112+
sandbox.stub(stripeClientStub, 'getCLIPath').returns(Promise.resolve(path));
113+
114+
const stripeTerminal = new StripeTerminal(stripeClientStub);
115+
116+
// Expect an error to be thrown due to invalid project name
117+
await assert.rejects(
118+
async () => {
119+
await stripeTerminal.execute('listen', ['--forward-to', 'localhost']);
120+
},
121+
{
122+
name: 'Error',
123+
message: "Invalid project name: 'Invalid Project Name!'. Project names can only contain letters, numbers, spaces, underscores, and hyphens.",
124+
}
125+
);
126+
});
127+
});
128+
});
129+
130+
suite('with no Stripe CLI installed', () => {
131+
test('does not run command', async () => {
132+
const sendTextStub = sandbox.stub(terminalStub, 'sendText');
133+
const createTerminalStub = sandbox.stub(vscode.window, 'createTerminal').returns(terminalStub);
134+
const stripeClientStub = <any>{getCLIPath: () => { }, isAuthenticated: () => true};
135+
sandbox.stub(stripeClientStub, 'getCLIPath').returns(null);
136+
137+
const stripeTerminal = new StripeTerminal(stripeClientStub);
138+
await stripeTerminal.execute('listen', ['--forward-to', 'localhost']);
139+
140+
assert.strictEqual(createTerminalStub.callCount, 0);
141+
assert.deepStrictEqual(sendTextStub.callCount, 0);
142+
});
86143
});
87-
});
88144
});

0 commit comments

Comments
 (0)