Files

294 lines
6.7 KiB
Vue
Raw Permalink 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.
<script lang="ts" setup>
const challenges = [
{
name: "Cloudflare Interstitial",
desc: '"Just a moment" redirect page',
trawl: "415s · fresh context",
trawlSupport: "yes",
flaresolver: "1118s",
fsSupport: "partial",
byparr: "1318s",
byparrSupport: "partial",
},
{
name: "CF Turnstile Widget",
desc: "Embedded checkbox on target page",
trawl: "shadow DOM click · auto",
trawlSupport: "yes",
flaresolver: "not handled",
fsSupport: "no",
byparr: "not handled",
byparrSupport: "no",
},
{
name: "Akamai Bot Manager",
desc: "Behavioral interstitial + press-and-hold",
trawl: "sensor flow · human-like input",
trawlSupport: "yes",
flaresolver: "not handled",
fsSupport: "no",
byparr: "not handled",
byparrSupport: "no",
},
{
name: "Imperva / Incapsula",
desc: "Sensor-cookie WAF challenge",
trawl: "browser sensor flow · best effort",
trawlSupport: "partial",
flaresolver: "not handled",
fsSupport: "no",
byparr: "not handled",
byparrSupport: "no",
},
{
name: "reCAPTCHA v2",
desc: "Google checkbox + audio challenge",
trawl: "Google STT audio · free",
trawlSupport: "yes",
flaresolver: "not handled",
fsSupport: "no",
byparr: "not handled",
byparrSupport: "no",
},
{
name: "hCaptcha",
desc: "Checkbox + image challenge",
trawl: "auto-pass path · click",
trawlSupport: "yes",
flaresolver: "not handled",
fsSupport: "no",
byparr: "not handled",
byparrSupport: "no",
},
{
name: "GeeTest v4 Slide",
desc: "Drag-to-fit puzzle captcha",
trawl: "canvas gap detection · drag",
trawlSupport: "yes",
flaresolver: "not handled",
fsSupport: "no",
byparr: "not handled",
byparrSupport: "no",
},
{
name: "No protection",
desc: "Plain HTML, no bot check",
trawl: "< 100ms · plain HTTP",
trawlSupport: "yes",
flaresolver: "23s · full browser",
fsSupport: "partial",
byparr: "23s · full browser",
byparrSupport: "partial",
},
]
function icon(s: string) {
return s === "yes" ? "✓" : s === "partial" ? "~" : "✗"
}
function iconClass(s: string) {
return s === "yes" ? "cell-yes" : s === "partial" ? "cell-partial" : "cell-no"
}
</script>
<template>
<section id="captcha" class="section">
<div class="container">
<p class="eyebrow">challenge coverage</p>
<h2 class="section-title">solve more than Cloudflare.</h2>
<p class="section-sub">
Native flows cover Cloudflare, Akamai, Imperva, and the captchas embedded inside protected pages all within
the same self-hosted tier engine.
</p>
<div class="table-wrap">
<table>
<caption class="sr-only">
Challenge handling support across TRAWL, FlareSolverr, and Byparr
</caption>
<thead>
<tr>
<th class="col-name">challenge type</th>
<th class="col-trawl"><span class="accent">TRAWL</span></th>
<th>FlareSolverr</th>
<th>Byparr</th>
</tr>
</thead>
<tbody>
<tr v-for="c in challenges" :key="c.name">
<td class="col-name">
<span class="challenge-name">{{ c.name }}</span>
<span class="challenge-desc">{{ c.desc }}</span>
</td>
<td class="col-trawl">
<span :class="iconClass(c.trawlSupport)">{{ icon(c.trawlSupport) }}</span>
{{ c.trawl }}
</td>
<td>
<span :class="iconClass(c.fsSupport)">{{ icon(c.fsSupport) }}</span>
{{ c.flaresolver }}
</td>
<td>
<span :class="iconClass(c.byparrSupport)">{{ icon(c.byparrSupport) }}</span>
{{ c.byparr }}
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td class="col-name score-spacer"></td>
<td class="col-trawl score-cell">
<span class="score-num accent">8</span>
<span class="score-label">challenge paths</span>
</td>
<td class="score-cell">
<span class="score-num">1</span>
<span class="score-label">dedicated path</span>
</td>
<td class="score-cell">
<span class="score-num">1</span>
<span class="score-label">dedicated path</span>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</section>
</template>
<style scoped>
.section-title {
font-size: clamp(22px, 3vw, 34px);
font-weight: 700;
letter-spacing: -0.02em;
color: var(--text);
margin-bottom: 12px;
}
.section-sub {
font-size: 13px;
line-height: 1.75;
color: var(--text-muted);
max-width: 580px;
margin-bottom: 48px;
}
.table-wrap {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
border: 1px solid var(--border);
}
table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
min-width: 580px;
}
th,
td {
padding: 12px 18px;
text-align: left;
border-bottom: 1px solid var(--border);
white-space: nowrap;
}
th {
font-size: 10px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-muted);
background: var(--bg-subtle);
font-weight: 600;
}
td {
color: var(--text-muted);
}
tbody tr:last-child td {
border-bottom: none;
}
tbody tr:hover td {
background: var(--bg-subtle);
}
/* Challenge name column — allow wrapping */
.col-name {
white-space: normal;
min-width: 160px;
color: var(--text);
}
.challenge-name {
display: block;
font-weight: 600;
color: var(--text);
margin-bottom: 2px;
}
.challenge-desc {
display: block;
font-size: 11px;
color: var(--text-muted);
}
/* TRAWL column highlight */
.col-trawl {
background: var(--accent-tint);
}
tbody tr:hover .col-trawl {
background: color-mix(in srgb, var(--accent-tint) 160%, transparent);
}
/* Support icons */
.cell-yes {
color: var(--accent);
font-weight: 700;
margin-right: 6px;
}
.cell-no {
color: var(--text-muted);
opacity: 0.4;
margin-right: 6px;
}
.cell-partial {
color: #f59e0b;
margin-right: 6px;
}
/* Score footer */
tfoot tr td {
border-top: 2px solid var(--border-strong);
border-bottom: none;
background: var(--bg-subtle);
}
.score-spacer {
background: var(--bg-subtle);
}
.score-cell {
display: table-cell;
}
.score-num {
font-size: 16px;
font-weight: 700;
letter-spacing: -0.02em;
color: var(--text-muted);
margin-right: 6px;
}
.score-label {
font-size: 10px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--text-muted);
opacity: 0.6;
}
</style>