Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions components/checklist/Checklist.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
.checklist {
margin-top: -0.25rem;
}

.checklist ul {
list-style: none !important;
padding-left: 0;
margin: 0.25em 0;
}

.checklist ul ul {
list-style: none !important;
padding-left: 1.25rem;
margin: 0;
}

.checklist li {
position: relative;
padding-left: 26px;
margin: 4px 0;
line-height: 1.5;
color: var(--color-text);
}

/* Non-checkbox items: show a bullet and reduce left indent */
.checklist li:not(:has(> input[type="checkbox"])) {
padding-left: 1rem;
}

.checklist li:not(:has(> input[type="checkbox"]))::before {
content: "•";
position: absolute;
left: 0;
top: 0;
}

/* Non-checkbox nested items: tighter spacing */
.checklist ul ul li:not(:has(input[type="checkbox"])) {
margin: 1px 0;
}

.checklist li input[type="checkbox"] {
position: absolute;
left: 0;
top: 4px;
width: 16px;
height: 16px;
margin: 0;
cursor: pointer;
accent-color: var(--color-primary, #4339db);
}

/* Strikethrough applied only to the label span, never cascades into nested lists. */
.checklist .checklist-label.is-checked {
text-decoration: line-through;
color: var(--color-text-muted);
}

/* Tighten paragraph wrappers that loose lists add around item text. */
.checklist li p {
margin: 0;
}

/* When a checklist follows a list item label, indent to match list depth. */
ul + .checklist,
ol + .checklist {
padding-left: 1.25rem;
}
84 changes: 84 additions & 0 deletions components/checklist/Checklist.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useEffect, useRef, type ReactNode } from "react";
import "./Checklist.css";

interface ChecklistProps {
/** Namespace for localStorage keys — must be unique per checklist section on the page. */
id: string;
children: ReactNode;
}

function slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "")
.slice(0, 60);
}

