Files
SneakyScope/app/static/style.css
Phillip Tarrant 3a24b392f2 feat: on-demand external script analysis + code viewer; refactor form analysis to rule engine
- API: add `POST /api/analyze_script` (app/blueprints/api.py)
  - Fetch one external script to artifacts, run rules, return findings + snippet
  - Uses new ExternalScriptFetcher (results_path aware) and job UUID
  - Returns: { ok, final_url, status_code, bytes, truncated, sha256, artifact_path, findings[], snippet, snippet_len }
  - TODO: document in openapi/openapi.yaml

- Fetcher: update `app/utils/external_fetch.py`
  - Constructed with `results_path` (UUID dir); writes to `<results_path>/scripts/fetched/<index>.js`
  - Loads settings via `get_settings()`, logs via std logging

- UI (results.html):
  - Move “Analyze external script” action into **Content Snippet** column for external rows
  - Clicking replaces button with `<details>` snippet, shows rule matches, and adds “open in viewer” link
  - Robust fetch handler (checks JSON, shows errors); builds viewer URL from absolute artifact path

- Viewer:
  - New route: `GET /view/artifact/<run_uuid>/<path:filename>` (app/blueprints/ui.py)
  - New template: Monaco-based read-only code viewer (viewer.html)
  - Removes SRI on loader to avoid integrity block; loads file via `raw_url` and detects language by extension

- Forms:
  - Refactor `analyze_forms` to mirror scripts analysis:
    - Uses rule engine (`category == "form"`) across regex/function rules
    - Emits rows only when matches exist
    - Includes `content_snippet`, `action`, `method`, `inputs`, `rules`
  - Replace legacy plumbing (`flagged`, `flag_reasons`, `status`) in output
  - Normalize form function rules to canonical returns `(bool, Optional[str])`:
    - `form_action_missing`
    - `form_http_on_https_page`
    - `form_submits_to_different_host`
    - Add minor hardening (lowercasing hosts, no-op actions, clearer reasons)

- CSS: add `.forms-table` to mirror `.scripts-table` (5 columns)
  - Fixed table layout, widths per column, chip/snippet styling, responsive tweaks

- Misc:
  - Fix “working outside app context” issue by avoiding `current_app` at import time (left storage logic inside routes)
  - Add “View Source” link to open page source in viewer

Refs:
- Roadmap: mark “Source code viewer” done; keep TODO to add `/api/analyze_script` to OpenAPI
2025-08-21 15:32:24 -05:00

