@lockhawk/core 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -4
- package/dist/index.js +201 -52
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -547,10 +547,12 @@ declare function toSarifString(result: ScanResult): string;
|
|
|
547
547
|
* Produce a self-contained HTML report for a scan result.
|
|
548
548
|
*
|
|
549
549
|
* The page is fully server-rendered: no client-side JavaScript, no embedded
|
|
550
|
-
* data blob, no external resources. Every dynamic value is
|
|
551
|
-
* layout flows
|
|
552
|
-
* text lives in a collapsed, contained block — so the report cannot
|
|
553
|
-
* viewport or render attacker-controlled advisory text as live
|
|
550
|
+
* data blob, no external resources (fonts included). Every dynamic value is
|
|
551
|
+
* HTML-escaped, the layout flows within a width-constrained container, and long
|
|
552
|
+
* advisory text lives in a collapsed, contained block — so the report cannot
|
|
553
|
+
* overflow the viewport or render attacker-controlled advisory text as live
|
|
554
|
+
* markup. Copy affordances use CSS `user-select: all` (one click selects a whole
|
|
555
|
+
* command/version) rather than a clipboard script, preserving the no-JS model.
|
|
554
556
|
*/
|
|
555
557
|
declare function toHtml(result: ScanResult): string;
|
|
556
558
|
|
package/dist/index.js
CHANGED
|
@@ -2147,12 +2147,12 @@ function toHtml(result) {
|
|
|
2147
2147
|
return renderHtml(result);
|
|
2148
2148
|
}
|
|
2149
2149
|
var SEVERITY_COLORS = {
|
|
2150
|
-
critical: "#
|
|
2151
|
-
high: "#
|
|
2152
|
-
medium: "#
|
|
2153
|
-
low: "#
|
|
2154
|
-
unknown: "#
|
|
2155
|
-
none: "#
|
|
2150
|
+
critical: "#ff5d67",
|
|
2151
|
+
high: "#ff9f43",
|
|
2152
|
+
medium: "#ffd23f",
|
|
2153
|
+
low: "#5b9dff",
|
|
2154
|
+
unknown: "#8b95a5",
|
|
2155
|
+
none: "#34d399"
|
|
2156
2156
|
};
|
|
2157
2157
|
function esc(text) {
|
|
2158
2158
|
return text.replace(
|
|
@@ -2168,41 +2168,83 @@ function httpHref(url) {
|
|
|
2168
2168
|
return void 0;
|
|
2169
2169
|
}
|
|
2170
2170
|
}
|
|
2171
|
+
var MARK = `<svg class="mark" viewBox="0 0 48 48" fill="none" aria-hidden="true">
|
|
2172
|
+
<defs>
|
|
2173
|
+
<linearGradient id="lh-g" x1="6" y1="4" x2="40" y2="44" gradientUnits="userSpaceOnUse">
|
|
2174
|
+
<stop stop-color="#ffc266"/><stop offset="1" stop-color="#ff5d67"/>
|
|
2175
|
+
</linearGradient>
|
|
2176
|
+
</defs>
|
|
2177
|
+
<path d="M24 4 40 11v13c0 9-7 15.2-16 20-9-4.8-16-11-16-20V11Z" stroke="url(#lh-g)" stroke-width="2.2" stroke-linejoin="round"/>
|
|
2178
|
+
<path d="M14 18.5 24 15l10 3.5" stroke="url(#lh-g)" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
2179
|
+
<circle cx="24" cy="23" r="3.2" fill="url(#lh-g)"/>
|
|
2180
|
+
<path d="M24 26.2V32" stroke="url(#lh-g)" stroke-width="2.2" stroke-linecap="round"/>
|
|
2181
|
+
</svg>`;
|
|
2182
|
+
var CHEVRON = `<svg class="chev" width="11" height="11" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M6 4l4 4-4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
2183
|
+
function renderFix(rec) {
|
|
2184
|
+
const match = rec.match(/`([^`]+)`/);
|
|
2185
|
+
const command = match?.[1];
|
|
2186
|
+
const pending = /^No fixed version/i.test(rec);
|
|
2187
|
+
const prose = command ? rec.replace(/\s*\(e\.g\.[^)]*\)/, "") : rec;
|
|
2188
|
+
const bar = command ? `<div class="cmdbar"><code class="cmd" title="Click to select, then \u2318/Ctrl+C to copy">${esc(
|
|
2189
|
+
command
|
|
2190
|
+
)}</code><span class="hint">click to select</span></div>` : "";
|
|
2191
|
+
return `<div class="fix${pending ? " fix-pending" : ""}">
|
|
2192
|
+
<div class="fix-label">${CHEVRON.replace("chev", "spark")}${pending ? "No fix available yet" : "Recommended fix"}</div>
|
|
2193
|
+
<p class="fix-text">${esc(prose)}</p>${bar}
|
|
2194
|
+
</div>`;
|
|
2195
|
+
}
|
|
2171
2196
|
function renderHtml(result) {
|
|
2172
2197
|
const { summary } = result;
|
|
2173
2198
|
const cards = ["critical", "high", "medium", "low", "unknown"].map(
|
|
2174
|
-
(sev) => `
|
|
2175
|
-
<div class="card"
|
|
2199
|
+
(sev, i) => `
|
|
2200
|
+
<div class="card${summary[sev] === 0 ? " zero" : ""}" style="--c:${SEVERITY_COLORS[sev]};--i:${i}">
|
|
2176
2201
|
<div class="count">${summary[sev]}</div><div class="label">${sev}</div>
|
|
2177
2202
|
</div>`
|
|
2178
2203
|
).join("");
|
|
2179
|
-
const rows = result.findings.map((f) => {
|
|
2204
|
+
const rows = result.findings.map((f, i) => {
|
|
2180
2205
|
const path = f.dependencyPaths[0]?.join(" \u203A ") ?? f.packageName;
|
|
2181
|
-
const refs = f.references.slice(0, 3).map((u) => httpHref(u)).filter((u) => u !== void 0).map((u) => `<a href="${esc(u)}" target="_blank" rel="noopener">link</a>`).join("
|
|
2182
|
-
const
|
|
2206
|
+
const refs = f.references.slice(0, 3).map((u) => httpHref(u)).filter((u) => u !== void 0).map((u) => `<a class="ref" href="${esc(u)}" target="_blank" rel="noopener">link</a>`).join("");
|
|
2207
|
+
const fixed = f.fixedVersions[0] ? `<span class="fixchip" title="Click to select, then \u2318/Ctrl+C to copy">${esc(
|
|
2208
|
+
f.fixedVersions[0]
|
|
2209
|
+
)}</span>` : '<span class="nofix">\u2014</span>';
|
|
2183
2210
|
return `
|
|
2184
|
-
<tr>
|
|
2185
|
-
<td><span class="pill" style="
|
|
2211
|
+
<tr class="main" style="--i:${i}">
|
|
2212
|
+
<td><span class="pill" style="--c:${SEVERITY_COLORS[f.severity.level]}">${esc(
|
|
2186
2213
|
f.severity.level
|
|
2187
2214
|
)}${f.severity.score ? ` ${f.severity.score}` : ""}</span></td>
|
|
2188
|
-
<td><
|
|
2189
|
-
|
|
2215
|
+
<td><span class="pkg">${esc(f.packageName)}@${esc(f.version)}</span><div class="scope">${esc(
|
|
2216
|
+
f.scope
|
|
2217
|
+
)}${f.direct ? " \xB7 direct" : ""}</div></td>
|
|
2218
|
+
<td><a class="advid" href="https://osv.dev/${encodeURIComponent(
|
|
2190
2219
|
f.id
|
|
2191
|
-
)}</a></td>
|
|
2192
|
-
<td>${esc(f.summary)}
|
|
2193
|
-
<td>${
|
|
2220
|
+
)}" target="_blank" rel="noopener">${esc(f.id)}</a></td>
|
|
2221
|
+
<td><div class="summary">${esc(f.summary)}</div><div class="path">${esc(path)}</div></td>
|
|
2222
|
+
<td>${fixed}</td>
|
|
2194
2223
|
<td class="refs">${refs}</td>
|
|
2195
2224
|
</tr>
|
|
2196
2225
|
<tr class="detail-row">
|
|
2197
2226
|
<td colspan="6">
|
|
2227
|
+
${f.recommendation ? renderFix(f.recommendation) : ""}
|
|
2198
2228
|
<details>
|
|
2199
|
-
<summary>Advisory details</summary>
|
|
2200
|
-
|
|
2229
|
+
<summary>${CHEVRON}<span>Advisory details</span></summary>
|
|
2230
|
+
<div class="detail-body">
|
|
2231
|
+
<div class="advisory-wrap">
|
|
2232
|
+
<div class="advisory-head"><span class="dots"><i></i><i></i><i></i></span><span class="advisory-title">advisory \xB7 ${esc(
|
|
2233
|
+
f.id
|
|
2234
|
+
)}</span></div>
|
|
2235
|
+
<pre class="advisory">${esc(f.details ?? f.summary)}</pre>
|
|
2236
|
+
</div>
|
|
2237
|
+
</div>
|
|
2201
2238
|
</details>
|
|
2202
2239
|
</td>
|
|
2203
2240
|
</tr>`;
|
|
2204
2241
|
}).join("");
|
|
2205
|
-
const dbWarnings = result.database.warnings.map((w) => `<div class="warn">\u26A0
|
|
2242
|
+
const dbWarnings = result.database.warnings.map((w) => `<div class="warn"><span class="warn-i">\u26A0</span>${esc(w)}</div>`).join("");
|
|
2243
|
+
const resultsMeta = result.findings.length === 0 ? "" : `<div class="results-meta">
|
|
2244
|
+
<span><b>${summary.total}</b> findings</span><span class="sep">\xB7</span>
|
|
2245
|
+
<span><b>${summary.vulnerablePackages}</b> packages</span><span class="sep">\xB7</span>
|
|
2246
|
+
<span><b>${summary.fixable}</b> fixable</span>
|
|
2247
|
+
</div>`;
|
|
2206
2248
|
return `<!doctype html>
|
|
2207
2249
|
<html lang="en">
|
|
2208
2250
|
<head>
|
|
@@ -2210,51 +2252,158 @@ function renderHtml(result) {
|
|
|
2210
2252
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
2211
2253
|
<title>lockhawk report \u2014 ${esc(result.target.root.name)}</title>
|
|
2212
2254
|
<style>
|
|
2213
|
-
:root {
|
|
2255
|
+
:root {
|
|
2256
|
+
--bg:#070a12; --panel:#11151e; --panel-2:#141926; --elev:#171d2b;
|
|
2257
|
+
--border:#222a3a; --border-soft:#1b2230;
|
|
2258
|
+
--text:#f1f4fa; --muted:#aeb8cc; --faint:#808b9f;
|
|
2259
|
+
--accent:#ffc266; --fix:#48e0a6;
|
|
2260
|
+
--glass: rgba(255,255,255,.055); --glass-2: rgba(255,255,255,.09);
|
|
2261
|
+
--glass-line: rgba(255,255,255,.14); --glass-hi: inset 0 1px 0 rgba(255,255,255,.10);
|
|
2262
|
+
--glass-dark: rgba(8,11,18,.42);
|
|
2263
|
+
--blur: blur(18px) saturate(160%);
|
|
2264
|
+
--mono: ui-monospace,'SF Mono','JetBrains Mono','Cascadia Code','Fira Code',Menlo,Consolas,monospace;
|
|
2265
|
+
--sans: -apple-system,BlinkMacSystemFont,'SF Pro Text','Segoe UI',system-ui,Roboto,sans-serif;
|
|
2266
|
+
color-scheme: dark;
|
|
2267
|
+
}
|
|
2214
2268
|
* { box-sizing: border-box; }
|
|
2215
2269
|
html, body { max-width: 100%; overflow-x: hidden; }
|
|
2216
|
-
body {
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
.
|
|
2239
|
-
|
|
2240
|
-
.
|
|
2241
|
-
|
|
2270
|
+
body {
|
|
2271
|
+
margin: 0; min-height: 100vh; font: 15px/1.6 var(--sans); color: var(--text);
|
|
2272
|
+
background:
|
|
2273
|
+
radial-gradient(820px 620px at 8% -8%, rgba(91,157,255,.22), transparent 58%),
|
|
2274
|
+
radial-gradient(760px 600px at 94% -4%, rgba(255,93,103,.16), transparent 56%),
|
|
2275
|
+
radial-gradient(900px 760px at 80% 108%, rgba(72,224,166,.16), transparent 58%),
|
|
2276
|
+
radial-gradient(720px 640px at 16% 98%, rgba(255,179,71,.13), transparent 58%),
|
|
2277
|
+
linear-gradient(180deg, #0a1020, #070a12 60%);
|
|
2278
|
+
background-attachment: fixed;
|
|
2279
|
+
-webkit-font-smoothing: antialiased;
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
/* ---- Header / wordmark ------------------------------------------------ */
|
|
2283
|
+
header { position: relative; overflow: hidden; padding: 30px clamp(18px,5vw,40px) 28px; background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.03)); -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur); border-bottom: 1px solid var(--glass-line); box-shadow: var(--glass-hi); }
|
|
2284
|
+
header::before { content:''; position:absolute; inset:0; pointer-events:none;
|
|
2285
|
+
background: radial-gradient(620px 220px at 10% 0%, rgba(255,194,102,.12), transparent 70%),
|
|
2286
|
+
radial-gradient(520px 240px at 58% -25%, rgba(255,93,103,.10), transparent 70%); }
|
|
2287
|
+
header::after { content:''; position:absolute; inset:0; opacity:.35; pointer-events:none;
|
|
2288
|
+
background-image: linear-gradient(rgba(255,255,255,.025) 1px,transparent 1px), linear-gradient(90deg,rgba(255,255,255,.025) 1px,transparent 1px);
|
|
2289
|
+
background-size: 34px 34px; -webkit-mask: linear-gradient(180deg,#000,transparent); mask: linear-gradient(180deg,#000,transparent); }
|
|
2290
|
+
header > * { position: relative; }
|
|
2291
|
+
.brand { display:flex; align-items:center; gap:14px; }
|
|
2292
|
+
.mark { width:38px; height:38px; flex:none; filter: drop-shadow(0 3px 12px rgba(255,93,103,.3)); }
|
|
2293
|
+
.wordmark { font-family: var(--mono); font-size: clamp(22px,4vw,27px); letter-spacing:-.02em; display:flex; align-items:center; }
|
|
2294
|
+
.wordmark .lock { color: var(--muted); font-weight:500; }
|
|
2295
|
+
.wordmark .hawk { color: var(--text); font-weight:700; }
|
|
2296
|
+
.caret { display:inline-block; width:9px; height:1em; margin-left:5px; background: var(--accent); border-radius:1px; box-shadow:0 0 12px var(--accent); animation: blink 1.2s steps(1) infinite; }
|
|
2297
|
+
.meta { margin-top:12px; color: var(--muted); font:12.5px/1.7 var(--mono); letter-spacing:.01em; overflow-wrap:anywhere; }
|
|
2298
|
+
.meta b { color: var(--text); font-weight:600; }
|
|
2299
|
+
|
|
2300
|
+
/* ---- Layout ----------------------------------------------------------- */
|
|
2301
|
+
main { max-width: 1120px; margin: 0 auto; padding: 26px clamp(16px,4vw,32px) 48px; }
|
|
2302
|
+
.cards { display:grid; grid-template-columns: repeat(auto-fit, minmax(116px,1fr)); gap:14px; }
|
|
2303
|
+
.card { position:relative; overflow:hidden; background: linear-gradient(180deg, var(--glass-2), var(--glass)); -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur); border:1px solid var(--glass-line); border-radius:16px; padding:16px 18px; box-shadow: 0 12px 36px -18px rgba(0,0,0,.75), var(--glass-hi); animation: rise .55s both cubic-bezier(.2,.7,.2,1); animation-delay: calc(var(--i)*55ms); }
|
|
2304
|
+
.card::before { content:''; position:absolute; top:0; left:0; right:0; height:3px; background:var(--c); box-shadow:0 0 16px var(--c); }
|
|
2305
|
+
.card .count { font:700 32px/1 var(--mono); font-variant-numeric: tabular-nums; letter-spacing:-.03em; color:var(--c); text-shadow: 0 0 24px color-mix(in srgb, var(--c) 45%, transparent); }
|
|
2306
|
+
.card .label { margin-top:7px; color:var(--muted); font-size:11.5px; text-transform:uppercase; letter-spacing:.09em; }
|
|
2307
|
+
.card.zero .count { color: var(--faint); text-shadow:none; }
|
|
2308
|
+
.card.zero::before { box-shadow:none; opacity:.45; }
|
|
2309
|
+
|
|
2310
|
+
.warn { display:flex; gap:9px; align-items:flex-start; background: rgba(255,210,63,.1); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); border:1px solid rgba(255,210,63,.32); color:#ffe6a6; padding:10px 14px; border-radius:12px; margin-top:14px; font-size:13px; overflow-wrap:anywhere; }
|
|
2311
|
+
.warn-i { color: var(--accent); }
|
|
2312
|
+
|
|
2313
|
+
.results-meta { display:flex; gap:14px; flex-wrap:wrap; align-items:center; margin:26px 2px 0; color:var(--muted); font:12px/1 var(--mono); letter-spacing:.04em; }
|
|
2314
|
+
.results-meta b { color:var(--text); font-weight:600; }
|
|
2315
|
+
.results-meta .sep { color:var(--faint); }
|
|
2316
|
+
|
|
2317
|
+
/* ---- Table ------------------------------------------------------------ */
|
|
2318
|
+
.table-wrap { margin-top:14px; border:1px solid var(--glass-line); border-radius:18px; overflow:hidden; background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.025)); -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur); box-shadow: 0 22px 60px -32px rgba(0,0,0,.85), var(--glass-hi); }
|
|
2319
|
+
.scroll { overflow-x:auto; -webkit-overflow-scrolling:touch; }
|
|
2320
|
+
table { width:100%; border-collapse:collapse; }
|
|
2321
|
+
thead th { text-align:left; background: rgba(255,255,255,.05); color:var(--muted); font:600 11px/1 var(--mono); text-transform:uppercase; letter-spacing:.08em; padding:14px 16px; border-bottom:1px solid var(--glass-line); white-space:nowrap; }
|
|
2322
|
+
tbody td { padding:15px 16px; border-bottom:1px solid rgba(255,255,255,.07); vertical-align:top; font-size:13.5px; overflow-wrap:anywhere; word-break:break-word; }
|
|
2323
|
+
tbody tr.main { animation: rise .5s both ease; animation-delay: calc(var(--i)*30ms + 120ms); }
|
|
2324
|
+
tbody tr.main:hover { background: rgba(255,255,255,.05); }
|
|
2325
|
+
|
|
2326
|
+
.pill { display:inline-flex; align-items:center; gap:7px; font:600 11.5px/1 var(--mono); padding:6px 10px; border-radius:8px; white-space:nowrap; text-transform:capitalize; color:var(--c); background: color-mix(in srgb, var(--c) 15%, transparent); border:1px solid color-mix(in srgb, var(--c) 34%, transparent); }
|
|
2327
|
+
.pill::before { content:''; width:7px; height:7px; border-radius:50%; background:var(--c); box-shadow:0 0 9px var(--c); flex:none; }
|
|
2328
|
+
|
|
2329
|
+
.pkg { display:inline-block; font-family:var(--mono); font-size:12.5px; color:var(--text); background: rgba(255,255,255,.07); border:1px solid var(--glass-line); padding:4px 9px; border-radius:8px; }
|
|
2330
|
+
.scope { margin-top:7px; color:var(--faint); font:11px/1 var(--mono); text-transform:uppercase; letter-spacing:.06em; }
|
|
2331
|
+
.advid { font-family:var(--mono); font-size:12.5px; color:var(--accent); text-decoration:none; border-bottom:1px solid transparent; }
|
|
2332
|
+
.advid:hover { border-bottom-color: color-mix(in srgb, var(--accent) 60%, transparent); }
|
|
2333
|
+
.summary { color:var(--text); }
|
|
2334
|
+
.path { margin-top:7px; color:var(--faint); font:11.5px/1.5 var(--mono); }
|
|
2335
|
+
|
|
2336
|
+
.fixchip { display:inline-block; font-family:var(--mono); font-size:12.5px; color:var(--fix); background: color-mix(in srgb, var(--fix) 13%, transparent); border:1px solid color-mix(in srgb, var(--fix) 30%, transparent); padding:4px 10px; border-radius:8px; cursor:copy; white-space:nowrap; user-select:all; -webkit-user-select:all; transition: background .15s ease; }
|
|
2337
|
+
.fixchip:hover { background: color-mix(in srgb, var(--fix) 22%, transparent); }
|
|
2338
|
+
.nofix { color:var(--faint); }
|
|
2339
|
+
.refs { white-space:nowrap; }
|
|
2340
|
+
.ref { display:inline-block; font:11.5px/1 var(--mono); color:var(--muted); text-decoration:none; padding:4px 8px; border:1px solid var(--glass-line); border-radius:7px; margin:0 4px 4px 0; background: rgba(255,255,255,.04); transition: color .15s, border-color .15s, background .15s; }
|
|
2341
|
+
.ref:hover { color:var(--text); border-color: rgba(255,255,255,.28); background: rgba(255,255,255,.08); }
|
|
2342
|
+
|
|
2343
|
+
/* ---- Disclosure / advisory ------------------------------------------- */
|
|
2344
|
+
.detail-row td { padding:0 16px 4px; background: rgba(0,0,0,.16); border-bottom:1px solid rgba(255,255,255,.07); }
|
|
2345
|
+
details { margin:0; }
|
|
2346
|
+
summary { list-style:none; cursor:pointer; display:inline-flex; align-items:center; gap:9px; color:var(--muted); font:600 11px/1 var(--mono); letter-spacing:.08em; text-transform:uppercase; padding:13px 0; user-select:none; transition: color .15s ease; }
|
|
2347
|
+
summary::-webkit-details-marker { display:none; }
|
|
2348
|
+
summary:hover { color:var(--text); }
|
|
2349
|
+
summary .chev { color:var(--accent); transition: transform .25s cubic-bezier(.2,.7,.2,1); flex:none; }
|
|
2350
|
+
details[open] summary .chev { transform: rotate(90deg); }
|
|
2351
|
+
.detail-body { padding-bottom:18px; animation: fade .3s both ease; }
|
|
2352
|
+
|
|
2353
|
+
.fix { border:1px solid color-mix(in srgb, var(--fix) 28%, transparent); background: linear-gradient(180deg, color-mix(in srgb, var(--fix) 12%, transparent), rgba(255,255,255,.02)); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); border-radius:13px; padding:13px 15px; margin:12px 0; box-shadow: var(--glass-hi); }
|
|
2354
|
+
.fix-label { display:flex; align-items:center; gap:7px; font:700 10px/1 var(--mono); letter-spacing:.12em; text-transform:uppercase; color:var(--fix); margin-bottom:9px; }
|
|
2355
|
+
.fix-label .spark { color:var(--fix); }
|
|
2356
|
+
.fix-pending { border-color: color-mix(in srgb, var(--accent) 32%, transparent); background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 11%, transparent), rgba(255,255,255,.02)); }
|
|
2357
|
+
.fix-pending .fix-label, .fix-pending .spark { color: var(--accent); }
|
|
2358
|
+
.fix-text { margin:0; color:var(--text); font-size:13px; }
|
|
2359
|
+
.fix-text + .cmdbar { margin-top:11px; }
|
|
2360
|
+
.cmdbar { display:flex; align-items:center; gap:14px; flex-wrap:wrap; background: rgba(4,7,12,.55); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); border:1px solid var(--glass-line); border-radius:11px; padding:11px 14px; transition: border-color .18s ease, background .18s ease; }
|
|
2361
|
+
.cmdbar:hover { border-color: color-mix(in srgb, var(--fix) 55%, transparent); background: rgba(4,7,12,.68); }
|
|
2362
|
+
.cmdbar .cmd { font-family:var(--mono); font-size:13px; color:#d7f7e8; cursor:copy; white-space:pre-wrap; word-break:break-all; user-select:all; -webkit-user-select:all; }
|
|
2363
|
+
.cmdbar .cmd::before { content:'$\\00a0\\00a0'; color:var(--fix); user-select:none; -webkit-user-select:none; }
|
|
2364
|
+
.cmdbar .hint { margin-left:auto; font:11px/1 var(--sans); color:var(--muted); white-space:nowrap; }
|
|
2365
|
+
|
|
2366
|
+
.advisory-wrap { border:1px solid var(--glass-line); border-radius:13px; overflow:hidden; background: var(--glass-dark); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); box-shadow: var(--glass-hi); }
|
|
2367
|
+
.advisory-head { display:flex; align-items:center; gap:11px; padding:10px 15px; background: rgba(255,255,255,.05); border-bottom:1px solid var(--glass-line); font:600 10.5px/1 var(--mono); letter-spacing:.07em; text-transform:uppercase; color:var(--muted); }
|
|
2368
|
+
.dots { display:flex; gap:6px; }
|
|
2369
|
+
.dots i { width:9px; height:9px; border-radius:50%; background: rgba(255,255,255,.18); }
|
|
2370
|
+
pre.advisory { margin:0; padding:15px 16px; white-space:pre-wrap; word-break:break-word; overflow-x:auto; max-width:100%; font-family:var(--mono); font-size:12.5px; line-height:1.65; color:#d2dae8; }
|
|
2371
|
+
|
|
2372
|
+
.empty { background: linear-gradient(180deg, var(--glass-2), var(--glass)); -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur); border:1px solid var(--glass-line); border-radius:18px; padding:48px; text-align:center; color:var(--fix); font:600 19px/1.4 var(--sans); margin-top:16px; box-shadow: var(--glass-hi); }
|
|
2373
|
+
footer { max-width:1120px; margin:0 auto; padding:18px clamp(16px,4vw,32px) 44px; color:var(--faint); font:12px/1.6 var(--mono); letter-spacing:.02em; }
|
|
2374
|
+
|
|
2375
|
+
/* Entrance animates position only \u2014 content stays fully opaque/visible throughout. */
|
|
2376
|
+
@keyframes rise { from { transform: translateY(12px); } to { transform: none; } }
|
|
2377
|
+
@keyframes fade { from { opacity:0; } to { opacity:1; } }
|
|
2378
|
+
@keyframes blink { 50% { opacity:0; } }
|
|
2379
|
+
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation:none !important; transition:none !important; } }
|
|
2380
|
+
|
|
2381
|
+
@media print {
|
|
2382
|
+
body { background:#fff; color:#111; }
|
|
2383
|
+
header, .card, .table-wrap, .advisory-wrap, .cmdbar, .fix, thead th, .warn, .empty {
|
|
2384
|
+
background:#fff !important; -webkit-backdrop-filter:none !important; backdrop-filter:none !important;
|
|
2385
|
+
box-shadow:none !important; border-color:#ddd !important; color:#111;
|
|
2386
|
+
}
|
|
2387
|
+
.caret { display:none; }
|
|
2388
|
+
pre.advisory, .fix-text, .summary, .pkg { color:#111 !important; }
|
|
2389
|
+
}
|
|
2242
2390
|
</style>
|
|
2243
2391
|
</head>
|
|
2244
2392
|
<body>
|
|
2245
2393
|
<header>
|
|
2246
|
-
<
|
|
2247
|
-
<div class="meta">${esc(result.target.root.name)}${result.target.root.version ? `@${esc(result.target.root.version)}` : ""} \xB7 ${esc(result.target.manager)} \xB7 ${result.stats.totalPackages} packages \xB7 scanned ${esc(
|
|
2394
|
+
<div class="brand">${MARK}<div class="wordmark"><span class="lock">lock</span><span class="hawk">hawk</span><span class="caret"></span></div></div>
|
|
2395
|
+
<div class="meta"><b>${esc(result.target.root.name)}${result.target.root.version ? `@${esc(result.target.root.version)}` : ""}</b> \xB7 ${esc(result.target.manager)} \xB7 ${result.stats.totalPackages} packages \xB7 scanned ${esc(
|
|
2248
2396
|
result.scannedAt
|
|
2249
2397
|
)}</div>
|
|
2250
2398
|
</header>
|
|
2251
2399
|
<main>
|
|
2252
2400
|
<div class="cards">${cards}</div>
|
|
2253
2401
|
${dbWarnings}
|
|
2254
|
-
${result.findings.length === 0 ? '<div class="empty">\u2713 No known vulnerabilities found.</div>' :
|
|
2402
|
+
${result.findings.length === 0 ? '<div class="empty">\u2713 No known vulnerabilities found.</div>' : `${resultsMeta}
|
|
2403
|
+
<div class="table-wrap"><div class="scroll"><table>
|
|
2255
2404
|
<thead><tr><th>Severity</th><th>Package</th><th>Advisory</th><th>Summary & path</th><th>Fixed in</th><th>Refs</th></tr></thead>
|
|
2256
2405
|
<tbody>${rows}</tbody>
|
|
2257
|
-
</table></div>`}
|
|
2406
|
+
</table></div></div>`}
|
|
2258
2407
|
</main>
|
|
2259
2408
|
<footer>Generated by lockhawk v${esc(result.tool.version)} \xB7 data from OSV.dev \xB7 database: ${esc(
|
|
2260
2409
|
result.database.source
|