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
316 changes: 146 additions & 170 deletions packages/config/src/configProcessor/helpers/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,144 @@ const schemaKV = {
},
};

const firewallRulesBehaviorsSchema = {
type: 'array',
minItems: 1,
maxItems: 10,
items: {
type: 'object',
oneOf: [
{
properties: {
runFunction: {
type: ['string', 'number'],
errorMessage: "The 'runFunction' behavior must be a string or number",
},
},
required: ['runFunction'],
additionalProperties: false,
},
{
properties: {
setWafRuleset: {
type: 'object',
properties: {
wafMode: {
type: 'string',
enum: FIREWALL_WAF_MODES,
errorMessage: `The wafMode must be one of: ${FIREWALL_WAF_MODES.join(', ')}`,
},
wafId: {
type: ['string', 'number'],
errorMessage: 'The wafId must be a string or number',
},
},
required: ['wafMode', 'wafId'],
additionalProperties: false,
errorMessage: {
additionalProperties: 'No additional properties are allowed in the setWafRuleset object',
required: "Both 'wafMode' and 'wafId' fields are required in setWafRuleset",
},
},
},
required: ['setWafRuleset'],
additionalProperties: false,
},
{
properties: {
setRateLimit: {
type: 'object',
properties: {
type: {
type: 'string',
enum: FIREWALL_RATE_LIMIT_TYPES,
errorMessage: `The rate limit type must be one of: ${FIREWALL_RATE_LIMIT_TYPES.join(', ')}`,
},
limitBy: {
type: 'string',
enum: FIREWALL_RATE_LIMIT_BY,
errorMessage: `The rate limit must be applied by one of: ${FIREWALL_RATE_LIMIT_BY.join(', ')}`,
},
averageRateLimit: {
type: 'string',
errorMessage: 'The averageRateLimit must be a string',
},
maximumBurstSize: {
type: 'string',
errorMessage: 'The maximumBurstSize must be a string',
},
},
required: ['type', 'limitBy', 'averageRateLimit', 'maximumBurstSize'],
additionalProperties: false,
errorMessage: {
additionalProperties: 'No additional properties are allowed in the setRateLimit object',
required:
"All fields ('type', 'limitBy', 'averageRateLimit', 'maximumBurstSize') are required in setRateLimit",
},
},
},
required: ['setRateLimit'],
additionalProperties: false,
},
{
properties: {
deny: {
type: 'boolean',
const: true,
errorMessage: 'The deny behavior must be true',
},
},
required: ['deny'],
additionalProperties: false,
},
{
properties: {
drop: {
type: 'boolean',
const: true,
errorMessage: 'The drop behavior must be true',
},
},
required: ['drop'],
additionalProperties: false,
},
{
properties: {
setCustomResponse: {
type: 'object',
properties: {
statusCode: {
type: ['integer', 'string'],
minimum: 200,
maximum: 499,
errorMessage: 'The statusCode must be a number or string between 200 and 499',
},
contentType: {
type: 'string',
errorMessage: 'The contentType must be a string',
},
contentBody: {
type: 'string',
errorMessage: 'The contentBody must be a string',
},
},
required: ['statusCode', 'contentType', 'contentBody'],
additionalProperties: false,
errorMessage: {
additionalProperties: 'No additional properties are allowed in the setCustomResponse object',
required: "All fields ('statusCode', 'contentType', 'contentBody') are required in setCustomResponse",
},
},
},
required: ['setCustomResponse'],
additionalProperties: false,
},
],
errorMessage: 'Each behavior item must contain exactly one behavior type',
},
errorMessage: 'The behaviors array must contain between 1 and 10 behavior items.',
};

