Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e5cfbab
chore(ci): add GitHub Actions workflow for Rails smoke test
pmstss Jan 9, 2026
890c6d1
chore(ci): run test job in container mode for legacy Ruby compatibility
pmstss Jan 9, 2026
5408c9b
chore(ci): switch job image to buildpack-deps:buster for native build…
pmstss Jan 9, 2026
0f619cc
chore(ci): build Ruby 2.3.3 from source via ruby-build
pmstss Jan 9, 2026
4059255
chore(ci): use Debian archive mirrors for EOL buster apt repos
pmstss Jan 9, 2026
b4a6ba1
chore(ci): fix Postgres host and readiness checks for container-mode …
pmstss Jan 9, 2026
f821f91
chore(ci): add install postgresql-client
pmstss Feb 16, 2026
d6ab332
chore(ci): set bash as explicit default shell
pmstss Feb 16, 2026
6cd4ce4
chore: initialize workflow
tssbox Feb 17, 2026
a2bac58
ci: temporarily disable workflows while addressing security issues
tssbox Feb 17, 2026
5829ae2
test: add auto-generated e2e security tests
tssbox Feb 17, 2026
bc27d62
ci: add CI workflow to run e2e security tests
tssbox Feb 17, 2026
29bc1d8
test: remove completed test files that are no longer relevant
tssbox Feb 17, 2026
d850949
test: optimize security tests to focus on specific vulnerabilities
tssbox Feb 17, 2026
4803cf2
fix: apply automated fixes for detected vulnerabilities
tssbox Feb 17, 2026
8d5a129
test: remove completed test files that are no longer relevant
tssbox Feb 17, 2026
1ec41d6
test: optimize security tests to focus on specific vulnerabilities
tssbox Feb 17, 2026
e36a3d9
fix: apply automated fixes for detected vulnerabilities
tssbox Feb 17, 2026
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.
39 changes: 39 additions & 0 deletions .brightsec/tests/get-posts-search.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
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('GET /posts/search?search_term=example', { signal: AbortSignal.timeout(timeout) }, async () => {
await runner
.createScan({
tests: ['csrf'],
attackParamLocations: [AttackParamLocation.QUERY],
starMetadata: {
code_source: 'NeuraLegion/ruby-example-app:chore/ci',
databases: ['PostgreSQL'],
user_roles: []
},
poolSize: +process.env.SECTESTER_SCAN_POOL_SIZE || undefined
})
.setFailFast(false)
.timeout(timeout)
.run({
method: HttpMethod.GET,
url: `${baseUrl}/posts/search?search_term=example`
});
});
131 changes: 131 additions & 0 deletions .github/workflows/bright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: Bright

on:
pull_request:
branches:
- '**'

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

jobs:
test:
runs-on: ubuntu-latest
container:
image: buildpack-deps:buster
defaults:
run:
shell: bash
services:
postgres:
image: postgres:9.6
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: blog_development
ports:
- 5432:5432
options: >-
--name postgres
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 10
env:
PGHOST: postgres
PGPORT: 5432
PGUSER: postgres
PGPASSWORD: postgres
PGDATABASE: blog_development
DATABASE_URL: postgres://postgres:postgres@postgres:5432/blog_development
steps:
- uses: actions/checkout@v4

- name: Point apt to Debian archive
run: |
sed -i 's|deb.debian.org/debian|archive.debian.org/debian|g' /etc/apt/sources.list
sed -i 's|security.debian.org/debian-security|archive.debian.org/debian-security|g' /etc/apt/sources.list
sed -i '/buster-updates/d' /etc/apt/sources.list

- name: Install system deps
run: |
apt-get -o Acquire::Check-Valid-Until=false -o Acquire::AllowInsecureRepositories=true update
apt-get -o Acquire::Check-Valid-Until=false -o Acquire::AllowInsecureRepositories=true install -y --no-install-recommends \
build-essential \
libffi-dev \
libgdbm-dev \
libpq-dev \
libreadline-dev \
libssl-dev \
libxml2-dev \
libxslt1-dev \
postgresql-client \
nodejs \
libyaml-dev \
zlib1g-dev

- name: Install Ruby 2.3.3
env:
RUBY_PREFIX: /opt/ruby-2.3.3
run: |
git clone --depth=1 https://github.com/rbenv/ruby-build.git /tmp/ruby-build
/tmp/ruby-build/install.sh
ruby-build 2.3.3 "$RUBY_PREFIX"
echo "$RUBY_PREFIX/bin" >> "$GITHUB_PATH"

- name: Install bundler
run: gem install bundler -v 1.10.6

- name: Configure bundler for nokogiri
run: bundle config build.nokogiri --use-system-libraries

- name: Install gems
run: bundle install

- 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: |
for i in {1..30}; do curl -sS -o /dev/null http://127.0.0.1:3000 && exit 0 || sleep 5; 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: dhSq9jPULmyZnndTYVtd6o
BRIGHT_TOKEN: ${{ secrets.BRIGHT_TOKEN }}

- name: Run security tests
env:
BRIGHT_HOSTNAME: development.playground.brightsec.com
BRIGHT_PROJECT_ID: dhSq9jPULmyZnndTYVtd6o
BRIGHT_AUTH_ID: ${{ vars.BRIGHT_AUTH_ID }}
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: |
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
7 changes: 4 additions & 3 deletions app/controllers/posts_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class PostsController < ApplicationController
before_action :confirm_logged_in
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :verify_authenticity_token, only: [:search]

# GET /posts
# GET /posts.json
Expand Down Expand Up @@ -29,9 +30,9 @@ def recent
def search
# http://localhost:3000/posts/search?search_term=t%25%27%3Bselect%20*%20from%20users%3B%20--
if current_user.admin?
@search_results = Post.where("posts.content::text LIKE '%#{params[:search_term]}%'")
@search_results = Post.where("posts.content::text LIKE ?", "%#{params[:search_term]}%")
else
@search_results = Post.where("posts.content::text LIKE '%#{params[:search_term]}%' AND posts.public=true")
@search_results = Post.where("posts.content::text LIKE ? AND posts.public=true", "%#{params[:search_term]}%")
end
end

Expand Down Expand Up @@ -99,4 +100,4 @@ def set_post
def post_params
params.require(:post).permit(:title, :content, :public)
end
end
end
4 changes: 2 additions & 2 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@ def set_user

# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:email, :password, :password_digest, :admin)
params.require(:user).permit(:email, :password, :admin)
end
end
end
Loading