From 5308cc224102658df12e35535910b656c10278a4 Mon Sep 17 00:00:00 2001 From: afeferman <105450048+afeferman@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:07:13 -0500 Subject: [PATCH 1/4] chore: initialize PR with an empty commit skip-checks:true From 8a7cf7dc8e9136580a24eeb1adc11d4925c3e159 Mon Sep 17 00:00:00 2001 From: afeferman <105450048+afeferman@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:08:58 -0500 Subject: [PATCH 2/4] ci: temporarily disable workflows while addressing security issues skip-checks:true From 617f49874bcec2a8b1747fa79624105f57b70bb9 Mon Sep 17 00:00:00 2001 From: afeferman <105450048+afeferman@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:12:37 -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-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-usernamecheck.test.ts | 33 ++++++++++++++ .../get-vulnerability-send-message.test.ts | 34 ++++++++++++++ .brightsec/tests/get-xxe.test.ts | 33 ++++++++++++++ .brightsec/tests/post-addpage.test.ts | 37 ++++++++++++++++ .brightsec/tests/post-emailcheck.test.ts | 35 +++++++++++++++ .brightsec/tests/post-install.test.ts | 44 +++++++++++++++++++ .brightsec/tests/post-login-validator.test.ts | 37 ++++++++++++++++ .brightsec/tests/post-open.test.ts | 36 +++++++++++++++ .brightsec/tests/post-register.test.ts | 37 ++++++++++++++++ .brightsec/tests/post-username-check.test.ts | 35 +++++++++++++++ .../post-vulnerability-send-message.test.ts | 34 ++++++++++++++ .brightsec/tests/post-xpathquery.test.ts | 37 ++++++++++++++++ .brightsec/tests/post-xxe.test.ts | 37 ++++++++++++++++ 19 files changed, 667 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-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-usernamecheck.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-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..63811459 --- /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: ['xss', 'excessive_data_exposure'], + 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..60bb3dfa --- /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', 'xss'], + 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-install.test.ts b/.brightsec/tests/get-install.test.ts new file mode 100644 index 00000000..095cf5d1 --- /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', 'secret_tokens'], + 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=javavulnerablelab&siteTitle=Java%20Vulnerable%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..14317386 --- /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', 'csrf', 'xss', '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..38e4485b --- /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=sampleSecret` + }); +}); diff --git a/.brightsec/tests/get-usernamecheck.test.ts b/.brightsec/tests/get-usernamecheck.test.ts new file mode 100644 index 00000000..3f1b299c --- /dev/null +++ b/.brightsec/tests/get-usernamecheck.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 /UsernameCheck', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'excessive_data_exposure', 'xss', 'csrf'], + 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..24feabc9 --- /dev/null +++ b/.brightsec/tests/get-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('GET /vulnerability/SendMessage.jsp', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['xss', 'csrf', 'excessive_data_exposure', 'email_injection', 'sqli'], + attackParamLocations: [AttackParamLocation.QUERY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.GET, + url: `${baseUrl}/vulnerability/SendMessage.jsp`, + skipStaticParams: false + }); +}); 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..cdb0eaf0 --- /dev/null +++ b/.brightsec/tests/post-addpage.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 /AddPage', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['file_upload', 'lfi', 'stored_xss', 'xss', 'full_path_disclosure'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .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=Hello World' + }); +}); diff --git a/.brightsec/tests/post-emailcheck.test.ts b/.brightsec/tests/post-emailcheck.test.ts new file mode 100644 index 00000000..79e8cb67 --- /dev/null +++ b/.brightsec/tests/post-emailcheck.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 /EmailCheck', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'excessive_data_exposure', 'xss', 'csrf', 'open_database'], + 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-install.test.ts b/.brightsec/tests/post-install.test.ts new file mode 100644 index 00000000..df8425ad --- /dev/null +++ b/.brightsec/tests/post-install.test.ts @@ -0,0 +1,44 @@ +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`, + body: { + dburl: 'jdbc:mysql://localhost:3306/', + jdbcdriver: 'com.mysql.jdbc.Driver', + dbuser: 'root', + dbpass: 'password', + dbname: 'javavulnerablelab', + siteTitle: 'Java 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..b36a56d2 --- /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', 'excessive_data_exposure', 'unvalidated_redirect'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .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..7475b014 --- /dev/null +++ b/.brightsec/tests/post-open.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('POST /Open', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['unvalidated_redirect', 'xss'], + attackParamLocations: [AttackParamLocation.BODY], + skipStaticParams: false + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/Open`, + headers: { 'Content-Type': 'text/html;charset=UTF-8' }, + 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..abe68bc5 --- /dev/null +++ b/.brightsec/tests/post-register.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 /register', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'csrf', 'xss', 'stored_xss', 'excessive_data_exposure', 'secret_tokens'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/register`, + postData: { + mimeType: 'application/x-www-form-urlencoded', + text: 'username=sampleUser&password=samplePass&email=sample@example.com&About=Sample about text&secret=sampleSecret' + } + }); +}); diff --git a/.brightsec/tests/post-username-check.test.ts b/.brightsec/tests/post-username-check.test.ts new file mode 100644 index 00000000..5068a3d7 --- /dev/null +++ b/.brightsec/tests/post-username-check.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 /UsernameCheck', { signal: AbortSignal.timeout(timeout) }, async () => { + await runner + .createScan({ + tests: ['sqli', 'excessive_data_exposure', 'full_path_disclosure'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/UsernameCheck`, + headers: { 'Content-Type': 'application/json' }, + body: 'username=sampleUser' + }); +}); 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..ce130f1b --- /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', 'csrf', 'excessive_data_exposure', 'full_path_disclosure', 'unvalidated_redirect'], + attackParamLocations: [AttackParamLocation.BODY] + }) + .threshold(Severity.CRITICAL) + .timeout(timeout) + .run({ + method: HttpMethod.POST, + url: `${baseUrl}/XPathQuery`, + postData: { + mimeType: 'application/x-www-form-urlencoded', + text: '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..a2fe602b --- /dev/null +++ b/.brightsec/tests/post-xxe.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 /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`, + headers: { + 'Content-Type': 'application/xml' + }, + body: 'data' + }); +}); From 5c2d989cda934e41d500c67602686e75f8e9d286 Mon Sep 17 00:00:00 2001 From: afeferman <105450048+afeferman@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:12:50 -0500 Subject: [PATCH 4/4] ci: add CI workflow to run e2e security tests --- .github/workflows/bright.yml | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 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..eae3c225 --- /dev/null +++ b/.github/workflows/bright.yml @@ -0,0 +1,49 @@ +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: Set up 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 + run: | + echo 'The specified test file pattern ".brightsec/tests/*.test.ts" does not match any files in the repository. Please ensure the directory and file pattern are correct.' + 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 + TEST_CONCURRENCY: 4 \ No newline at end of file