Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sentinel — one-click website security audit

Sentinel is a security auditor for small businesses that don't have a security team. Paste your website address, click Scan, and get a plain-language report of what's wrong and exactly how to fix it.

Because it scans your live website over HTTP/TLS (black-box), it works on any site no matter what framework or language it's built in — WordPress, Shopify, Django, Rails, a custom Node app, plain HTML, anything with a URL.

Website checks run in the web app; deep project scans run from your terminal. The website (and dashboard) perform safe, passive website scans for any URL. The terminal (after you log in on the site) deep-scans projects you have access tosentinel . --code statically analyses a whole codebase, its dependencies and its git history. You can only deep-scan code on your own machine, never attack an arbitrary website.

⚠️ Only scan websites you own or have permission to test.

What it checks

Area Examples
Encryption HTTPS enforced, valid & current TLS certificate, no weak protocols, weak cipher suites, TLS 1.0/1.1 still enabled, HSTS preload eligibility
Security headers CSP, HSTS, clickjacking, MIME-sniffing, referrer/permissions policy
DNS & email SPF, DMARC, DKIM, MTA-STS, CAA, DNSSEC, open DNS zone transfer (AXFR)
Attack surface Subdomain discovery from public Certificate Transparency logs; risky dev/staging/admin hosts
Exposed files .env, .git, database dumps, backups, config files left public
Cookies Missing Secure / HttpOnly / SameSite flags
Information leaks Server/version disclosure, debug error pages, secret HTML comments
Injection Reflected-input / possible XSS, open redirects (safe, non-destructive)
Malware Crypto-miner & obfuscated-script heuristics, optional Google Safe Browsing
Other Directory listing, mixed content, dangerous CORS

Every finding comes with a severity, a plain-language explanation, a concrete fix, and compliance cross-references (OWASP Top 10, PCI-DSS, CIS Controls) so reports speak the language auditors and customers expect.

The DNS/email and attack-surface checks are fully passive — they query public DNS and Certificate Transparency infrastructure, never attacking your site — so they run in the standard (web app + API) profile alongside the other safe checks. The zone-transfer probe is SSRF-guarded: nameservers that resolve to internal addresses are skipped.

Deep project scan — from your terminal (--code)

The terminal's deep scan targets a project you have access to, not a live website: sentinel . --code statically analyses an entire codebase plus its dependencies and git history, offline and read-only. You can only run it against code on your own machine, so it can never be pointed at someone else's site.

Active website attacking is intentionally not exposed. Earlier versions offered a --deep mode that actively attacked a live URL (SQLi/XSS/etc.). That was removed from the CLI so the tool can only ever deep-scan projects you control — not arbitrary websites. The web app and REST API perform safe, passive website checks only.

See Scan your local codebase (--code) below for everything the project scan covers (secrets, injection, vulnerable dependencies, secrets-in-git-history, and more).

Accounts & dashboard

