Security Assessment

System: NetShield Intelligence Pipeline — gap-analysis web report
Date: March 6, 2026
Scope: reports/serve_report.py, reports/batch_report.py, Cloudflare Tunnel integration
Overall verdict: The system presents no meaningful attack surface to remote users beyond viewing the report HTML. No database credentials, filesystem paths, or execution capabilities are exposed through the web interface. The architecture is read-only by design.

1. System Architecture

The pipeline report is served through a layered architecture with clearly separated responsibilities:

Remote user (browser / phone)
        │ HTTPS (TLS 1.3)
        ▼
┌─── Cloudflare Edge Network ──────────────────────────┐
│ • Terminates TLS                                           │
│ • DDoS protection                                        │
│ • Real IP never exposed                                 │
└──────────────────────────────────────────────────────┘
        │ Proxied HTTP (internal)
        ▼
┌─── cloudflared.exe (on local PC) ────────────────────┐
│ • Outbound-only tunnel (no open inbound ports)       │
│ • Forwards to 127.0.0.1:5050 only                    │
└──────────────────────────────────────────────────────┘
        │ localhost only
        ▼
┌─── Flask (serve_report.py, port 5050) ───────────────┐
│ • HTTP Basic Auth (username + password)              │
│ • Serves ONE static HTML file                         │
│ • No DB queries triggered by browser requests        │
└──────────────────────────────────────────────────────┘
        │ no connection
        ╳ (browser CANNOT reach this)
        ▼
PostgreSQL :5432  — local only, never exposed

2. Network Security

2.1 No Open Inbound Ports

The Cloudflare tunnel works by making an outbound TCP connection from the local machine to Cloudflare's global network — identical in nature to a browser opening a tab. The local router/firewall has no port-forwarding rules and no inbound ports are opened. An attacker scanning the host's public IP address will find nothing.

Attack VectorExposed?Reason
Port scan of public IPNOReal IP never published; no inbound ports open
Direct connection to PostgreSQLNOPort 5432 bound to 127.0.0.1 only
Direct connection to FlaskNOPort 5050 bound to 127.0.0.1 only
Man-in-the-middle (MITM)NOCloudflare enforces TLS 1.3 end-to-end

2.2 Transport Encryption

All traffic between the remote user and Cloudflare's edge is encrypted with TLS 1.3 (HTTPS). The public URL (*.trycloudflare.com) is issued with a valid Cloudflare-managed certificate — no self-signed certificates, no certificate warnings.

3. Authentication

Flask uses HTTP Basic Authentication enforced on every route via a @login_required decorator. No page — including the /refresh endpoint — is accessible without valid credentials.

def login_required(f):
    @functools.wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not _check_auth(auth.username, auth.password):
            return _require_auth()   # → HTTP 401
        return f(*args, **kwargs)
    return decorated

@app.route("/")
@login_required          # ← applied to every route
def index(): ...
ScenarioResult
Request with no credentialsHTTP 401 — browser shows login dialog
Request with wrong credentialsHTTP 401 — access denied
Request with correct credentialsReport HTML served
Recommendation: The default credentials (netshield / netshield123) should be changed before sharing the URL externally. Edit the REPORT_USER and REPORT_PASS values in the .env file. The Flask dev server is suitable for internal/trusted sharing — for a production deployment, a WSGI server (e.g. Gunicorn) would be appropriate.

4. What the Report HTML Contains

4.1 Embedded Data

The report HTML is a fully self-contained static file. It contains:

The Base64 strings are not encrypted or obfuscated — they decode to exactly the same data shown in the visible table. Anyone who can view the HTML source can decode them. This is intentional and expected: the purpose of the report is to share this data.

4.2 JavaScript Audit

The report contains exactly two JavaScript functions. Neither makes any network request:

FunctionPurposeNetwork access?
togglePanel(id) Shows/hides the KPI detail panel when the ? button is clicked. Purely DOM manipulation. None
dlCSV(btn) Decodes the Base64 attribute into a Blob, creates a temporary object URL, triggers a download, then revokes the URL. No server round-trip. None

There is no fetch(), XMLHttpRequest, WebSocket, eval(), or dynamic script loading anywhere in the page.

5. Database Isolation

The PostgreSQL database is never reachable from a browser for two independent reasons:

  1. Browser same-origin policy + CORS: A web page is prohibited by browser security from opening a TCP connection to localhost:5432. Even if malicious JavaScript were injected into the page, it could not connect to PostgreSQL — browsers only allow HTTP/HTTPS requests, not raw TCP.
  2. Flask serves no DB-proxying endpoint: The Flask server has two routes — / (read file from disk) and /refresh (run batch_report.py, then redirect). Neither accepts user-supplied SQL or exposes query results as an API. The database credentials exist only in the .env file on disk, loaded at Python startup — they are never transmitted to the browser.
# The only routes in serve_report.py:
@app.route("/")           → reads pipeline_report.html from disk
@app.route("/refresh")    → runs batch_report.py, then redirects to /
# No /query, /api, /exec, or any other endpoint exists.

6. Known Limitations

ItemSeverityNotes
Flask development server LOW Not hardened for high-traffic public deployment. Appropriate for internal/trusted sharing with ≤10 concurrent users.
HTTP Basic Auth over HTTPS ACCEPTABLE Credentials are Base64-encoded in the HTTP header, but TLS encryption prevents interception in transit.
/refresh endpoint LOW An authenticated user can trigger report regeneration, consuming CPU/RAM for ~5–10 seconds. Not exploitable for data exfiltration, only mild resource exhaustion.
Default credentials MEDIUM Must be changed before sharing the URL publicly. Configure via .env: REPORT_USER and REPORT_PASS.
No rate limiting LOW No brute-force protection on the login. Cloudflare's free tier provides basic bot protection at the edge.

7. Summary

Security PropertyStatus
Public IP / open ports exposed✓ Not exposed
Transit encryption (TLS)✓ Enforced by Cloudflare
Authentication required✓ HTTP Basic Auth on all routes
Database accessible from browser✓ Impossible by design
Filesystem accessible from browser✓ Not exposed
Remote code execution via web✓ Not possible
JavaScript makes network requests✓ None
Credentials in HTML source✓ Not present
Default password changed⚠ Recommended before sharing