const azionConfigSchema = {
$id: 'azionConfig',
definitions: {
Expand Down Expand Up @@ -1227,171 +1365,7 @@ const azionConfigSchema = {
enum: FIREWALL_VARIABLES,
errorMessage: `The 'variable' field must be one of: ${FIREWALL_VARIABLES.join(', ')}`,
},
behaviors: {
type: 'array',
minItems: 1,
anyOf: [
{
maxItems: 1,
},
{
minItems: 2,
contains: {
type: 'object',
required: ['runFunction'],
},
items: [
{
type: 'object',
required: ['runFunction'],
properties: {
runFunction: {
type: ['string', 'number'],
},
},
additionalProperties: false,
},
],
},
],
items: {
type: 'object',
oneOf: [
{
properties: {
runFunction: {
type: ['string', 'number'],
errorMessage: "The 'runFunction' behavior must be a string or number",
},
},
required: ['runFunction'],
additionalProperties: false,
},
{
properties: {
setWafRuleset: {
type: 'object',
properties: {
wafMode: {
type: 'string',
enum: FIREWALL_WAF_MODES,
errorMessage: `The wafMode must be one of: ${FIREWALL_WAF_MODES.join(', ')}`,
},
wafId: {
type: ['string', 'number'],
errorMessage: 'The wafId must be a string or number',
},
},
required: ['wafMode', 'wafId'],
additionalProperties: false,
errorMessage: {
additionalProperties:
'No additional properties are allowed in the setWafRuleset object',
required: "Both 'wafMode' and 'wafId' fields are required in setWafRuleset",
},
},
},
required: ['setWafRuleset'],
additionalProperties: false,
},
{
properties: {
setRateLimit: {
type: 'object',
properties: {
type: {
type: 'string',
enum: FIREWALL_RATE_LIMIT_TYPES,
errorMessage: `The rate limit type must be one of: ${FIREWALL_RATE_LIMIT_TYPES.join(', ')}`,
},
limitBy: {
type: 'string',
enum: FIREWALL_RATE_LIMIT_BY,
errorMessage: `The rate limit must be applied by one of: ${FIREWALL_RATE_LIMIT_BY.join(', ')}`,
},
averageRateLimit: {
type: 'string',
errorMessage: 'The averageRateLimit must be a string',
},
maximumBurstSize: {
type: 'string',
errorMessage: 'The maximumBurstSize must be a string',
},
},
required: ['type', 'limitBy', 'averageRateLimit', 'maximumBurstSize'],
additionalProperties: false,
errorMessage: {
additionalProperties:
'No additional properties are allowed in the setRateLimit object',
required:
"All fields ('type', 'limitBy', 'averageRateLimit', 'maximumBurstSize') are required in setRateLimit",
},
},
},
required: ['setRateLimit'],
additionalProperties: false,
},
{
properties: {
deny: {
type: 'boolean',
const: true,
errorMessage: 'The deny behavior must be true',
},
},
required: ['deny'],
additionalProperties: false,
},
{
properties: {
drop: {
type: 'boolean',
const: true,
errorMessage: 'The drop behavior must be true',
},
},
required: ['drop'],
additionalProperties: false,
},
{
properties: {
setCustomResponse: {
type: 'object',
properties: {
statusCode: {
type: ['integer', 'string'],
minimum: 200,
maximum: 499,
errorMessage: 'The statusCode must be a number or string between 200 and 499',
},
contentType: {
type: 'string',
errorMessage: 'The contentType must be a string',
},
contentBody: {
type: 'string',
errorMessage: 'The contentBody must be a string',
},
},
required: ['statusCode', 'contentType', 'contentBody'],
additionalProperties: false,
errorMessage: {
additionalProperties:
'No additional properties are allowed in the setCustomResponse object',
required:
"All fields ('statusCode', 'contentType', 'contentBody') are required in setCustomResponse",
},
},
},
required: ['setCustomResponse'],
additionalProperties: false,
},
],
errorMessage: 'Each behavior item must contain exactly one behavior type',
},
errorMessage:
'Multiple behaviors are only allowed when the first behavior is runFunction. Otherwise, only one behavior is permitted.',
},
behaviors: firewallRulesBehaviorsSchema,
criteria: {
type: 'array',
minItems: 1,
Expand Down Expand Up @@ -1419,14 +1393,16 @@ const azionConfigSchema = {
errorMessage: 'The argument must be a string',
},
},
required: ['conditional', 'variable', 'operator', 'argument'],
additionalProperties: false,
errorMessage: {
additionalProperties: 'No additional properties are allowed in the criteria object',
required:
"The 'variable', 'operator', 'argument' and 'conditional' fields are required in each criteria object",
},
},
required: ['conditional', 'variable', 'operator', 'argument'],
additionalProperties: false,
errorMessage: {
type: 'The criteria field must be an array with at least one criteria item',
additionalProperties: 'No additional properties are allowed in the criteria object',
required:
"The 'variable', 'operator', 'argument' and 'conditional' fields are required in each criteria object",
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ class FirewallProcessConfigStrategy extends ProcessConfigStrategy {

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private transformBehaviorsToManifest(behaviorArray: any[]) {
// Runtime validation: deny, drop, and setCustomResponse are terminal behaviors
// and cannot be combined with other behaviors
if (behaviorArray.length > 1) {
const firstBehavior = behaviorArray[0];
const hasTerminalBehavior = firstBehavior.deny || firstBehavior.drop || firstBehavior.setCustomResponse;

if (hasTerminalBehavior) {
const behaviorType = firstBehavior.deny ? 'deny' : firstBehavior.drop ? 'drop' : 'setCustomResponse';
throw new Error(
`The behavior '${behaviorType}' is a terminal behavior and must be used alone. ` +
`It cannot be combined with other behaviors in the same rule.`,
);
}
}

const behaviors = [];

for (const behaviorItem of behaviorArray) {
Expand Down