Skip to content
Merged
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
10 changes: 7 additions & 3 deletions .github/workflows/deploy-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ jobs:
bundler-cache: true

- name: Install dependencies
run: |
bundle config set --local without 'development'
bundle install --jobs 4 --retry 3
run: bundle install --jobs 4 --retry 3

- name: Setup database
env:
Expand All @@ -109,13 +107,16 @@ jobs:
run: |
bundle exec rails db:create
bundle exec rails db:schema:load
bundle exec rails db:migrate

- name: Run RSpec tests
env:
RAILS_ENV: test
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/prostaff_test
REDIS_URL: redis://localhost:6379/0
COVERAGE: true
INTERNAL_JWT_SECRET: test-internal-secret-ci
PANDASCORE_API_KEY: test-pandascore-key-ci
run: |
bundle exec rspec --format documentation --format json --out rspec-results.json

Expand Down Expand Up @@ -144,6 +145,7 @@ jobs:
ref: ${{ github.event.inputs.version || github.ref }}

- name: Run Trivy vulnerability scanner
continue-on-error: true
uses: aquasecurity/trivy-action@22438a435773de8c97dc0958cc0b823c45b064ac # master
with:
scan-type: 'fs'
Expand All @@ -152,6 +154,8 @@ jobs:
output: 'trivy-results.sarif'

- name: Upload Trivy results
if: always()
continue-on-error: true
uses: github/codeql-action/upload-sarif@b5ebac6f4c00c8ccddb7cdcd45fdb248329f808a # v3
with:
sarif_file: 'trivy-results.sarif'
Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/deploy-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ jobs:
bundler-cache: true

- name: Install dependencies
run: |
bundle config set --local without 'development'
bundle install --jobs 4 --retry 3
run: bundle install --jobs 4 --retry 3

- name: Setup database
env:
Expand All @@ -68,13 +66,16 @@ jobs:
run: |
bundle exec rails db:create
bundle exec rails db:schema:load
bundle exec rails db:migrate

- name: Run RSpec tests
env:
RAILS_ENV: test
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/prostaff_test
REDIS_URL: redis://localhost:6379/0
COVERAGE: true
INTERNAL_JWT_SECRET: test-internal-secret-ci
PANDASCORE_API_KEY: test-pandascore-key-ci
run: bundle exec rspec --format documentation
continue-on-error: ${{ github.event.inputs.force_deploy == 'true' }}

Expand Down
144 changes: 144 additions & 0 deletions .pentest/BURP-SEED.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Seed do Burp: populando o mapa antes de testar

O `burp-seed.sh` dispara tráfego real contra os alvos locais **através do proxy do
Burp**, sem interceptar. O objetivo não é atacar nada: é deixar o **HTTP history** e o
**Site map** do Burp cheios de rotas, respostas e HTML de verdade, pra você (ou um agente)
inspecionar shapes de resposta e layout antes de decidir onde bater.

Existem dois gêmeos, com a mesma engine:

| Script | Alvo |
|-----------------------------------------|----------------------------------|
| `prostaff-api/.pentest/scripts/burp-seed.sh` | API Rails + analytics-hub + ArenaBR |
| `credscout/scripts/burp-seed.sh` | Front Vite + backend Spring (licita) |

