Files
SneakyScope/app/templates/partials/result_scripts.html
Phillip Tarrant 55cd81aec0 feat(text): add text analysis pipeline & surface results in UI
- engine: add analyse_text() to extract visible page text and evaluate
  category="text" rules; collect matched phrases and expose as
  `content_snippet` (deduped, length-capped via settings.ui.snippet_preview_len).
- engine: removed unused code
- browser: removed double call for enrichment
- engine: improve regex compilation — honor per-rule flags (string or list)
  and default IGNORECASE when category=="text".
- engine: add dispatch logging "[engine] applying categories: …" gated by
  settings.app.print_rule_dispatch.
- ui(templates): add `templates/partials/result_text.html` mirroring the forms
  table; renders page-level records and their matched rules.
- ui(controller): wire `analyse_text()` into scan path and expose
  `payload["suspicious_text"]`.
- rules(text): add `identity_verification_prompt`, `gated_document_access`,
  `email_collection_prompt`; broaden `credential_reset`.

fix: text indicators were not displayed due to missing analyzer and mismatched result shape.

Result shape:
  suspicious_text: [
    {
      "type": "page",
      "content_snippet": "...matched phrases…",
      "rules": [
        {"name": "...", "description": "...", "severity": "medium", "tags": ["..."]}
      ]
    }
  ]
2025-08-22 17:18:50 -05:00

120 lines
5.0 KiB
HTML

<!-- /templates/partials/result_scripts.html -->
<section id="scripts" class="card">
<h2 class="text-lg font-semibold mb-3">Suspicious Scripts</h2>
{% if suspicious_scripts %}
<div class="overflow-x-auto">
<table class="w-full table-fixed text-sm">
<colgroup>
<col class="w-[10%]"> <!-- Type -->
<col class="w-[15%]"> <!-- Source -->
<col class="w-[45%]"> <!-- Matches -->
<col class="w-[30%]"> <!-- Snippet -->
</colgroup>
<thead class="text-gray-400 border-b border-gray-800">
<tr>
<th class="text-left py-2 pr-4 whitespace-normal break-words">Type</th>
<th class="text-left py-2 pr-4 whitespace-normal break-words">Source URL</th>
<th class="text-left py-2 pr-4 whitespace-normal break-words">Matches (Rules &amp; Heuristics)</th>
<th class="text-left py-2 pr-4 whitespace-normal break-words">Content Snippet</th>
</tr>
</thead>
<tbody>
{% for s in suspicious_scripts %}
<tr class="border-b border-gray-900 align-top">
<td class="py-2 pr-4 whitespace-nowrap">{{ s.type or 'unknown' }}</td>
<td class="py-2 pr-4 break-all">
{% if s.src %}
<a href="{{ s.src }}" target="_blank" rel="noopener" class="hover:text-blue-400">
{{ s.src[:100] }}{% if s.src|length > 100 %}…{% endif %}
</a>
{% else %}
<span class="text-gray-500">N/A</span>
{% endif %}
</td>
<!-- Matches (Rules & Heuristics) -->
<td class="py-2 pr-4 break-words" data-role="matches-cell">
{% set has_rules = s.rules and s.rules|length > 0 %}
{% set has_heur = s.heuristics and s.heuristics|length > 0 %}
{% if has_rules %}
<div class="mb-1"><strong>Rules</strong></div>
<ul class="space-y-1">
{% for r in s.rules %}
<li title="{{ r.description or '' }}">
{{ r.name }}
{% if r.severity %}
{% set sev = r.severity|lower %}
<span class="ml-2 rounded-full px-2 py-0.5 text-xs border
{% if sev == 'high' %} badge badge-danger
{% elif sev == 'medium' %} badge badge-warn
{% else %} badge badge-info {% endif %}">
{{ r.severity|title }}
</span>
{% endif %}
{% if r.tags %}
{% for t in r.tags %}
<span class="chip" title="Tag: {{ t }}">{{ t }}</span>
{% endfor %}
{% endif %}
{% if r.description %}
<small class="text-gray-400"> — {{ r.description }}</small>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% if has_heur %}
<div class="mt-2 mb-1"><strong>Heuristics</strong></div>
<ul class="list-disc list-inside space-y-1">
{% for h in s.heuristics %}
<li>{{ h }}</li>
{% endfor %}
</ul>
{% endif %}
{% if not has_rules and not has_heur %}
<span class="text-gray-500">N/A</span>
{% endif %}
</td>
<!-- Content Snippet (let column width control it) -->
<td class="py-2 pr-4 align-top" data-role="snippet-cell">
{% if s.content_snippet %}
<details>
<summary class="cursor-pointer text-blue-300 hover:underline">
View snippet ({{ s.content_snippet|length }} chars)
</summary>
<pre class="mt-1 bg-[#0b0f14] border border-gray-800 rounded-lg p-3
w-full max-w-full overflow-auto max-h-64
whitespace-pre-wrap break-words font-mono text-xs">{{ s.content_snippet }}</pre>
</details>
{% else %}
{% if s.type == 'external' and s.src %}
<button
type="button"
class="btn-analyze-snippet inline-flex items-center gap-2 rounded-lg px-3 py-1.5 bg-blue-600 hover:bg-blue-500 text-white text-xs"
data-url="{{ s.src }}"
data-job="{{ uuid }}">
Analyze external script
</button>
{% else %}
<span class="text-gray-500">N/A</span>
{% endif %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-sm text-gray-500">No suspicious scripts detected.</p>
{% endif %}
<p class="mt-2"><a href="#url-overview" class="text-sm text-gray-400 hover:text-blue-400">Back to top</a></p>
</section>