Accounts, authentication and scan history all live in the Laravel + MySQL app. The Python service is a stateless scanning engine — it just runs a scan and returns the report; it stores nothing.

  • Auth: Laravel session auth against the users table. Passwords are bcrypt hashed (Laravel's hashed cast).
  • Per-user history: every scan is saved to the MySQL scans table and is only visible to the user who ran it (admins can see all).
  • The Laravel app shows a login screen, then a dashboard with a standard scan bar and a history sidebar, plus a "Use in terminal" page that walks users through installing the CLI, logging in, and deep-scanning their projects (whose results then sync back into this same history).

Seeded accounts

php artisan migrate:fresh --seed creates two accounts:

Role Email Password
Admin (super-admin) admin@sentinel.test admin12345
Demo (user) demo@sentinel.test demo12345

(Seeders: database/seeders/AdminUserSeeder.php and DemoUserSeeder.php.)

Roles: user vs admin

Every account has a role (user or admin); admins also have an is_super flag.

  • User → the standard dashboard: run scans, view their own history.
  • Admin → an extra dashboard with full controls:
    • Overview — platform stats (users, scans, project scans, average score, grade distribution).
    • Users — list every account, promote/demote roles, delete users.
    • All scans — view and delete any user's scan reports.
    • Scanner — the normal scan tool (admins can scan too).

The seeded admin is the super-admin: only a super-admin can create/promote other admins, and the super-admin can't be demoted or deleted (prevents lockout). The sentinel.admin middleware guards /admin/* routes.

Architecture

backend/sentinel/        # Python: STATELESS scanning engine
  core/                  # framework-agnostic scanning library
    checks/              # one module per check (+ checks/active/ for deep mode)
    crawler.py           # same-origin crawler (deep mode)
    scanner.py           # orchestrator
  api/main.py            # FastAPI: POST /api/scan -> ScanResult (no auth/DB)
  cli.py                 # command-line tool

frontend-laravel/        # Laravel + Livewire: UI + accounts + history (MySQL)
  app/Models/            # User, Scan (Eloquent)
  app/Livewire/          # Login, Scanner, Admin\{Overview,Users,Scans}
  app/Services/SentinelApi.php   # calls the Python scanner
  database/{migrations,seeders}/

The Python core has no web/auth/DB dependency — the CLI, the scan API and the Laravel UI all sit on top of the same scan() function. Laravel owns all state in MySQL and calls the scanner over HTTP (optionally with a shared SENTINEL_INTERNAL_KEY header for server-to-server auth).

Quick start

Use it from your terminal (log in once — like Claude Code)

The whole point: a user logs in on the Sentinel website, and from then on can run scans from their own terminal, in any project, with results saved to their dashboard. There's nothing to configure and no API keys to paste.

# 1. install the CLI once (one command — no virtualenv to manage)
curl -fsSL https://your-sentinel-site/install.sh | sh

# 2. log in through your browser (opens a consent page on the website)
export SENTINEL_WEB_URL=https://your-sentinel-site   # where your Sentinel lives
sentinel login                 # ✓ Logged in as you@example.com

# 3. use it anywhere — scans auto-sync to your dashboard history
cd ~/my-project
sentinel .                                             # deep-scan this whole project
sentinel whoami                # who am I logged in as?
sentinel logout                # disconnect this terminal

How the login works (browser "loopback" flow, like gcloud/gh): sentinel login starts a tiny local server on 127.0.0.1, opens your browser to the website's /cli/authorize consent page, and — once you click Approve — the site redirects back to http://127.0.0.1:<port>/callback with a fresh personal access token. The token only ever travels to your own machine and is stored in ~/.sentinel/config.json (owner-only, chmod 600). Override per command with SENTINEL_TOKEN / SENTINEL_WEB_URL, or skip saving a scan with --no-sync.

The installer is a thin wrapper (it uses pipx/uv/pip under the hood); to install straight from git instead:

pipx install 'git+https://github.com/<you>/sentinel.git#subdirectory=backend'

The package is sentinel-scanner; the command it installs is sentinel.

Backend (editable, for development)

cd backend
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"      # ".[dev]" adds pytest; plain "-e ." for runtime only

CLI:

sentinel .                             # deep-scan the current project (code + deps + git history)
sentinel ./path/to/project             # deep-scan a specific directory
sentinel . --fail-on high              # exit non-zero in CI if High+ issues
sentinel . --json                      # machine-readable

The terminal has one scan: it deep-scans a project you have access to. It can't be pointed at a live website — passive website scanning lives in the web app and REST API. (sentinel . --code still works; --code is now the implied default.)

Scan your local codebase (--code):

Point Sentinel at a local repo to statically analyse the source code for vulnerabilities and get fix suggestions — no website or deployment needed. This runs entirely offline and read-only (it never executes your code or makes network calls), so no ownership verification is required: it's your machine.

sentinel . --code                      # scan the current repo
sentinel ./path/to/project --code      # scan a specific directory
sentinel . --code --json               # machine-readable output
sentinel . --code --fail-on high       # exit non-zero in CI on High+ findings

It flags issues like hardcoded secrets/keys, SQL & command injection, eval/ unsafe deserialization, XSS sinks (innerHTML, dangerouslySetInnerHTML, unescaped templates), debug mode left on, disabled TLS verification, wildcard CORS, and weak hashing (MD5/SHA1) — across Python, PHP, JS/TS and config files. Each finding includes the file:line, a plain-language explanation, and a fix. Secrets are redacted in the output, and node_modules/vendor/.venv/build artifacts and lock/binary files are skipped automatically.

The code scan also does two extra offline passes:

  • Dependency / SBOM scan. It parses your lockfiles and manifests (requirements.txt, Pipfile.lock, package-lock.json, package.json exact pins, composer.lock) and flags pinned versions that match a bundled snapshot of well-known advisories (CVEs for popular packages like requests, lodash, guzzlehttp/guzzle, …). Only exact-pinned versions are evaluated, so there are no false positives from open ranges. This is a curated subset, not a complete database — for exhaustive, always-current coverage run a dedicated SCA tool (pip-audit, npm audit, osv-scanner) too.
  • Git-history secret scan. When the target is a git repository, it walks every blob in history (read-only git rev-list/cat-file) and reports secrets that were committed at any point — even if they were later deleted from the current files. A deleted-but-still-in-history key must be treated as leaked; the finding tells you to rotate it and purge history with git filter-repo/BFG.

Both passes are offline and read-only (no network, no code execution).

Scanner API (stateless engine — Laravel calls this):

uvicorn sentinel.api.main:app --reload --port 8000
# Standard:  POST /api/scan    {"url": "https://example.com"}
# Deep:      POST /api/scan    {"url": "https://example.com", "deep": true, "authorized": true, "verification_token": "<token>"}
#            (the target must serve <token> at /.well-known/sentinel-verify-<token>.txt)
# Verify:    POST /api/verify  {"url": "https://example.com", "token": "<token>"}
#            -> {"verified": bool, "method": "file"|"dns"|null, "path": "...", "dns_record": "sentinel-verify=<token>"}
# Optional:  set SENTINEL_INTERNAL_KEY and send it as the X-Internal-Key header
# docs at http://localhost:8000/docs

Frontend — Laravel + Livewire (MySQL)

The UI is a Laravel + Livewire app (server-rendered PHP, reactive without hand-written JS). It owns all data in MySQL and calls the Python scanner.

cd frontend-laravel
composer install
# configure .env: DB_* (MySQL) and SENTINEL_API_URL=http://localhost:8000
php artisan key:generate          # if APP_KEY is missing
php artisan migrate --seed        # create tables + seed admin/demo accounts
php artisan serve --port 8090     # open http://localhost:8090

It uses Livewire components (no controllers): Auth\Login, Scanner, and Admin\{Overview,Users,Scans}, with Eloquent User/Scan models. Auth is Laravel's session guard; App\Services\SentinelApi calls the scanner over HTTP; auth + sentinel.admin middleware guard the routes. Needs PHP 8.2+, Composer, and a MySQL database.

Set GOOGLE_SAFE_BROWSING_KEY in the backend environment to enable the reputable-blocklist check.

REST API (use it from the terminal)

The Laravel app exposes a token-authenticated JSON API under /api/v1, so you can drive Sentinel from a script or the terminal. It uses the same accounts and scan history as the web app. (The Python engine stays internal — the API calls it for you.)

The API performs standard (passive) website scans only. Active website attacking isn't offered anywhere; sending deep=1 returns 422. Deep project scans are run from the terminal (sentinel . --code) and synced to history via POST /api/v1/scans/import.

Auth is a personal access token sent as Authorization: Bearer <token>.

BASE=http://localhost:8090

# 1. Get a token (or POST /api/v1/register to create an account + token)
TOKEN=$(curl -s -X POST $BASE/api/v1/tokens \
  -d 'email=demo@sentinel.test&password=demo12345' | jq -r .token)

# 2. Run a standard scan
curl -s -X POST $BASE/api/v1/scans \
  -H "Authorization: Bearer $TOKEN" -d 'url=example.com' | jq '.result.grade, .result.score'

# 3. List / fetch / delete your scans
curl -s $BASE/api/v1/scans            -H "Authorization: Bearer $TOKEN"
curl -s $BASE/api/v1/scans/123        -H "Authorization: Bearer $TOKEN"
curl -s -X DELETE $BASE/api/v1/scans/123 -H "Authorization: Bearer $TOKEN"

# Deep project scans run in the terminal and sync to this same history:
#   sentinel . --code
Method & path Auth What it does
POST /api/v1/register Create an account, returns a token
POST /api/v1/tokens Email+password → new token
GET /api/v1/me token Current account
DELETE /api/v1/tokens/current token Revoke the current token
GET /api/v1/scans token List your scans (paginated)
POST /api/v1/scans token Run a standard scan (deep=1 is rejected — use the CLI)
POST /api/v1/scans/import token Save a locally-run (deep/--code) scan to history — used by the CLI after sentinel login
GET /api/v1/scans/{id} token One scan report (yours only)
DELETE /api/v1/scans/{id} token Delete a scan

Tokens are stored hashed; the plaintext (prefixed stnl_) is shown once, and the public endpoints are rate-limited. Deep/active vulnerability scanning lives only in the CLI, which verifies you own the domain before testing.

Deploy (free)

Run the whole app for free on an Oracle Cloud Always Free VM (Nginx + PHP-FPM for Laravel, the FastAPI engine as a systemd service, SQLite for storage — no cold starts, no external DB). A ready-to-run kit lives in deploy/oracle/:

sudo git clone <your-repo-url> /opt/sentinel
sudo bash /opt/sentinel/deploy/oracle/setup.sh yourdomain.com

It installs everything, configures the services, sets production env (debug off, fresh APP_KEY, a shared internal key), runs migrations, and prints the next steps (HTTPS via certbot, creating your admin). Full walkthrough — including the Oracle firewall rules you must add — is in deploy/oracle/DEPLOY.md.

Safety & scope

  • All active checks are non-destructive: GET-only probes and a single benign, inert marker for the XSS/redirect tests. Nothing is submitted, written, or deleted.
  • The API refuses to scan private/loopback/internal addresses (SSRF guard).
  • Heuristic checks (malware, XSS) are flagged as possible and recommend manual confirmation.

License

Released under the MIT License.

About

No description, website, or topics provided.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages