Files
cve-dashboard/backend/__tests__/ivanti-queue-remediation-vendor-validation.property.test.js

142 lines
4.9 KiB
JavaScript
Raw Normal View History

/**
* Property-Based Tests: Ivanti Queue Remediation Vendor Validation
*
* Feature: ivanti-queue-remediation
* Property 1: Remediate vendor validation
*
* For any non-empty string of 1200 characters (trimmed, with at least one
* non-whitespace character), submitting it as the vendor field with workflow_type
* "Remediate" to the queue API SHALL be accepted; and for any empty,
* whitespace-only, or >200 character vendor string, the request SHALL be
* rejected with a 400 status.
*
* **Validates: Requirements 1.2, 1.3**
*/
const fc = require('fast-check');
// ---------------------------------------------------------------------------
// Replicate the pure validation logic from ivantiTodoQueue.js
// ---------------------------------------------------------------------------
const VALID_WORKFLOW_TYPES = ['FP', 'Archer', 'CARD', 'GRANITE', 'DECOM', 'Remediate'];
const INVENTORY_TYPES = ['CARD', 'GRANITE', 'DECOM'];
function isValidVendor(vendor) {
if (typeof vendor !== 'string') return false;
const trimmed = vendor.trim();
return trimmed.length > 0 && trimmed.length <= 200;
}
/**
* Simulates the validation logic for the batch/single add endpoints.
* Returns { accepted: boolean, status: number } mirroring the route behavior.
*/
function validateRemediateRequest(vendor) {
const workflow_type = 'Remediate';
if (!VALID_WORKFLOW_TYPES.includes(workflow_type)) {
return { accepted: false, status: 400 };
}
// Remediate is NOT in INVENTORY_TYPES, so vendor is required
if (!INVENTORY_TYPES.includes(workflow_type)) {
if (!isValidVendor(vendor)) {
return { accepted: false, status: 400 };
}
}
return { accepted: true, status: 201 };
}
// ---------------------------------------------------------------------------
// Arbitraries
// ---------------------------------------------------------------------------
// Valid vendor: 1200 chars trimmed, at least one non-whitespace char
const arbValidVendor = fc.string({ minLength: 1, maxLength: 200 }).filter(s => {
const trimmed = s.trim();
return trimmed.length > 0 && trimmed.length <= 200;
});
// Invalid vendor: empty string
const arbEmptyVendor = fc.constant('');
// Invalid vendor: whitespace-only strings
const arbWhitespaceOnlyVendor = fc.array(
fc.constantFrom(' ', '\t', '\n', '\r'),
{ minLength: 1, maxLength: 50 }
).map(arr => arr.join(''));
// Invalid vendor: strings > 200 chars when trimmed
const arbOverlengthVendor = fc.string({ minLength: 201, maxLength: 400 }).filter(s => {
return s.trim().length > 200;
});
// ---------------------------------------------------------------------------
// Property 1: Remediate vendor validation
// ---------------------------------------------------------------------------
describe('Feature: ivanti-queue-remediation, Property 1: Remediate vendor validation', () => {
it('accepts any non-empty vendor string of 1200 trimmed characters with at least one non-whitespace', () => {
fc.assert(
fc.property(arbValidVendor, (vendor) => {
const result = validateRemediateRequest(vendor);
expect(result.accepted).toBe(true);
expect(result.status).toBe(201);
}),
{ numRuns: 100 }
);
});
it('rejects empty string as vendor for Remediate workflow', () => {
fc.assert(
fc.property(arbEmptyVendor, (vendor) => {
const result = validateRemediateRequest(vendor);
expect(result.accepted).toBe(false);
expect(result.status).toBe(400);
}),
{ numRuns: 10 }
);
});
it('rejects whitespace-only strings as vendor for Remediate workflow', () => {
fc.assert(
fc.property(arbWhitespaceOnlyVendor, (vendor) => {
const result = validateRemediateRequest(vendor);
expect(result.accepted).toBe(false);
expect(result.status).toBe(400);
}),
{ numRuns: 100 }
);
});
it('rejects vendor strings exceeding 200 characters when trimmed', () => {
fc.assert(
fc.property(arbOverlengthVendor, (vendor) => {
const result = validateRemediateRequest(vendor);
expect(result.accepted).toBe(false);
expect(result.status).toBe(400);
}),
{ numRuns: 100 }
);
});
it('rejects non-string vendor values (undefined, null, number)', () => {
const arbNonString = fc.oneof(
fc.constant(undefined),
fc.constant(null),
fc.integer(),
fc.boolean()
);
fc.assert(
fc.property(arbNonString, (vendor) => {
const result = validateRemediateRequest(vendor);
expect(result.accepted).toBe(false);
expect(result.status).toBe(400);
}),
{ numRuns: 100 }
);
});
});