Home

Awesome

SecTester SDK demo project for Broken Crystals

Table of contents

About this project

This is a demo project for the SecTester JS SDK framework, with some installation and usage examples. We recommend forking it and playing around, that’s what it’s for!

About SecTester

Bright is a developer-first Dynamic Application Security Testing (DAST) platform.

SecTester is a new open-source tool that integrates our enterprise-grade scan engine directly into your unit tests, integration tests, e2e tests or whichever automated tests you use.

With SecTester you can:

Trying out Bright’s SecTester is free 💸, so let’s get started!

⚠️ Disclaimer

The SecTester project is currently in beta as an early-access tool. We are looking for your feedback to make it the best possible solution for developers, aimed to be used as part of your team’s SDLC. We apologize if not everything will work smoothly from the start, but we are constantly improving!

Thank you! We appreciate your help and feedback!

Initial setup

Fork this repo

  1. Press the ‘fork’ button to make a copy of this repo in your own GitHub account

Get a Bright API key

  1. Register for a free account at Bright’s signup page
  2. Skip the quickstart wizard and go directly to User API key creation
  3. Create a Bright API key (check out our docs on how to create a user key)
  4. Save the Bright API key
    1. For this demo, we recommend using your Github repository secrets feature to store the key, accessible via the Settings > Security > Secrets > Actions configuration. We use the ENV variable called BRIGHT_TOKEN in our examples
    2. If you don’t use that option, make sure you save the key in a secure location. You will need to access it later on in the project but will not be able to view it again.
    3. More info on how to use ENV vars in Github actions

⚠️ Make sure your API key is saved in a location where you can retrieve it later! You will need it in these next steps!

Running in a CI pipeline

Once you create your own unit tests using SecTester, you can run it in any CI you choose. To simplify things in this demo we provide an example using the GitHub Actions CI.

GitHub Actions setup

  1. After forking the main repo, you will need to go to the Actions tab and click on the I understand my workflows, go ahead an enable them button. This will enable you to run the pre-configured CI example from this demo
  2. Next, add your BRIGHT_TOKEN to the GitHub Actions environment variables via the Secret Variables, accessible via the Settings > Security > Secrets > Actions configuration. Please make sure that the ENV variable is called BRIGHT_TOKEN, as that is what we use in our example

You can integrate SecTester into any CI you use. For that, you will need to add the BRIGHT_TOKEN ENV vars to your CI.

Using with GitHub Actions

The following is a minimal configuration for starting SecTester, which is running on the latest LTS version of Node and triggers only when a pull request is opened.

As you can see, the workflow creates a test job that runs SecTester only after all dependencies are installed:

.github/workflows/auto-test.yml

name: CI / Automated testing

on:
  pull_request:
    branches:
      - '**'

jobs:
  test:
    name: Testing
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 'lts/*'
          cache: npm

      - name: Install deps in quiet mode
        run: npm ci -q

      - name: Run SecTester
        run: npm run test:sec
        env:
          BRIGHT_TOKEN: ${{ secrets.BRIGHT_TOKEN }}
          BRIGHT_HOSTNAME: app.neuralegion.com
          BROKEN_CRYSTALS_URL: http://localhost:3000

The whole list of environment variables to start the demo application is described in .env.example file. However, you can start with the minimal three VARs, as shown in the example above, ignoring others.

In the example above, the CI flow is set to run all the SecTester tests, but if we would like to run a single test we can update the workflow by specifying a regex pattern that matches a test name, as follows:

- name: Run SecTester
  run: npm run test:sec -- -t 'testimonials'
  env:
    BRIGHT_TOKEN: ${{ secrets.BRIGHT_TOKEN }}
    BRIGHT_HOSTNAME: app.neuralegion.com
    BROKEN_CRYSTALS_URL: http://localhost:3000

Furthermore, to trigger SecTester on demand, you can use Manual Triggers for GitHub Actions as follows:

name: CI / Automated testing

on:
  workflow_dispatch:

You will then see a Run workflow button on the Actions tab, enabling you to easily trigger a run.

You can also use the repository_dispatch event to have control on when to start a workflow by making an HTTP request:

name: CI / Automated testing

on:
  repository_dispatch:
    types: [sectester]

To trigger a workflow, issue the following command using a Personal Access Tokens:

$ curl -H "Accept: application/vnd.github.everest-preview+json" \
  -H "Authorization: token ${GITHUB_TOKEN}" \
  https://api.github.com/repos/[org-name-or-username]/[repository]/dispatches \
  -d '{ "event_type": "sectester" }'

