From 900615e3902063b39284a65b45a10a867bd2c2e5 Mon Sep 17 00:00:00 2001 From: afeferman <105450048+afeferman@users.noreply.github.com> Date: Fri, 28 Mar 2025 11:38:24 -0500 Subject: [PATCH 1/4] chore: initialize PR with an empty commit skip-checks:true From 824b77e2ed1de9e3284906301c2fe0f0fb252b2f Mon Sep 17 00:00:00 2001 From: afeferman <105450048+afeferman@users.noreply.github.com> Date: Fri, 28 Mar 2025 11:41:47 -0500 Subject: [PATCH 2/4] ci: temporarily disable workflows while addressing security issues skip-checks:true From 8eb5573c8355a207c8e5871a54c1506a23ae5e44 Mon Sep 17 00:00:00 2001 From: afeferman <105450048+afeferman@users.noreply.github.com> Date: Fri, 28 Mar 2025 11:49:22 -0500 Subject: [PATCH 3/4] test: add auto-generated e2e security tests skip-checks:true --- .brightsec/tests/get-addpage.test.ts | 33 +++++++++++++++++ .brightsec/tests/get-emailcheck.test.ts | 33 +++++++++++++++++ .brightsec/tests/get-forwardme.test.ts | 36 ++++++++++++++++++ .brightsec/tests/get-install.test.ts | 33 +++++++++++++++++ .brightsec/tests/get-login-validator.test.ts | 33 +++++++++++++++++ .brightsec/tests/get-open.test.ts | 33 +++++++++++++++++ .brightsec/tests/get-register.test.ts | 33 +++++++++++++++++ .brightsec/tests/get-username-check.test.ts | 33 +++++++++++++++++ .../get-vulnerability-send-message.test.ts | 33 +++++++++++++++++ .brightsec/tests/get-xxe.test.ts | 33 +++++++++++++++++ .brightsec/tests/post-addpage.test.ts | 35 ++++++++++++++++++ .brightsec/tests/post-emailcheck.test.ts | 37 +++++++++++++++++++ .brightsec/tests/post-forwardme.test.ts | 35 ++++++++++++++++++ .brightsec/tests/post-install.test.ts | 35 ++++++++++++++++++ .brightsec/tests/post-login-validator.test.ts | 37 +++++++++++++++++++ .brightsec/tests/post-open.test.ts | 34 +++++++++++++++++ .brightsec/tests/post-register.test.ts | 37 +++++++++++++++++++ .brightsec/tests/post-username-check.test.ts | 37 +++++++++++++++++++ .../post-vulnerability-send-message.test.ts | 34 +++++++++++++++++ .brightsec/tests/post-xpathquery.test.ts | 37 +++++++++++++++++++ .brightsec/tests/post-xxe.test.ts | 33 +++++++++++++++++ 21 files changed, 724 insertions(+) create mode 100644 .brightsec/tests/get-addpage.test.ts create mode 100644 .brightsec/tests/get-emailcheck.test.ts create mode 100644 .brightsec/tests/get-forwardme.test.ts create mode 100644 .brightsec/tests/get-install.test.ts create mode 100644 .brightsec/tests/get-login-validator.test.ts create mode 100644 .brightsec/tests/get-open.test.ts create mode 100644 .brightsec/tests/get-register.test.ts create mode 100644 .brightsec/tests/get-username-check.test.ts create mode 100644 .brightsec/tests/get-vulnerability-send-message.test.ts create mode 100644 .brightsec/tests/get-xxe.test.ts create mode 100644 .brightsec/tests/post-addpage.test.ts create mode 100644 .brightsec/tests/post-emailcheck.test.ts create mode 100644 .brightsec/tests/post-forwardme.test.ts create mode 100644 .brightsec/tests/post-install.test.ts create mode 100644 .brightsec/tests/post-login-validator.test.ts create mode 100644 .brightsec/tests/post-open.test.ts create mode 100644 .brightsec/tests/post-register.test.ts create mode 100644 .brightsec/tests/post-username-check.test.ts create mode 100644 .brightsec/tests/post-vulnerability-send-message.test.ts create mode 100644 .brightsec/tests/post-xpathquery.test.ts create mode 100644 .brightsec/tests/post-xxe.test.ts diff --git a/.brightsec/tests/get-addpage.test.ts b/.brightsec/tests/get-addpage.test.ts new file mode 100644 index 00000000..a4648e44 --- /dev/null +++ b/.brightsec/tests/get-addpage.test.ts @@ -0,0 +1,33 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('GET /AddPage', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['html_injection', 'stored_xss'], + attackParamLocations: [AttackParamLocation.QUERY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.GET, + url: `${baseUrl}/AddPage?filename=example.html&content=%3Chtml%3E%3Cbody%3EHello%20World%3C/body%3E%3C/html%3E` + }); +}); diff --git a/.brightsec/tests/get-emailcheck.test.ts b/.brightsec/tests/get-emailcheck.test.ts new file mode 100644 index 00000000..26df1642 --- /dev/null +++ b/.brightsec/tests/get-emailcheck.test.ts @@ -0,0 +1,33 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('GET /EmailCheck', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'excessive_data_exposure', 'full_path_disclosure'], + attackParamLocations: [AttackParamLocation.QUERY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.GET, + url: `${baseUrl}/EmailCheck?email=example@example.com` + }); +}); diff --git a/.brightsec/tests/get-forwardme.test.ts b/.brightsec/tests/get-forwardme.test.ts new file mode 100644 index 00000000..ad430b31 --- /dev/null +++ b/.brightsec/tests/get-forwardme.test.ts @@ -0,0 +1,36 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('GET /ForwardMe', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['unvalidated_redirect', 'xss'], + attackParamLocations: [AttackParamLocation.QUERY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.GET, + url: `${baseUrl}/ForwardMe?location=/example/path`, + headers: { + 'Content-Type': 'text/html;charset=UTF-8' + } + }); +}); diff --git a/.brightsec/tests/get-install.test.ts b/.brightsec/tests/get-install.test.ts new file mode 100644 index 00000000..dfd15382 --- /dev/null +++ b/.brightsec/tests/get-install.test.ts @@ -0,0 +1,33 @@ +import { test, before, after } from 'node:test'; +import { SecRunner } from '@sectester/runner'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('GET /install', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'open_database', 'xss', 'csrf', 'excessive_data_exposure', 'full_path_disclosure'], + attackParamLocations: [AttackParamLocation.QUERY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.GET, + url: `${baseUrl}/install?dburl=jdbc:mysql://localhost:3306/&jdbcdriver=com.mysql.jdbc.Driver&dbuser=root&dbpass=password&dbname=vulnerable_db&siteTitle=Vulnerable%20Lab&adminuser=admin&adminpass=adminpass&setup=1` + }); +}); diff --git a/.brightsec/tests/get-login-validator.test.ts b/.brightsec/tests/get-login-validator.test.ts new file mode 100644 index 00000000..867b9a55 --- /dev/null +++ b/.brightsec/tests/get-login-validator.test.ts @@ -0,0 +1,33 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('GET /LoginValidator', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'xss', 'csrf', 'unvalidated_redirect', 'excessive_data_exposure'], + attackParamLocations: [AttackParamLocation.QUERY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.GET, + url: `${baseUrl}/LoginValidator?username=sampleUser&password=samplePass` + }); +}); diff --git a/.brightsec/tests/get-open.test.ts b/.brightsec/tests/get-open.test.ts new file mode 100644 index 00000000..417115d9 --- /dev/null +++ b/.brightsec/tests/get-open.test.ts @@ -0,0 +1,33 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('GET /Open', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['unvalidated_redirect', 'xss', 'excessive_data_exposure'], + attackParamLocations: [AttackParamLocation.QUERY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.GET, + url: `${baseUrl}/Open?url=http://example.com` + }); +}); diff --git a/.brightsec/tests/get-register.test.ts b/.brightsec/tests/get-register.test.ts new file mode 100644 index 00000000..77475fe0 --- /dev/null +++ b/.brightsec/tests/get-register.test.ts @@ -0,0 +1,33 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('GET /register', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'xss', 'csrf', 'excessive_data_exposure', 'unvalidated_redirect'], + attackParamLocations: [AttackParamLocation.QUERY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.GET, + url: `${baseUrl}/register?username=sampleUser&password=samplePass&email=sample@example.com&About=Sample%20about%20text&secret=nosecret` + }); +}); diff --git a/.brightsec/tests/get-username-check.test.ts b/.brightsec/tests/get-username-check.test.ts new file mode 100644 index 00000000..85929c43 --- /dev/null +++ b/.brightsec/tests/get-username-check.test.ts @@ -0,0 +1,33 @@ +import { test, before, after } from 'node:test'; +import { SecRunner } from '@sectester/runner'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('GET /UsernameCheck', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'excessive_data_exposure', 'full_path_disclosure'], + attackParamLocations: [AttackParamLocation.QUERY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.GET, + url: `${baseUrl}/UsernameCheck?username=sampleUser` + }); +}); diff --git a/.brightsec/tests/get-vulnerability-send-message.test.ts b/.brightsec/tests/get-vulnerability-send-message.test.ts new file mode 100644 index 00000000..93490d88 --- /dev/null +++ b/.brightsec/tests/get-vulnerability-send-message.test.ts @@ -0,0 +1,33 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('GET /vulnerability/SendMessage.jsp', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['csrf', 'xss', 'excessive_data_exposure', 'email_injection', 'sqli'], + attackParamLocations: [AttackParamLocation.QUERY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.GET, + url: `${baseUrl}/vulnerability/SendMessage.jsp` + }); +}); diff --git a/.brightsec/tests/get-xxe.test.ts b/.brightsec/tests/get-xxe.test.ts new file mode 100644 index 00000000..f1ce77ac --- /dev/null +++ b/.brightsec/tests/get-xxe.test.ts @@ -0,0 +1,33 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('GET /xxe', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['xxe'], + attackParamLocations: [AttackParamLocation.PATH] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.GET, + url: `${baseUrl}/xxe` + }); +}); diff --git a/.brightsec/tests/post-addpage.test.ts b/.brightsec/tests/post-addpage.test.ts new file mode 100644 index 00000000..9338e6b8 --- /dev/null +++ b/.brightsec/tests/post-addpage.test.ts @@ -0,0 +1,35 @@ +import { test, before, after } from 'node:test'; +import { SecRunner } from '@sectester/runner'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('POST /AddPage', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['stored_xss', 'file_upload'], + attackParamLocations: [AttackParamLocation.BODY, AttackParamLocation.QUERY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/AddPage`, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: 'filename=example.html&content=%3Chtml%3E%3Cbody%3EHello%20World%3C/body%3E%3C/html%3E' + }); +}); diff --git a/.brightsec/tests/post-emailcheck.test.ts b/.brightsec/tests/post-emailcheck.test.ts new file mode 100644 index 00000000..55a5e8f5 --- /dev/null +++ b/.brightsec/tests/post-emailcheck.test.ts @@ -0,0 +1,37 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('POST /EmailCheck', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'excessive_data_exposure', 'xss', 'csrf'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/EmailCheck`, + headers: { + 'Content-Type': 'application/json' + }, + body: 'email=example@example.com' + }); +}); diff --git a/.brightsec/tests/post-forwardme.test.ts b/.brightsec/tests/post-forwardme.test.ts new file mode 100644 index 00000000..7ddf8b87 --- /dev/null +++ b/.brightsec/tests/post-forwardme.test.ts @@ -0,0 +1,35 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('POST /ForwardMe', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['unvalidated_redirect', 'xss'], + attackParamLocations: [AttackParamLocation.BODY, AttackParamLocation.HEADER] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/ForwardMe`, + headers: { 'Content-Type': 'text/html;charset=UTF-8' }, + body: 'location=/example/path' + }); +}); diff --git a/.brightsec/tests/post-install.test.ts b/.brightsec/tests/post-install.test.ts new file mode 100644 index 00000000..8be0ad6d --- /dev/null +++ b/.brightsec/tests/post-install.test.ts @@ -0,0 +1,35 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('POST /install', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'open_database', 'csrf', 'xss', 'excessive_data_exposure', 'full_path_disclosure'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/install`, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: 'dburl=jdbc:mysql://localhost:3306/&jdbcdriver=com.mysql.jdbc.Driver&dbuser=root&dbpass=password&dbname=vulnerable_db&siteTitle=Vulnerable Lab&adminuser=admin&adminpass=adminpass&setup=1' + }); +}); diff --git a/.brightsec/tests/post-login-validator.test.ts b/.brightsec/tests/post-login-validator.test.ts new file mode 100644 index 00000000..70a5417e --- /dev/null +++ b/.brightsec/tests/post-login-validator.test.ts @@ -0,0 +1,37 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('POST /LoginValidator', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'csrf', 'xss', 'unvalidated_redirect', 'excessive_data_exposure'], + attackParamLocations: [AttackParamLocation.BODY, AttackParamLocation.HEADER] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/LoginValidator`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: 'username=sampleUser&password=samplePass&RememberMe=on' + }); +}); diff --git a/.brightsec/tests/post-open.test.ts b/.brightsec/tests/post-open.test.ts new file mode 100644 index 00000000..e6658bea --- /dev/null +++ b/.brightsec/tests/post-open.test.ts @@ -0,0 +1,34 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('POST /Open', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['unvalidated_redirect', 'csrf'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/Open`, + body: 'url=http://example.com' + }); +}); diff --git a/.brightsec/tests/post-register.test.ts b/.brightsec/tests/post-register.test.ts new file mode 100644 index 00000000..4893654a --- /dev/null +++ b/.brightsec/tests/post-register.test.ts @@ -0,0 +1,37 @@ +import { test, before, after } from 'node:test'; +import { SecRunner } from '@sectester/runner'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('POST /register', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'xss', 'csrf', 'excessive_data_exposure', 'mass_assignment', 'secret_tokens'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/register`, + body: 'username=sampleUser&password=samplePass&email=sample@example.com&About=Sample about text&secret=nosecret', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); +}); diff --git a/.brightsec/tests/post-username-check.test.ts b/.brightsec/tests/post-username-check.test.ts new file mode 100644 index 00000000..b1fbc167 --- /dev/null +++ b/.brightsec/tests/post-username-check.test.ts @@ -0,0 +1,37 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('POST /UsernameCheck', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'excessive_data_exposure', 'xss', 'csrf', 'full_path_disclosure'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/UsernameCheck`, + body: 'username=sampleUser', + headers: { + 'Content-Type': 'application/json' + } + }); +}); diff --git a/.brightsec/tests/post-vulnerability-send-message.test.ts b/.brightsec/tests/post-vulnerability-send-message.test.ts new file mode 100644 index 00000000..0215ce3c --- /dev/null +++ b/.brightsec/tests/post-vulnerability-send-message.test.ts @@ -0,0 +1,34 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('POST /vulnerability/SendMessage.jsp', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['csrf', 'xss', 'excessive_data_exposure', 'email_injection'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/vulnerability/SendMessage.jsp`, + body: 'recipient=exampleRecipient&subject=exampleSubject&msg=exampleMessage&sender=exampleSender&send=true' + }); +}); diff --git a/.brightsec/tests/post-xpathquery.test.ts b/.brightsec/tests/post-xpathquery.test.ts new file mode 100644 index 00000000..f0b53c8c --- /dev/null +++ b/.brightsec/tests/post-xpathquery.test.ts @@ -0,0 +1,37 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('POST /XPathQuery', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['xpathi', 'excessive_data_exposure', 'full_path_disclosure', 'csrf'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/XPathQuery`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: 'username=sampleUser&password=samplePass' + }); +}); diff --git a/.brightsec/tests/post-xxe.test.ts b/.brightsec/tests/post-xxe.test.ts new file mode 100644 index 00000000..17a2a1d7 --- /dev/null +++ b/.brightsec/tests/post-xxe.test.ts @@ -0,0 +1,33 @@ +import { test, before, after } from 'node:test'; +import { Severity, AttackParamLocation, HttpMethod } from '@sectester/scan'; +import { SecRunner } from '@sectester/runner'; + +let runner!: SecRunner; + +before(async () => { + runner = new SecRunner({ + hostname: process.env.BRIGHT_HOSTNAME!, + projectId: process.env.BRIGHT_PROJECT_ID! + }); + + await runner.init(); +}); + +after(() => runner.clear()); + +const timeout = 40 * 60 * 1000; +const baseUrl = process.env.BRIGHT_TARGET_URL!; + +test('POST /xxe', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['xxe'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/xxe` + }); +}); From 35b10eafa7df454e0c2cc28c979a24e9132eeb4a Mon Sep 17 00:00:00 2001 From: afeferman <105450048+afeferman@users.noreply.github.com> Date: Fri, 28 Mar 2025 11:49:41 -0500 Subject: [PATCH 4/4] ci: add CI workflow to run e2e security tests --- .github/workflows/bright.yml | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/bright.yml diff --git a/.github/workflows/bright.yml b/.github/workflows/bright.yml new file mode 100644 index 00000000..b1d961da --- /dev/null +++ b/.github/workflows/bright.yml @@ -0,0 +1,55 @@ +name: Security Tests + +on: + pull_request: + branches: + - '**' + +permissions: + checks: write + contents: read + +jobs: + security-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '22' + + - name: Install SecTester dependencies + run: npm install --save-dev @sectester/core @sectester/repeater @sectester/scan @sectester/runner + + - name: Start application using Docker Compose + run: docker compose up --wait + env: + MYSQL_ROOT_PASSWORD: root + + - name: Verify application readiness + run: | + until nc -zv 127.0.0.1 8080; do + echo "Waiting for application to be ready..." + sleep 5 + done + + - name: Run security tests + env: + BRIGHT_HOSTNAME: ${{ vars.BRIGHT_HOSTNAME }} + BRIGHT_PROJECT_ID: ${{ vars.BRIGHT_PROJECT_ID }} + BRIGHT_TOKEN: ${{ secrets.BRIGHT_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRIGHT_TARGET_URL: http://127.0.0.1:8080 + run: | + node --experimental-transform-types \ + --experimental-strip-types \ + --experimental-detect-module \ + --disable-warning=MODULE_TYPELESS_PACKAGE_JSON \ + --disable-warning=ExperimentalWarning \ + --test-force-exit \ + --test-concurrency=4 \ + --test .brightsec/tests/*.test.ts