Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion apps/api/src/cloud-security/aws-command-executor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,12 +422,32 @@ describe('PutMetricFilterCommand required params + normalization', () => {
]);
expect(errors).toEqual(
expect.arrayContaining([
expect.stringMatching(/Required param "filterPattern" is missing/),
// filterPattern must be PRESENT (but may be empty), so a missing one is
// reported via the present-check, not the non-empty REQUIRED_PARAMS one.
expect.stringMatching(/Required param "filterPattern" must be provided/),
expect.stringMatching(/Required param "metricTransformations" is missing/),
]),
);
});

it('allows an empty filterPattern (AWS accepts "" — it matches all events)', () => {
const errors = validatePlanSteps([
step({
service: 'cloudwatch-logs',
command: 'PutMetricFilterCommand',
params: {
logGroupName: 'lg',
filterName: 'fn',
filterPattern: '',
metricTransformations: [
{ metricName: 'm', metricNamespace: 'CloudTrailMetrics', metricValue: '1' },
],
},
}),
]);
expect(errors.filter((e) => /filterPattern/.test(e))).toHaveLength(0);
});

it('does not error when all PutMetricFilter params are present', () => {
const errors = validatePlanSteps([
step({
Expand Down
30 changes: 24 additions & 6 deletions apps/api/src/cloud-security/aws-command-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,18 @@ export const REQUIRED_PARAMS: Record<string, readonly string[]> = {
StartConfigurationRecorderCommand: ['ConfigurationRecorderName'],
PutBucketPolicyCommand: ['Bucket', 'Policy'],
CreateTrailCommand: ['Name', 'S3BucketName'],
PutMetricFilterCommand: [
'logGroupName',
'filterName',
'filterPattern',
'metricTransformations',
],
PutMetricFilterCommand: ['logGroupName', 'filterName', 'metricTransformations'],
};

/**
* Params that must be PRESENT in the request but may legitimately be an empty
* string — unlike REQUIRED_PARAMS, which also rejects "". For example, AWS
* CloudWatch Logs PutMetricFilter requires `filterPattern` to be supplied but
* accepts an empty pattern (an empty filterPattern matches all log events), so
* we must reject only a missing/null value, not "".
*/
const REQUIRED_PRESENT_PARAMS: Record<string, readonly string[]> = {
PutMetricFilterCommand: ['filterPattern'],
};

const REQUIRED_PARAM_ONE_OF: Record<string, readonly (readonly string[])[]> = {
Expand Down Expand Up @@ -649,6 +655,18 @@ export function validatePlanSteps(steps: AwsCommandStep[]): string[] {
}
}

const requiredPresent = REQUIRED_PRESENT_PARAMS[step.command];
if (requiredPresent) {
for (const key of requiredPresent) {
const value = step.params?.[key];
// Must be supplied, but an empty string is valid (e.g. an empty
// CloudWatch filterPattern matches all log events).
if (value === undefined || value === null) {
errors.push(`${prefix}: Required param "${key}" must be provided`);
}
}
}

const oneOfGroups = REQUIRED_PARAM_ONE_OF[step.command];
if (oneOfGroups) {
for (const group of oneOfGroups) {
Expand Down
Loading