Finally, you may find useful to trigger a workflow at a scheduled time using the schedule event.

This example triggers the workflow every Sunday at 03:30 UTC:

name: CI / Automated testing

on:
  schedule:
    - cron: '30 3 * * sun'

* is a special character in YAML, so you have to quote this string

Once the CI runs and a SecTester test fails, this means a vulnerability was found and the output will look as follows:

FAIL  test/sec/render.e2e-spec.ts (143.608 s)
  /api
    POST /render
      ✕ should not contain possibility to server-side code execution (282227 ms)

  ● /api › POST /render › should not contain possibility to server-side code execution

    IssueFound: Target is vulnerable

    Issue in Bright UI:   https://development.playground.neuralegion.com/scans/bH2vd1CfxHtKjLNLxw94oj/issues/w68hhVa1We95UNZx4FmefT
    Name:                 SSTI - Server Side Template Injection
    Severity:             High
    Remediation:
    To protect against this type of attack, you shall sanitize input before passing to template directive and create a safe environment.
    Details:
    SSTI (Server Side Template Injection) is vulnerability that is exploited by malformed user input which allows embedding user input into different application without proper sanitization. The highest possibility of this vulnerability is to create a path for remote code execution capabilities and be exploited by malicious subjects. Identification of this vulnerability is possible with observation of the invalid syntax in the input with an error messages displayed after creating a response.
    References:
     ● https://www.owasp.org/index.php/Server-Side_Includes_(SSI)_Injection
     ● https://www.owasp.org/images/7/7e/Owasp_SSTI_final.pdf

      at SecScan.assert (../packages/runner/src/lib/SecScan.ts:59:13)
          at runMicrotasks (<anonymous>)
      at SecScan.run (../packages/runner/src/lib/SecScan.ts:37:7)
      at Object.<anonymous> (sec/render.e2e-spec.ts:21:7)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        143.677 s
Ran all test suites matching /render.e2e-spec.ts/i.

Running on a local machine

Alternatively to running the tests in the CI, you can run the tests locally on your machine, this is helpful for example if you want to run the tests while you’re working on your code and not consume CI resources.

Local setup

To begin, clone your forked repo of this project onto your local machine.

To do that, navigate to your local folder of choice and clone the project using either SSH or HTTP, for example using the following command:

$ git clone git@github.com:my-github-handle/sectester-js-demo-broken-crystals.git

In the same local folder, using your command line, install the dependencies:

$ npm ci

The whole list of required variables to start the demo application is described in .env.example file. The template for this .env file is available in the root folder.

You can easily create a .env file from the template by issuing the following command:

$ cp .env.example .env

Once this template is done copying over (should be instantaneous), navigate to your .env file, and paste your Bright API key as the value of the BRIGHT_TOKEN variable.

BRIGHT_TOKEN = <your_API_key_here>

Explore the demo application

Once the initial setup is complete, you have to build and run services with Docker. Start Docker, and issue the command as follows:

$ docker compose up -d

While having the application running, open a browser and type http://localhost:3000/swagger, and hit enter.

You should see the Swagger UI page for that application that allows you to test the RESTFul CRUD API, like in the following screenshot:

Swagger UI

To explore the Swagger UI:

Swagger UI

Run tests locally on the demo application

You can start tests with SecTester against these endpoints as follows (make sure you use a new terminal window, as the original is still running the API for us!)

$ npm run test:sec

You will find tests written with SecTester in the ./test/sec folder.

This can take a few minutes while the Bright engine spins up. After a moment, you should see the result:

 FAIL  test/sec/render.e2e-spec.ts (143.608 s)
  /api
    POST /render
      ✕ should not contain possibility to server-side code execution (282227 ms)

  ● /api › POST /render › should not contain possibility to server-side code execution

    IssueFound: Target is vulnerable

    Issue in Bright UI:   https://development.playground.neuralegion.com/scans/bH2vd1CfxHtKjLNLxw94oj/issues/w68hhVa1We95UNZx4FmefT
    Name:                 SSTI - Server Side Template Injection
    Severity:             High
    Remediation:
    To protect against this type of attack, you shall sanitize input before passing to template directive and create a safe environment.
    Details:
    SSTI (Server Side Template Injection) is vulnerability that is exploited by malformed user input which allows embedding user input into different application without proper sanitization. The highest possibility of this vulnerability is to create a path for remote code execution capabilities and be exploited by malicious subjects. Identification of this vulnerability is possible with observation of the invalid syntax in the input with an error messages displayed after creating a response.
    References:
     ● https://www.owasp.org/index.php/Server-Side_Includes_(SSI)_Injection
     ● https://www.owasp.org/images/7/7e/Owasp_SSTI_final.pdf

      at SecScan.assert (../packages/runner/src/lib/SecScan.ts:59:13)
          at runMicrotasks (<anonymous>)
      at SecScan.run (../packages/runner/src/lib/SecScan.ts:37:7)
      at Object.<anonymous> (sec/render.e2e-spec.ts:21:7)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        143.677 s
