diff --git a/.github/workflows/idor-audit.yml b/.github/workflows/idor-audit.yml new file mode 100644 index 0000000..fa9e986 --- /dev/null +++ b/.github/workflows/idor-audit.yml @@ -0,0 +1,49 @@ +name: Auditoria IDOR +on: [pull_request] + +jobs: + audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: { fetch-depth: 0 } + + - name: Auditar arquivos alterados no PR + env: + API_URL: ${{ secrets.IDOR_API_URL }} + API_TOKEN: ${{ secrets.IDOR_API_TOKEN }} + run: | + set -euo pipefail + if ! git fetch --no-tags --depth=1 origin "${{ github.base_ref }}"; then + echo "::error::Falha ao buscar a base; abortando."; exit 1 + fi + diff_out=$(git diff --name-only --diff-filter=ACMR "origin/${{ github.base_ref }}...") + mapfile -t files < <(printf '%s\n' "$diff_out" | grep -E '\.(py|js|ts)$' || true) + if [ "${#files[@]}" -eq 0 ]; then + echo "Nenhum arquivo .py/.js/.ts alterado. Nada a auditar."; exit 0 + fi + status=0 + for f in "${files[@]}"; do + [ -f "$f" ] || continue + case "$f" in + *.py) lang=python;; *.js) lang=javascript;; *.ts) lang=typescript;; *) continue;; + esac + echo "::group::Auditando $f" + code=$(jq -Rs . < "$f") + payload=$(jq -n --arg lang "$lang" --argjson code "$code" \ + --arg repo "$GITHUB_REPOSITORY" --arg sha "${{ github.event.pull_request.head.sha }}" \ + '{language:$lang, code:$code, source:"github_action", repo_full_name:$repo, commit_sha:$sha}') + resp=$(curl -sS -X POST "$API_URL/audits" -H "X-API-Key: $API_TOKEN" -H "Content-Type: application/json" -d "$payload") + echo "$resp" | jq -r '.findings[]? | " - [\(.severity)] \(.vuln_type) (\(.owasp_id) / \(.cwe_id)) na linha \(.line_start)\n \(.explanation)"' + n=$(echo "$resp" | jq -r '.n_findings // 0') + maxsev=$(echo "$resp" | jq -r '.max_severity // "nenhuma"') + echo "→ $n achado(s); severidade maxima: $maxsev" + if echo "$resp" | jq -e '.findings[]? | select(.severity=="critica")' >/dev/null; then + status=1 + fi + echo "::endgroup::" + done + if [ "$status" -ne 0 ]; then + echo "::error::Achado(s) 'critica' encontrado(s) — merge bloqueado." + fi + exit $status diff --git a/exemplo_vuln_idor.py b/exemplo_vuln_idor.py new file mode 100644 index 0000000..bcd23cf --- /dev/null +++ b/exemplo_vuln_idor.py @@ -0,0 +1,18 @@ +"""Endpoint de DEMONSTRACAO — versao VULNERAVEL (IDOR / BOLA). + +Busca o objeto somente pelo ID vindo da URL, sem checar se ele pertence ao +usuario autenticado. Qualquer pessoa logada troca o `item_id` na URL e le o +item de outro usuario. + +Falha: IDOR / BOLA — OWASP API1:2023, CWE-639. +""" +from fastapi import Depends, FastAPI + +app = FastAPI() + + +@app.get("/api/itens/{item_id}") +def get_item(item_id: int, current_user=Depends(get_current_user)): + # VULNERAVEL: filtra so por id e ignora o dono. Sem `Item.user_id == + # current_user.id`, qualquer item de qualquer usuario e acessivel. + return db.query(Item).filter(Item.id == item_id).first()