refactor(templates): extract sections to includes; feat(css): add reusable components; fix(tables): lock column widths & stop snippet reflow
- Move large sections into partials: - forms → templates/_include_forms.html - scripts → templates/_include_scripts.html (if applicable) - Add Tailwind component classes in assets/input.css: - .badge + variants (.badge-ok, .badge-warn, .badge-danger, .badge-muted, .badge-info, .badge-success, .badge-success-solid) - .chip - .card - Override .border-gray-800 to fixed color (no opacity var) - Stabilize table layouts: - Use table-fixed + <colgroup> with percentage widths - Forms cols: 10% / 10% / 15% / 45% / 25% - Scripts cols: 10% / 20% / 45% / 25% - Remove inner fixed-width wrapper from snippet cells; use w-full + wrapping to prevent column jitter - Update templates to use new badge/chip classes
This commit is contained in:
120
app/templates/partials/result_scripts.html
Normal file
120
app/templates/partials/result_scripts.html
Normal file
@@ -0,0 +1,120 @@
|
||||
<!-- /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 & 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="#top-jump-list" class="text-sm text-gray-400 hover:text-blue-400">Back to top</a></p>
|
||||
</section>
|
||||
Reference in New Issue
Block a user