Ran all test suites matching /render.e2e-spec.ts/i.

A full configuration example

Let’s look under the hood to see how this all works. In the following example, we will test the app we just set up for any instances of Server Side Template Injection. Jest is provided as the testing framework, that provides assert functions and test-double utilities that help with mocking, spying, etc.

The @sectester/runner package provides a set of utilities that allows scanning the demo application for vulnerabilities. Let's expand the previous example using the built-in SecRunner class:

test/sec/render.e2e-spec.ts

let runner!: SecRunner;

// ...

beforeEach(async () => {
  runner = new SecRunner({ hostname: 'app.neuralegion.com' });

  await runner.init();
});

afterEach(() => runner.clear());

To set up a runner, create a SecRunner instance on the top of the file, passing a configuration as follows:

import { SecRunner } from '@sectester/runner';

const runner = new SecRunner({ hostname: 'app.neuralegion.com' });

After that, you have to initialize a SecRunner instance:

await runner.init();

The runner is now ready to perform your tests. To start scanning your endpoint, first, you have to create a SecScan instance. We do this with runner.createScan as shown in the example below.

Now, you will write and run your first unit test!

Let's verify the POST /api/render endpoint for SSTI (read more in our docs):

describe('POST /render', () => {
  it('should not contain possibility to server-side code execution', async () => {
    await runner
      .createScan({
        tests: [TestType.SSTI]
      })
      .run({
        method: 'POST',
        headers: {
          'accept': 'application/json, text/plain, */*',
          'origin': process.env.BROKEN_CRYSTALS_URL!,
          'content-type': 'text/plain'
        },
        body: `Some text`,
        url: `${process.env.BROKEN_CRYSTALS_URL}/api/render`
      });
  });
});

This will raise an exception when the test fails, with remediation information and a deeper explanation of SSTI, right in your command line!

Let's look at another test for the GET /api/spawn endpoint, this time for OSI (read more in our docs).

describe('GET /spawn', () => {
  it('should not be able to execute shell commands on the host operating system', async () => {
    await runner
      .createScan({
        tests: [TestType.OSI]
      })
      .run({
        method: 'GET',
        url: `${process.env.BROKEN_CRYSTALS_URL}/api/spawn?command=pwd`
      });
  });
});

As you can see, writing a new test for OSI follows the same pattern as SSTI.

By default, each found issue will cause the scan to stop. To control this behavior you can set a severity threshold using the threshold method. Since SSTI (Server Side Template Injection) is considered to be high severity issue, we can pass Severity.HIGH for stricter checks:

scan.threshold(Severity.HIGH);

To avoid long-running test, you can specify a timeout, to say how long to wait before aborting it:

scan.timeout(300000);

To make sure that Jest won't abort tests early, you should align a test timeout with a scan timeout as follows:

jest.setTimeout(300000);

To clarify an attack surface and speed up the test, we suggest making clear where to discover the parameters according to the source code.

src/app.controller.ts

@Controller('/api')
@ApiTags('App controller')
export class AppController {
  constructor(private readonly usersService: UsersService) {}

  @Post('render')
  @ApiProduces('text/plain')
  @ApiConsumes('text/plain')
  @ApiOperation({
    description: SWAGGER_DESC_RENDER_REQUEST
  })
  @ApiBody({ description: 'Write your text here' })
  @ApiCreatedResponse({
    description: 'Rendered result'
  })
  async renderTemplate(@Body() raw): Promise<string> {
    if (typeof raw === 'string' || Buffer.isBuffer(raw)) {
      const text = raw.toString().trim();
      const res = dotT.compile(text)();
      this.logger.debug(`Rendered template: ${res}`);
      return res;
    }
  }

  // ...
}

For the example above, it should look like this:

const scan = runner.createScan({
  tests: [TestType.SSTI],
  attackParamLocations: [AttackParamLocation.BODY]
});

Finally, the test should look like this:

it('should not contain possibility to server-side code execution', async () => {
  await runner
    .createScan({
      tests: [TestType.SSTI],
      attackParamLocations: [AttackParamLocation.BODY]
    })
    .timeout(300000)
    .threshold(Severity.HIGH)
    .run({
      method: 'POST',
      headers: {
        'accept': 'application/json, text/plain, */*',
        'origin': process.env.BROKEN_CRYSTALS_URL!,
        'content-type': 'text/plain'
      },
      body: `Some text`,
      url: `${process.env.BROKEN_CRYSTALS_URL}/api/render`
    });
});

Here is a completed test/sec/render.e2e-spec.ts file with all the tests and configuration set up.

import { SecRunner } from '@sectester/runner';
import { AttackParamLocation, TestType } from '@sectester/scan';

describe('/api', () => {
  const timeout = 300000;
  jest.setTimeout(timeout);
  let runner!: SecRunner;

  beforeEach(() => {
    runner = new SecRunner({ hostname: process.env.BRIGHT_HOSTNAME! });

    return runner.init();
  });

  afterEach(() => runner.clear());

  describe('POST /render', () => {
    it('should not contain possibility to server-side code execution', async () => {
      await runner
        .createScan({
          tests: [TestType.SSTI],
          name: expect.getState().currentTestName,
          attackParamLocations: [AttackParamLocation.BODY]
        })
        .timeout(timeout)
        .threshold(Severity.HIGH)
        .run({
          method: 'POST',
          headers: {
            'accept': 'application/json, text/plain, */*',
            'origin': process.env.BROKEN_CRYSTALS_URL!,
            'content-type': 'text/plain'
          },
          body: `Some text`,
          url: `${process.env.BROKEN_CRYSTALS_URL}/api/render`
        });
    });
  });
});

Full documentation can be found in the @sectester/runner README.

Recommended tests