407 lines
9.2 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
:root {
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
}
body {
margin: 0;
background: #0b0f14;
color: #e6edf3;
}
header, footer {
padding: 1rem 1.25rem;
background: #0f1720;
border-bottom: 1px solid #1f2a36;
}
/* ===== main: now full-width (no 960px cap) ===== */
main {
padding: 1.5rem 2rem; /* a bit more horizontal breathing room */
max-width: 100%; /* remove fixed cap */
width: 100%;
margin: 0; /* no auto centering since were full-width */
box-sizing: border-box;
}
.card {
background: #111826;
padding: 1rem;
border: 1px solid #1f2a36;
border-radius: 12px;
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
}
input[type=url] {
width: 100%;
padding: 0.7rem;
border-radius: 8px;
border: 1px solid #243041;
background: #0b1220;
color: #e6edf3;
}
button, .button {
display: inline-block;
margin-top: 0.75rem;
padding: 0.6rem 1rem;
border-radius: 8px;
border: 1px solid #243041;
background: #1a2535;
color: #e6edf3;
text-decoration: none;
}
.flash {
list-style: none;
padding: 0.5rem 1rem;
}
.flash .error {
color: #ff6b6b;
}
.grid {
display: grid;
grid-template-columns: 150px 1fr;
gap: 0.5rem 1rem;
}
img {
max-width: 100%;
height: auto;
border-radius: 8px;
border: 1px solid #243041;
}
pre.code {
white-space: pre-wrap;
word-break: break-all;
background: #0b1220;
padding: 0.75rem;
border-radius: 8px;
border: 1px solid #243041;
}
/* Links */
a {
color: #7dd3fc; /* Soft cyan for dark background */
text-decoration: underline;
}
a:hover {
color: #38bdf8; /* Slightly brighter on hover */
}
/* Accordion / details summary */
details summary {
cursor: pointer;
padding: 0.5rem;
font-weight: bold;
border-radius: 8px;
background: #111826;
border: 1px solid #1f2a36;
margin-bottom: 0.5rem;
transition: background 0.3s ease;
}
details[open] summary {
background: #1a2535; /* Slightly lighter when expanded */
}
details > ul, details > table {
padding-left: 1rem;
margin: 0.5rem 0;
}
/* Highlight flagged forms */
details.flagged summary {
border-left: 4px solid #ff6b6b; /* Red accent for flagged forms */
}
/* Smooth collapse/expand */
details ul, details p {
transition: all 0.3s ease;
}
/* Enrichment / GeoIP / Forms / Redirects Tables */
.enrichment-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 1rem;
}
.enrichment-table th,
.enrichment-table td {
border: 1px solid #243041;
padding: 0.5rem;
vertical-align: top;
}
.enrichment-table th {
background: #111826;
text-align: left;
}
.enrichment-table td {
width: auto; /* browser resizes naturally */
word-break: break-word;
}
/* Scripts Table Special Handling */
.scripts-table pre.code {
margin: 0;
padding: 0.25rem;
font-size: 0.9rem;
}
/* Hover effects for table rows */
.enrichment-table tbody tr:hover {
background: #1f2a36;
}
/* Card table headings */
.enrichment-table thead th {
border-bottom: 2px solid #243041;
}
/* Ensure nested tables don't overflow */
.card table {
table-layout: auto;
word-break: break-word;
}
/* ============================
Results Table (3+ columns)
- Visual style matches .enrichment-table
- Adds better wrapping for long strings (URL/UUID)
- Right-aligns timestamps for scannability
============================ */
.results-table {
width: 100%;
border-collapse: collapse;
background: #111826; /* match card background */
border: 1px solid #1f2a36; /* subtle border like cards */
border-radius: 12px; /* rounded corners */
overflow: hidden; /* clip the rounded corners */
table-layout: auto; /* allow natural column sizing */
}
/* Header styling */
.results-table thead th {
padding: 0.6rem 0.75rem;
background: #0f1720; /* match header tone */
border-bottom: 1px solid #1f2a36;
text-align: left;
font-weight: 600;
white-space: nowrap; /* keep short headers on one line */
}
/* Body cells */
.results-table tbody td {
padding: 0.6rem 0.75rem;
border-top: 1px solid #1f2a36;
vertical-align: top;
text-align: left;
}
/* Zebra rows for readability (optional) */
.results-table tbody tr:nth-child(odd) {
background: #0d1522; /* slight contrast row */
}
/* Links inside table should inherit your global link colors */
.results-table a {
text-decoration: underline;
}
/* ---- Column-specific tweaks ---- */
/* URL column: allow wrapping of long URLs without blowing the layout */
.results-table td.url,
.results-table td.url a {
word-wrap: break-word; /* legacy support */
overflow-wrap: anywhere; /* modern wrapping for long URLs */
word-break: break-word;
}
/* UUID column: force wrap to avoid overflow */
.results-table td.uuid {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
word-break: break-all; /* split at any point to keep table narrow */
max-width: 28ch; /* reasonable width to avoid stretching */
}
/* Timestamp column: align right and keep on a single line */
.results-table td.timestamp {
text-align: right;
white-space: nowrap; /* keep ISO timestamps on one line */
}
/* Optional: make the newest (first) row stand out subtly */
.results-table tbody tr:first-child {
box-shadow: inset 0 0 0 1px #243041;
}
/* Optional: small, subtle buttons in table cells (e.g., copy UUID) */
.results-table .copy-btn {
margin-left: 0.4rem;
padding: 0.2rem 0.45rem;
border-radius: 6px;
border: 1px solid #243041;
background: #1a2535;
color: #e6edf3;
cursor: pointer;
line-height: 1;
font-size: 0.9rem;
}
.results-table .copy-btn:hover {
filter: brightness(1.1);
}
/* ===== Responsive niceties for very small screens ===== */
@media (max-width: 768px) {
main {
padding: 1rem; /* a tad tighter on mobile */
}
.enrichment-table,
.results-table {
display: block;
overflow-x: auto; /* allow horizontal scroll if needed */
white-space: nowrap;
}
}
/* SCRIPTS TABLE */
.scripts-table td ul {
margin: 0.25rem 0 0.25rem 1rem;
padding-left: 1rem;
}
.scripts-table td small {
opacity: 0.85;
}
/* keep the table from exploding */
.scripts-table {
table-layout: fixed;
width: 100%;
}
/* columns: Type | Source URL | Snippet | Matches */
.scripts-table th:nth-child(1) { width: 8rem; }
.scripts-table th:nth-child(2) { width: 32rem; } /* tweak as you like */
.scripts-table th:nth-child(3) { width: 24rem; }
.scripts-table th:nth-child(4) { width: auto; }
/* ellipsize the table cells by default */
.scripts-table td, .scripts-table th {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* lists & small text inside cells */
.forms-table td ul {
margin: 0.25rem 0 0.25rem 1rem;
padding-left: 1rem;
}
.forms-table td small {
opacity: 0.85;
}
/* keep the table from exploding */
.forms-table {
table-layout: fixed;
width: 100%;
}
/* columns: Action | Method | Inputs | Matches | Form Snippet */
.forms-table th:nth-child(1) { width: 15rem; } /* Action */
.forms-table th:nth-child(2) { width: 5rem; } /* Method */
.forms-table th:nth-child(3) { width: 15rem; } /* Inputs */
.forms-table th:nth-child(5) { width: 24rem; } /* Snippet */
.forms-table th:nth-child(4) { width: auto; } /* Matches grows */
/* ellipsize cells by default */
.forms-table td,
.forms-table th {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* nicer wrapping inside snippet/details & input chips */
.forms-table details { white-space: normal; }
.forms-table details > pre.code {
white-space: pre-wrap; /* let long lines wrap */
max-height: 28rem;
overflow: auto;
}
.forms-table .chips {
display: flex;
gap: 0.25rem;
flex-wrap: wrap;
white-space: normal; /* allow chip text to wrap if needed */
}
/* (optional) responsive tweaks */
@media (max-width: 1200px) {
.forms-table th:nth-child(1) { width: 22rem; }
.forms-table th:nth-child(3) { width: 16rem; }
.forms-table th:nth-child(5) { width: 18rem; }
}
/* let URLs/snippets wrap *inside* their cell when expanded content shows */
.breakable {
white-space: normal;
overflow-wrap: anywhere;
word-break: break-word;
}
/* when user opens <details>, keep code readable without blowing layout */
details pre.code {
white-space: pre-wrap;
word-break: break-word;
max-height: 18rem;
overflow: auto;
}
/* Generic badge */
.badge {
display: inline-block;
padding: 0.1rem 0.4rem;
margin-left: 0.35rem;
border-radius: 0.4rem;
font-size: 0.75rem;
line-height: 1;
vertical-align: middle;
user-select: none;
}
/* Severity colors */
.sev-high { background: #fdecea; color: #b71c1c; border: 1px solid #f5c6c4; }
.sev-medium { background: #fff8e1; color: #8a6d3b; border: 1px solid #ffe0a3; }
.sev-low { background: #e8f5e9; color: #1b5e20; border: 1px solid #b9e6be; }
/* Tag chips */
.chip {
display: inline-block;
padding: 0.1rem 0.35rem;
margin-left: 0.25rem;
border-radius: 999px;
font-size: 0.7rem;
line-height: 1;
background: #eef2f7;
color: #425466;
border: 1px solid #d9e2ec;
}