O tutorial descreve o do prostaff; o do credscout é idêntico no fluxo, só muda os defaults
de alvo e o endpoint de login (veja [Variáveis](#variáveis-de-ambiente)).

## Pré-requisitos

1. **Burp aberto** com o Proxy listener ativo em `127.0.0.1:8080`
(Proxy > Proxy settings > Proxy listeners).
2. **Intercept OFF** (Proxy > Intercept > botão em "Intercept is off"). Com o intercept
ligado o script trava esperando você liberar cada request.
3. **Os serviços de pé.** O seed não sobe nada - ele só bate no que já está rodando.
- API: `cd prostaff-api && docker compose up -d` (porta `3333`)
- analytics-hub: `npm run dev` (porta `8888`)
- ArenaBR: `npm run dev` (porta `4000`) - opcional, desligue com `ARENA=""`
4. **`curl`** (sempre presente) e, opcional, **`python3`** - usado pra extrair token de JSON
aninhado. Sem ele, o script cai num fallback por `grep` (funciona pros casos comuns).

> Como os alvos locais são `http://`, **não precisa instalar o CA do Burp**. O CA só é
> necessário pra interceptar `https://` (staging/produção) sem erro de certificado.

## Rodando

```bash
cd prostaff-api/.pentest
./scripts/burp-seed.sh
```

Saída esperada (resumida):

```
>> prostaff burp-seed Proxy: http://127.0.0.1:8080
>> API: http://localhost:3333 Front: http://localhost:8888 Arena: http://localhost:4000
>> Log: .../snapshots/burp-seed-2026-08-24-090012.log

>> [auth] login em http://localhost:3333/api/v1/auth/login
token capturado (312 chars) - rotas autenticadas serao populadas

>> [1/4] API recon / health / docs (anonimo)
GET localhost:3333/up 200
...
>> [3/4] API resources (autenticado se houver token)
GET localhost:3333/api/v1/players 200
...
Seed: 34 ok | 6 4xx/5xx | 0 erro -> .../snapshots/burp-seed-...log
```

- **Verde** = 2xx/3xx (ok). **Amarelo** = 4xx/5xx (rota existe mas negou/erro).
**Vermelho** = `ERR`, sem conexão (proxy caiu ou serviço fora do ar).
- O rodapé conta `ok | 4xx/5xx | erro`. **`0 erro` é o sinal de que o proxy e os alvos
responderam** - um monte de amarelo é normal (rotas auth-gated batidas sem/antes do login).
- Tudo também vai pro log em `snapshots/` (já no `.gitignore`, nunca commitado).

## Como a autenticação é capturada

Este é o ponto que faz o Site map ficar **populado de verdade** e não só cheio de 401.
No login o script guarda corpo + headers + cookies e procura o token em três lugares, nesta
ordem:

1. **Body JSON** - busca recursiva por `access_token`, `accessToken`, `token`, `jwt`,
`id_token`, `authToken`, `bearer` (inclusive aninhado em `data: {...}`).
2. **Header de resposta** - `Authorization`, `X-Auth-Token`, `X-Access-Token`.
3. **Cookie de sessão** - o cookie jar fica ligado em todas as requests, então auth por
cookie (comum em Spring) flui sozinha.

Achou token → refaz os GETs de recurso com `Authorization: Bearer <token>`.
Só cookie → segue via jar. Nada → anônimo, e avisa que o JSON autenticado não vem.

O cookie jar vive num `mktemp -d` apagado no fim (`trap ... EXIT`) - **nenhuma credencial
toca o disco do repositório**.

## Populando as views autenticadas

Os defaults usam a credencial de teste (`test@prostaff.gg` / `Test123!@#`). Se ela logar,
os recursos já voltam 200. Pra usar outra conta, passe por env - **nunca** edite o arquivo:

```bash
SEED_EMAIL='owner@org.com' SEED_PASSWORD='...' ./scripts/burp-seed.sh
```

Se os recursos continuam 401 mesmo logando, cheque: a conta existe? A API criou o usuário
de teste (`rails runner scripts/create_test_user.rb`)? O endpoint de login é o esperado?

## Onde olhar no Burp

- **Proxy > HTTP history**: lista cronológica de tudo que o seed disparou, com request e
response completos. Filtre por host pra separar API de front.
- **Target > Site map**: árvore de rotas por host. É daqui que sai o "mapa" - clique numa
rota pra ver o response salvo. As páginas Next (`/login`, `/dashboard`) trazem o HTML/SSR
que serve de base pro layout; a API traz o JSON real dos recursos.
- Marque o host como **in-scope** (botão direito > Add to scope) pra focar o Site map.

## Variáveis de ambiente

| Var | O que é | Default (prostaff) | Default (credscout) |
|-------------------|---------------------------------|------------------------|-----------------------|
| `PROXY` | proxy do Burp | `http://127.0.0.1:8080`| `http://127.0.0.1:8080`|
| `API` | base do backend | `http://localhost:3333`| `http://localhost:9991`|
| `FRONT` | front principal (dev server) | `http://localhost:8888`| `http://localhost:3001`|
| `ARENA` | 2o front; `""` desliga | `http://localhost:4000`| (n/a) |
| `SEED_EMAIL`/`SEED_USER` | login de teste | `test@prostaff.gg` | `seed@example.test` |
| `SEED_PASSWORD`/`SEED_PASS` | senha de teste | `Test123!@#` | `seed` |
| `LOGIN_PATH` | endpoint de login (só credscout)| (fixo `/api/v1/auth/login`) | `/api/login` |
| `LOGIN_FIELD` | campo de usuário (só credscout) | (fixo `email`) | `username` |
| `TIMEOUT` | max-time por request (s) | `6` | `6` |

## Fluxo recomendado

1. Suba os serviços e abra o Burp (Intercept OFF).
2. Rode o seed - confirme `0 erro` no rodapé.
3. Marque o host como in-scope no Target.
4. Explore o Site map pra entender rotas e shapes.
5. Só então parta pros scripts de vetor (`03_jwt_attacks.sh` etc.) ou pro Repeater/Intruder,
já com o mapa na mão.

## Troubleshooting

| Sintoma | Causa provável / ação |
|--------------------------------------|-----------------------------------------------------------------|
| `!! proxy nao respondeu` | Burp fechado, listener fora do `:8080`, ou API/front down. |
| Tudo `ERR` (vermelho) | Proxy ok mas alvo fora do ar - suba o serviço. |
| Script trava/pendura | Intercept ligado - desligue em Proxy > Intercept. |
| `sem token/cookie` no login | Credencial inválida ou endpoint de login diferente - ajuste env.|
| Recursos todos `401` com token | Conta sem permissão, ou usuário de teste não criado na API. |
| Site map vazio no Burp | Host fora de escopo - Add to scope, ou olhe em HTTP history. |

## Segurança e higiene

- Credenciais **sempre por env**, nunca hardcoded no script versionado.
- Logs em `snapshots/` já estão no `.gitignore` - não commitar.
- O seed é **não-destrutivo**: só `GET`/`OPTIONS` mais o `POST` de login. Não cria, edita
nem apaga recurso. Rode à vontade quantas vezes quiser.
69 changes: 58 additions & 11 deletions .pentest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Lab de testes de segurança para a API ProStaff

- **API**: http://localhost:3333/api/v1
- **WebSocket**: ws://localhost:3333/cable
- **Stack**: Rails 7.2, PostgreSQL, Redis, JWT, Pundit, Rack::Attack, Meilisearch
- **Stack**: Rails 8.1, PostgreSQL, Redis, JWT, Pundit, Rack::Attack, Meilisearch

## Pre-requisitos

Expand All @@ -23,10 +23,16 @@ Credenciais de teste: `test@prostaff.gg` / `Test123!@#`
## Instalacao das ferramentas

```bash
./tools/install.sh # instala nuclei, pd-httpx, sqlmap, websocat
./tools/install.sh check # verifica status das ferramentas
./tools/install.sh # instala nuclei, pd-httpx, sqlmap, websocat, playwright
./tools/install.sh check # verifica status das ferramentas
./tools/install.sh playwright # so o playwright (chromium headless)
```

`playwright` (`tools/playwright/`) e usado pelos scripts `*-live.sh` do frontend para
capturar dados reais quando o target esta atras de um WAF/challenge JS (ex: Vercel
Security Checkpoint em prostaff.gg) que bloqueia qualquer request sem execucao de JS —
ver `front/check-waf-fingerprint.sh` e a nota no final da secao de Frontend.

## Scripts — API (scripts/)

| Script | Vetor | Destrutivo | Lento |
Expand Down Expand Up @@ -59,22 +65,60 @@ Credenciais de teste: `test@prostaff.gg` / `Test123!@#`
| 25_mass_assignment.sh | Strong Param: role, org_id, puuid, plan escalation | Nao | Nao |
| 26_tournaments_security.sh | Auth/IDOR/mass assignment no modulo tournaments | Nao | Nao |
| 27_supabase_direct_bypass.sh | Bypass da camada Rails via Supabase REST API direto | Nao | Nao |
| 28_prostaff_audit.sh | Bridge p/ ProStaff-Audit: findings + relatorio + CVSS | Nao | Sim |

## Scripts — Frontend (front/)

| Script | Vetor |
|---------------------------|-----------------------------------------------------------------------|
| check-security-headers.sh | Todos os 22 checkers CaramelScan no prostaff.gg |
| check-cookies.sh | Flags de cookie, SameSite, duracao, CSRF token |
| check-sri.sh | SRI em scripts/CSS externos, source maps, scripts inline |
| check-content-security.sh | Version disclosure, Referrer-Policy, cache em paginas auth, COOP/CORP |
| check-info-disclosure.sh | .env, .git, __NEXT_DATA__, BUILD_ID, comentarios HTML, robots.txt |
| Script | Vetor |
|------------------------------|-----------------------------------------------------------------------|
| check-security-headers.sh | Todos os 22 checkers CaramelScan no prostaff.gg |
| check-cookies.sh | Flags de cookie, SameSite, duracao, CSRF token |
| check-sri.sh | SRI em scripts/CSS externos, source maps, scripts inline |
| check-content-security.sh | Version disclosure, Referrer-Policy, cache em paginas auth, COOP/CORP |
| check-info-disclosure.sh | .env, .git, __NEXT_DATA__, BUILD_ID, comentarios HTML, robots.txt |
| check-waf-fingerprint.sh | Detecta WAF/anti-bot (Cloudflare, Vercel Security Checkpoint) e se esta bloqueando paths de forma indiscriminada (invalida testes por status HTTP) |
| check-security-headers-live.sh | Mesmos 22 checkers do check-security-headers.sh, mas via Chromium headless (Playwright) — necessario em prostaff.gg pois o Vercel Security Checkpoint bloqueia 100% das requests via curl, mesmo com UA de browser real |

Todos os scripts de frontend aceitam o target como primeiro argumento:
```bash
./front/check-security-headers.sh https://staging.prostaff.gg
```

### Nota: WAF/challenge JS em prostaff.gg (producao)

`prostaff.gg` roda atras de um challenge JS (Vercel Security Checkpoint, por tras da
Cloudflare) que retorna 403 para **qualquer** request sem execucao de JS — confirmado
com `curl` inclusive usando User-Agent de browser real (nao e filtro por UA, e um
challenge de verdade). Isso significa:

- `check-security-headers.sh`, `check-cookies.sh`, `check-sri.sh` e
`check-content-security.sh` (baseados em `curl`) so enxergam os headers/corpo da
**pagina de bloqueio**, nunca do app real, quando rodados contra producao.
- `check-info-disclosure.sh` ja tem um guard de calibracao (baseline contra path
aleatorio) que marca resultados 401/403 como inconclusivos quando isso acontece,
em vez de reportar falso-positivo de "protegido".
- Rode `check-waf-fingerprint.sh` primeiro para confirmar se o bloqueio esta ativo.
- Para headers reais, use `check-security-headers-live.sh` (Chromium via
Playwright, `tools/install.sh playwright`) — ele deixa o challenge resolver
como um browser de verdade resolveria e le os headers da resposta final.
- Os demais scripts baseados em `curl` continuam validos rodando contra
`localhost:3000` (dev) ou um ambiente sem a camada Vercel/Cloudflare na frente.

## Utilitarios (nao-vetores)

| Script | Funcao | Destrutivo |
|--------------------|-------------------------------------------------------------|------------|
| scripts/burp-seed.sh | Semeia o Burp com trafego real (API + fronts) pra popular HTTP history e Site map. Loga, captura token/cookie e refaz GETs autenticados. So GET/OPTIONS + login. | Nao |

Tutorial completo do fluxo: [`BURP-SEED.md`](./BURP-SEED.md).

```bash
# API 3333 + analytics-hub 8888 + ArenaBR 4000, com Burp em 127.0.0.1:8080
./scripts/burp-seed.sh
# so a API, credencial explicita
API=http://localhost:3333 ARENA="" SEED_EMAIL=test@prostaff.gg SEED_PASSWORD='Test123!@#' ./scripts/burp-seed.sh
```

## Uso rapido

```bash
Expand Down Expand Up @@ -102,7 +146,9 @@ Todos os scripts de frontend aceitam o target como primeiro argumento:
./scripts/27_supabase_direct_bypass.sh

# Auditoria completa frontend
./front/check-security-headers.sh
./front/check-waf-fingerprint.sh # rodar primeiro — calibra se ha bloqueio generico de WAF
./front/check-security-headers.sh # curl — invalido contra prod se o WAF acima bloquear tudo
./front/check-security-headers-live.sh # Chromium/Playwright — headers reais mesmo com o WAF ativo
./front/check-cookies.sh
./front/check-sri.sh
./front/check-content-security.sh
Expand Down Expand Up @@ -134,6 +180,7 @@ Salvos em `reports/` com data no nome. Formato: `security-audit-YYYY-MM-DD.md`.
|---------------------------------------------|----------------|----------|-----------|
| JWT Race Condition + Token Confusion | 2026-04-11 | 3 | Corrigido |
| JWT-02 Reaberto + Admin Cross-Tenant (novo) | 2026-06-06 | 2 | Corrigido |
| api.prostaff.gg — ProStaff-Audit (Swagger, headers, health, throttle) | 2026-10-16 | 0 | Triagem |

### Historico de vulnerabilidades corrigidas

Expand Down
Loading
Loading