Skip to content
Closed
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
5 changes: 4 additions & 1 deletion packages/runner/src/lib/IssueFound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { SecTesterError } from '@sectester/core';
import { Issue } from '@sectester/scan';

export class IssueFound extends SecTesterError {
constructor(public readonly issue: Issue, formatter: Formatter) {
constructor(
public readonly issue: Issue,
formatter: Formatter
) {
super(`Target is vulnerable\n\n${formatter.format(issue)}`);
}
}
1 change: 1 addition & 0 deletions packages/runner/src/lib/SecScanOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export type SecScanOptions = Pick<
| 'skipStaticParams'
| 'attackParamLocations'
| 'starMetadata'
| 'testMetadata'
>;
102 changes: 102 additions & 0 deletions packages/scan/src/ScanSettings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,5 +187,107 @@ describe('ScanSettings', () => {
name: expect.stringMatching(/^.{1,199}…$/)
});
});

it('should handle broken access control test with string auth', () => {
// arrange
const testConfig = {
name: 'broken_access_control' as const,
options: {
auth: 'auth-object-id'
}
};
const settings: ScanSettingsOptions = {
tests: [testConfig],
target: { url: 'https://example.com' }
};

// act
const result = new ScanSettings(settings);

// assert
expect(result.tests).toEqual([testConfig]);
});

it('should handle broken access control test with tuple auth', () => {
// arrange
const testConfig = {
name: 'broken_access_control' as const,
options: {
auth: ['key', 'value'] as [string, string]
}
};
const settings: ScanSettingsOptions = {
tests: [testConfig],
target: { url: 'https://example.com' }
};

// act
const result = new ScanSettings(settings);

// assert
expect(result.tests).toEqual([testConfig]);
});

it('should deduplicate string tests', () => {
// arrange
const testName = 'xss';
const settings: ScanSettingsOptions = {
tests: [testName, testName],
target: { url: 'https://example.com' }
};

// act
const result = new ScanSettings(settings);

// assert
expect(result.tests).toEqual([testName]);
});

it('should not deduplicate object tests', () => {
// arrange
const testConfig1 = {
name: 'broken_access_control' as const,
options: {
auth: 'auth1'
}
};
const testConfig2 = {
name: 'broken_access_control' as const,
options: {
auth: 'auth2'
}
};
const settings: ScanSettingsOptions = {
tests: [testConfig1, testConfig2],
target: { url: 'https://example.com' }
};

// act
const result = new ScanSettings(settings);

// assert
expect(result.tests).toEqual([testConfig1, testConfig2]);
});

it('should handle mixed string and object tests', () => {
// arrange
const testName = 'xss';
const testConfig = {
name: 'broken_access_control' as const,
options: {
auth: 'auth-object-id'
}
};
const settings: ScanSettingsOptions = {
tests: [testName, testConfig],
target: { url: 'https://example.com' }
};

// act
const result = new ScanSettings(settings);

// assert
expect(result.tests).toEqual([testName, testConfig]);
});
});
});
25 changes: 16 additions & 9 deletions packages/scan/src/ScanSettings.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { AttackParamLocation, HttpMethod } from './models';
import { AttackParamLocation, HttpMethod, Test } from './models';
import { Target, TargetOptions } from './target';
import { checkBoundaries, contains, truncate } from '@sectester/core';

export interface ScanSettingsOptions {
// The list of tests to be performed against the target application
tests: string[];
tests: Test[];
// The target that will be attacked
target: Target | TargetOptions;
// The scan name
Expand Down Expand Up @@ -120,20 +120,27 @@ export class ScanSettings implements ScanSettingsOptions {
this._requestsRateLimit = value;
}

private _tests!: string[];
private _tests!: Test[];

get tests(): string[] {
get tests(): Test[] {
return this._tests;
}

private set tests(value: string[]) {
const uniqueTestTypes = new Set<string>(value);

if (uniqueTestTypes.size < 1) {
private set tests(value: Test[]) {
if (value.length < 1) {
throw new Error('Please provide at least one test.');
}

this._tests = [...uniqueTestTypes];
// For string tests, ensure uniqueness
const stringTests = value.filter(
(test): test is string => typeof test === 'string'
);
const uniqueStringTests = [...new Set(stringTests)];

// Preserve non-string tests (like BrokenAccessControlTest)
const nonStringTests = value.filter(test => typeof test !== 'string');

this._tests = [...uniqueStringTests, ...nonStringTests];
}

private _attackParamLocations!: AttackParamLocation[];
Expand Down
3 changes: 2 additions & 1 deletion packages/scan/src/models/ScanConfig.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { AttackParamLocation } from './AttackParamLocation';
import { Test } from './Tests';

export interface ScanConfig {
name: string;
projectId: string;
entryPointIds: string[];
tests?: string[];
tests?: Test[];
poolSize?: number;
requestsRateLimit?: number;
attackParamLocations?: AttackParamLocation[];
Expand Down
4 changes: 2 additions & 2 deletions packages/scan/src/models/Severity.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ describe('Severity', () => {
item.expected === 0
? 'zero'
: item.expected > 0
? 'positive'
: 'negative'
? 'positive'
: 'negative'
}))
)(
'should return $expectedLabel comparing $input.a and $input.b',
Expand Down
14 changes: 14 additions & 0 deletions packages/scan/src/models/Tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export type Test = string | BrokenAccessControlTest;

export type BrokenAccessControlOptions =
| {
auth: string; // + anon
}
| {
auth: [string, string];
};

export type BrokenAccessControlTest = {
name: 'broken_access_control';
options: BrokenAccessControlOptions;
};
1 change: 1 addition & 0 deletions packages/scan/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './IssueGroup';
export * from './ScanState';
export * from './ScanConfig';
export * from './HttpMethod';
export * from './Tests';
Loading