export function Checklist({ id, children }: ChecklistProps) {
const ref = useRef<HTMLDivElement>(null);

useEffect(() => {
const container = ref.current;
if (!container || typeof window === "undefined") return;

const items = container.querySelectorAll<HTMLLIElement>("li");
const cleanups: (() => void)[] = [];

items.forEach((li) => {
// Only pair with a checkbox that is a direct child — prevents parent
// navigation items (e.g. "Account >") from sharing a descendant's checkbox.
const checkbox = Array.from(li.children).find(
(child): child is HTMLInputElement =>
child instanceof HTMLInputElement && child.type === "checkbox"
);
if (!checkbox) return;

// Wrap the label text (everything except the checkbox and nested lists)
// in a span so strikethrough only paints over the label, not nested items.
let labelSpan = li.querySelector<HTMLSpanElement>(":scope > .checklist-label");
if (!labelSpan) {
labelSpan = document.createElement("span");
labelSpan.className = "checklist-label";
const toMove: Node[] = [];
li.childNodes.forEach((node) => {
if (node !== checkbox && !(node instanceof HTMLUListElement)) {
toMove.push(node);
}
});
toMove.forEach((node) => labelSpan!.appendChild(node));
checkbox.insertAdjacentElement("afterend", labelSpan);
}

const key = `checklist:${id}:${slugify(labelSpan.textContent ?? "")}`;

checkbox.disabled = false;

try {
if (localStorage.getItem(key) === "true") {
checkbox.checked = true;
labelSpan.classList.add("is-checked");
}
} catch {}

const handler = () => {
const checked = checkbox.checked;
try {
localStorage.setItem(key, checked ? "true" : "false");
} catch {}
labelSpan!.classList.toggle("is-checked", checked);
};

checkbox.addEventListener("change", handler);
cleanups.push(() => checkbox.removeEventListener("change", handler));
});

return () => cleanups.forEach((fn) => fn());
}, [id]);

return (
<div ref={ref} className="checklist">
{children}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
import { useEffect, useMemo, useState, type ReactNode } from "react";
import "./ChecklistItem.css";

/**
* ChecklistItem — renders a governance SDLC checklist item as a single
* interactive checkbox next to a bold title, with a plain-paragraph
* description below (no nested list marker).
*
* State is persisted to localStorage so a signer / reviewer can work
* through the list across sessions. The storage key is
* `governance-sdlc-checklist:<id>`, where `id` defaults to a slugified
* version of the title.
*/
import "./GovernanceChecklistItem.css";

const STORAGE_PREFIX = "governance-sdlc-checklist:";

interface ChecklistItemProps {
interface GovernanceChecklistItemProps {
title: string;
id?: string;
children: ReactNode;
Expand All @@ -27,7 +16,7 @@ function slugify(input: string): string {
.replace(/^-+|-+$/g, "");
}

export function ChecklistItem({ title, id, children }: ChecklistItemProps) {
export function GovernanceChecklistItem({ title, id, children }: GovernanceChecklistItemProps) {
const slug = useMemo(() => id ?? slugify(title), [id, title]);
const storageKey = `${STORAGE_PREFIX}${slug}`;
const descId = `${slug}-desc`;
Expand Down
3 changes: 2 additions & 1 deletion components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ export { BadgeLegend } from './contributors/BadgeLegend'
export { DevOnly } from './dev-only/DevOnly'
export { AttackSurfaceDashboard } from './attack-surface/AttackSurfaceDashboard'
export { GovernanceSDLCPipeline } from './governance-sdlc/GovernanceSDLCPipeline'
export { ChecklistItem } from './governance-sdlc/ChecklistItem'
export { GovernanceChecklistItem } from './governance-sdlc/GovernanceChecklistItem'
export { Checklist } from './checklist/Checklist'
20 changes: 19 additions & 1 deletion docs/pages/config/template.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,13 @@

{/*
IMPORT PATH: the '../../../components' path below works for pages placed at
docs/pages/<framework-folder>/<your-page>.mdx (one level deep inside a

Check failure on line 113 in docs/pages/config/template.mdx

View workflow job for this annotation

GitHub Actions / lint

Inline HTML

docs/pages/config/template.mdx:113:33 MD033/no-inline-html Inline HTML [Element: your-page] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md033.md

Check failure on line 113 in docs/pages/config/template.mdx

View workflow job for this annotation

GitHub Actions / lint

Inline HTML

docs/pages/config/template.mdx:113:14 MD033/no-inline-html Inline HTML [Element: framework-folder] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md033.md
framework folder). If your page sits one level deeper, for example
docs/pages/<framework-folder>/<subfolder>/<your-page>.mdx, add one more '../'

Check failure on line 115 in docs/pages/config/template.mdx

View workflow job for this annotation

GitHub Actions / lint

Inline HTML

docs/pages/config/template.mdx:115:45 MD033/no-inline-html Inline HTML [Element: your-page] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md033.md

Check failure on line 115 in docs/pages/config/template.mdx

View workflow job for this annotation

GitHub Actions / lint

Inline HTML

docs/pages/config/template.mdx:115:33 MD033/no-inline-html Inline HTML [Element: subfolder] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md033.md

Check failure on line 115 in docs/pages/config/template.mdx

View workflow job for this annotation

GitHub Actions / lint

Inline HTML

docs/pages/config/template.mdx:115:14 MD033/no-inline-html Inline HTML [Element: framework-folder] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md033.md
so the import becomes '../../../../components'.

If your page uses interactive checklists, add Checklist to the import:
import { ..., Checklist } from '../../../components'
*/}
import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components'

Expand Down Expand Up @@ -163,7 +166,22 @@
Common formats to mix and match within a section:

- **Walkthrough**: numbered steps, top to bottom. Use when order matters.
- **Checklist**: bullet points the reader can tick. Use when order does not matter.
- **Checklist**: action items the reader can tick off. Use when order does not matter.
Use the `<Checklist>` component — never plain `- [ ]` syntax, which renders as a static,
non-interactive checkbox. Each block needs a unique `id` (kebab-case) per page:

```mdx

Check failure on line 173 in docs/pages/config/template.mdx

View workflow job for this annotation

GitHub Actions / lint

Code block style

docs/pages/config/template.mdx:173 MD046/code-block-style Code block style [Expected: indented; Actual: fenced] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md046.md
<Checklist id="section-name">
- [ ] First item
- [ ] Second item
- Descriptive sub-bullet (no checkbox, renders as a bullet)
</Checklist>
```

Rules: `id` values must be unique within the page. Do not use `> blockquote` syntax
inside a `<Checklist>` block (use `- _Note: ..._` instead). Keep nesting shallow
(avoid 5+ levels). See [`guides/account-management/github.mdx`](https://github.com/security-alliance/frameworks/blob/develop/docs/pages/guides/account-management/github.mdx)
for a full worked example.
- **Decision tree or matrix**: table or branching guide. Use when the right
answer depends on the reader's situation.
- **Plain explanation**: paragraphs. Use when the content is conceptual or
Expand Down
8 changes: 7 additions & 1 deletion docs/pages/devsecops/data-security-upgrade-checklist.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
users: [mattaereal]
---

import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components'
import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter, Checklist } from '../../../components'

<TagProvider>
<TagFilter />
Expand All @@ -27,6 +27,7 @@
Protecting critical data through automated, encrypted backups and tested recovery procedures is essential for business
continuity in Web3 operations.

<Checklist id="1-data-backup-disaster-recovery">

Check failure on line 30 in docs/pages/devsecops/data-security-upgrade-checklist.mdx

View workflow job for this annotation

GitHub Actions / lint

Inline HTML

docs/pages/devsecops/data-security-upgrade-checklist.mdx:30:1 MD033/no-inline-html Inline HTML [Element: Checklist] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md033.md
- [ ] **Automated backups for critical databases, configurations, and off-chain state**
- Configure automated backup schedules for all mission-critical systems, including databases (PostgreSQL, MongoDB,
etc.), application configurations, environment variables, and off-chain state data (indexer databases, cache
Expand Down Expand Up @@ -72,12 +73,14 @@
Splunk, DataDog, or ELK stack. Set up alerts for backup failures, unauthorized access attempts, or unusual
deletion activities. For compliance, retain backup logs for at least 1 year and ensure they are tamper-resistant
using immutable or write-once logging/storage controls where appropriate.
</Checklist>

## 2. Secure Storage & Encryption

Proper classification and encryption of sensitive data protects user privacy, prevents credential theft, and ensures
regulatory compliance.

<Checklist id="2-secure-storage-encryption">

Check failure on line 83 in docs/pages/devsecops/data-security-upgrade-checklist.mdx

View workflow job for this annotation

GitHub Actions / lint

Inline HTML

docs/pages/devsecops/data-security-upgrade-checklist.mdx:83:1 MD033/no-inline-html Inline HTML [Element: Checklist] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md033.md
- [ ] **Sensitive data classification (PII, credentials, secrets, API keys)**
- Conduct a data inventory and classify all data by sensitivity: Public (marketing materials), Internal (business
data), Confidential (user PII, financial records), and Restricted (credentials, private keys, secrets). In Web3
Expand Down Expand Up @@ -119,12 +122,14 @@
configuration files. Scan repositories with tools like git-secrets, TruffleHog, or GitHub's secret scanning to
detect accidentally committed credentials. For Web3 projects, use hardware wallets or MPC systems for deployment
keys rather than filesystem-stored private keys. Implement pre-commit hooks to prevent secret commits.
</Checklist>

## 3. Third-Party Integrations

Web3 projects rely heavily on external services—from RPC providers to cloud infrastructure. Properly vetting and
securing these integrations prevents supply chain attacks and data breaches.

<Checklist id="3-third-party-integrations">

Check failure on line 132 in docs/pages/devsecops/data-security-upgrade-checklist.mdx

View workflow job for this annotation

GitHub Actions / lint

Inline HTML

docs/pages/devsecops/data-security-upgrade-checklist.mdx:132:1 MD033/no-inline-html Inline HTML [Element: Checklist] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md033.md
- [ ] **Maintain comprehensive inventory of all services and integrations**
- Document every third-party service your project uses: infrastructure (AWS, Google Cloud, Vercel), blockchain
services (Infura, Alchemy, QuickNode), analytics (Mixpanel, Amplitude), monitoring (DataDog, Sentry),
Expand Down Expand Up @@ -167,6 +172,7 @@
contract termination), subprocessor restrictions (vendor must disclose and get approval for subcontractors), audit
rights (you can audit vendor's security practices), and liability for breaches. For GDPR/CCPA compliance, require
Data Processing Agreements (DPAs). Review contracts annually and before renewals.
</Checklist>

---

Expand Down
Loading