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
221 changes: 221 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
name: CI

on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

env:
NO_CREDS: HACKERONE_USERNAME="" HACKERONE_API_KEY=""
FAKE_CREDS: HACKERONE_USERNAME="x" HACKERONE_API_KEY="x"

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Set up Python
run: uv python install 3.10

- name: Install dependencies
run: uv sync

- name: Ruff check
run: uv run ruff check .

- name: Ruff format check
run: uv run ruff format --check .

test:
name: Test
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python: ["3.10", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Set up Python
run: uv python install ${{ matrix.python }}

- name: Install dependencies
run: uv sync

# --- Help & JSON output ---

- name: "help: shows output"
shell: bash
run: uv run hackerone help | grep -q "Hacker Modules"

- name: "help: works without credentials"
shell: bash
run: |
HACKERONE_USERNAME="" HACKERONE_API_KEY="" uv run hackerone --env-file /dev/null help | grep -q "Hacker Modules"

- name: "help --json: returns valid JSON"
shell: bash
run: uv run hackerone help --json | python3 -m json.tool > /dev/null

- name: "help --json: contains commands key"
shell: bash
run: |
uv run hackerone help --json | python3 -c "import json,sys; d=json.load(sys.stdin); assert 'commands' in d"

- name: "help --json: lists all hacker commands"
shell: bash
run: |
uv run hackerone help --json | python3 -c "
import json,sys
keys = ' '.join(json.load(sys.stdin)['commands'].keys())
for c in ['balance','reports','programs','profile','earnings','payouts','report','program','burp','csv','scope']:
assert c in keys, f'missing {c}'
"

- name: "help --json: lists all org commands"
shell: bash
run: |
uv run hackerone help --json | python3 -c "
import json,sys
keys = ' '.join(json.load(sys.stdin)['commands'].keys())
for c in ['org','org-members','org-reports','org-report','org-update-report','org-activities','org-metrics','org-scopes','org-invite-hacker','org-bounty','org-swag']:
assert c in keys, f'missing {c}'
"

# --- Error handling ---

- name: "error: no credentials"
shell: bash
run: |
output=$(HACKERONE_USERNAME="" HACKERONE_API_KEY="" uv run hackerone --env-file /dev/null balance 2>&1 || true)
echo "$output" | grep -q "No username provided"

- name: "error: no API key"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="" uv run hackerone --env-file /dev/null balance 2>&1 || true)
echo "$output" | grep -q "No API key provided"

- name: "error: no arguments"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null 2>&1 || true)
echo "$output" | grep -q "No argument provided"

- name: "error: invalid module"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null notamodule 2>&1 || true)
echo "$output" | grep -q "Invalid module"

- name: "error: no-creds JSON outputs valid JSON"
shell: bash
run: |
output=$(HACKERONE_USERNAME="" HACKERONE_API_KEY="" uv run hackerone --env-file /dev/null --json balance 2>&1 || true)
echo "$output" | python3 -c "import json,sys; d=json.load(sys.stdin); assert 'error' in d"

# --- Argument validation ---

- name: "report: rejects non-numeric ID"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null report abc 2>&1 || true)
echo "$output" | grep -q "Invalid ID"

- name: "org-report: rejects non-numeric ID"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-report abc 2>&1 || true)
echo "$output" | grep -q "Invalid ID"

- name: "org-update-report: rejects invalid state"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-update-report 123 badstate 2>&1 || true)
echo "$output" | grep -q "Invalid state"

- name: "org-update-report: rejects missing args"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-update-report 2>&1 || true)
echo "$output" | grep -q "Usage"

- name: "org-bounty: rejects invalid amount"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-bounty 123 notanumber 2>&1 || true)
echo "$output" | grep -q "Invalid amount"

- name: "org-bounty: rejects missing args"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-bounty 2>&1 || true)
echo "$output" | grep -q "Usage"

- name: "org-members: requires org ID"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-members 2>&1 || true)
echo "$output" | grep -q "No organization ID"

- name: "org-groups: requires org ID"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-groups 2>&1 || true)
echo "$output" | grep -q "No organization ID"

- name: "org-reports: requires handle"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-reports 2>&1 || true)
echo "$output" | grep -q "No program handle"

- name: "org-metrics: requires handle"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-metrics 2>&1 || true)
echo "$output" | grep -q "No program handle"

- name: "org-scopes: requires handle"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-scopes 2>&1 || true)
echo "$output" | grep -q "No program handle"

- name: "org-invite-hacker: requires args"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-invite-hacker 2>&1 || true)
echo "$output" | grep -q "Usage"

- name: "org-swag: requires report ID"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null org-swag 2>&1 || true)
echo "$output" | grep -q "Usage"

# --- Scope (no creds needed) ---

- name: "scope: works without credentials"
shell: bash
run: |
HACKERONE_USERNAME="" HACKERONE_API_KEY="" uv run hackerone --env-file /dev/null scope 2>&1 | grep -q "Invalid arguments"

# --- Verbose flag ---

- name: "verbose: shows auth info"
shell: bash
run: |
output=$(HACKERONE_USERNAME="x" HACKERONE_API_KEY="x" uv run hackerone --env-file /dev/null -v help 2>&1 || true)
# help doesn't show auth (no creds needed), but should still work
echo "$output" | grep -q "Hacker Modules"
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
__pycache__/
*.py[cod]
*$py.class
*.egg-info/
dist/
build/
.eggs/
venv/
.venv/
.env
.ruff_cache/
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
just 1.47.1
Loading