Skip to content

Commit 1e3960d

Browse files
authored
Feat: extra validations for browser checks (#942)
* fix: scenario names can be any value * feat: add validation for invalid properties * chore: add more granular validations for browser scripts * fix: improve validation on invalid properties to prevent bypassing
1 parent 26d0c5b commit 1e3960d

File tree

3 files changed

+115
-9
lines changed

3 files changed

+115
-9
lines changed

src/page/NewCheck/__tests__/BrowserChecks/Scripted/1-script.ui.test.tsx

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ const checkType = CheckType.Browser;
77

88
const browserImport = `import { browser } from 'k6/browser';`;
99
const exportOptions = `export const options = { };`;
10-
const exportCorrectOptions = `export const options = {scenarios: {
10+
const exportCorrectOptions = `export const options = {
11+
scenarios: {
1112
ui: {
1213
options: {
1314
browser: {
@@ -62,5 +63,77 @@ describe(`BrowserCheck - 1 (Script) UI`, () => {
6263
const err = await screen.findByText('Script must set the type to chromium in the browser options.');
6364
expect(err).toBeInTheDocument();
6465
});
66+
67+
it(`will display an error when it defines a duration prop`, async () => {
68+
const { user } = await renderNewForm(checkType);
69+
const scriptTextAreaPreSubmit = screen.getByTestId(`code-editor`);
70+
await user.clear(scriptTextAreaPreSubmit);
71+
72+
const invalidDurationOptions = `export const options = {
73+
scenarios: {
74+
ui: {
75+
duration: '1m',
76+
options: {
77+
browser: {
78+
type: 'chromium',
79+
},
80+
},
81+
},
82+
},
83+
};`;
84+
await user.type(scriptTextAreaPreSubmit, browserImport + invalidDurationOptions);
85+
86+
await submitForm(user);
87+
const err = await screen.findByText("Script can't define a duration value for this check");
88+
expect(err).toBeInTheDocument();
89+
});
90+
91+
it(`will display an error when it defines vus > 1`, async () => {
92+
const { user } = await renderNewForm(checkType);
93+
const scriptTextAreaPreSubmit = screen.getByTestId(`code-editor`);
94+
await user.clear(scriptTextAreaPreSubmit);
95+
96+
const invalidVusOptions = `export const options = {
97+
scenarios: {
98+
ui: {
99+
vus: 2,
100+
options: {
101+
browser: {
102+
type: 'chromium',
103+
},
104+
},
105+
},
106+
},
107+
};`;
108+
await user.type(scriptTextAreaPreSubmit, browserImport + invalidVusOptions);
109+
110+
await submitForm(user);
111+
const err = await screen.findByText("Script can't define vus > 1 for this check");
112+
expect(err).toBeInTheDocument();
113+
});
114+
115+
it(`will display an error when it defines iterations > 1`, async () => {
116+
const { user } = await renderNewForm(checkType);
117+
const scriptTextAreaPreSubmit = screen.getByTestId(`code-editor`);
118+
await user.clear(scriptTextAreaPreSubmit);
119+
120+
const invalidIterationsOptions = `export const options = {
121+
scenarios: {
122+
ui: {
123+
iterations: 2,
124+
options: {
125+
browser: {
126+
type: 'chromium',
127+
},
128+
},
129+
},
130+
},
131+
};`;
132+
await user.type(scriptTextAreaPreSubmit, browserImport + invalidIterationsOptions);
133+
134+
await submitForm(user);
135+
const err = await screen.findByText("Script can't define iterations > 1 for this check");
136+
expect(err).toBeInTheDocument();
137+
});
65138
});
66139
});

src/schemas/forms/script/parser.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function getPropertyValueByPath(obj: ObjectExpression, path: string[]): any {
3232
} else if (property?.value.type === 'Literal') {
3333
current = property.value.value;
3434
} else {
35-
return undefined;
35+
return property?.value;
3636
}
3737
}
3838
return current;
@@ -60,12 +60,18 @@ const optionExportMatcher: SimpleVisitors<{ options: ObjectExpression | null }>
6060
},
6161
};
6262

63-
export function checkForChromium(objectExpression: ObjectExpression): boolean {
64-
// Path to the property we're interested in
65-
const path = ['scenarios', 'ui', 'options', 'browser', 'type'];
66-
const value = getPropertyValueByPath(objectExpression, path);
63+
export function getProperty(objectExpression: ObjectExpression, propPath: string[]) {
64+
const scenarios = getPropertyValueByPath(objectExpression, ['scenarios']);
65+
if (!scenarios || scenarios.type !== 'ObjectExpression') {
66+
return false;
67+
}
6768

68-
return value === 'chromium';
69+
for (const scenario of scenarios.properties) {
70+
if (scenario.key.type === 'Identifier') {
71+
const propValue = getPropertyValueByPath(scenario.value, propPath);
72+
return propValue;
73+
}
74+
}
6975
}
7076

7177
interface ImportBrowserState {

src/schemas/forms/script/validation.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { RefinementCtx, ZodIssueCode } from 'zod';
22

3-
import { checkForChromium, extractImportStatement, extractOptionsExport, parseScript } from './parser';
3+
import { extractImportStatement, extractOptionsExport, getProperty, parseScript } from './parser';
44

55
const MAX_SCRIPT_IN_KB = 128;
66

@@ -36,13 +36,40 @@ export function validateBrowserScript(script: string, context: RefinementCtx) {
3636
});
3737
}
3838

39-
if (!checkForChromium(options)) {
39+
const hasChromium = getProperty(options, ['options', 'browser', 'type']) === 'chromium';
40+
if (!hasChromium) {
4041
return context.addIssue({
4142
code: ZodIssueCode.custom,
4243
message: 'Script must set the type to chromium in the browser options.',
4344
});
4445
}
4546

47+
const hasInvalidDuration = getProperty(options, ['duration']) !== undefined;
48+
if (hasInvalidDuration) {
49+
return context.addIssue({
50+
code: ZodIssueCode.custom,
51+
message: "Script can't define a duration value for this check",
52+
});
53+
}
54+
55+
const vus = getProperty(options, ['vus']);
56+
const hasInvaludVus = vus !== undefined && vus !== 1;
57+
if (hasInvaludVus) {
58+
return context.addIssue({
59+
code: ZodIssueCode.custom,
60+
message: "Script can't define vus > 1 for this check",
61+
});
62+
}
63+
64+
const iterations = getProperty(options, ['iterations']);
65+
const hasInvalidIterations = iterations !== undefined && iterations !== 1;
66+
if (hasInvalidIterations) {
67+
return context.addIssue({
68+
code: ZodIssueCode.custom,
69+
message: "Script can't define iterations > 1 for this check",
70+
});
71+
}
72+
4673
const browserImport = extractImportStatement(program);
4774

4875
if (browserImport === null) {

0 commit comments

Comments
 (0)