Skip to content
Open
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
Empty file added .brightsec/.gitkeep
Empty file.
47 changes: 47 additions & 0 deletions .brightsec/tests/post-users.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { test, before, after } from 'node:test';
import { SecRunner } from '@sectester/runner';
import { AttackParamLocation, HttpMethod } from '@sectester/scan';

const timeout = 40 * 60 * 1000;
const baseUrl = process.env.BRIGHT_TARGET_URL!;

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());

test('POST /users', { signal: AbortSignal.timeout(timeout) }, async () => {
await runner
.createScan({
tests: ['sqli'],
attackParamLocations: [AttackParamLocation.BODY],
starMetadata: {
code_source: 'NeuraLegion/ruby-example-app:master',
databases: ['PostgreSQL'],
user_roles: ['admin']
},
poolSize: +process.env.SECTESTER_SCAN_POOL_SIZE || undefined
})
.setFailFast(false)
.timeout(timeout)
.run({
method: HttpMethod.POST,
url: `${baseUrl}/users`,
body: {
email: 'example@example.com',
password: 'password123',
password_digest: '$2a$12$KIXQ1Y1rZ1u1Q1u1Q1u1QO',
admin: false
},
headers: { 'Content-Type': 'application/json' },
auth: process.env.BRIGHT_AUTH_ID
});
});
79 changes: 43 additions & 36 deletions .github/workflows/ci.yml → .github/workflows/bright.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
name: ci
name: Bright

on:
push:
branches:
- main
pull_request:
branches:
- '**'

permissions:
checks: write
contents: read
id-token: write

jobs:
test:
Expand Down Expand Up @@ -80,45 +84,48 @@ jobs:
- name: Install gems
run: bundle install

- name: Wait for postgres
run: |
for i in $(seq 1 30); do
pg_isready -h "$PGHOST" -p "$PGPORT" -d "$PGDATABASE" && break
sleep 2
done
bundle exec ruby -e "require 'pg'; PG.connect(host: ENV['PGHOST'], port: ENV['PGPORT'].to_i, dbname: ENV['PGDATABASE'], user: ENV['PGUSER'], password: ENV['PGPASSWORD']);"

- name: Setup database
run: bundle exec rake db:create db:migrate db:seed

- name: Start server
env:
DATABASE_URL: postgres://postgres:postgres@postgres:5432/blog_development
PGDATABASE: blog_development
PGHOST: postgres
PGPASSWORD: postgres
PGPORT: 5432
PGUSER: postgres
run: |
bundle exec rails server -b 0.0.0.0 -p 3000 >rails.log 2>&1 &

- name: Wait for server readiness
run: |
READY=0
for i in $(seq 1 30); do
echo "Attempt ${i}/30: checking http://localhost:3000"
if curl -sSfL http://localhost:3000 >/dev/null; then
echo "Server responded successfully on attempt ${i}."
READY=1
break
fi
echo "Server still starting up, waiting 2s before retry."
sleep 2
done
if [ "$READY" -ne 1 ]; then
echo "Server failed to start"
tail -n 200 rails.log || true
exit 1
fi
echo "Server is ready; last 200 lines of rails.log:"
tail -n 200 rails.log || true

- name: Check homepage
i=1; while [ "$i" -le 30 ]; do curl -sS -o /dev/null http://127.0.0.1:3000 && exit 0; sleep 5; i=$((i + 1)); done; exit 1

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22.x

- name: Install SecTesterJS dependencies
run: |
npm i --save=false --prefix .brightsec @sectester/core@0.49.0 @sectester/repeater@0.49.0 @sectester/scan@0.49.0 @sectester/runner@0.49.0 @sectester/reporter@0.49.0

- name: Authenticate with Bright
uses: ./.github/workflows/composite/configure-bright-credentials
with:
BRIGHT_HOSTNAME: development.playground.brightsec.com
BRIGHT_PROJECT_ID: 5naKKxNc3e4Akp1GuEdmiK
BRIGHT_TOKEN: ${{ secrets.BRIGHT_TOKEN }}

- name: Run security tests
env:
BRIGHT_HOSTNAME: development.playground.brightsec.com
BRIGHT_PROJECT_ID: 5naKKxNc3e4Akp1GuEdmiK
BRIGHT_AUTH_ID: m8XBzLrw8pWSjHQDhKQgCr
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRIGHT_TOKEN: ${{ env.BRIGHT_TOKEN }}
BRIGHT_TARGET_URL: http://127.0.0.1:3000
SECTESTER_SCAN_POOL_SIZE: ${{ vars.SECTESTER_SCAN_POOL_SIZE }}
run: |
if ! curl -sSfL http://localhost:3000 | grep -F "Home"; then
echo "Homepage check failed"
exit 1
fi
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: 'Configure BrightSec credentials'

inputs:
BRIGHT_HOSTNAME:
description: 'Hostname for the BrightSec environment'
required: true
BRIGHT_PROJECT_ID:
description: 'Project ID for BrightSec'
required: true
BRIGHT_TOKEN:
description: 'Pre-configured token'
required: false

runs:
using: 'composite'
steps:
- id: configure_env_from_input
name: 'Set existing token in env'
shell: bash
if: ${{ inputs.BRIGHT_TOKEN != '' }}
env:
BRIGHT_TOKEN: ${{ inputs.BRIGHT_TOKEN }}
run: |
echo "BRIGHT_TOKEN=${BRIGHT_TOKEN}" >> $GITHUB_ENV

- id: configure_bright_credentials_through_oidc
name: 'Exchange OIDC credentials for Bright token'
shell: bash
if: ${{ inputs.BRIGHT_TOKEN == '' }}
env:
BRIGHT_HOSTNAME: ${{ inputs.BRIGHT_HOSTNAME }}
BRIGHT_PROJECT_ID: ${{ inputs.BRIGHT_PROJECT_ID }}
run: |
# Retrieve OIDC token from GitHub
OIDC_TOKEN=$(curl -sS -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"${ACTIONS_ID_TOKEN_REQUEST_URL}" | jq -r '.value')

# Post the token to BrightSec
RESPONSE=$(curl -s -X POST "https://${BRIGHT_HOSTNAME}/api/v1/projects/${BRIGHT_PROJECT_ID}/api-keys/oidc" \
-H "Content-Type: application/json" \
-d "{\"token\": \"${OIDC_TOKEN}\"}")

if ! echo "$RESPONSE" | jq -e . > /dev/null 2>&1; then
echo "Error: $RESPONSE" 1>&2
exit 1
fi

# Extract the pureKey
PURE_KEY=$(echo "$RESPONSE" | jq -r '.pureKey')

# Mask and store in environment
echo "::add-mask::$PURE_KEY"
echo "BRIGHT_TOKEN=$PURE_KEY" >> $GITHUB_ENV
2 changes: 1 addition & 1 deletion app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,4 @@ def set_user
def user_params
params.require(:user).permit(:email, :password, :password_digest, :admin)
end
end
end
6 changes: 2 additions & 4 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ class User < ActiveRecord::Base
validates :email, presence: true , length: {minimum: 2}
validates :password, presence: true, length: {minimum: 2}

def has_secure_password
true
end
end
has_secure_password
end
Loading