fix(security): require fresh file parameter input (#11455)

This commit is contained in:
Jasper
2026-08-23 23:05:42 +00:00
committed by GitHub
parent 8d844eecbd
commit 74393e90bf
4 changed files with 72 additions and 3 deletions
@@ -82,6 +82,27 @@ describe('ACP recipe param requests', () => {
scope.finish();
});
it('does not offer deeplink values for file parameters', async () => {
setRecipeParameters({ document: '/tmp/private.txt', topic: 'release notes' });
const scope = requests.beginConfiguredRecipeParameterScope()!;
const request = recipeParamRequest('session-1', scope.id);
request.parameters.unshift({
key: 'document',
description: 'Document',
input_type: 'file',
requirement: 'required',
});
const response = requests.requestAcpRecipeParams(request);
const [pendingRequest] = requests.getAcpRecipeParamRequestsSnapshot();
expect(pendingRequest.initialValues).toEqual({ topic: 'release notes' });
requests.cancelAcpRecipeParamRequest(pendingRequest.id);
await expect(response).resolves.toEqual({ action: 'cancel' });
scope.finish();
});
it('allows same-session retries until a terminal response consumes the values', async () => {
setRecipeParameters({ topic: 'release notes' });
const scope = requests.beginConfiguredRecipeParameterScope()!;
+9 -3
View File
@@ -81,8 +81,7 @@ export function beginConfiguredRecipeParameterScope(): ConfiguredRecipeParameter
}
const configured = window.appConfig?.get('recipeParameters') as
| Record<string, string>
| undefined;
Record<string, string> | undefined;
if (!configured || Object.keys(configured).length === 0) {
configuredParameterState = { status: 'consumed' };
return undefined;
@@ -122,8 +121,15 @@ function configuredParameterValues(request: RequestRecipeParams_unstable): {
if (configuredParameterState.sessionId !== request.sessionId) {
return { values: {}, usesConfiguredParameters: false };
}
const fileParameterKeys = new Set(
request.parameters
.filter((parameter) => parameter.input_type === 'file')
.map((parameter) => parameter.key)
);
return {
values: { ...configuredParameterState.values },
values: Object.fromEntries(
Object.entries(configuredParameterState.values).filter(([key]) => !fileParameterKeys.has(key))
),
usesConfiguredParameters: true,
};
}
@@ -101,6 +101,10 @@ const ParameterInputModal: React.FC<ParameterInputModalProps> = ({
useEffect(() => {
const values = createParameterValueMap();
parameters.forEach((param) => {
if (param.input_type === 'file') {
return;
}
if (param.requirement === 'optional' && param.default != null) {
if (isValidParameterValue(param, param.default)) {
values[param.key] = param.default;
@@ -507,6 +507,44 @@ describe('ParameterInputModal', () => {
expect((screen.getByLabelText(/boolean parameter/i) as HTMLSelectElement).value).toBe('true');
});
it('does not initialize file parameters from prefills or defaults', async () => {
const user = userEvent.setup();
const onSubmit = vi.fn();
renderWithIntl(
<ParameterInputModal
{...defaultProps}
onSubmit={onSubmit}
parameters={[
{
key: 'document',
description: 'Document',
input_type: 'file',
requirement: 'required',
default: '/tmp/default.txt',
},
{
key: 'topic',
description: 'Topic',
input_type: 'string',
requirement: 'required',
},
]}
initialValues={{ document: '/tmp/private.txt', topic: 'release notes' }}
/>
);
expect(screen.getByLabelText(/^Document/)).toHaveValue('');
expect(screen.getByLabelText(/^Topic/)).toHaveValue('release notes');
await user.type(screen.getByLabelText(/^Document/), '/tmp/selected.txt');
await user.click(screen.getByText('Start Recipe'));
expect(onSubmit).toHaveBeenCalledWith({
document: '/tmp/selected.txt',
topic: 'release notes',
});
});
it('keeps a valid default when an invalid prefill is supplied', () => {
renderWithIntl(
<ParameterInputModal