Test nameDescriptionUsage in SecTesterDetectable vulnerabilities
Amazon S3 Bucket TakeoverTests for S3 buckets that no longer exist to prevent data breaches and malware distributionamazon_s3_takeover- Amazon S3 Bucket Takeover
Broken JWT AuthenticationTests for secure implementation of JSON Web Token (JWT) in the applicationjwt- Broken JWT Authentication
Broken SAML AuthenticationTests for secure implementation of SAML authentication in the applicationbroken_saml_auth- Broken SAML Authentication
Brute Force LoginTests for availability of commonly used credentialsbrute_force_login- Brute Force Login
Business Constraint BypassTests if the limitation of number of retrievable items via an API call is configured properlybusiness_constraint_bypass- Business Constraint Bypass
Client-Side XSS <br>(DOM Cross-Site Scripting)Tests if various application DOM parameters are vulnerable to JavaScript injectionsdom_xss- Reflective Cross-site scripting (rXSS)<br> <br> - Persistent Cross-site scripting (pXSS)
Common Files ExposureTests if common files that should not be accessible are accessiblecommon_files- Exposed Common File
Cookie Security CheckTests if the application uses and implements cookies with secure attributescookie_security- Sensitive Cookie in HTTPS Session Without Secure Attribute<br> <br> - Sensitive Cookie Without HttpOnly Flag<br> <br>- Sensitive Cookie Weak Session ID
Cross-Site Request Forgery (CSRF)Tests application forms for vulnerable cross-site filling and submittingcsrf- Unauthorized Cross-Site Request Forgery (CSRF)<br> <br> - Authorized Cross-Site Request Forgery (CSRF)
Cross-Site Scripting (XSS)Tests if various application parameters are vulnerable to JavaScript injectionsxss- Reflective Cross-Site Scripting (rXSS)<br> <br> - Persistent Cross-Site Scripting (pXSS)
Common Vulnerability Exposure (CVEs)Tests for known third-party common vulnerability exposurescve_test- Common Vulnerability Exposure
Default Login LocationTests if login form location in the target application is easy to guess and accessibledefault_login_location- Default Login Location
Directory ListingTests if server-side directory listing is possibledirectory_listing- Directory Listing
Email Header InjectionTests if it is possible to send emails to other addresses through the target application mailing server, which can lead to spam and phishingemail_injection- Email Header Injection
Exposed AWS S3 Buckets Details <br>(Open Buckets)Tests if exposed AWS S3 links lead to anonymous read access to the bucketopen_buckets- Exposed AWS S3 Buckets Details
Exposed Database Details <br>(Open Database)Tests if exposed database connection strings are open to public connectionsopen_buckets- Exposed Database Details<br> <br> - Exposed Database Connection String
Full Path Disclosure (FPD)Tests if various application parameters are vulnerable to exposure of errors that include full webroot pathfull_path_disclosure- Full Path Disclosure
Headers Security CheckTests for proper Security Headers configurationheader_security- Misconfigured Security Headers<br> <br> - Missing Security Headers<br> <br>- Insecure Content Secure Policy Configuration
HTML InjectionTests if various application parameters are vulnerable to HTML injectionhtml_injection- HTML Injection
Improper Assets ManagementTests if older or development versions of API endpoints are exposed and can be used to get unauthorized access to data and privilegesimproper_asset_management- Improper Assets Management
Insecure HTTP Method <br>(HTTP Method Fuzzer)Tests enumeration of possible HTTP methods for vulnerabilitieshttp_method_fuzzing- Insecure HTTP Method
Insecure TLS ConfigurationTests SSL/TLS ciphers and configurations for vulnerabilitiesinsecure_tls_configuration- Insecure TLS Configuration
Known JavaScript Vulnerabilities <br>(JavaScript Vulnerabilities Scanning)Tests for known JavaScript component vulnerabilitiesretire_js- JavaScript Component with Known Vulnerabilities
Known WordPress Vulnerabilities <br>(WordPress Scan)Tests for known WordPress vulnerabilities and tries to enumerate a list of userswordpress- WordPress Component with Known Vulnerabilities
LDAP InjectionTests if various application parameters are vulnerable to unauthorized LDAP accessldapi- LDAP Injection<br> <br> - LDAP Error
Local File Inclusion (LFI)Tests if various application parameters are vulnerable to loading of unauthorized local system resourceslfi- Local File Inclusion (LFI)
Mass AssignmentTests if it is possible to create requests with additional parameters to gain privilege escalationmass_assignment- Mass Assignment
OS Command InjectionTests if various application parameters are vulnerable to Operation System (OS) commands injectionosi- OS Command Injection
Prototype PollutionTests if it is possible to inject properties into existing JavaScript objectsproto_pollution- Prototype Pollution
Remote File Inclusion (RFI)Tests if various application parameters are vulnerable to loading of unauthorized remote system resourcesrfi- Remote File Inclusion (RFI)
Secret Tokens LeakTests for exposure of secret API tokens or keys in the target applicationsecret_tokens- Secret Tokens Leak
Server Side Template Injection (SSTI)Tests if various application parameters are vulnerable to server-side code executionssti- Server Side Template Injection (SSTI)
Server Side Request Forgery (SSRF)Tests if various application parameters are vulnerable to internal resources accessssrf- Server Side Request Forgery (SSRF)
SQL Injection (SQLI)SQL Injection tests vulnerable parameters for SQL database accesssqli- SQL Injection: Blind Boolean Based<br> <br> - SQL Injection: Blind Time Based<br> <br> - SQL Injection<br> <br> - SQL Database Error Message in Response
Unrestricted File UploadTests if file upload mechanisms are validated properly and denies upload of malicious contentfile_upload- Unrestricted File Upload
Unsafe Date Range <br>(Date Manipulation)Tests if date ranges are set and validated properlydate_manipulation- Unsafe Date Range
Unsafe Redirect <br>(Unvalidated Redirect)Tests if various application parameters are vulnerable to injection of a malicious link which can redirect a user without validationunvalidated_redirect- Unsafe Redirect
User ID EnumerationTests if it is possible to collect valid user ID data by interacting with the target applicationid_enumeration- Enumerable Integer-Based ID
Version Control System Data LeakTests if it is possible to access Version Control System (VCS) resourcesversion_control_systems- Version Control System Data Leak
XML External Entity InjectionTests if various XML parameters are vulnerable to XML parsing of unauthorized external entitiesxxe- XML External Entity Injection

Documentation & Help

Contributing

Please read contributing guidelines here.

<a href="https://github.com/NeuraLegion/sectester-js-demo-broken-crystals/graphs/contributors"> <img src="https://contrib.rocks/image?repo=NeuraLegion/sectester-js-demo-broken-crystals"/> </a>

License

Copyright © 2022 Bright Security.

This project is licensed under the MIT License - see the LICENSE file for details.