Current
This commit is contained in:
1 parent
5a2d49c17f
commit
49850fe485
10 files changed
+46
-22
No files matched your search
@@ -9,3 +9,4 @@ ds-bundle/
|
||||
.design-sync/.cache/
|
||||
.design-sync/learnings/
|
||||
.design-sync/node_modules
|
||||
.DS_Store
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -19,7 +19,7 @@ const isCurrent = (href: string) => href.split('#')[0] === path && !href.include
|
||||
---
|
||||
<header class="header" data-header>
|
||||
<div class="container header__inner">
|
||||
<a class="header__home" href={home} aria-label={ui.nav.homeLabel}><Wordmark /></a>
|
||||
<a class="header__home" href={home} aria-label={ui.nav.homeLabel}><Wordmark inline /></a>
|
||||
|
||||
<nav class="header__nav" aria-label="Primary" id="primary-nav">
|
||||
<ul class="header__list" role="list">
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
---
|
||||
/** The wordmark is set in type. No symbol, no colour. */
|
||||
interface Props { class?: string }
|
||||
const { class: className } = Astro.props;
|
||||
/** The logo, as supplied. Sized by its height so it sits on the header's baseline grid.
|
||||
`inline` writes it into the page as a data URI, so it paints with the first frame instead of
|
||||
blinking in on every navigation; the header needs that, the footer does not. */
|
||||
import logoInline from '../assets/logo.webp?inline';
|
||||
import logoUrl from '../assets/logo.webp?url';
|
||||
|
||||
interface Props { class?: string; inline?: boolean }
|
||||
const { class: className, inline = false } = Astro.props;
|
||||
---
|
||||
<span class:list={['wordmark', className]}>CipherCell</span>
|
||||
<img
|
||||
class:list={['wordmark', className]}
|
||||
src={inline ? logoInline : logoUrl}
|
||||
alt="CipherCell"
|
||||
width="311"
|
||||
height="72"
|
||||
loading={inline ? undefined : 'lazy'}
|
||||
/>
|
||||
|
||||
<style>
|
||||
.wordmark {
|
||||
display: inline-block;
|
||||
font-weight: var(--weight-medium);
|
||||
letter-spacing: var(--track-h3);
|
||||
font-size: var(--step-h3);
|
||||
line-height: 1;
|
||||
display: block;
|
||||
height: var(--space-5);
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -166,7 +166,6 @@ const groups = v.groups.map((g, i) => ({ title: g.title, body: g.body, desc: g.d
|
||||
right: calc(-1 * (var(--gap) + 200%));
|
||||
width: 200%;
|
||||
padding: var(--space-6);
|
||||
border: var(--hairline) solid var(--line);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity var(--dur-slow) var(--ease);
|
||||
|
||||
@@ -41,6 +41,12 @@ layout.edges.forEach((e) => seen(e.group));
|
||||
layout.texts?.forEach((t) => seen(t.group));
|
||||
|
||||
const freeEdges = layout.edges.filter((e) => !e.group).map((e, i) => ({ e, points: pts(e), tick: e.tick ? edgeTick(e.points) : null, d: i * 150 }));
|
||||
/* A group is lit by its own hover and by the hover of any group that names it in `layout.lit`. */
|
||||
const litBy = (key: string) => [key, ...Object.entries(layout.lit ?? {}).filter(([, ks]) => ks.includes(key)).map(([k]) => k)].join(' ');
|
||||
const hoverCss = Object.keys(layout.lit ?? {})
|
||||
.map((k) => `.schematic:has(.schematic__group[data-group="${k}"]:hover) .schematic__group[data-lit~="${k}"] { --dim: 1; }`)
|
||||
.join('');
|
||||
|
||||
const groups = order.map((g, gi) => {
|
||||
const start = 320 + gi * 200;
|
||||
const nodes = layout.nodes.filter((n) => n.group === g);
|
||||
@@ -48,6 +54,7 @@ const groups = order.map((g, gi) => {
|
||||
const texts = (layout.texts ?? []).filter((t) => t.group === g);
|
||||
return {
|
||||
key: g,
|
||||
lit: litBy(g),
|
||||
rings: nodes.filter((n) => n.ring).map((n) => ({ x: n.x, y: n.y, r: n.ring!, d: start })),
|
||||
edges: edges.map((e, i) => ({ e, points: pts(e), tick: e.tick ? edgeTick(e.points) : null, d: start + 80 + i * 45 })),
|
||||
dots: nodes.filter((n) => n.dot !== 'none').map((n, i) => ({ x: n.x, y: n.y, sub: n.dot === 'sub', d: start + 120 + i * 45 })),
|
||||
@@ -82,7 +89,7 @@ const fontSub = layout.fontSub ?? 15;
|
||||
</g>
|
||||
|
||||
{groups.map((g) => (
|
||||
<g class="schematic__group" data-group={g.key}>
|
||||
<g class="schematic__group" data-group={g.key} data-lit={g.lit}>
|
||||
<g fill="none" stroke="currentColor" stroke-width={stroke} vector-effect="non-scaling-stroke">
|
||||
{g.rings.map((r) => <circle class="s-ring" cx={r.x} cy={r.y} r={r.r} stroke-dasharray="2 5" style={`--d: ${r.d}ms`} />)}
|
||||
{g.edges.map((f) => (
|
||||
@@ -154,8 +161,11 @@ const fontSub = layout.fontSub ?? 15;
|
||||
/* Hover singles out one node and what belongs to it. */
|
||||
@media (hover: hover) and (min-width: 64rem) {
|
||||
.schematic__group, .schematic__edges, .schematic__free { transition: opacity var(--dur) var(--ease); }
|
||||
.schematic:has(.schematic__group:hover) .schematic__group:not(:hover) { opacity: 0.28; }
|
||||
.schematic:has(.schematic__group:hover) .schematic__group:not(:hover) { opacity: var(--dim, 0.28); }
|
||||
.schematic:has(.schematic__group:hover) .schematic__edges,
|
||||
.schematic:has(.schematic__group:hover) .schematic__free { opacity: 0.45; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Cross-group hovers from `layout.lit`: written out here because the group keys are data. -->
|
||||
{hoverCss && <style is:inline set:html={`@media (hover: hover) and (min-width: 64rem) { ${hoverCss} }`} />}
|
||||
@@ -13,7 +13,9 @@ export interface Node { x: number; y: number; label?: Label; dot?: 'main' | 'sub
|
||||
export interface Edge { points: Pt[]; tick?: boolean; faint?: boolean; dashed?: boolean; group?: string }
|
||||
export interface Box { x: number; y: number; w: number; h: number; dashed?: boolean }
|
||||
export interface Text { x: number; y: number; text: Label; anchor?: 'start' | 'end' | 'middle'; strong?: boolean; group?: string }
|
||||
export interface Layout { w: number; h: number; nodes: Node[]; edges: Edge[]; boxes?: Box[]; texts?: Text[]; fontMain?: number; fontSub?: number }
|
||||
/* `lit` names, per group, the other groups a hover on it keeps lit: a path that leaves one node
|
||||
and ends at another belongs to both. */
|
||||
export interface Layout { w: number; h: number; nodes: Node[]; edges: Edge[]; boxes?: Box[]; texts?: Text[]; fontMain?: number; fontSub?: number; lit?: Record<string, string[]> }
|
||||
export interface Pair { wide: Layout; tall: Layout }
|
||||
|
||||
const RING = 36;
|
||||
@@ -124,11 +126,13 @@ export function verdict(g: Groups[2]): Pair {
|
||||
nodes: [dot(140, y, L.finding, 'finding'), ...context.nodes, ...ctx.nodes, ...s1.nodes, ...s2.nodes, ...tools.nodes, ...v.nodes, ...outcomes.nodes],
|
||||
edges: [
|
||||
...ctx.edges, ...tools.edges, ...outcomes.edges,
|
||||
...chain([[140, y], [380, y], [640, y], [920, y], [1220, y]]),
|
||||
{ points: [[640, y], [640, top], [1220, top], [1220, y]], tick: true },
|
||||
...chain([[140, y], [380, y], [640, y], [920, y]]),
|
||||
{ points: [[920, y], [1220, y]], tick: true, group: 'escalation' },
|
||||
{ points: [[640, y], [640, top], [1220, top], [1220, y]], tick: true, group: 'static' },
|
||||
],
|
||||
texts: [...v.texts, { x: 930, y: top - 12, text: L.certain, anchor: 'middle' }, { x: 780, y: y + 30, text: splitTwo(L.unsure), anchor: 'middle' }],
|
||||
texts: [...v.texts, { x: 930, y: top - 12, text: L.certain, anchor: 'middle', group: 'static' }, { x: 780, y: y - 52, text: splitTwo(L.unsure), anchor: 'middle' }],
|
||||
fontMain: 19, fontSub: 17,
|
||||
lit: { static: ['verdict'], escalation: ['verdict'], verdict: ['static'] },
|
||||
};
|
||||
const tall = spine([
|
||||
{ title: L.finding },
|
||||
|
||||
@@ -3,7 +3,7 @@ export interface Group<L> { title: string; body: string; desc: string; labels: L
|
||||
|
||||
export interface OverviewLabels { repo: string; sbom: string[]; sbomSub: string; dt: string[]; dtSub: string; sast: string[]; sastSub: string; poly: string[]; polySub: string; prism: string[]; prismSub: string; report: string }
|
||||
export interface ScannersLabels { one: string; oneSub: string; cwe: string[]; cweSub: string; merge: string[]; mergeSub: string; resolve: string[]; resolveSub: string }
|
||||
export interface VerdictLabels { finding: string; context: string; ctx: string[]; stage1: string[]; stage2: string; tools: string[]; verdict: string; outcomes: string[]; carries: string; certain: string; unsure: string }
|
||||
export interface VerdictLabels { finding: string; context: string; ctx: string[]; stage1: string[]; stage2: string[]; tools: string[]; verdict: string; outcomes: string[]; carries: string; certain: string; unsure: string }
|
||||
export interface DependenciesLabels { syft: string; cdxgen: string; sbom: string; sbomSub: string[]; dt: string; dtSub: string[]; vdr: string[]; vdrSub: string; report: string }
|
||||
export interface OperateLabels { hub: string; sources: string[]; outputs: string[]; scopes: string[]; scopesTitle: string }
|
||||
|
||||
|
||||
+4
-4
@@ -179,8 +179,8 @@ const en = {
|
||||
{
|
||||
title: 'Two stages of judgement.',
|
||||
body: 'Polygraph reads every finding with its code, its provenance and the file around it, and returns a verdict with a certainty and a priority. A finding it is unsure about, or a false-positive call it is not certain of, goes to a second agent that can read the repository itself, within a fixed budget of requests and tokens. Every verdict carries its reasoning. A cached verdict is reused only when the code it was based on is unchanged.',
|
||||
desc: 'A finding is assembled with its context: provenance, code snippet, file header and an optional dataflow trace. A first stage returns a static verdict. Certain verdicts go straight to the result. Uncertain verdicts, and false-positive verdicts below high certainty, are escalated to a second agent that reads files, lists directories and searches the tree within a bounded budget. The result is true positive, false positive or uncertain, with certainty, priority and reasoning.',
|
||||
labels: { finding: 'Finding', context: 'Context', ctx: ['Provenance', 'Code snippet', 'File header', 'Dataflow trace, optional'], stage1: ['Static', 'verdict'], stage2: 'Escalation', tools: ['Reads files', 'Lists directories', 'Searches the tree', 'Bounded budget'], verdict: 'Verdict', outcomes: ['True positive', 'False positive', 'Uncertain'], carries: 'Certainty, priority, reasoning', certain: 'Certain', unsure: 'Uncertain, or an unsure false positive' },
|
||||
desc: 'A finding is assembled with its context: provenance, code snippet, file header and an optional dataflow trace. A static verdict agent returns a first verdict. Certain verdicts go straight to the result. Uncertain verdicts, and false-positive verdicts below high certainty, are escalated to an escalation agent that reads files, lists directories and searches the tree within a bounded budget. The result is true positive, false positive or uncertain, with certainty, priority and reasoning.',
|
||||
labels: { finding: 'Finding', context: 'Context', ctx: ['Provenance', 'Code snippet', 'File header', 'Dataflow trace, optional'], stage1: ['Static verdict', 'Agent'], stage2: ['Escalation', 'Agent'], tools: ['Reads files', 'Lists directories', 'Searches the tree', 'Bounded budget'], verdict: 'Verdict', outcomes: ['True positive', 'False positive', 'Uncertain'], carries: 'Certainty, priority, reasoning', certain: 'Certain', unsure: 'Uncertain, or an unsure false positive' },
|
||||
},
|
||||
{
|
||||
title: 'The dependency side.',
|
||||
@@ -397,8 +397,8 @@ const de: typeof en = {
|
||||
{
|
||||
title: 'Urteil in zwei Stufen.',
|
||||
body: 'Polygraph liest jeden Fund mit seinem Code, seiner Herkunft und der Datei darum herum und gibt ein Urteil mit Sicherheit und Priorität zurück. Ein Fund, bei dem es unsicher ist, oder ein Fehlalarm-Urteil, dessen es sich nicht sicher ist, geht an einen zweiten Agenten, der das Repository selbst lesen kann, innerhalb eines festen Budgets an Anfragen und Tokens. Jedes Urteil trägt seine Begründung. Ein gemerktes Urteil wird nur wiederverwendet, wenn der Code, auf dem es beruht, unverändert ist.',
|
||||
desc: 'Ein Fund wird mit seinem Kontext zusammengestellt: Herkunft, Code-Ausschnitt, Dateikopf und optional eine Datenfluss-Spur. Eine erste Stufe liefert ein statisches Urteil. Sichere Urteile gehen direkt ins Ergebnis. Unsichere Urteile und Fehlalarm-Urteile unterhalb hoher Sicherheit werden an einen zweiten Agenten eskaliert, der Dateien liest, Verzeichnisse auflistet und den Baum durchsucht, innerhalb eines begrenzten Budgets. Das Ergebnis ist echt, Fehlalarm oder unsicher, mit Sicherheit, Priorität und Begründung.',
|
||||
labels: { finding: 'Fund', context: 'Kontext', ctx: ['Herkunft', 'Code-Ausschnitt', 'Dateikopf', 'Datenfluss-Spur, optional'], stage1: ['Statisches', 'Urteil'], stage2: 'Eskalation', tools: ['Liest Dateien', 'Listet Verzeichnisse', 'Durchsucht den Baum', 'Begrenztes Budget'], verdict: 'Urteil', outcomes: ['Echt', 'Fehlalarm', 'Unsicher'], carries: 'Sicherheit, Priorität, Begründung', certain: 'Sicher', unsure: 'Unsicher oder unsicherer Fehlalarm' },
|
||||
desc: 'Ein Fund wird mit seinem Kontext zusammengestellt: Herkunft, Code-Ausschnitt, Dateikopf und optional eine Datenfluss-Spur. Ein Agent für statische Urteile liefert ein erstes Urteil. Sichere Urteile gehen direkt ins Ergebnis. Unsichere Urteile und Fehlalarm-Urteile unterhalb hoher Sicherheit werden an einen Eskalations-Agenten weitergegeben, der Dateien liest, Verzeichnisse auflistet und den Baum durchsucht, innerhalb eines begrenzten Budgets. Das Ergebnis ist echt, Fehlalarm oder unsicher, mit Sicherheit, Priorität und Begründung.',
|
||||
labels: { finding: 'Fund', context: 'Kontext', ctx: ['Herkunft', 'Code-Ausschnitt', 'Dateikopf', 'Datenfluss-Spur, optional'], stage1: ['Agent für', 'statische Urteile'], stage2: ['Eskalations-', 'Agent'], tools: ['Liest Dateien', 'Listet Verzeichnisse', 'Durchsucht den Baum', 'Begrenztes Budget'], verdict: 'Urteil', outcomes: ['Echt', 'Fehlalarm', 'Unsicher'], carries: 'Sicherheit, Priorität, Begründung', certain: 'Sicher', unsure: 'Unsicher oder unsicherer Fehlalarm' },
|
||||
},
|
||||
{
|
||||
title: 'Die Seite der Abhängigkeiten.',
|
||||
|
||||
Reference in new issue
Block a user