Skip to content

Commit 5ee4376

Browse files
authored
Merge pull request #8 from owtf/codex/passive-scanner-tailwind-rewrite
Rewrite online passive scanner in Tailwind
2 parents 825a70d + 05d3d70 commit 5ee4376

File tree

9 files changed

+522
-363
lines changed

9 files changed

+522
-363
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ jobs:
3939
root / 'passive-scanner' / 'report' / 'index.html',
4040
root / 'online-passive-scanner' / 'index.html',
4141
root / 'online-passive-scanner' / 'report.html',
42-
root / 'online-passive-scanner' / 'report-legacy.html',
4342
]
4443
missing_html = [str(p) for p in html_files if not p.exists()]
4544
if missing_html:
@@ -48,14 +47,6 @@ jobs:
4847
4948
pattern = re.compile(r'(?:href|src)=["\']([^"\']+)["\']')
5049
missing_targets = []
51-
ignore_exact = {
52-
'link',
53-
'link_to_plugin',
54-
}
55-
ignore_regex = [
56-
re.compile(r'^http__.+\.html$'),
57-
]
58-
5950
for html_file in html_files:
6051
text = html_file.read_text(encoding='utf-8')
6152
for target in pattern.findall(text):
@@ -75,12 +66,6 @@ jobs:
7566
if not normalized:
7667
continue
7768
78-
if normalized in ignore_exact:
79-
continue
80-
81-
if any(regex.match(normalized) for regex in ignore_regex):
82-
continue
83-
8469
if normalized.startswith('/'):
8570
candidate = root / normalized[1:]
8671
else:

.github/workflows/main.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,8 @@ jobs:
4848
Path('passive-scanner/report/index.html'),
4949
Path('online-passive-scanner/index.html'),
5050
Path('online-passive-scanner/report.html'),
51-
Path('online-passive-scanner/report-legacy.html'),
5251
]
5352
pattern = re.compile(r'(?:href|src)=["\']([^"\']+)["\']')
54-
ignore_exact = {'link', 'link_to_plugin'}
55-
ignore_regex = [re.compile(r'^http__.+\.html$')]
5653
5754
missing_targets = []
5855
for html_file in html_files:
@@ -74,12 +71,6 @@ jobs:
7471
if not normalized:
7572
continue
7673
77-
if normalized in ignore_exact:
78-
continue
79-
80-
if any(regex.match(normalized) for regex in ignore_regex):
81-
continue
82-
8374
if normalized.startswith('/'):
8475
candidate = root / normalized[1:]
8576
else:

README.md

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,16 @@ The generated output is written to `_site/` and should not be committed.
2727

2828
The initial Jekyll migration preserves existing OWTF landing page content and formatting. Modernization changes should be made incrementally after parity sign-off.
2929

30-
## Passive scanner source
30+
## Passive scanner routes
3131

32-
Passive scanner integration in this repository is pinned to upstream `owtf/online-passive-scanner`:
33-
34-
- https://github.com/owtf/online-passive-scanner
35-
36-
See `/docs/PASSIVE_SCANNER_SOURCE.md` for the integration contract.
37-
38-
The scanner is vendored at `/online-passive-scanner/` and is accessible at:
32+
The online passive scanner is now implemented as a Jekyll-processed Tailwind + vanilla JS experience in this repository:
3933

4034
- `/online-passive-scanner/`
35+
- `/online-passive-scanner/report.html`
36+
37+
Compatibility routes are kept for older links and redirect to the online scanner routes:
4138

42-
A Jekyll-native launcher page is available at:
39+
- `/passive-scanner/` -> `/online-passive-scanner/`
40+
- `/passive-scanner/report/` -> `/online-passive-scanner/report.html`
4341

44-
- `/passive-scanner/`
45-
- `/passive-scanner/report/`
42+
See `/docs/PASSIVE_SCANNER_SOURCE.md` for implementation details and maintenance scope.

_includes/header.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<header class="section-container pt-10"><nav class="flex items-center justify-between rounded-full border border-slate-200 bg-white/80 px-6 py-4 shadow-card backdrop-blur"><a class="flex items-center gap-3" href="/"><span class="flex h-10 w-10 items-center justify-center rounded-full border border-slate-200 bg-white text-sm font-semibold text-slate-900">OW</span><span class="hidden text-sm font-medium tracking-wide text-slate-600 sm:inline">Offensive Web Testing Framework</span></a><div class="hidden items-center gap-8 text-sm text-slate-600 md:flex">
2-
<a href="#overview" class="transition hover:text-slate-900">Overview</a><a href="#capabilities" class="transition hover:text-slate-900">Capabilities</a><a href="#standards" class="transition hover:text-slate-900">Standards</a><a href="/passive-scanner/" class="transition hover:text-slate-900">Scanner</a><a href="https://owtf.readthedocs.io/en/develop/" class="transition hover:text-slate-900">Docs</a>
2+
<a href="#overview" class="transition hover:text-slate-900">Overview</a><a href="#capabilities" class="transition hover:text-slate-900">Capabilities</a><a href="#standards" class="transition hover:text-slate-900">Standards</a><a href="/online-passive-scanner/" class="transition hover:text-slate-900">Scanner</a><a href="https://owtf.readthedocs.io/en/develop/" class="transition hover:text-slate-900">Docs</a>
33
</div>
44
<div class="flex items-center gap-3">
55
<a href="https://github.com/owtf/owtf" class="hidden whitespace-nowrap rounded-full border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-700 transition hover:border-slate-300 hover:text-slate-900 lg:inline-flex">GitHub</a><a href="https://github.com/owtf/owtf/releases" class="inline-flex items-center gap-2 rounded-full bg-slate-900 px-4 py-2 text-xs font-semibold text-white transition hover:bg-slate-700">Get OWTF<span class="text-base leading-none">&rarr;</span></a>

docs/PASSIVE_SCANNER_SOURCE.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1-
# Passive Scanner Source of Truth
1+
# Passive Scanner Implementation Scope
22

3-
The passive scanner integration in this repository must use upstream code from:
3+
The scanner experience published in this repository is now a local implementation (Jekyll + Tailwind + vanilla JavaScript), served from:
44

5-
- `https://github.com/owtf/online-passive-scanner`
5+
- `/online-passive-scanner/`
6+
- `/online-passive-scanner/report.html`
67

7-
## Integration contract
8+
## Design goals
89

9-
1. Do not reimplement scanner logic locally when upstream code is available.
10-
2. Use a sync model (git subtree or submodule) and record the upstream commit/version used.
11-
3. Keep wrapper logic in this repository limited to configuration, execution, and report shaping for site publishing.
10+
1. Keep scanner UI and report pages fully Jekyll-compatible for GitHub Pages.
11+
2. Avoid legacy iframe embedding and legacy jQuery/Bootstrap dependencies.
12+
3. Preserve compatibility for historical links via redirects:
13+
- `/passive-scanner/` redirects to `/online-passive-scanner/`
14+
- `/passive-scanner/report/` redirects to `/online-passive-scanner/report.html`
1215

13-
## Planned implementation
16+
## Current scanner behavior
1417

15-
Current implementation in this repository:
18+
1. URL posture checks (scheme, credentials in URL, risky route patterns, sensitive query keys).
19+
2. DNS posture checks through DNS-over-HTTPS (`dns.google/resolve`) for A/AAAA/CAA/MX/NS/TXT.
20+
3. Risk scoring and findings rendering in-browser.
1621

17-
1. Upstream scanner is vendored under `online-passive-scanner/`.
18-
2. Upstream provenance is tracked in `online-passive-scanner/UPSTREAM_SOURCE.txt`.
19-
3. Sync helper script is available at `scripts/sync-online-passive-scanner.sh`.
22+
## Maintenance notes
2023

21-
Planned next implementation:
22-
23-
1. Add a local wrapper entrypoint under `tools/passive_scanner/`.
24-
2. Add scheduled workflow to run passive scan automation and publish `_data/passive_scan/latest.json`.
24+
1. Keep logic and UX inside repository-owned pages under `online-passive-scanner/`.
25+
2. If upstream OWTF scanner work is reused, treat it as reference input and port intentionally rather than embedding legacy runtime directly.

online-passive-scanner/index.html

Lines changed: 78 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,175 +1,89 @@
1-
<!DOCTYPE html>
2-
<html lang="en">
3-
<head>
4-
<meta charset="utf-8" />
5-
<meta name="viewport" content="width=device-width, initial-scale=1" />
6-
<meta name="description" content="OWASP OWTF Summary Report" />
7-
<meta name="author" content="OWASP OWTF Team" />
8-
<title>OWASP OWTF Summary Report</title>
9-
<link
10-
rel="stylesheet"
11-
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
12-
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
13-
crossorigin="anonymous"
14-
/>
15-
<style>
16-
body {
17-
background-color: #f5f7fa;
18-
min-height: 100vh;
19-
position: relative;
20-
}
21-
22-
.github-ribbon {
23-
position: fixed;
24-
top: 1rem;
25-
right: -3.5rem;
26-
display: block;
27-
width: 12rem;
28-
padding: 0.75rem 0;
29-
background: linear-gradient(135deg, #1f2328, #24292f);
30-
color: #f8fbff;
31-
text-align: center;
32-
text-decoration: none;
33-
font-weight: 600;
34-
font-size: 0.9rem;
35-
letter-spacing: 0.05em;
36-
text-transform: uppercase;
37-
transform: rotate(45deg);
38-
transform-origin: top right;
39-
box-shadow: 0 6px 12px rgba(15, 23, 42, 0.35);
40-
z-index: 1000;
41-
}
42-
43-
.github-ribbon:focus-visible {
44-
outline: 2px solid #f0f6ff;
45-
outline-offset: 2px;
46-
}
1+
---
2+
layout: default
3+
title: "OWTF Online Passive Scanner"
4+
description: "Run OWTF passive URL and DNS reconnaissance checks from a modern Tailwind interface."
5+
---
6+
<div class="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[420px] bg-gradient-to-b from-slate-100 via-white to-white"></div>
7+
<div class="relative flex min-h-screen flex-col">
8+
{% include header.html %}
9+
<main class="space-y-32 pb-16">
10+
<section class="section-container pb-24 pt-24">
11+
<div class="max-w-3xl space-y-6">
12+
<p class="text-xs font-semibold uppercase tracking-[0.4em] text-slate-500">Online passive scanner</p>
13+
<h1 class="text-4xl font-semibold tracking-tight text-slate-900 sm:text-5xl">Quick passive reconnaissance for web targets</h1>
14+
<p class="text-lg leading-relaxed text-slate-600">This scanner reviews a target URL and its DNS posture for externally visible risk indicators. It is passive-only and does not run exploit payloads against the target.</p>
15+
</div>
4716

48-
@media (max-width: 576px) {
49-
.github-ribbon {
50-
width: 10rem;
51-
right: -3rem;
52-
font-size: 0.75rem;
53-
}
54-
}
17+
<div class="mt-12 rounded-3xl border border-slate-200 bg-white p-8 shadow-card">
18+
<label for="target-url" class="text-sm font-semibold text-slate-900">Target URL</label>
19+
<input id="target-url" type="url" placeholder="https://www.example.com" class="mt-3 block rounded-2xl border border-slate-200 bg-white p-5 text-sm text-slate-900" style="width:100%" autocomplete="url" />
20+
<p id="scan-error" class="mt-2 text-sm text-slate-600" style="display:none"></p>
21+
<div class="mt-6 flex flex-col gap-4 sm:flex-row sm:items-center">
22+
<button id="open-report" class="inline-flex items-center justify-center gap-2 rounded-full bg-slate-900 px-6 py-3 text-sm font-semibold text-white transition hover:bg-slate-700">Run passive scan<span class="text-lg leading-none">&rarr;</span></button>
23+
<a href="https://github.com/owtf/online-passive-scanner" class="inline-flex items-center justify-center gap-2 rounded-full border border-slate-200 px-6 py-3 text-sm font-semibold text-slate-700 transition hover:border-slate-300 hover:text-slate-900">Scanner source</a>
24+
</div>
25+
</div>
5526

56-
.page-header img {
57-
max-width: 150px;
58-
}
27+
<div class="mt-8 grid gap-4 md:grid-cols-3">
28+
<div class="rounded-2xl border border-slate-200 bg-white p-6 shadow-inset">
29+
<p class="text-sm font-semibold text-slate-900">URL posture checks</p>
30+
<p class="mt-2 text-sm text-slate-600">Flags insecure schemes, exposed credentials, risky parameters, and suspicious route patterns.</p>
31+
</div>
32+
<div class="rounded-2xl border border-slate-200 bg-white p-6 shadow-inset">
33+
<p class="text-sm font-semibold text-slate-900">DNS posture checks</p>
34+
<p class="mt-2 text-sm text-slate-600">Queries A, AAAA, CAA, MX, NS, and TXT records through DNS-over-HTTPS for baseline exposure signals.</p>
35+
</div>
36+
<div class="rounded-2xl border border-slate-200 bg-white p-6 shadow-inset">
37+
<p class="text-sm font-semibold text-slate-900">Shareable report URLs</p>
38+
<p class="mt-2 text-sm text-slate-600">Each report can be reopened by sharing a link with the encoded `#q=` target value.</p>
39+
</div>
40+
</div>
41+
</section>
42+
</main>
43+
{% include footer.html %}
44+
</div>
5945

60-
.page-header h1 {
61-
font-size: 2rem;
62-
margin-bottom: 0;
63-
}
46+
<script>
47+
(function () {
48+
var input = document.getElementById('target-url');
49+
var button = document.getElementById('open-report');
50+
var error = document.getElementById('scan-error');
51+
var reportPath = "{{ '/online-passive-scanner/report.html' | relative_url }}";
6452

65-
#targetbar h2 {
66-
font-weight: 600;
53+
function setError(message) {
54+
if (!message) {
55+
error.style.display = 'none';
56+
error.textContent = '';
57+
return;
6758
}
59+
error.style.display = 'block';
60+
error.textContent = message;
61+
}
6862

69-
.error-message {
70-
display: none;
63+
function openReport() {
64+
setError('');
65+
var value = (input.value || '').trim();
66+
if (!value) {
67+
setError('Please enter a URL to continue.');
68+
input.focus();
69+
return;
7170
}
72-
</style>
73-
<script>
74-
const isGithubPages =
75-
window.location.hostname === "owtf.github.io" &&
76-
window.location.pathname.startsWith("/online-passive-scanner");
7771

78-
if (isGithubPages && window.location.protocol === "http:") {
79-
const { host, pathname, search, hash } = window.location;
80-
window.location.replace(`https://${host}${pathname}${search}${hash}`);
72+
try {
73+
var parsed = new URL(value);
74+
window.location.assign(reportPath + '#q=' + encodeURIComponent(parsed.href));
75+
} catch (err) {
76+
setError('The value provided is not a valid URL.');
77+
input.focus();
8178
}
82-
</script>
83-
</head>
84-
<body>
85-
<div class="container py-5">
86-
<a
87-
href="https://github.com/owtf/online-passive-scanner"
88-
aria-label="Fork me on GitHub"
89-
class="github-ribbon"
90-
>
91-
Fork me on GitHub
92-
</a>
93-
<header class="page-header row align-items-center g-4 mb-5">
94-
<div class="col-md-3 text-center text-md-start">
95-
<a href="https://owasp.org/www-project-owtf/">
96-
<img
97-
src="images/owtf-logo.svg"
98-
class="img-fluid rounded"
99-
alt="OWASP OWTF Logo"
100-
width="150"
101-
height="150"
102-
/>
103-
</a>
104-
</div>
105-
<div class="col-md-9">
106-
<h1 class="fw-semibold">Online Passive Scanner</h1>
107-
<p class="text-muted mb-0">
108-
Version 1.0.1 · Release <strong>Lionheart</strong>
109-
<span class="visually-hidden" id="seed">2NfaMaxkF_</span>
110-
</p>
111-
</div>
112-
</header>
113-
<section id="targetbar" class="row mb-4">
114-
<div class="col">
115-
<h2 class="h4">Enter the target URL</h2>
116-
<p class="text-muted">
117-
Paste the URL you would like to review. We will generate a passive report using the
118-
bundled sample data.
119-
</p>
120-
</div>
121-
</section>
122-
<form class="row gy-3" novalidate>
123-
<div class="col-lg-8">
124-
<label class="form-label" for="q">Target URL</label>
125-
<input
126-
type="url"
127-
id="q"
128-
name="q"
129-
class="form-control form-control-lg"
130-
placeholder="https://www.example.com"
131-
autocomplete="url"
132-
required
133-
/>
134-
<p id="error" class="error-message text-danger mt-2" role="alert"></p>
135-
</div>
136-
<div class="col-lg-4 d-flex align-items-end">
137-
<button type="submit" class="btn btn-primary btn-lg w-100" id="submit">Generate report</button>
138-
</div>
139-
</form>
140-
<footer id="footer" class="mt-5 text-muted">
141-
Built during GSoC 2014 by the <a href="https://owtf.github.io/">OWTF team</a>.
142-
</footer>
143-
</div>
144-
<script>
145-
const form = document.querySelector("form");
146-
const input = document.getElementById("q");
147-
const errorMessage = document.getElementById("error");
79+
}
14880

149-
form.addEventListener("submit", (event) => {
81+
button.addEventListener('click', openReport);
82+
input.addEventListener('keydown', function (event) {
83+
if (event.key === 'Enter') {
15084
event.preventDefault();
151-
errorMessage.style.display = "none";
152-
errorMessage.textContent = "";
153-
154-
const value = input.value.trim();
155-
if (!value) {
156-
errorMessage.textContent = "Please enter a URL to continue.";
157-
errorMessage.style.display = "block";
158-
input.focus();
159-
return;
160-
}
161-
162-
try {
163-
const parsed = new URL(value);
164-
const destination = new URL("report.html", window.location.href);
165-
destination.hash = `q=${encodeURIComponent(parsed.href)}`;
166-
window.location.assign(destination.toString());
167-
} catch (error) {
168-
errorMessage.textContent = "The value provided is not a valid URL. Please check and try again.";
169-
errorMessage.style.display = "block";
170-
input.focus();
171-
}
172-
});
173-
</script>
174-
</body>
175-
</html>
85+
openReport();
86+
}
87+
});
88+
})();
89+
</script>

0 commit comments

Comments
 (0)