From c89810880ff6f71d4fe298f2b0d7319d6f6cbaa8 Mon Sep 17 00:00:00 2001 From: artemisclaw82 Date: Mon, 6 Apr 2026 11:37:31 -0400 Subject: [PATCH 01/92] content(opsec): endpoint security tiers and DPRK liveness verification (#400) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * content(opsec): endpoint security tiers and DPRK liveness verification - Add endpoint security device provisioning tiers (managed, VDI, enterprise browser) - Add deepfake liveness verification techniques to DPRK TTP page - Add andrew-chang-gu to contributors.json (username TBD) Co-authored-by: DicksonWu654 Co-authored-by: andrew-chang-gu <> * chore: fill andrew-chang-gu contributor fields from public LinkedIn profile * Add contributor Andrew Chang-Gu to contributors.json Added new contributor Andrew Chang-Gu with details. --------- Co-authored-by: DicksonWu654 Co-authored-by: Matías Aereal Aeón <388605+mattaereal@users.noreply.github.com> --- docs/pages/config/contributors.json | 13 ++++ .../techniques-tactics-and-procedures.mdx | 13 ++++ docs/pages/opsec/endpoint/overview.mdx | 69 ++++++++++++++++++- 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/docs/pages/config/contributors.json b/docs/pages/config/contributors.json index b0dd19a61..a6f05343a 100644 --- a/docs/pages/config/contributors.json +++ b/docs/pages/config/contributors.json @@ -624,6 +624,19 @@ { "name": "Dormant-90d+", "lastActive": "2025-09-18" } ] }, +"andrew-chang-gu": { + "slug": "andrew-chang-gu", + "name": "Andrew Chang-Gu", + "avatar": "", + "github": "", + "twitter": "", + "website": "https://www.linkedin.com/in/achanggu", + "company": "Google Cloud Security", + "job_title": "Google Cloud Security", + "role": "contributor", + "description": "Google Cloud Security", + "badges": [] +}, "JosepBove": { "slug": "JosepBove", "name": "Josep Bove", diff --git a/docs/pages/dprk-it-workers/techniques-tactics-and-procedures.mdx b/docs/pages/dprk-it-workers/techniques-tactics-and-procedures.mdx index e02302461..3c8f0f9dd 100644 --- a/docs/pages/dprk-it-workers/techniques-tactics-and-procedures.mdx +++ b/docs/pages/dprk-it-workers/techniques-tactics-and-procedures.mdx @@ -210,6 +210,19 @@ hiring a DPRK IT Worker. profile, which can uncover further identity mismatches. 1. On LinkedIn, examine the strength of the actor's connection network. + + +### Defeating Deepfakes: Liveness Verification + +Pre-recorded deepfake video can fool a casual interviewer, particularly when audio "technical difficulties" are used as cover. Incorporate unpredictable, interactive requests that a pre-rendered deepfake cannot handle: + +- Ask the candidate to turn their head sideways and hold the position +- Have them read a randomly generated phrase displayed on screen for the first time during the call +- Request a hand movement across the face mid-stream +- Ask them to screen-share and perform a live technical task requiring real-time interaction with their environment + +> Any candidate who persistently avoids in-person interaction — even for high-value roles — warrants a security review. This is a documented indicator of DPRK IT worker activity. + ## Did I hire a DPRK IT Worker? 1. The list below serves as a guide for confirming your suspicions if one of your employees is a potential diff --git a/docs/pages/opsec/endpoint/overview.mdx b/docs/pages/opsec/endpoint/overview.mdx index de4085827..d960d0ea5 100644 --- a/docs/pages/opsec/endpoint/overview.mdx +++ b/docs/pages/opsec/endpoint/overview.mdx @@ -1,8 +1,15 @@ --- title: "Endpoint Security | Security Alliance" +description: "Device provisioning tiers for Web3 organizations: managed devices with EDR/MDM, virtual desktops for global contractors, and enterprise browsers for minimum viable security." tags: - Security Specialist - Operations & Strategy + - HR +contributors: + - role: wrote + users: [andrew-chang-gu, dickson] + - role: reviewed + users: [] --- import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' @@ -15,7 +22,67 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } fr -Placeholder for Endpoint Security content +> **Key Takeaway:** Match device security investment to role risk. Managed hardware for privileged operators, VDI for global contractors, enterprise browsers as minimum viable security for everyone else. + +Unmanaged personal devices are a primary vector for credential theft and lateral movement in Web3 organizations. Infostealers, malicious browser extensions, and compromised development environments all start at the endpoint. Organizations need a device provisioning strategy that scales security with role sensitivity. + +## Device Security Tiers + +### Tier 1: Managed Devices (Gold Standard) + +Issue organization-managed hardware to your highest-risk roles. This provides full security stack visibility and control. + +- **EDR** (CrowdStrike Falcon, SentinelOne) for real-time behavioral monitoring and threat hunting +- **MDM** (Intune, JAMF) to enforce configuration policy and enable remote wipe +- **Full disk encryption** (BitLocker, FileVault) so stolen devices reveal nothing +- **Biometric authentication** (TouchID, Windows Hello) for phishing-resistant local auth +- **Centralized logging** for threat hunting and incident reconstruction + +**Target roles:** Developers with production access, leadership, treasury custodians, key signers, security leads. + +### Tier 2: Virtual Desktop Infrastructure (Privacy-First Scale) + +For global contractors where issuing hardware is impractical, VDI provides a secure cloud-hosted environment accessible from any device. The employee's personal machine becomes a thin client — all sensitive work happens inside the managed virtual desktop. + +- Complete visibility and control inside the virtual environment +- Corporate web proxying and traffic inspection +- Protects employee device privacy (organization sees inside VDI, not the host) +- **Limitation:** Susceptible to host-level keyloggers and screen capture +- **Limitation:** Performance and latency overhead +- **Limitation:** Hardware authentication dongle (YubiKey) compatibility issues in virtualized environments + +**Target roles:** Global operations, customer support, regional teams, contractors with defined scopes. Providers: AWS WorkSpaces, Azure Virtual Desktop, Google Cloud Workstations. + +### Tier 3: Enterprise Browser (Minimum Viable Security) + +For general staff and short-term contractors, an enterprise browser provides a managed browsing environment on any machine. + +- **Extension allowlisting** — eliminates malicious extension vectors (e.g., Discord session cookie theft) +- **IdP integration** — enforces identity and access policies at the browser layer +- **Isolated history and cookies** — work browsing sandboxed from personal browsing +- **Limitation:** Zero protection if the host OS is compromised +- **Limitation:** Cannot block host-level screen capture or USB access + +**Target roles:** General staff, community managers, short-term contractors. + +> If you use Google Workspace, you already have **Chrome Enterprise Core** at no additional cost. Enabling extension allowlisting alone eliminates one of the most common attack vectors against Discord and web-based platforms. + +## Choosing the Right Tier + +| Factor | Managed Device | VDI | Enterprise Browser | +|--------|---------------|-----|-------------------| +| **Visibility** | Full (OS + apps) | Inside VDI only | Browser only | +| **Host compromise protection** | Yes — EDR on host | Partial — Host keyloggers | No — None | +| **Hardware cost** | High (org buys devices) | Low (any device) | None | +| **Privacy** | Low (org owns device) | Medium (host is private) | High (only browser managed) | +| **Best for** | Core team, signers | Global contractors | General staff | + +Most Web3 organizations will use all three tiers simultaneously — the goal is to match investment to actual risk, not to force a single approach across all roles. + +## Further Reading + +- [Hardening your organization](/dprk-it-workers/mitigating-dprk-it-workers#hardening-your-organization) — Access control policies for remote workers +- [Browser Security](/opsec/browser/overview) — Browser-specific hardening From 1686c166096a8ed5e5b9ac30481fab346e1e62fb Mon Sep 17 00:00:00 2001 From: Dickson Wu <33645481+DicksonWu654@users.noreply.github.com> Date: Mon, 6 Apr 2026 09:53:56 -0600 Subject: [PATCH 02/92] Add password manager endpoint hardening guide (#419) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add password manager endpoint hardening guide * Tone down password manager hardening guide * Shorten password manager hardening guide * Make wallet secret storage guidance explicit * Remove decorative separators from password manager hardening guide * Fix lint in password manager endpoint guide * Tighten password manager endpoint guide * Shorten password manager endpoint guide * Clarify password manager endpoint guidance * Add password manager endpoint hardening guide * Tone down password manager hardening guide * Shorten password manager hardening guide * Make wallet secret storage guidance explicit * Remove decorative separators from password manager hardening guide * Fix lint in password manager endpoint guide * Tighten password manager endpoint guide * Shorten password manager endpoint guide * Clarify password manager endpoint guidance * Expand password manager hardening scope * Revert "Expand password manager hardening scope" This reverts commit 4bf04d63f70c7d9a93f3182e255c0f3cc3bca0d9. * docs: tighten password manager browser guidance --------- Co-authored-by: Matías Aereal Aeón <388605+mattaereal@users.noreply.github.com> --- AGENTS.md | 5 - docs/pages/guides/endpoint-security/index.mdx | 1 + .../password-manager-endpoint-hardening.mdx | 100 ++++++++++++++++++ vocs.config.tsx | 1 + 4 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 docs/pages/guides/endpoint-security/password-manager-endpoint-hardening.mdx diff --git a/AGENTS.md b/AGENTS.md index b59b3e5db..e69de29bb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +0,0 @@ -# Clone Notes - -- This is an isolated checkout for PR `#418` on `security-alliance/frameworks`. -- Edit `docs/pages/contribute/contributing.mdx` and regenerate the root `CONTRIBUTING.md` with `node utils/sync-contributing.js`. -- Keep Discord channel references aligned with the reviewer-approved `frameworks-contribs` wording. diff --git a/docs/pages/guides/endpoint-security/index.mdx b/docs/pages/guides/endpoint-security/index.mdx index f9f5eae71..216334993 100644 --- a/docs/pages/guides/endpoint-security/index.mdx +++ b/docs/pages/guides/endpoint-security/index.mdx @@ -11,4 +11,5 @@ title: "Endpoint Security" ## Pages +- [Password Manager Endpoint Hardening](/guides/endpoint-security/password-manager-endpoint-hardening) - [Zoom Hardening Guide](/guides/endpoint-security/zoom-hardening) diff --git a/docs/pages/guides/endpoint-security/password-manager-endpoint-hardening.mdx b/docs/pages/guides/endpoint-security/password-manager-endpoint-hardening.mdx new file mode 100644 index 000000000..d560ee959 --- /dev/null +++ b/docs/pages/guides/endpoint-security/password-manager-endpoint-hardening.mdx @@ -0,0 +1,100 @@ +--- +title: "Password Manager Endpoint Hardening | Security Alliance" +description: "Reduce password manager endpoint risk with stronger device, browser, MFA, and recovery practices." +tags: + - Security Specialist + - Operations & Strategy + - Engineer/Developer +contributors: + - role: wrote + users: [dickson] +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' + + + + +# Password Manager Endpoint Hardening + + + + +## Summary + +> 🔑 **Key Takeaway:** Encrypt every device that can unlock your vault, lock it quickly, keep it updated, use +> phishing-resistant MFA on the password manager account, minimize browser and clipboard exposure, and rehearse +> recovery before an incident. + +A password manager concentrates access to many critical accounts, so the device and browser that can unlock it deserve +extra hardening. + +## For Individuals + +### Minimum Device Baseline + +- [ ] Use only supported, regularly updated operating systems and browsers +- [ ] Enable full-disk encryption on every device that can access the vault +- [ ] Require a real screen lock with a 5-minute-or-less idle timeout and password on wake +- [ ] Enable device location, remote lock, and remote wipe features before you need them +- [ ] Keep the password manager set to lock on sleep, device lock, and browser or app exit + +### Safer Browser and Vault Usage + +- [ ] Use a dedicated browser profile for work or other high-value accounts +- [ ] Keep that profile minimal: ideally only the password manager extension, and otherwise a short allowlist of + extensions you actively need and trust +- [ ] Prefer user-initiated fill over broad automatic fill on page load +- [ ] Verify the domain before filling credentials for high-impact accounts such as registrars, GitHub, cloud, + finance, or admin panels +- [ ] Avoid logging into the work vault from throwaway browsers, borrowed devices, or lightly managed systems + +### Clipboard and Copy/Paste Hygiene + +- [ ] Prefer direct fill into the browser or app instead of copying secrets to the clipboard +- [ ] Disable clipboard history and cross-device clipboard sync on endpoints used for sensitive workflows +- [ ] If you must copy a secret, paste it immediately and clear the clipboard or rely on the password manager's + auto-clear setting if supported + +### Protecting the Password Manager Account + +- [ ] Use phishing-resistant MFA such as FIDO2/WebAuthn security keys where supported +- [ ] For highest-risk operators, prefer hardware security keys over broadly syncable authenticators when possible +- [ ] Keep at least two recovery-capable authenticators enrolled +- [ ] Do not rely on SMS as the primary recovery or second-factor method +- [ ] Store recovery codes or emergency-kit material offline and separately from the device +- [ ] Avoid circular dependency: do not make the only copy of recovery material depend on access to the same vault + +## Web3-Specific Operational Rules + +Password managers in Web3 often gate access to registrars, code hosting, cloud, and finance systems. Treat the +endpoints that can unlock the vault accordingly. + +- Use a separate browser profile, and ideally a separate device, for the highest-risk admin workflows +- Prefer hardware security keys for the password manager account and highest-impact downstream services +- Never store wallet seed phrases, private keys, or recovery phrases in a password manager, browser storage, or notes + app +- If an endpoint used for registrar, GitHub, cloud, or finance access looks compromised, rotate those credentials first + +## Lost or Stolen Device Response + +Use these steps if a device with possible vault access is lost, stolen, or suspected compromised: + +1. Lock, mark lost, or wipe the device as quickly as possible. +2. Revoke the device or active sessions from the password manager or identity provider if the product supports it. +3. Change the password manager account password and review enrolled MFA methods. +4. If the vault may have been exposed, rotate the highest-risk downstream credentials first. +5. Replace recovery material and backup authenticators if their custody is uncertain. + +If you cannot confirm the vault was locked at the time of loss, treat exposed credentials as the default assumption and +rotate accordingly. + +## Further Reading + +- [NIST SP 800-63 Digital Identity Guidelines](https://pages.nist.gov/800-63-4/) +- [CISA: Implementing Phishing-Resistant MFA](https://www.cisa.gov/news-events/cybersecurity-advisories/aa23-320a) +- [NCSC: Password Managers](https://www.ncsc.gov.uk/collection/top-tips-for-staying-secure-online/password-managers) +- [Apple: Protect Data on Your Mac with FileVault](https://support.apple.com/guide/mac-help/protect-data-on-your-mac-with-filevault-mh11785/mac) + + + diff --git a/vocs.config.tsx b/vocs.config.tsx index bd89b9efb..8a68f369d 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -546,6 +546,7 @@ const config = { text: 'Endpoint Security', collapsed: true, items: [ + { text: 'Password Manager Endpoint Hardening', link: '/guides/endpoint-security/password-manager-endpoint-hardening', dev: true }, { text: 'Zoom Hardening', link: '/guides/endpoint-security/zoom-hardening' }, ] }, From d270ff433adbcfa6c89b5bcfb34a92ef519024e1 Mon Sep 17 00:00:00 2001 From: Dickson Wu <33645481+DicksonWu654@users.noreply.github.com> Date: Mon, 6 Apr 2026 10:37:29 -0600 Subject: [PATCH 03/92] Add YubiKey and hardware security key guide (#416) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add hardware security key guide * Credit Opsek authors on YubiKey guide * Remove decorative separators from hardware security keys guide * Fix lint in hardware security keys guide * Tighten hardware security keys guide * Move hardware security keys guide to endpoint security * Tighten hardware keys guide metadata * Update hardware-security-keys.mdx * Update hardware-security-keys.mdx * Add hardware security key guide * Credit Opsek authors on YubiKey guide * Remove decorative separators from hardware security keys guide * Fix lint in hardware security keys guide * Tighten hardware security keys guide * Move hardware security keys guide to endpoint security * Tighten hardware keys guide metadata * Add YubiKey-specific setup guidance * docs: tighten hardware key setup guidance * Delete AGENTS.md --------- Co-authored-by: Matías Aereal Aeón <388605+mattaereal@users.noreply.github.com> --- .../hardware-security-keys.mdx | 96 +++++++++++++++++++ docs/pages/guides/endpoint-security/index.mdx | 1 + vocs.config.tsx | 1 + wordlist.txt | 1 + 4 files changed, 99 insertions(+) create mode 100644 docs/pages/guides/endpoint-security/hardware-security-keys.mdx diff --git a/docs/pages/guides/endpoint-security/hardware-security-keys.mdx b/docs/pages/guides/endpoint-security/hardware-security-keys.mdx new file mode 100644 index 000000000..0cad9161d --- /dev/null +++ b/docs/pages/guides/endpoint-security/hardware-security-keys.mdx @@ -0,0 +1,96 @@ +--- +title: "Hardware Security Keys | Security Alliance" +description: "Use hardware security keys on critical accounts, keep a backup enrolled, and avoid weak recovery paths." +tags: + - Security Specialist +contributors: + - role: wrote + users: [dickson, louis, pablo] +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' + + + + +# Hardware Security Keys + + + + +## Summary + +> 🔑 **Key Takeaway for Hardware Security Keys:** Use FIDO2/WebAuthn security keys on high-value accounts, register +> at least two keys per critical account, disable SMS fallback where possible, and test recovery before you need it. + +Hardware security keys are one of the strongest practical defenses against phishing, credential stuffing, and +SIM-swap-based account takeovers. They are especially valuable for email, source control, registrars, cloud platforms, +social accounts, and any admin or financial account that could be used to pivot into the rest of your organization. +This guide focuses on YubiKeys and similar FIDO2/WebAuthn hardware keys. + +## For Individuals + +These steps apply to personal and work accounts that support FIDO2/WebAuthn security keys or passkeys stored on a +hardware key. + +### Setup Checklist + +- [ ] Buy at least **two** security keys from a reputable vendor such as Yubico +- [ ] Prefer keys that match your device mix: + - USB-C for modern laptops and phones + - NFC if you regularly authenticate on mobile +- [ ] Label one key **Primary** and the other **Backup** +- [ ] Register both keys on every critical account that supports them: + - Primary email + - GitHub and code hosting + - Registrar and DNS providers + - Cloud and deployment platforms + - Banking, custody, or treasury accounts + - Social and communication accounts +- [ ] Where offered, prefer: + - **Security key** + - **Passkey on hardware key** + - Other phishing-resistant WebAuthn/FIDO2 options +- [ ] Disable **SMS** as a recovery or second-factor method wherever the service allows it +- [ ] Save provider-issued backup or recovery codes offline +- [ ] Test both the primary and backup key after enrollment + +### YubiKey Setup Notes + +- Register both your primary and spare YubiKeys during the same setup session whenever the service allows it +- Set a PIN on the YubiKey or FIDO2 credential where the workflow supports it, and store that PIN separately from the + key itself +- If your YubiKey supports NFC and it is new, activate NFC before you rely on it for mobile logins +- Prefer the service's **Security key** or **Passkey** option on a YubiKey over app-based OTP when phishing-resistant + login is available +- If a service only supports authenticator-app codes, Yubico Authenticator can keep those codes tied to the YubiKey, + but treat that as a fallback rather than equivalent protection to WebAuthn security-key login + +### Practical Use + +- Keep the **Primary** key with you for normal logins +- Store the **Backup** key in a separate secure location, not in the same bag or drawer +- Maintain a short note in your password manager listing which critical accounts have which keys enrolled +- For high-value accounts, avoid storing passkeys in a password manager; keep them on the hardware key or another + dedicated phishing-resistant authenticator instead +- If a service allows multiple authentication methods, avoid leaving weaker fallback paths enabled unless they are + operationally necessary +- Replace lost or damaged keys immediately and re-test the remaining enrolled key + +### Recovery Discipline + +- Do not wait until you lose a key to learn how account recovery works +- If you lose your only key and do not have a second enrolled key or a usable recovery path, you can lock yourself out + of critical accounts at the moment you most need them +- Verify that your recovery path does not depend on a phone number if you are trying to reduce SIM-swap risk +- If an account only supports app-based MFA or SMS, record that exception clearly and prioritize moving the account to + a stronger provider or stronger configuration when possible + +## Further Reading + +- [Opsek YubiKeys Cheatsheet](https://github.com/Opsek/Yubikeys-cheatsheet) +- [Yubico Setup Guide](https://www.yubico.com/setup/) +- [Yubico: YubiKey Authenticator](https://www.yubico.com/products/yubico-authenticator/) + + + diff --git a/docs/pages/guides/endpoint-security/index.mdx b/docs/pages/guides/endpoint-security/index.mdx index 216334993..4ce168f61 100644 --- a/docs/pages/guides/endpoint-security/index.mdx +++ b/docs/pages/guides/endpoint-security/index.mdx @@ -11,5 +11,6 @@ title: "Endpoint Security" ## Pages +- [Hardware Security Keys](/guides/endpoint-security/hardware-security-keys) - [Password Manager Endpoint Hardening](/guides/endpoint-security/password-manager-endpoint-hardening) - [Zoom Hardening Guide](/guides/endpoint-security/zoom-hardening) diff --git a/vocs.config.tsx b/vocs.config.tsx index 8a68f369d..3e8a57bad 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -546,6 +546,7 @@ const config = { text: 'Endpoint Security', collapsed: true, items: [ + { text: 'Hardware Security Keys', link: '/guides/endpoint-security/hardware-security-keys' }, { text: 'Password Manager Endpoint Hardening', link: '/guides/endpoint-security/password-manager-endpoint-hardening', dev: true }, { text: 'Zoom Hardening', link: '/guides/endpoint-security/zoom-hardening' }, ] diff --git a/wordlist.txt b/wordlist.txt index c54146621..ac6954ca0 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -337,6 +337,7 @@ rootfs GitHub GitLab GoDaddy +Opsek Alchemix Bram dmarcian From 4b2071c04a4be887be071011017d8ec468d80a86 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Mon, 6 Apr 2026 18:41:01 +0200 Subject: [PATCH 04/92] Chore: add stewards for AI Security and Incident Management fws (#440) * Add stewards for Incident Management and AI Security fws * sync contributors company --- docs/pages/config/contributors.json | 51 ++++++++++++++++++----------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/docs/pages/config/contributors.json b/docs/pages/config/contributors.json index a6f05343a..1faf0e430 100644 --- a/docs/pages/config/contributors.json +++ b/docs/pages/config/contributors.json @@ -182,8 +182,8 @@ "avatar": "https://avatars.githubusercontent.com/pinalikefruit", "github": "https://github.com/pinalikefruit", "twitter": "https://x.com/pinalikefruit", - "website": "https://www.coinspect.com/", - "company": "Coinspect", + "website": null, + "company": null, "job_title": null, "role": "steward", "description": "Steward of Wallet-Security framework", @@ -503,25 +503,27 @@ "website": null, "company": null, "job_title": null, - "role": "contributor", - "description": "Frameworks Contributor", + "role": "steward", + "description": "Frameworks Steward", "badges": [ - { "name": "First-Contribution", "assigned": "2026-02-27" } + { "name": "First-Contribution", "assigned": "2026-02-27" }, + { "name": "Framework-Steward", "assigned": "2026-02-27", "framework": "AI Security" } ] }, - "jubos": { - "slug": "jubos", - "name": "jubos", - "avatar": "https://avatars.githubusercontent.com/jubos", - "github": "https://github.com/jubos", + "curtis0x": { + "slug": "curtis0x", + "name": "curtis0x", + "avatar": "https://avatars.githubusercontent.com/curtis0x", + "github": "https://github.com/curtis0x", "twitter": null, "website": null, "company": null, "job_title": null, - "role": "contributor", - "description": "Frameworks Contributor", + "role": "steward", + "description": "Frameworks Steward", "badges": [ - { "name": "First-Contribution", "assigned": "2026-02-27" } + { "name": "First-Contribution", "assigned": "2026-02-27" }, + { "name": "Framework-Steward", "assigned": "2026-02-27", "framework": "AI Security" } ] }, "masterfung": { @@ -533,10 +535,11 @@ "website": null, "company": null, "job_title": null, - "role": "contributor", - "description": "Frameworks Contributor", + "role": "steward", + "description": "Frameworks Steward", "badges": [ - { "name": "First-Contribution", "assigned": "2026-02-27" } + { "name": "First-Contribution", "assigned": "2026-02-27" }, + { "name": "Framework-Steward", "assigned": "2026-02-27", "framework": "AI Security" } ] }, "quillaudits": { @@ -561,8 +564,12 @@ "website": "https://lido.fi/", "company": "Lido", "job_title": null, - "role": "contributor", - "description": "Frameworks Contributor" + "role": "steward", + "description": "Frameworks Steward", + "badges": [ + { "name": "Framework-Steward", "assigned": "2026-03-26", "framework": "Incident Management" }, + { "name": "First-Contribution", "assigned": "2026-03-26" } + ] }, "n0guest": { "slug": "n0guest", @@ -573,8 +580,12 @@ "website": "https://lido.fi", "company": "Lido", "job_title": null, - "role": "contributor", - "description": "Frameworks Contributor" + "role": "steward", + "description": "Frameworks Steward", + "badges": [ + { "name": "Framework-Steward", "assigned": "2026-03-26", "framework": "Incident Management" }, + { "name": "First-Contribution", "assigned": "2026-03-26" } + ] }, "smagdali": { "slug": "smagdali", From efc396169e953b3d6f313edb2004ef713b18ce62 Mon Sep 17 00:00:00 2001 From: Dickson Wu <33645481+DicksonWu654@users.noreply.github.com> Date: Mon, 6 Apr 2026 10:49:36 -0600 Subject: [PATCH 05/92] Add SSH client and key management hardening guide (#420) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add SSH client and key management hardening guide * Remove decorative separators from SSH hardening guide * Fix markdownlint spacing in SSH hardening guide * Tighten SSH hardening guide * Shorten SSH hardening guide * Clarify SSH hardening guidance * Introduce SSH certificates for better key management Added a section on using SSH certificates for improved access management. * Clarify SSH host verification guidance * Fix SSH guide markdown formatting * Finish SSH guide review fixes * Add SSH client and key management hardening guide * Remove decorative separators from SSH hardening guide * Fix markdownlint spacing in SSH hardening guide * Tighten SSH hardening guide * Shorten SSH hardening guide * Clarify SSH hardening guidance * Introduce SSH certificates for better key management Added a section on using SSH certificates for improved access management. * Clarify SSH host verification guidance * Fix SSH guide markdown formatting * Finish SSH guide review fixes * Trim unrelated SSH wordlist entries * Update wordlist.txt * Add reviewers to SSH client hardening guide --------- Co-authored-by: Matías Aereal Aeón <388605+mattaereal@users.noreply.github.com> --- docs/pages/guides/endpoint-security/index.mdx | 1 + ...sh-client-and-key-management-hardening.mdx | 167 ++++++++++++++++++ vocs.config.tsx | 1 + 3 files changed, 169 insertions(+) create mode 100644 docs/pages/guides/endpoint-security/ssh-client-and-key-management-hardening.mdx diff --git a/docs/pages/guides/endpoint-security/index.mdx b/docs/pages/guides/endpoint-security/index.mdx index 4ce168f61..df75b2f13 100644 --- a/docs/pages/guides/endpoint-security/index.mdx +++ b/docs/pages/guides/endpoint-security/index.mdx @@ -11,6 +11,7 @@ title: "Endpoint Security" ## Pages +- [SSH Client and Key Management Hardening](/guides/endpoint-security/ssh-client-and-key-management-hardening) - [Hardware Security Keys](/guides/endpoint-security/hardware-security-keys) - [Password Manager Endpoint Hardening](/guides/endpoint-security/password-manager-endpoint-hardening) - [Zoom Hardening Guide](/guides/endpoint-security/zoom-hardening) diff --git a/docs/pages/guides/endpoint-security/ssh-client-and-key-management-hardening.mdx b/docs/pages/guides/endpoint-security/ssh-client-and-key-management-hardening.mdx new file mode 100644 index 000000000..75808e185 --- /dev/null +++ b/docs/pages/guides/endpoint-security/ssh-client-and-key-management-hardening.mdx @@ -0,0 +1,167 @@ +--- +title: "SSH Client and Key Management Hardening | Security Alliance" +description: "Harden SSH client usage with better key handling, agent discipline, host verification, and rotation." +tags: + - Engineer/Developer + - Security Specialist +contributors: + - role: wrote + users: [dickson] + - role: reviewed + users: [mattaereal, pinalikefruit] +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' + + + + +# SSH Client and Key Management Hardening + + + + +## Summary + +> 🔑 **Key Takeaway for SSH Client and Key Management Hardening:** Use dedicated SSH keys for distinct purposes, +> prefer Ed25519 for new software keys, use hardware-backed `*-sk` keys for higher-risk access where practical, +> protect keys with passphrases, keep agent forwarding off by default, and maintain a simple revoke-and-reissue plan +> for lost devices or role changes. + +SSH is often the path from a developer laptop into code hosting, CI/CD, bastions, and production infrastructure. Treat +SSH keys as privileged access. + +## For Individuals + +These steps apply to developers, operators, admins, and contributors who use SSH from a local workstation. + +### Setup Checklist + +- [ ] Use **Ed25519** for new software keys by default +- [ ] Add a **strong passphrase** to every human-held software key +- [ ] For higher-risk access, prefer a **hardware-backed security-key SSH credential** such as `ed25519-sk` +- [ ] Keep separate keys for: + Git hosting, staging/internal infrastructure, production/bastion access, and automation +- [ ] Keep `ForwardAgent no` unless there is a specific and reviewed operational need +- [ ] Use explicit per-host entries in `~/.ssh/config` with `IdentityFile` and `IdentitiesOnly yes` +- [ ] Keep `StrictHostKeyChecking` enabled: + use `accept-new` only for lower-risk hosts where trust-on-first-use is acceptable, and use `yes` for bastions + and production systems where host verification should be explicit +- [ ] Use `UpdateHostKeys yes` only for hosts you already trust and manage, especially where you expect planned key + rotation +- [ ] Keep `~/.ssh` and private key files accessible only to you +- [ ] Review and remove stale keys after device loss, role changes, or offboarding + +### Passphrases and Local Protection + +- Use a passphrase on every human-held software key +- Avoid leaving sensitive keys loaded into an agent indefinitely +- Prefer confirmation or time-limited agent loading for sensitive keys +- Do not store private keys in cloud notes, chat, or shared drives +- Avoid copying the same private key between multiple laptops or admin workstations + +### `~/.ssh/config` Hygiene + +Keep `~/.ssh/config` explicit and purpose-specific so your SSH client offers only the intended key to each service. + +- Use per-host entries with `IdentityFile` and `IdentitiesOnly yes` +- Keep `ForwardAgent no` unless there is a reviewed exception +- Use stricter host verification for bastions and production hosts than for low-risk systems + +### Agent Forwarding and Remote Risk + +Treat agent forwarding as an exception, not a default workflow. + +- Keep `ForwardAgent no` by default +- Never use `Host *` with `ForwardAgent yes` +- Prefer `ProxyJump`, a bastion pattern, or a purpose-specific key on the intermediary host instead + +Agent forwarding lets a remote system use your loaded identities for as long as the session is live, so it is a poor +default for weakly trusted systems. + +### Host Verification and `known_hosts` + +Do not normalize `StrictHostKeyChecking no` or "just click through" behavior for host key changes. + +- Verify important host fingerprints from official documentation when available +- Use `accept-new` only where trust-on-first-use is acceptable for the environment +- Use `yes` for bastions, production systems, and other high-sensitivity hosts +- Do not rely on `accept-new` for first contact with high-sensitivity systems +- Preseed `known_hosts` for CI and automation from a trusted source instead of discovering keys live in the job +- Investigate unexpected host key changes instead of bypassing the warning + +### File Permissions and Device Hygiene + +- On Unix-like systems, use `chmod 700 ~/.ssh` +- Use `chmod 600` for private keys and for `~/.ssh/config` +- On Windows, keep private keys readable only by your user account and administrators + +If a laptop is lost, stolen, or suspected compromised, assume the SSH material on it may also need to be revoked and +reissued. + +## For Admins + +These practices apply to administrators responsible for issuing, reviewing, rotating, and revoking SSH access across a +team. + +### Program Checklist + +- [ ] Maintain an inventory for each SSH credential: + owner, device, purpose, target systems, creation date, and revoke trigger +- [ ] Require one key per person, device, and purpose instead of shared identities +- [ ] Prefer hardware-backed SSH credentials for privileged production access where practical +- [ ] Review account SSH keys, deploy keys, and automation keys on a regular cadence +- [ ] Remove unknown, stale, or unapproved keys during access reviews +- [ ] Revoke keys immediately after offboarding, device loss, or suspected compromise +- [ ] Keep human access and automation access logically separate +- [ ] Document a simple recovery and reissue process for lost devices and security keys + +### Git Hosting, CI/CD, and Automation + +- Human users should have account-level SSH keys with clear labels +- CI/CD and deployment systems should use dedicated automation credentials, not a developer's personal laptop key +- Review deploy keys carefully because they are often long-lived and easy to forget +- Revoke and reissue keys promptly after device loss, offboarding, or suspected compromise + +### Higher-Maturity Option: SSH Certificates + +For larger teams or higher-sensitivity environments, consider using SSH certificates instead of managing long-lived +authorized keys on each target system. + +With SSH certificates, a trusted internal certificate authority (CA) signs user or host keys for a limited period +of time. This can make access management easier by allowing teams to: + +- Issue short-lived SSH access without distributing permanent keys to every host +- Centralize approval, expiration, and revocation workflows +- Reduce the operational burden of updating `authorized_keys` across many systems +- Tie SSH access more closely to role, device, or incident-response requirements + +SSH certificates introduce their own operational complexity and should be implemented carefully, but they are worth +evaluating for organizations that need stronger central control over SSH access. + +## Web3-Specific Operational Rules + +Use these rules consistently: + +1. Do not use the same SSH key for Git hosting, deploy systems, and production administration. +2. Treat bastion, production, and incident-response SSH access as privileged and higher assurance. +3. Keep agent forwarding off unless there is a reviewed exception. +4. Investigate host key changes before reconnecting to important systems. +5. Revoke and replace SSH keys immediately after device loss or admin-role changes. +6. Keep automation credentials separate from human workstation credentials. + +## Further Reading + +- [NIST IR 7966: Security of Interactive and Automated Access Management Using Secure Shell (SSH)](https://csrc.nist.gov/pubs/ir/7966/final) +- [OpenBSD `ssh_config(5)`](https://man.openbsd.org/ssh_config) +- [OpenBSD `ssh-keygen(1)`](https://man.openbsd.org/ssh-keygen.1) +- [OpenBSD `ssh-add(1)`](https://man.openbsd.org/ssh-add.1) +- [GitHub: Generating a New SSH Key and Adding It to the SSH Agent](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent) +- [GitHub: Reviewing Your SSH Keys](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/reviewing-your-ssh-keys) +- [GitHub: GitHub's SSH Key Fingerprints](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints) +- [GitLab: SSH Keys](https://docs.gitlab.com/user/ssh/) +- [GitLab: Using SSH Keys with GitLab CI/CD](https://docs.gitlab.com/ci/jobs/ssh_keys/) +- [Microsoft Learn: OpenSSH Key Management on Windows](https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_keymanagement) + + + diff --git a/vocs.config.tsx b/vocs.config.tsx index 3e8a57bad..55576c952 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -546,6 +546,7 @@ const config = { text: 'Endpoint Security', collapsed: true, items: [ + { text: 'SSH Client and Key Management Hardening', link: '/guides/endpoint-security/ssh-client-and-key-management-hardening' }, { text: 'Hardware Security Keys', link: '/guides/endpoint-security/hardware-security-keys' }, { text: 'Password Manager Endpoint Hardening', link: '/guides/endpoint-security/password-manager-endpoint-hardening', dev: true }, { text: 'Zoom Hardening', link: '/guides/endpoint-security/zoom-hardening' }, From 8e5fe712624b1694cc890c760d4f6bd9e09d6c06 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Wed, 8 Apr 2026 17:49:27 +0200 Subject: [PATCH 06/92] feat/github workflow to upload images to S3 bucket (#290) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add github workflow to autmatically upload images to S3 * implement review + deps revamp * Reorganize AWS credentials configuration in workflow * Add --ignore-scripts to install step for supply chain attack mitigation --------- Co-authored-by: Matías Aereal Aeón <388605+mattaereal@users.noreply.github.com> --- .github/scripts/process-images.js | 492 +++++++ .github/workflows/s3-upload.yml | 428 ++++++ CONTRIBUTING.md | 35 +- docs/pages/contribute/contributing.mdx | 36 +- package.json | 7 +- pnpm-lock.yaml | 1749 +++++++++++++++++++++++- 6 files changed, 2717 insertions(+), 30 deletions(-) create mode 100644 .github/scripts/process-images.js create mode 100644 .github/workflows/s3-upload.yml diff --git a/.github/scripts/process-images.js b/.github/scripts/process-images.js new file mode 100644 index 000000000..5b9edcbbd --- /dev/null +++ b/.github/scripts/process-images.js @@ -0,0 +1,492 @@ +const { S3Client } = require('@aws-sdk/client-s3'); +const { Upload } = require('@aws-sdk/lib-storage'); +const axios = require('axios'); +const sharp = require('sharp'); +const { v4: uuidv4 } = require('uuid'); +const crypto = require('crypto'); +const path = require('path'); +const fs = require('fs'); + +// ============================================================================ +// CONFIGURATION CONSTANTS +// ============================================================================ +const MAX_FILE_SIZE = 10 * 1024 * 1024; +const MAX_WIDTH = 4096; +const MAX_HEIGHT = 4096; +const MAX_PIXELS = 16777216; +const MAX_COMPRESSION_RATIO = 50; + +// Allowed image formats +const ALLOWED_FORMATS = ['jpeg', 'jpg', 'png']; + +// Request timeout in milliseconds (30 seconds) +const REQUEST_TIMEOUT = 30000; + +// Maximum images per request (rate limiting) +const MAX_IMAGES_PER_REQUEST = 10; + +// Allowed Content-Type prefixes for image responses +const ALLOWED_CONTENT_TYPES = ['image/jpeg', 'image/png', 'image/jpg']; + +// Only allowed hostname for GitHub PR comment images +const ALLOWED_HOSTNAME = 'private-user-images.githubusercontent.com'; + +// ============================================================================ +// AWS S3 CLIENT INITIALIZATION +// ============================================================================ +const s3Client = new S3Client({ + region: process.env.AWS_REGION +}); + +// ============================================================================ +// URL SANITIZATION HELPER +// ============================================================================ +// Redacts sensitive information from URLs for logging +function sanitizeUrlForLogging(url) { + try { + const urlObj = new URL(url); + const sensitiveParams = ['jwt', 'token', 'key', 'secret', 'auth', 'signature', 'sig']; + + for (const param of sensitiveParams) { + if (urlObj.searchParams.has(param)) { + urlObj.searchParams.set(param, '[REDACTED]'); + } + } + + // Also check for JWT-like patterns in any parameter + for (const [key, value] of urlObj.searchParams.entries()) { + if (value && value.length > 50 && /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(value)) { + urlObj.searchParams.set(key, '[REDACTED_JWT]'); + } + } + + return urlObj.toString(); + } catch { + // If URL parsing fails, do basic redaction + return url.replace(/([?&](jwt|token|key|secret|auth|signature|sig)=)[^&]+/gi, '$1[REDACTED]'); + } +} + +// ============================================================================ +// IMAGE DOWNLOAD FUNCTION +// ============================================================================ +// Downloads the image from the URL +async function downloadImage(url) { + const safeLogUrl = sanitizeUrlForLogging(url); + console.log(`Downloading: ${safeLogUrl.substring(0, 100)}...`); + + // Validate URL hostname before making request + try { + const urlObj = new URL(url); + if (urlObj.hostname.toLowerCase() !== ALLOWED_HOSTNAME) { + throw new Error(`Invalid hostname. Only ${ALLOWED_HOSTNAME} URLs are accepted.`); + } + if (urlObj.protocol !== 'https:') { + throw new Error('Only HTTPS URLs are allowed.'); + } + if (!urlObj.searchParams.has('jwt')) { + throw new Error('URL missing required JWT token. Please copy the full image URL from GitHub.'); + } + } catch (error) { + if (error.message.includes('Invalid hostname') || + error.message.includes('Only HTTPS') || + error.message.includes('JWT token')) { + throw error; + } + throw new Error(`Invalid URL format: ${error.message}`); + } + + try { + const headers = { + 'User-Agent': 'GitHub-Image-Upload-Bot/1.0', + 'Accept': 'image/*' + }; + + const response = await axios({ + url, + method: 'GET', + responseType: 'arraybuffer', + maxContentLength: MAX_FILE_SIZE, + maxBodyLength: MAX_FILE_SIZE, + timeout: REQUEST_TIMEOUT, + maxRedirects: 0, + headers, + validateStatus: (status) => status >= 200 && status < 300, + beforeRedirect: (options, { headers: responseHeaders }) => { + throw new Error('Redirects are not allowed for security reasons'); + } + }); + + // Validate Content-Type header to ensure we're receiving an image + const contentType = response.headers['content-type']; + if (contentType) { + const normalizedContentType = contentType.toLowerCase().split(';')[0].trim(); + const isValidImageType = ALLOWED_CONTENT_TYPES.some( + allowed => normalizedContentType === allowed + ); + + if (!isValidImageType) { + throw new Error(`Invalid Content-Type: ${normalizedContentType}. Expected: ${ALLOWED_CONTENT_TYPES.join(', ')}`); + } + console.log(`Content-Type: ${normalizedContentType}`); + } else { + throw new Error('Missing Content-Type header in response'); + } + + const buffer = Buffer.from(response.data); + + if (buffer.length > MAX_FILE_SIZE) { + throw new Error(`Image size (${(buffer.length / 1024 / 1024).toFixed(2)}MB) exceeds maximum allowed size (${MAX_FILE_SIZE / 1024 / 1024}MB)`); + } + + if (buffer.length === 0) { + throw new Error('Downloaded image is empty'); + } + + console.log(`✓ Downloaded ${(buffer.length / 1024).toFixed(2)}KB`); + return buffer; + } catch (error) { + if (error.response) { + const status = error.response.status; + + if (status === 400 || status === 404) { + throw new Error( + 'Image URL expired or invalid (HTTP ' + status + '). ' + + 'GitHub image URLs expire in ~5 minutes. ' + + 'Please: 1) Refresh the PR page, 2) Copy a fresh image URL, 3) Run /img-bot again immediately.' + ); + } + throw new Error(`HTTP ${status}: ${error.response.statusText || 'Request failed'}`); + } + + if (error.code === 'ECONNABORTED') { + throw new Error('Download timed out. Please try again.'); + } + + throw new Error(`Download failed: ${error.message}`); + } +} + +// ============================================================================ +// IMAGE VALIDATION FUNCTION +// ============================================================================ +// Validates that the downloaded buffer is a valid image +async function validateImage(buffer) { + console.log('Validating image...'); + + try { + const sharpInstance = sharp(buffer, { + limitInputPixels: MAX_PIXELS, + sequentialRead: true, + failOn: 'error' + }); + + const metadata = await sharpInstance.metadata(); + + console.log('Image metadata:', { + format: metadata.format, + width: metadata.width, + height: metadata.height, + size: `${(buffer.length / 1024).toFixed(2)}KB` + }); + + // Validate format + if (!ALLOWED_FORMATS.includes(metadata.format)) { + throw new Error(`Invalid format: ${metadata.format}. Allowed: ${ALLOWED_FORMATS.join(', ')}`); + } + + // Validate dimensions (prevents overly large images) + if (!metadata.width || !metadata.height) { + throw new Error('Unable to determine image dimensions'); + } + + if (metadata.width > MAX_WIDTH || metadata.height > MAX_HEIGHT) { + throw new Error(`Image dimensions ${metadata.width}x${metadata.height} exceed maximum ${MAX_WIDTH}x${MAX_HEIGHT}`); + } + + if (metadata.width < 1 || metadata.height < 1) { + throw new Error('Invalid image dimensions'); + } + + // Check total pixel count + const totalPixels = metadata.width * metadata.height; + if (totalPixels > MAX_PIXELS) { + throw new Error(`Image pixel count ${totalPixels.toLocaleString()} exceeds maximum ${MAX_PIXELS.toLocaleString()}`); + } + + const channels = metadata.channels || 4; + const bitsPerChannel = metadata.depth === 'uchar' ? 8 : (metadata.depth === 'ushort' ? 16 : 8); + const bytesPerPixel = channels * (bitsPerChannel / 8); + const estimatedUncompressedSize = metadata.width * metadata.height * bytesPerPixel; + const compressionRatio = estimatedUncompressedSize / buffer.length; + + console.log(`Compression ratio: ${compressionRatio.toFixed(1)}:1`); + + if (compressionRatio > MAX_COMPRESSION_RATIO) { + throw new Error( + `Suspicious compression ratio (${compressionRatio.toFixed(1)}:1) exceeds maximum allowed (${MAX_COMPRESSION_RATIO}:1). ` + + `This may indicate a decompression bomb.` + ); + } + + console.log('✓ Image validation passed'); + return metadata; + } catch (error) { + const safeMessage = error.message + .replace(/\/[^\s]+/g, '[PATH]') + .substring(0, 200); + throw new Error(`Image validation failed: ${safeMessage}`); + } +} + +// ============================================================================ +// EXIF STRIPPING FUNCTION +// ============================================================================ +// Strips EXIF metadata from images +async function stripExifMetadata(buffer, format) { + console.log('Stripping EXIF metadata...'); + + try { + const sharpInstance = sharp(buffer, { + limitInputPixels: MAX_PIXELS, + sequentialRead: true + }); + + let processed = sharpInstance.rotate(); + + if (format === 'png') { + processed = processed.png({ + compressionLevel: 9, + effort: 10 + }); + } else { + processed = processed.jpeg({ + quality: 95, + mozjpeg: true + }); + } + + const strippedBuffer = await processed.toBuffer(); + + console.log(`✓ EXIF stripped. Original: ${(buffer.length / 1024).toFixed(2)}KB, New: ${(strippedBuffer.length / 1024).toFixed(2)}KB`); + return strippedBuffer; + } catch (error) { + console.warn(`Warning: Could not strip EXIF metadata: ${error.message}`); + return buffer; + } +} + +// ============================================================================ +// FILENAME GENERATION FUNCTION +// ============================================================================ +// Generates a unique filename for the uploaded image +function generateUniqueFilename(originalUrl, format) { + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + + // Use full UUID for better collision resistance + const uniqueId = uuidv4(); + const randomBytes = crypto.randomBytes(16).toString('hex'); + const hash = crypto.createHash('sha256') + .update(originalUrl) + .update(randomBytes) + .update(Date.now().toString()) + .digest('hex') + .substring(0, 12); + + const sanitizedFormat = format.replace(/[^a-zA-Z0-9]/g, '').toLowerCase(); + + const safeFormats = ['jpeg', 'jpg', 'png']; + if (!safeFormats.includes(sanitizedFormat)) { + throw new Error(`Invalid format for filename: ${format}`); + } + + return `${timestamp}_${uniqueId}_${hash}.${sanitizedFormat}`; +} + +// ============================================================================ +// S3 UPLOAD FUNCTION +// ============================================================================ +// Uploads the image buffer to AWS S3 +async function uploadToS3(buffer, filename, contentType) { + console.log(`Uploading to S3: ${filename}`); + + try { + const bucket = process.env.AWS_S3_BUCKET; + const region = process.env.AWS_REGION; + + if (!bucket || !region) { + throw new Error('AWS_S3_BUCKET and AWS_REGION environment variables are required'); + } + + const upload = new Upload({ + client: s3Client, + params: { + Bucket: bucket, + Key: `images/${filename}`, + Body: buffer, + ContentType: contentType, + CacheControl: 'public, max-age=31536000, immutable', + ContentDisposition: `inline; filename="${filename}"`, + Metadata: { + 'uploaded-by': 'img-bot', + 'upload-timestamp': new Date().toISOString() + } + } + }); + + await upload.done(); + + const publicUrl = `https://${bucket}.s3.${region}.amazonaws.com/images/${filename}`; + + console.log(`✓ Uploaded to S3: ${publicUrl}`); + return publicUrl; + } catch (error) { + const safeMessage = error.message + .replace(/arn:[^\s]+/gi, '[ARN]') + .replace(/[a-z0-9-]+\.s3\.[a-z0-9-]+\.amazonaws\.com/gi, '[S3_ENDPOINT]') + .replace(/s3:\/\/[^\s]+/gi, '[S3_PATH]') + .substring(0, 200); + + throw new Error(`S3 upload failed: ${safeMessage}`); + } +} + +// ============================================================================ +// MAIN IMAGE PROCESSING FUNCTION +// ============================================================================ +// Processes a single image: download, validate, strip EXIF, upload +async function processImage(url, index) { + const safeLogUrl = sanitizeUrlForLogging(url).substring(0, 80) + '...'; + + try { + const buffer = await downloadImage(url); + + const metadata = await validateImage(buffer); + + const strippedBuffer = await stripExifMetadata(buffer, metadata.format); + + const filename = generateUniqueFilename(url, metadata.format); + + const s3Url = await uploadToS3( + strippedBuffer, + filename, + `image/${metadata.format}` + ); + + return { + success: true, + originalUrl: safeLogUrl, + s3Url: s3Url, + filename: filename, + metadata: { + format: metadata.format, + width: metadata.width, + height: metadata.height, + size: `${(strippedBuffer.length / 1024).toFixed(2)}KB` + } + }; + } catch (error) { + // We don't throw the error here so other images can still be processed + return { + success: false, + originalUrl: safeLogUrl, + error: error.message.substring(0, 300) + }; + } +} + +// ============================================================================ +// MAIN ENTRY POINT +// ============================================================================ +// Reads URLs from file and processes them sequentially +async function main() { + let urls; + + try { + const urlsPath = process.env.URLS_FILE_PATH || 'urls.json'; + + if (!fs.existsSync(urlsPath)) { + throw new Error(`URLs file not found: ${urlsPath}`); + } + + const rawData = fs.readFileSync(urlsPath, 'utf8'); + urls = JSON.parse(rawData); + + if (!Array.isArray(urls)) { + throw new Error('URLs file does not contain an array'); + } + + if (urls.length === 0) { + throw new Error('URLs array is empty'); + } + + if (urls.length > MAX_IMAGES_PER_REQUEST) { + throw new Error( + `Too many images (${urls.length}). Maximum allowed per request: ${MAX_IMAGES_PER_REQUEST}` + ); + } + + for (const url of urls) { + if (typeof url !== 'string' || !url.startsWith('https://')) { + throw new Error('Invalid URL format in input array'); + } + } + + console.log(`Processing ${urls.length} image(s)...`); + } catch (error) { + console.error('Failed to read/parse URLs file:', error.message); + // Output error result so workflow can parse and report it + console.log('\nRESULTS:', JSON.stringify([{ + success: false, + originalUrl: 'unknown', + error: `Failed to read/parse URLs: ${error.message.substring(0, 200)}` + }], null, 2)); + process.exit(1); + } + + // Process each URL sequentially + const results = []; + for (let i = 0; i < urls.length; i++) { + const url = urls[i]; + const safeLogUrl = sanitizeUrlForLogging(url).substring(0, 60); + console.log(`\n[${i + 1}/${urls.length}] Processing: ${safeLogUrl}...`); + + const result = await processImage(url, i); + results.push(result); + + if (result.success) { + console.log(`✓ Success: ${result.filename}`); + } else { + console.error(`✗ Failed: ${result.error}`); + } + } + + // Summary + const successCount = results.filter(r => r.success).length; + const failCount = results.filter(r => !r.success).length; + console.log(`\n========================================`); + console.log(`Summary: ${successCount} succeeded, ${failCount} failed`); + console.log(`========================================`); + + // Always output RESULTS, even if empty (for workflow parsing) + console.log('\nRESULTS:', JSON.stringify(results, null, 2)); + + // Exit with code 1 only if all images failed + const allFailed = results.length > 0 && results.every(r => !r.success); + if (allFailed) { + console.error('\n✗ All images failed to process'); + process.exit(1); + } +} + +// Run main function and handle any unhandled errors +main().catch(error => { + console.error('Fatal error:', error.message); + // Output empty results so workflow can still parse and report the error + console.log('\nRESULTS:', JSON.stringify([{ + success: false, + originalUrl: 'unknown', + error: `Fatal error: ${error.message.substring(0, 200)}` + }], null, 2)); + process.exit(1); +}); diff --git a/.github/workflows/s3-upload.yml b/.github/workflows/s3-upload.yml new file mode 100644 index 000000000..db8b227f8 --- /dev/null +++ b/.github/workflows/s3-upload.yml @@ -0,0 +1,428 @@ +name: Process and Upload Images + +# This workflow processes image URLs from PRs comments and uploads them to the S3 bucket +# It triggers when a comment is created on an issue or pull request +on: + issue_comment: + types: [created] + +permissions: + pull-requests: write + issues: write + contents: read + id-token: write + +jobs: + upload-images: + concurrency: + group: upload-images-${{ github.event.issue.number }} + cancel-in-progress: false + + # Conditional execution: Only run if ALL conditions are met: + # 1. Comment is on a pull request (not a regular issue) + # 2. Comment starts with '/img-bot' command + # 3. Commenter has appropriate permissions (COLLABORATOR/MEMBER/OWNER) + if: | + github.event.issue.pull_request && + startsWith(github.event.comment.body, '/img-bot') && + ( + github.event.comment.author_association == 'COLLABORATOR' || + github.event.comment.author_association == 'MEMBER' || + github.event.comment.author_association == 'OWNER' + ) + + runs-on: ubuntu-latest + + steps: + # Step 0: Verify user permissions + - name: Verify user permissions + id: check-permissions + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const allowed = ['admin', 'maintain', 'triage']; + const username = context.payload.comment.user.login; + + // Get the permission level via API + const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: username, + }); + + const perm = data.permission; + + if (!allowed.includes(perm)) { + core.setFailed(`User ${username} has insufficient permissions (${perm}).`); + return; + } + + console.log(`✅ User ${username} allowed (${perm} permission).`); + + # Step 1: Acknowledge the command by adding a reaction + - name: React to command + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + // Add an "eyes" emoji reaction to the comment to show we're processing + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'eyes' + }); + + # Step 2: Extract GitHub image URLs from the comment + - name: Extract GitHub image URLs + id: extract-urls + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const comment = context.payload.comment.body; + + // Extract URLs from comment using regex + const urlRegex = /(https:\/\/[^\s\]\)]+)/g; + const urls = comment.match(urlRegex) || []; + + const validatedUrls = []; + const validationErrors = []; + + // Only allowed hostname + const ALLOWED_HOSTNAME = 'private-user-images.githubusercontent.com'; + + const VALID_PATH_PATTERN = /^\/\d+\/\d+-[a-f0-9-]{36}\.(png|jpg|jpeg)$/i; + + for (const url of urls) { + if (url === '/img-bot') continue; + + try { + const urlObj = new URL(url); + + if (urlObj.protocol !== 'https:') { + validationErrors.push(`Only HTTPS URLs are allowed`); + continue; + } + + // Strict hostname validation + const hostname = urlObj.hostname.toLowerCase(); + + if (hostname !== ALLOWED_HOSTNAME) { + validationErrors.push(`Invalid hostname '${hostname}'. Only ${ALLOWED_HOSTNAME} URLs are accepted.`); + continue; + } + + // Validate pathname format + const pathname = decodeURIComponent(urlObj.pathname); + + // Check for path traversal attempts + if (pathname.includes('..') || pathname.includes('//') || pathname.includes('\\')) { + validationErrors.push(`Invalid path pattern detected`); + continue; + } + + // Validate path matches expected GitHub image URL format + if (!VALID_PATH_PATTERN.test(pathname)) { + validationErrors.push(`URL path does not match expected GitHub image format`); + continue; + } + + // Must have JWT token (required for authentication) + if (!urlObj.searchParams.has('jwt')) { + validationErrors.push(`URL missing required JWT token. Please copy the full image URL from GitHub.`); + continue; + } + + // Store original URL for download, sanitized for logging + const sanitizedUrl = `${urlObj.protocol}//${urlObj.hostname}${urlObj.pathname}?jwt=[REDACTED]`; + + validatedUrls.push({ + original: url, + sanitized: sanitizedUrl + }); + } catch (error) { + validationErrors.push(`Invalid URL format - ${error.message}`); + } + } + + if (validatedUrls.length === 0 && validationErrors.length === 0) { + throw new Error( + 'No valid GitHub image URLs found in comment.\n\n' + + 'To get the correct URL:\n' + + '1. Refresh the PR page (important - JWT tokens expire in ~5 minutes)\n' + + '2. Right-click the image and select "Copy image address"\n' + + '3. Paste the URL in your /img-bot comment' + ); + } + + if (validationErrors.length > 0) { + throw new Error( + `URL validation failed:\n${validationErrors.join('\n')}\n\n` + + `Please ensure all URLs are GitHub-hosted image URLs.` + ); + } + + console.log(`Extracted ${validatedUrls.length} GitHub image URL(s)`); + console.log('URLs (sanitized):', validatedUrls.map(u => u.sanitized)); + + // Warn about JWT expiration + console.log('⚠️ Note: JWT tokens expire in ~5 minutes. Ensure URLs were copied from a freshly loaded page.'); + + // Return only the original URLs for processing (script needs full URL with JWT) + return validatedUrls.map(u => u.original); + result-encoding: json + + # Step 3: Validate that all required AWS secrets are configured + - name: Validate OIDC authentication + run: | + if [ -z "${{ secrets.AWS_ROLE_ARN }}" ]; then + echo "::error::AWS_ROLE_ARN secret is not configured" + exit 1 + fi + + if [ -z "${{ secrets.AWS_REGION }}" ]; then + echo "::error::AWS_REGION secret is not configured" + exit 1 + fi + + if [ -z "${{ secrets.AWS_S3_BUCKET }}" ]; then + echo "::error::AWS_S3_BUCKET secret is not configured" + exit 1 + fi + + echo "✓ OIDC authentication configured" + + # Step 4: Checkout repository + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + # Step 5: Setup Node.js and pnpm + - name: Setup Node.js + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + with: + node-version: "20" + + # Step 6: Install pnpm + - name: Setup pnpm + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 + with: + version: 10.15.0 + run_install: false + + # Step 7: Cache pnpm dependencies + - name: Cache pnpm dependencies + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + id: pnpm-cache + with: + path: | + node_modules + ~/.pnpm-store + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm- + + # Step 8: Install dependencies + - name: Install dependencies + run: | + # Install dependencies using pnpm + pnpm install --frozen-lockfile --ignore-scripts + + # Step 9: Write URLs to a temp file for the processing script + - name: Write URLs to file + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const fs = require('fs'); + const path = require('path'); + + // Get URLs directly from the step output (already JSON) + const urls = ${{ steps.extract-urls.outputs.result }}; + + // Write to a secure temp location + const urlsPath = path.join(process.env.RUNNER_TEMP || '/tmp', 'urls.json'); + fs.writeFileSync(urlsPath, JSON.stringify(urls, null, 2), { mode: 0o600 }); + + // Export path for next step + core.exportVariable('URLS_FILE_PATH', urlsPath); + + console.log(`Wrote ${urls.length} URL(s) to secure temp file`); + + # Step 9b: Configure AWS credentials using OIDC (just-in-time before upload) + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 # v4.1.0 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + aws-region: ${{ secrets.AWS_REGION }} + role-session-name: img-bot-${{ github.run_id }} + + # Step 10: Process and upload images + - name: Process and upload images + id: process + env: + AWS_REGION: ${{ secrets.AWS_REGION }} + AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} + URLS_FILE_PATH: ${{ env.URLS_FILE_PATH }} + run: | + # Run script and capture output + TEMP_OUTPUT=$(mktemp) + set +e + node .github/scripts/process-images.js > "$TEMP_OUTPUT" 2>&1 + EXIT_CODE=$? + set -e + + # Extract RESULTS from script output + RESULTS=$(perl -0777 -pe 's/.*RESULTS:\s*//s' "$TEMP_OUTPUT" 2>/dev/null || echo '') + RESULTS=$(echo "$RESULTS" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + + # Validate and write results + RESULTS_PATH=$(mktemp) + if [ -z "$RESULTS" ] || ! echo "$RESULTS" | grep -q '^\['; then + echo "::error::Failed to extract valid RESULTS from script output" + tail -20 "$TEMP_OUTPUT" | grep -v -E '(jwt=|token=|Bearer |Authorization:)' + rm -f "$TEMP_OUTPUT" + exit 1 + fi + + echo "$RESULTS" > "$RESULTS_PATH" + echo "RESULTS_FILE_PATH=$RESULTS_PATH" >> $GITHUB_ENV + + # Show script output for debugging (only if script failed) + if [ "$EXIT_CODE" -ne 0 ]; then + echo "Script output (filtered):" + cat "$TEMP_OUTPUT" | grep -v -E '(jwt=|token=|Bearer |Authorization:)' + fi + + # Clean up temp output + rm -f "$TEMP_OUTPUT" + + # Step 11: Comment results back to PR and check for failures + - name: Comment results and check failures + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + RESULTS_FILE_PATH: ${{ env.RESULTS_FILE_PATH }} + with: + script: | + const fs = require('fs'); + let results; + try { + const resultsPath = process.env.RESULTS_FILE_PATH; + if (!resultsPath || !fs.existsSync(resultsPath)) { + throw new Error('Results file not found - workflow may have failed before processing'); + } + + const resultsStr = fs.readFileSync(resultsPath, 'utf8').trim(); + if (!resultsStr || resultsStr === '') { + throw new Error('Results file is empty - workflow may have failed before processing'); + } + + results = JSON.parse(resultsStr); + + if (!Array.isArray(results)) { + throw new Error(`Results is not an array: ${typeof results}`); + } + + if (results.length === 0) { + throw new Error('No results found in results array'); + } + } catch (error) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: `❌ **Error**: Failed to parse workflow results.\n\nPlease check the workflow logs for details.` + }); + throw error; + } + + // URL sanitization to prevent markdown injection + function sanitizeUrl(str) { + if (!str) return ''; + return str + .replace(/[<>]/g, '') + .replace(/[\[\]()]/g, '') + .replace(/\n/g, ' ') + .replace(/\|/g, '\\|') + .substring(0, 500); + } + + // Separate successful and failed uploads + const successful = results.filter(r => r.success); + const failed = results.filter(r => !r.success); + + // Build comment body with results + let commentBody = '## Image Upload Results\n\n'; + + // Add successful uploads as a table with preview + if (successful.length > 0) { + commentBody += '| S3 URL | Preview |\n'; + commentBody += '|:------:|:-------:|\n'; + + successful.forEach((result) => { + const safeS3Url = sanitizeUrl(result.s3Url); + commentBody += `| \`${safeS3Url}\` | |\n`; + }); + + commentBody += '\n**Next step:** Copy the URL and use it in your contribution:\n'; + commentBody += '```md\n![alt text](S3_URL)\n```\n\n'; + } + + // Add failed uploads section + if (failed.length > 0) { + commentBody += '**Failed:**\n'; + failed.forEach((result) => { + const safeError = sanitizeUrl(result.error); + commentBody += `- ${safeError}\n`; + }); + } + + // Post comment to PR + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: commentBody + }); + + // Add reaction to original comment to indicate completion + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: successful.length === results.length ? 'hooray' : 'confused' + }); + + # Step 12: Check for failures and fail workflow if needed + - name: Check for failures + env: + RESULTS_FILE_PATH: ${{ env.RESULTS_FILE_PATH }} + run: | + # Read results from file + if [ -z "$RESULTS_FILE_PATH" ] || [ ! -f "$RESULTS_FILE_PATH" ]; then + echo "::error::Results file not found" + exit 1 + fi + + # Count failed uploads using jq (JSON processor) + FAILED=$(jq '[.[] | select(.success == false)] | length' "$RESULTS_FILE_PATH") + + # If any uploads failed, exit with error + if [ "$FAILED" -gt 0 ]; then + echo "::error::$FAILED image(s) failed to upload" + exit 1 + fi + + echo "✓ All images uploaded successfully!" + + # Step 13: Cleanup temporary files + - name: Cleanup temporary files + if: always() + run: | + # Remove temporary files + if [ -n "$URLS_FILE_PATH" ] && [ -f "$URLS_FILE_PATH" ]; then + shred -u "$URLS_FILE_PATH" 2>/dev/null || rm -f "$URLS_FILE_PATH" + fi + if [ -n "$RESULTS_FILE_PATH" ] && [ -f "$RESULTS_FILE_PATH" ]; then + shred -u "$RESULTS_FILE_PATH" 2>/dev/null || rm -f "$RESULTS_FILE_PATH" + fi + # Clean up any stray temp files + rm -f urls.json output.log results.json 2>/dev/null || true + echo "✓ Cleaned up temporary files" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 637969adc..6c0a82b63 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -343,19 +343,28 @@ fits, for example in block-quotes. ### Visual representation / drawings -Like GitHub, we also support Mermaid! - -You can use codeblocks using the `mermaid` keyword, and you can create beautiful graphical representations. -There's a -[playground](https://www.mermaidchart.com/play#pako:eNqrVkrOT0lVslIqyExVKMksyUlVCM9ILFEIy89JSc3NLypRSMlPLc5TL1HISCxLtY_JU4CBGCW3IE9XP5fgGCUFKwUjVBlHX0-fSLCEMYqEn3-wK1jYxDQmT6kWAEyMIfc) -where you can jump straight to draw! - -```mermaid -pie title What Voldemort doesn't have? - "FRIENDS" : 2 - "FAMILY" : 3 - "NOSE" : 45 -``` +- Like GitHub, we also support Mermaid! + + You can use codeblocks using the `mermaid` keyword, and you can create beautiful graphical representations. + There's a + [playground](https://www.mermaidchart.com/play#pako:eNqrVkrOT0lVslIqyExVKMksyUlVCM9ILFEIy89JSc3NLypRSMlPLc5TL1HISCxLtY_JU4CBGCW3IE9XP5fgGCUFKwUjVBlHX0-fSLCEMYqEn3-wK1jYxDQmT6kWAEyMIfc) + where you can jump straight to draw! + + ```mermaid + pie title What Voldemort doesn't have? + "FRIENDS" : 2 + "FAMILY" : 3 + "NOSE" : 45 + ``` +- Adding images is welcome and encouraged. + Please follow the steps below to include them correctly: + + 1. After making your changes and opening a PR, add the images you want to include in the PR's comments (by uploading them directly) + 2. During the review, a maintainer will upload your images to our S3 bucket and reply with the links you should use. + 3. Once you receive the new links, update your PR to add the images' links. + +> ⚠️ Please do not add images directly to the repository. +> This helps us avoid bloating the Git history and ensures all images follow our standardized storage and delivery method. ### Linking resources diff --git a/docs/pages/contribute/contributing.mdx b/docs/pages/contribute/contributing.mdx index ea2f47668..6ba348a05 100644 --- a/docs/pages/contribute/contributing.mdx +++ b/docs/pages/contribute/contributing.mdx @@ -355,19 +355,29 @@ fits, for example in block-quotes. ### Visual representation / drawings -Like GitHub, we also support Mermaid! - -You can use codeblocks using the `mermaid` keyword, and you can create beautiful graphical representations. -There's a -[playground](https://www.mermaidchart.com/play#pako:eNqrVkrOT0lVslIqyExVKMksyUlVCM9ILFEIy89JSc3NLypRSMlPLc5TL1HISCxLtY_JU4CBGCW3IE9XP5fgGCUFKwUjVBlHX0-fSLCEMYqEn3-wK1jYxDQmT6kWAEyMIfc) -where you can jump straight to draw! - - +- Like GitHub, we also support Mermaid! + + You can use codeblocks using the `mermaid` keyword, and you can create beautiful graphical representations. + There's a + [playground](https://www.mermaidchart.com/play#pako:eNqrVkrOT0lVslIqyExVKMksyUlVCM9ILFEIy89JSc3NLypRSMlPLc5TL1HISCxLtY_JU4CBGCW3IE9XP5fgGCUFKwUjVBlHX0-fSLCEMYqEn3-wK1jYxDQmT6kWAEyMIfc) + where you can jump straight to draw! + + + +- Adding images is welcome and encouraged. + Please follow the steps below to include them correctly: + + 1. After making your changes and opening a PR, add the images you want to include in the PR's comments (by uploading them directly) + 2. During the review, a maintainer will upload your images to our S3 bucket and reply with the links you should use. + 3. Once you receive the new links, update your PR to add the images' links. + +> ⚠️ Please do not add images directly to the repository. +> This helps us avoid bloating the Git history and ensures all images follow our standardized storage and delivery method. ### Linking resources diff --git a/package.json b/package.json index 502173a72..d1ee19218 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,9 @@ }, "packageManager": "pnpm@10.15.0", "dependencies": { + "@aws-sdk/client-s3": "^3.968.0", + "@aws-sdk/lib-storage": "^3.967.0", + "axios": "^1.13.2", "exceljs": "^4.4.0", "gray-matter": "^4.0.3", "just-install": "^2.0.2", @@ -42,6 +45,8 @@ "playwright": "^1.57.0", "react": "^19.2.1", "react-dom": "^19.2.1", - "react-router-dom": "^7.12.0" + "react-router-dom": "^7.12.0", + "sharp": "^0.34.5", + "uuid": "^13.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index edc705af0..abf30ed74 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,15 @@ importers: .: dependencies: + '@aws-sdk/client-s3': + specifier: ^3.968.0 + version: 3.971.0 + '@aws-sdk/lib-storage': + specifier: ^3.967.0 + version: 3.971.0(@aws-sdk/client-s3@3.971.0) + axios: + specifier: ^1.13.2 + version: 1.13.2 exceljs: specifier: ^4.4.0 version: 4.4.0 @@ -35,6 +44,12 @@ importers: react-router-dom: specifier: ^7.12.0 version: 7.12.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + sharp: + specifier: ^0.34.5 + version: 0.34.5 + uuid: + specifier: ^13.0.0 + version: 13.0.0 devDependencies: '@types/exceljs': specifier: ^1.3.2 @@ -60,6 +75,175 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-s3@3.971.0': + resolution: {integrity: sha512-BBUne390fKa4C4QvZlUZ5gKcu+Uyid4IyQ20N4jl0vS7SK2xpfXlJcgKqPW5ts6kx6hWTQBk6sH5Lf12RvuJxg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-sso@3.971.0': + resolution: {integrity: sha512-Xx+w6DQqJxDdymYyIxyKJnRzPvVJ4e/Aw0czO7aC9L/iraaV7AG8QtRe93OGW6aoHSh72CIiinnpJJfLsQqP4g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.970.0': + resolution: {integrity: sha512-klpzObldOq8HXzDjDlY6K8rMhYZU6mXRz6P9F9N+tWnjoYFfeBMra8wYApydElTUYQKP1O7RLHwH1OKFfKcqIA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/crc64-nvme@3.969.0': + resolution: {integrity: sha512-IGNkP54HD3uuLnrPCYsv3ZD478UYq+9WwKrIVJ9Pdi3hxPg8562CH3ZHf8hEgfePN31P9Kj+Zu9kq2Qcjjt61A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.970.0': + resolution: {integrity: sha512-rtVzXzEtAfZBfh+lq3DAvRar4c3jyptweOAJR2DweyXx71QSMY+O879hjpMwES7jl07a3O1zlnFIDo4KP/96kQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.970.0': + resolution: {integrity: sha512-CjDbWL7JxjLc9ZxQilMusWSw05yRvUJKRpz59IxDpWUnSMHC9JMMUUkOy5Izk8UAtzi6gupRWArp4NG4labt9Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.971.0': + resolution: {integrity: sha512-c0TGJG4xyfTZz3SInXfGU8i5iOFRrLmy4Bo7lMyH+IpngohYMYGYl61omXqf2zdwMbDv+YJ9AviQTcCaEUKi8w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.971.0': + resolution: {integrity: sha512-yhbzmDOsk0RXD3rTPhZra4AWVnVAC4nFWbTp+sUty1hrOPurUmhuz8bjpLqYTHGnlMbJp+UqkQONhS2+2LzW2g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.971.0': + resolution: {integrity: sha512-epUJBAKivtJqalnEBRsYIULKYV063o/5mXNJshZfyvkAgNIzc27CmmKRXTN4zaNOZg8g/UprFp25BGsi19x3nQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.970.0': + resolution: {integrity: sha512-0XeT8OaT9iMA62DFV9+m6mZfJhrD0WNKf4IvsIpj2Z7XbaYfz3CoDDvNoALf3rPY9NzyMHgDxOspmqdvXP00mw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.971.0': + resolution: {integrity: sha512-dY0hMQ7dLVPQNJ8GyqXADxa9w5wNfmukgQniLxGVn+dMRx3YLViMp5ZpTSQpFhCWNF0oKQrYAI5cHhUJU1hETw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.971.0': + resolution: {integrity: sha512-F1AwfNLr7H52T640LNON/h34YDiMuIqW/ZreGzhRR6vnFGaSPtNSKAKB2ssAMkLM8EVg8MjEAYD3NCUiEo+t/w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/lib-storage@3.971.0': + resolution: {integrity: sha512-THTCXZiYjuAU2kPD8rIuvtYRT83BxEzbv4uayPlQJ8v5bybLTYDbNEbpfZGilyAqUAdSGTMOkoLu9ROryCJ3/g==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@aws-sdk/client-s3': 3.971.0 + + '@aws-sdk/middleware-bucket-endpoint@3.969.0': + resolution: {integrity: sha512-MlbrlixtkTVhYhoasblKOkr7n2yydvUZjjxTnBhIuHmkyBS1619oGnTfq/uLeGYb4NYXdeQ5OYcqsRGvmWSuTw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-expect-continue@3.969.0': + resolution: {integrity: sha512-qXygzSi8osok7tH9oeuS3HoKw6jRfbvg5Me/X5RlHOvSSqQz8c5O9f3MjUApaCUSwbAU92KrbZWasw2PKiaVHg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.971.0': + resolution: {integrity: sha512-+hGUDUxeIw8s2kkjfeXym0XZxdh0cqkHkDpEanWYdS1gnWkIR+gf9u/DKbKqGHXILPaqHXhWpLTQTVlaB4sI7Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.969.0': + resolution: {integrity: sha512-AWa4rVsAfBR4xqm7pybQ8sUNJYnjyP/bJjfAw34qPuh3M9XrfGbAHG0aiAfQGrBnmS28jlO6Kz69o+c6PRw1dw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-location-constraint@3.969.0': + resolution: {integrity: sha512-zH7pDfMLG/C4GWMOpvJEoYcSpj7XsNP9+irlgqwi667sUQ6doHQJ3yyDut3yiTk0maq1VgmriPFELyI9lrvH/g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-logger@3.969.0': + resolution: {integrity: sha512-xwrxfip7Y2iTtCMJ+iifN1E1XMOuhxIHY9DreMCvgdl4r7+48x2S1bCYPWH3eNY85/7CapBWdJ8cerpEl12sQQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.969.0': + resolution: {integrity: sha512-2r3PuNquU3CcS1Am4vn/KHFwLi8QFjMdA/R+CRDXT4AFO/0qxevF/YStW3gAKntQIgWgQV8ZdEtKAoJvLI4UWg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.970.0': + resolution: {integrity: sha512-v/Y5F1lbFFY7vMeG5yYxuhnn0CAshz6KMxkz1pDyPxejNE9HtA0w8R6OTBh/bVdIm44QpjhbI7qeLdOE/PLzXQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-ssec@3.971.0': + resolution: {integrity: sha512-QGVhvRveYG64ZhnS/b971PxXM6N2NU79Fxck4EfQ7am8v1Br0ctoeDDAn9nXNblLGw87we9Z65F7hMxxiFHd3w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-user-agent@3.970.0': + resolution: {integrity: sha512-dnSJGGUGSFGEX2NzvjwSefH+hmZQ347AwbLhAsi0cdnISSge+pcGfOFrJt2XfBIypwFe27chQhlfuf/gWdzpZg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.971.0': + resolution: {integrity: sha512-TWaILL8GyYlhGrxxnmbkazM4QsXatwQgoWUvo251FXmUOsiXDFDVX3hoGIfB3CaJhV2pJPfebHUNJtY6TjZ11g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.969.0': + resolution: {integrity: sha512-scj9OXqKpcjJ4jsFLtqYWz3IaNvNOQTFFvEY8XMJXTv+3qF5I7/x9SJtKzTRJEBF3spjzBUYPtGFbs9sj4fisQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.970.0': + resolution: {integrity: sha512-z3syXfuK/x/IsKf/AeYmgc2NT7fcJ+3fHaGO+fkghkV9WEba3fPyOwtTBX4KpFMNb2t50zDGZwbzW1/5ighcUQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.971.0': + resolution: {integrity: sha512-4hKGWZbmuDdONMJV0HJ+9jwTDb0zLfKxcCLx2GEnBY31Gt9GeyIQ+DZ97Bb++0voawj6pnZToFikXTyrEq2x+w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.969.0': + resolution: {integrity: sha512-7IIzM5TdiXn+VtgPdVLjmE6uUBUtnga0f4RiSEI1WW10RPuNvZ9U+pL3SwDiRDAdoGrOF9tSLJOFZmfuwYuVYQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-arn-parser@3.968.0': + resolution: {integrity: sha512-gqqvYcitIIM2K4lrDX9de9YvOfXBcVdxfT/iLnvHJd4YHvSXlt+gs+AsL4FfPCxG4IG9A+FyulP9Sb1MEA75vw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.970.0': + resolution: {integrity: sha512-TZNZqFcMUtjvhZoZRtpEGQAdULYiy6rcGiXAbLU7e9LSpIYlRqpLa207oMNfgbzlL2PnHko+eVg8rajDiSOYCg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.2': + resolution: {integrity: sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-user-agent-browser@3.969.0': + resolution: {integrity: sha512-bpJGjuKmFr0rA6UKUCmN8D19HQFMLXMx5hKBXqBlPFdalMhxJSjcxzX9DbQh0Fn6bJtxCguFmRGOBdQqNOt49g==} + + '@aws-sdk/util-user-agent-node@3.971.0': + resolution: {integrity: sha512-Eygjo9mFzQYjbGY3MYO6CsIhnTwAMd3WmuFalCykqEmj2r5zf0leWrhPaqvA5P68V5JdGfPYgj7vhNOd6CtRBQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.969.0': + resolution: {integrity: sha512-BSe4Lx/qdRQQdX8cSSI7Et20vqBspzAjBy8ZmXVoyLkol3y4sXBXzn+BiLtR+oh60ExQn6o2DU4QjdOZbXaKIQ==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.3': + resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -401,6 +585,9 @@ packages: resolution: {integrity: sha512-nt88P6m20AaVbqMxsyPf8KqyWPaFEW2UANi0ijBxc2xTkD2KiUovxfZUYW6NMU9XBYZlovT5LztkEhst2yBcSA==} engines: {node: '>=20'} + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + '@emotion/hash@0.9.2': resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} @@ -759,6 +946,143 @@ packages: '@iconify/utils@3.1.0': resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1696,6 +2020,222 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@smithy/abort-controller@4.2.8': + resolution: {integrity: sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader-native@4.2.1': + resolution: {integrity: sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader@5.2.0': + resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==} + engines: {node: '>=18.0.0'} + + '@smithy/config-resolver@4.4.6': + resolution: {integrity: sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.20.7': + resolution: {integrity: sha512-aO7jmh3CtrmPsIJxUwYIzI5WVlMK8BMCPQ4D4nTzqTqBhbzvxHNzBMGcEg13yg/z9R2Qsz49NUFl0F0lVbTVFw==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.8': + resolution: {integrity: sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-codec@4.2.8': + resolution: {integrity: sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-browser@4.2.8': + resolution: {integrity: sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-config-resolver@4.3.8': + resolution: {integrity: sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-node@4.2.8': + resolution: {integrity: sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-universal@4.2.8': + resolution: {integrity: sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.9': + resolution: {integrity: sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-blob-browser@4.2.9': + resolution: {integrity: sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.8': + resolution: {integrity: sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-stream-node@4.2.8': + resolution: {integrity: sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.8': + resolution: {integrity: sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.0': + resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} + engines: {node: '>=18.0.0'} + + '@smithy/md5-js@4.2.8': + resolution: {integrity: sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.8': + resolution: {integrity: sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.8': + resolution: {integrity: sha512-TV44qwB/T0OMMzjIuI+JeS0ort3bvlPJ8XIH0MSlGADraXpZqmyND27ueuAL3E14optleADWqtd7dUgc2w+qhQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.4.24': + resolution: {integrity: sha512-yiUY1UvnbUFfP5izoKLtfxDSTRv724YRRwyiC/5HYY6vdsVDcDOXKSXmkJl/Hovcxt5r+8tZEUAdrOaCJwrl9Q==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.9': + resolution: {integrity: sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.8': + resolution: {integrity: sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.8': + resolution: {integrity: sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.4.8': + resolution: {integrity: sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.8': + resolution: {integrity: sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.8': + resolution: {integrity: sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.8': + resolution: {integrity: sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.8': + resolution: {integrity: sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.8': + resolution: {integrity: sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.3': + resolution: {integrity: sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.8': + resolution: {integrity: sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.10.9': + resolution: {integrity: sha512-Je0EvGXVJ0Vrrr2lsubq43JGRIluJ/hX17aN/W/A0WfE+JpoMdI8kwk2t9F0zTX9232sJDGcoH4zZre6m6f/sg==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.12.0': + resolution: {integrity: sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.8': + resolution: {integrity: sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.3.0': + resolution: {integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.2.0': + resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.2.1': + resolution: {integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.0': + resolution: {integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.2.0': + resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.3.23': + resolution: {integrity: sha512-mMg+r/qDfjfF/0psMbV4zd7F/i+rpyp7Hjh0Wry7eY15UnzTEId+xmQTGDU8IdZtDfbGQxuWNfgBZKBj+WuYbA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.26': + resolution: {integrity: sha512-EQqe/WkbCinah0h1lMWh9ICl0Ob4lyl20/10WTB35SC9vDQfD8zWsOT+x2FIOXKAoZQ8z/y0EFMoodbcqWJY/w==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.2.8': + resolution: {integrity: sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.0': + resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.8': + resolution: {integrity: sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.2.8': + resolution: {integrity: sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.10': + resolution: {integrity: sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.0': + resolution: {integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.0': + resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-waiter@4.2.8': + resolution: {integrity: sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.0': + resolution: {integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==} + engines: {node: '>=18.0.0'} + '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -2028,6 +2568,9 @@ packages: async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + autoprefixer@10.4.22: resolution: {integrity: sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==} engines: {node: ^10 || ^12 || >=14} @@ -2035,6 +2578,9 @@ packages: peerDependencies: postcss: ^8.1.0 + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -2070,6 +2616,9 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bowser@2.13.1: + resolution: {integrity: sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -2088,6 +2637,9 @@ packages: resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} engines: {node: '>=0.10'} + buffer@5.6.0: + resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -2106,6 +2658,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2169,6 +2725,10 @@ packages: collapse-white-space@2.1.0: resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -2494,6 +3054,10 @@ packages: delaunator@5.0.1: resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -2527,6 +3091,10 @@ packages: dompurify@3.3.0: resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + duplexer2@0.1.4: resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} @@ -2568,9 +3136,25 @@ packages: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esast-util-from-estree@2.0.0: resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} @@ -2638,6 +3222,10 @@ packages: resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} engines: {node: '>= 0.8'} + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + exceljs@4.4.0: resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==} engines: {node: '>=8.3.0'} @@ -2669,6 +3257,10 @@ packages: fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-xml-parser@5.2.5: + resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} + hasBin: true + fault@2.0.1: resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} @@ -2695,6 +3287,19 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} @@ -2735,6 +3340,9 @@ packages: engines: {node: '>=0.6'} deprecated: This package is no longer supported. + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gensequence@8.0.8: resolution: {integrity: sha512-omMVniXEXpdx/vKxGnPRoO2394Otlze28TyxECbFVyoSpZ9H3EO7lemjcB12OpQJzRW4e5tt/dL1rOxry6aMHg==} engines: {node: '>=20'} @@ -2743,10 +3351,18 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} @@ -2766,6 +3382,10 @@ packages: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} engines: {node: '>=18'} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -2776,6 +3396,18 @@ packages: hachure-fill@0.5.2: resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + hast-util-classnames@3.0.0: resolution: {integrity: sha512-tI3JjoGDEBVorMAWK4jNRsfLMYmih1BUOG3VV36pH36njs1IEl7xkNrVTD2mD2yYHmQCa5R/fj61a8IAF4bRaQ==} @@ -3135,6 +3767,10 @@ packages: engines: {node: '>= 20'} hasBin: true + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + mdast-util-directive@3.1.0: resolution: {integrity: sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==} @@ -3317,10 +3953,18 @@ packages: micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + mime-db@1.54.0: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} @@ -3544,6 +4188,9 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} @@ -3790,6 +4437,10 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -3833,6 +4484,9 @@ packages: resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + string-width@6.1.0: resolution: {integrity: sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==} engines: {node: '>=16'} @@ -3858,6 +4512,9 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + strnum@2.1.2: + resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} + style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -4025,6 +4682,10 @@ packages: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true + uuid@13.0.0: + resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -4176,10 +4837,506 @@ snapshots: package-manager-detector: 1.6.0 tinyexec: 1.0.2 - '@babel/code-frame@7.27.1': + '@aws-crypto/crc32@5.2.0': dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.969.0 + tslib: 2.8.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.969.0 + tslib: 2.8.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-locate-window': 3.965.2 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-locate-window': 3.965.2 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.969.0 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.971.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.970.0 + '@aws-sdk/credential-provider-node': 3.971.0 + '@aws-sdk/middleware-bucket-endpoint': 3.969.0 + '@aws-sdk/middleware-expect-continue': 3.969.0 + '@aws-sdk/middleware-flexible-checksums': 3.971.0 + '@aws-sdk/middleware-host-header': 3.969.0 + '@aws-sdk/middleware-location-constraint': 3.969.0 + '@aws-sdk/middleware-logger': 3.969.0 + '@aws-sdk/middleware-recursion-detection': 3.969.0 + '@aws-sdk/middleware-sdk-s3': 3.970.0 + '@aws-sdk/middleware-ssec': 3.971.0 + '@aws-sdk/middleware-user-agent': 3.970.0 + '@aws-sdk/region-config-resolver': 3.969.0 + '@aws-sdk/signature-v4-multi-region': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-endpoints': 3.970.0 + '@aws-sdk/util-user-agent-browser': 3.969.0 + '@aws-sdk/util-user-agent-node': 3.971.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/core': 3.20.7 + '@smithy/eventstream-serde-browser': 4.2.8 + '@smithy/eventstream-serde-config-resolver': 4.3.8 + '@smithy/eventstream-serde-node': 4.2.8 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/hash-blob-browser': 4.2.9 + '@smithy/hash-node': 4.2.8 + '@smithy/hash-stream-node': 4.2.8 + '@smithy/invalid-dependency': 4.2.8 + '@smithy/md5-js': 4.2.8 + '@smithy/middleware-content-length': 4.2.8 + '@smithy/middleware-endpoint': 4.4.8 + '@smithy/middleware-retry': 4.4.24 + '@smithy/middleware-serde': 4.2.9 + '@smithy/middleware-stack': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/node-http-handler': 4.4.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.23 + '@smithy/util-defaults-mode-node': 4.2.26 + '@smithy/util-endpoints': 3.2.8 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/util-stream': 4.5.10 + '@smithy/util-utf8': 4.2.0 + '@smithy/util-waiter': 4.2.8 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso@3.971.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.970.0 + '@aws-sdk/middleware-host-header': 3.969.0 + '@aws-sdk/middleware-logger': 3.969.0 + '@aws-sdk/middleware-recursion-detection': 3.969.0 + '@aws-sdk/middleware-user-agent': 3.970.0 + '@aws-sdk/region-config-resolver': 3.969.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-endpoints': 3.970.0 + '@aws-sdk/util-user-agent-browser': 3.969.0 + '@aws-sdk/util-user-agent-node': 3.971.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/core': 3.20.7 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/hash-node': 4.2.8 + '@smithy/invalid-dependency': 4.2.8 + '@smithy/middleware-content-length': 4.2.8 + '@smithy/middleware-endpoint': 4.4.8 + '@smithy/middleware-retry': 4.4.24 + '@smithy/middleware-serde': 4.2.9 + '@smithy/middleware-stack': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/node-http-handler': 4.4.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.23 + '@smithy/util-defaults-mode-node': 4.2.26 + '@smithy/util-endpoints': 3.2.8 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.970.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@aws-sdk/xml-builder': 3.969.0 + '@smithy/core': 3.20.7 + '@smithy/node-config-provider': 4.3.8 + '@smithy/property-provider': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/signature-v4': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/crc64-nvme@3.969.0': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.970.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.970.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/node-http-handler': 4.4.8 + '@smithy/property-provider': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/util-stream': 4.5.10 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.971.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/credential-provider-env': 3.970.0 + '@aws-sdk/credential-provider-http': 3.970.0 + '@aws-sdk/credential-provider-login': 3.971.0 + '@aws-sdk/credential-provider-process': 3.970.0 + '@aws-sdk/credential-provider-sso': 3.971.0 + '@aws-sdk/credential-provider-web-identity': 3.971.0 + '@aws-sdk/nested-clients': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/credential-provider-imds': 4.2.8 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-login@3.971.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/nested-clients': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.971.0': + dependencies: + '@aws-sdk/credential-provider-env': 3.970.0 + '@aws-sdk/credential-provider-http': 3.970.0 + '@aws-sdk/credential-provider-ini': 3.971.0 + '@aws-sdk/credential-provider-process': 3.970.0 + '@aws-sdk/credential-provider-sso': 3.971.0 + '@aws-sdk/credential-provider-web-identity': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/credential-provider-imds': 4.2.8 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.970.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.971.0': + dependencies: + '@aws-sdk/client-sso': 3.971.0 + '@aws-sdk/core': 3.970.0 + '@aws-sdk/token-providers': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.971.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/nested-clients': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/lib-storage@3.971.0(@aws-sdk/client-s3@3.971.0)': + dependencies: + '@aws-sdk/client-s3': 3.971.0 + '@smithy/abort-controller': 4.2.8 + '@smithy/middleware-endpoint': 4.4.8 + '@smithy/smithy-client': 4.10.9 + buffer: 5.6.0 + events: 3.3.0 + stream-browserify: 3.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-bucket-endpoint@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-arn-parser': 3.968.0 + '@smithy/node-config-provider': 4.3.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-config-provider': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-expect-continue@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.971.0': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.970.0 + '@aws-sdk/crc64-nvme': 3.969.0 + '@aws-sdk/types': 3.969.0 + '@smithy/is-array-buffer': 4.2.0 + '@smithy/node-config-provider': 4.3.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-stream': 4.5.10 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-location-constraint@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@aws/lambda-invoke-store': 0.2.3 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.970.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-arn-parser': 3.968.0 + '@smithy/core': 3.20.7 + '@smithy/node-config-provider': 4.3.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/signature-v4': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/util-config-provider': 4.2.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-stream': 4.5.10 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-ssec@3.971.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.970.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-endpoints': 3.970.0 + '@smithy/core': 3.20.7 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.971.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.970.0 + '@aws-sdk/middleware-host-header': 3.969.0 + '@aws-sdk/middleware-logger': 3.969.0 + '@aws-sdk/middleware-recursion-detection': 3.969.0 + '@aws-sdk/middleware-user-agent': 3.970.0 + '@aws-sdk/region-config-resolver': 3.969.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-endpoints': 3.970.0 + '@aws-sdk/util-user-agent-browser': 3.969.0 + '@aws-sdk/util-user-agent-node': 3.971.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/core': 3.20.7 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/hash-node': 4.2.8 + '@smithy/invalid-dependency': 4.2.8 + '@smithy/middleware-content-length': 4.2.8 + '@smithy/middleware-endpoint': 4.4.8 + '@smithy/middleware-retry': 4.4.24 + '@smithy/middleware-serde': 4.2.9 + '@smithy/middleware-stack': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/node-http-handler': 4.4.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.23 + '@smithy/util-defaults-mode-node': 4.2.26 + '@smithy/util-endpoints': 3.2.8 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/node-config-provider': 4.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.970.0': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@smithy/protocol-http': 5.3.8 + '@smithy/signature-v4': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.971.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/nested-clients': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.969.0': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/util-arn-parser@3.968.0': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.970.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-endpoints': 3.2.8 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.2': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/types': 4.12.0 + bowser: 2.13.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.971.0': + dependencies: + '@aws-sdk/middleware-user-agent': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@smithy/node-config-provider': 4.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.969.0': + dependencies: + '@smithy/types': 4.12.0 + fast-xml-parser: 5.2.5 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.3': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 picocolors: 1.1.1 '@babel/compat-data@7.28.5': {} @@ -4539,6 +5696,11 @@ snapshots: '@cspell/url@9.4.0': {} + '@emnapi/runtime@1.8.1': + dependencies: + tslib: 2.8.1 + optional: true + '@emotion/hash@0.9.2': {} '@esbuild/aix-ppc64@0.25.12': @@ -4755,6 +5917,102 @@ snapshots: '@iconify/types': 2.0.0 mlly: 1.8.0 + '@img/colour@1.0.0': {} + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.8.1 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -5747,6 +7005,344 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@smithy/abort-controller@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader-native@4.2.1': + dependencies: + '@smithy/util-base64': 4.3.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader@5.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/config-resolver@4.4.6': + dependencies: + '@smithy/node-config-provider': 4.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-config-provider': 4.2.0 + '@smithy/util-endpoints': 3.2.8 + '@smithy/util-middleware': 4.2.8 + tslib: 2.8.1 + + '@smithy/core@3.20.7': + dependencies: + '@smithy/middleware-serde': 4.2.9 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-stream': 4.5.10 + '@smithy/util-utf8': 4.2.0 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.2.8': + dependencies: + '@smithy/node-config-provider': 4.3.8 + '@smithy/property-provider': 4.2.8 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + tslib: 2.8.1 + + '@smithy/eventstream-codec@4.2.8': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.12.0 + '@smithy/util-hex-encoding': 4.2.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@4.2.8': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@4.3.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@4.2.8': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@4.2.8': + dependencies: + '@smithy/eventstream-codec': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.3.9': + dependencies: + '@smithy/protocol-http': 5.3.8 + '@smithy/querystring-builder': 4.2.8 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + tslib: 2.8.1 + + '@smithy/hash-blob-browser@4.2.9': + dependencies: + '@smithy/chunked-blob-reader': 5.2.0 + '@smithy/chunked-blob-reader-native': 4.2.1 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/hash-node@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/hash-stream-node@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/md5-js@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.2.8': + dependencies: + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.4.8': + dependencies: + '@smithy/core': 3.20.7 + '@smithy/middleware-serde': 4.2.9 + '@smithy/node-config-provider': 4.3.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-middleware': 4.2.8 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.4.24': + dependencies: + '@smithy/node-config-provider': 4.3.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/service-error-classification': 4.2.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + + '@smithy/middleware-serde@4.2.9': + dependencies: + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.3.8': + dependencies: + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.4.8': + dependencies: + '@smithy/abort-controller': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/querystring-builder': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/property-provider@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/protocol-http@5.3.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + '@smithy/util-uri-escape': 4.2.0 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + + '@smithy/shared-ini-file-loader@4.4.3': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.3.8': + dependencies: + '@smithy/is-array-buffer': 4.2.0 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-hex-encoding': 4.2.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-uri-escape': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/smithy-client@4.10.9': + dependencies: + '@smithy/core': 3.20.7 + '@smithy/middleware-endpoint': 4.4.8 + '@smithy/middleware-stack': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-stream': 4.5.10 + tslib: 2.8.1 + + '@smithy/types@4.12.0': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.2.8': + dependencies: + '@smithy/querystring-parser': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-base64@4.3.0': + dependencies: + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.2.1': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.2.0': + dependencies: + '@smithy/is-array-buffer': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.3.23': + dependencies: + '@smithy/property-provider': 4.2.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.2.26': + dependencies: + '@smithy/config-resolver': 4.4.6 + '@smithy/credential-provider-imds': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/property-provider': 4.2.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.2.8': + dependencies: + '@smithy/node-config-provider': 4.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-retry@4.2.8': + dependencies: + '@smithy/service-error-classification': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-stream@4.5.10': + dependencies: + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/node-http-handler': 4.4.8 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-hex-encoding': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-uri-escape@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.2.0': + dependencies: + '@smithy/util-buffer-from': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-waiter@4.2.8': + dependencies: + '@smithy/abort-controller': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/uuid@1.1.0': + dependencies: + tslib: 2.8.1 + '@standard-schema/spec@1.0.0': {} '@tailwindcss/node@4.1.15': @@ -6174,6 +7770,8 @@ snapshots: async@3.2.6: {} + asynckit@0.4.0: {} + autoprefixer@10.4.22(postcss@8.5.6): dependencies: browserslist: 4.28.1 @@ -6184,6 +7782,14 @@ snapshots: postcss: 8.5.6 postcss-value-parser: 4.2.0 + axios@1.13.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + bail@2.0.2: {} balanced-match@1.0.2: {} @@ -6217,6 +7823,8 @@ snapshots: boolbase@1.0.0: {} + bowser@2.13.1: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -6238,6 +7846,11 @@ snapshots: buffer-indexof-polyfill@1.0.2: {} + buffer@5.6.0: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -6254,6 +7867,11 @@ snapshots: cac@6.7.14: {} + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + callsites@3.1.0: {} caniuse-lite@1.0.30001759: {} @@ -6309,6 +7927,10 @@ snapshots: collapse-white-space@2.1.0: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + comma-separated-tokens@2.0.3: {} commander@14.0.2: {} @@ -6691,6 +8313,8 @@ snapshots: dependencies: robust-predicates: 3.0.2 + delayed-stream@1.0.0: {} + depd@2.0.0: {} dequal@2.0.3: {} @@ -6715,6 +8339,12 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + duplexer2@0.1.4: dependencies: readable-stream: 2.3.8 @@ -6746,8 +8376,23 @@ snapshots: env-paths@3.0.0: {} + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + es-module-lexer@1.7.0: {} + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esast-util-from-estree@2.0.0: dependencies: '@types/estree-jsx': 1.0.5 @@ -6874,6 +8519,8 @@ snapshots: '@types/node': 24.10.1 require-like: 0.1.2 + events@3.3.0: {} + exceljs@4.4.0: dependencies: archiver: 5.3.2 @@ -6923,6 +8570,10 @@ snapshots: fast-json-stable-stringify@2.1.0: {} + fast-xml-parser@5.2.5: + dependencies: + strnum: 2.1.2 + fault@2.0.1: dependencies: format: 0.2.2 @@ -6947,6 +8598,16 @@ snapshots: flatted@3.3.3: {} + follow-redirects@1.15.11: {} + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + format@0.2.2: {} formdata-polyfill@4.0.10: @@ -6980,12 +8641,32 @@ snapshots: mkdirp: 0.5.6 rimraf: 2.7.1 + function-bind@1.1.2: {} + gensequence@8.0.8: {} gensync@1.0.0-beta.2: {} + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + get-nonce@1.0.1: {} + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + get-stream@5.2.0: dependencies: pump: 3.0.3 @@ -7007,6 +8688,8 @@ snapshots: dependencies: ini: 4.1.1 + gopd@1.2.0: {} + graceful-fs@4.2.11: {} gray-matter@4.0.3: @@ -7018,6 +8701,16 @@ snapshots: hachure-fill@0.5.2: {} + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + hast-util-classnames@3.0.0: dependencies: '@types/hast': 3.0.4 @@ -7416,6 +9109,8 @@ snapshots: marked@16.4.2: {} + math-intrinsics@1.1.0: {} + mdast-util-directive@3.1.0: dependencies: '@types/mdast': 4.0.4 @@ -7921,8 +9616,14 @@ snapshots: transitivePeerDependencies: - supports-color + mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + mime@1.6.0: {} mimic-fn@2.1.0: {} @@ -8117,6 +9818,8 @@ snapshots: property-information@7.1.0: {} + proxy-from-env@1.1.0: {} + pump@3.0.3: dependencies: end-of-stream: 1.4.5 @@ -8528,6 +10231,37 @@ snapshots: setprototypeof@1.2.0: {} + sharp@0.34.5: + dependencies: + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -8565,6 +10299,11 @@ snapshots: dependencies: bl: 5.1.0 + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + string-width@6.1.0: dependencies: eastasianwidth: 0.2.0 @@ -8592,6 +10331,8 @@ snapshots: strip-final-newline@2.0.0: {} + strnum@2.1.2: {} + style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -8770,6 +10511,8 @@ snapshots: uuid@11.1.0: {} + uuid@13.0.0: {} + uuid@8.3.2: {} vary@1.1.2: {} From 04dc3e05c5a6996140fd94b719b1278bc1c2ac7f Mon Sep 17 00:00:00 2001 From: Seth Hallem Date: Thu, 9 Apr 2026 12:55:54 -0400 Subject: [PATCH 07/92] Feature/opsec/mfa (#452) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added MFA overview, and updated contributors file. * Build fix and language updates. * Refine MFA overview and recommendations Revised language for clarity and emphasis on MFA importance. Updated recommendations for MFA methods and highlighted security considerations for passkeys. * Further clarification about passkey storage. --------- Co-authored-by: Matías Aereal Aeón <388605+mattaereal@users.noreply.github.com> --- docs/pages/config/contributors.json | 14 ++++++ docs/pages/opsec/mfa/overview.mdx | 29 ++++++++++++- utils/fetched-tags.json | 67 +++++++++++++++++------------ 3 files changed, 82 insertions(+), 28 deletions(-) diff --git a/docs/pages/config/contributors.json b/docs/pages/config/contributors.json index 1faf0e430..ae43731a9 100644 --- a/docs/pages/config/contributors.json +++ b/docs/pages/config/contributors.json @@ -665,5 +665,19 @@ { "name": "New-Joiner", "lastActive": "2026-03-17" }, { "name": "Active-Last-7d", "lastActive": "2026-03-23" } ] +}, +"shallem": { + "slug": "shallem", + "name": "Seth Hallem", + "avatar": "", + "github": "https://github.com/shallem", + "twitter": "https://x.com/seth_certora", + "website": "https://www.certora.com/", + "company": "Certora", + "role": "steward", + "job_title": null, + "description": "Steward of Opsec framework", + "badges": [ + ] } } diff --git a/docs/pages/opsec/mfa/overview.mdx b/docs/pages/opsec/mfa/overview.mdx index 146f82ef2..b80776da2 100644 --- a/docs/pages/opsec/mfa/overview.mdx +++ b/docs/pages/opsec/mfa/overview.mdx @@ -3,6 +3,9 @@ title: "Multi-Factor Authentication | Security Alliance" tags: - Security Specialist - Operations & Strategy +contributors: + - role: wrote + users: [shallem] --- import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' @@ -15,7 +18,31 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } fr -Placeholder for Multi-Factor Authentication content +MFA is necessary but not sufficient as an OpSec strategy. If you have not yet implemented MFA, we suggest making it your first priority as soon as you finish reading this page. + +Not all MFAs are created equally. A few recommendations: + +1. **Stay away from text and email as MFA methods.** There are innumerable reasons why neither of these methods + is a good idea. Suffice it to say, best practices have long since outlawed these MFA methods. + +2. **TOTP (e.g., Google Authenticator) is good but not great.** Why? It is easy enough to trick users into + entering TOTP codes into a phishing site. The methods cited below are more difficult to exploit. Also, any manual typing is susceptible to keyloggers. + +3. **Push-based MFA is better.** Why? Because initiating a push notification on iOS/Android requires that the + device itself be enrolled with the identity provider. Phishing sites cannot initiate a push notification to + the Gmail app, for example, without a major compromise of Google's infrastructure. + +4. **Passkeys are the best.** Biometrics are hard to fake, and in a world where attackers are looking for low + hanging fruit, passkeys protected by biometric factors are typically too hard for them to reach. However, passkey storage is critical to ensuring that this choice is secure - please read the note below. + +5. **Key admins (e.g., your G Suite admin) should be using Yubikeys.** They are inexpensive and easy. There is + no excuse here for not protecting the keys to the castle with the industry gold standard for MFA. + +Once you have MFA in place, you are ready to move on to the next step in your Opsec framework. However, before you declare your MFA journey a success, make sure you haven't forgotten any of your communication tools along the way. In this industry, we often use a combination of X, Signal, and Telegram, and each of them can and should be protected with an additional authentication factor. Also, note that the more you allow one-off sign-ins for each tool you use, the more you have to be concerned about the MFA features of each tool. Implementing single sign-on wherever possible is the best way to enforce MFA across all the tools you use. + +Take into consideration that, while using passkeys seems the most appropriate course of action, using them irresponsibly could lower your security posture. Passkeys only improve security when they remain tied to a strong device and recovery model; used carelessly, they can weaken it by shifting trust from a hard-to-phish login flow to softer cloud sync and account recovery paths. A common failure case is storing high-value passkeys in consumer sync ecosystems protected by weak recovery, reused credentials, SMS reset, or unmanaged devices, so an attacker who compromises the sync account can restore those passkeys on their own device and log in cleanly with what appears to be strong authentication. In that setup, the passkey itself is not broken, but the overall security posture is worse because the real attack surface becomes account recovery, device enrollment, and endpoint compromise rather than direct credential theft. A typical mistake would be to store a passkey in the Google password manager for your personal Google account, which may not be adequately protected with a strong password, MFA, and a secure account recovery method. + +To address the passkey storage issue, this section should be read in conjunction with the section (coming soon) about password handling and password management. Passkeys are the strongest form of MFA when stored in a secure password manager with biometric authentication (e.g., Bitwarden, 1Password). Passkeys stored in a personal account that uses phone or SMS as a recovery mechanism make overall security worse, not better. Before finalizing any decision to migrate to passkeys, please read the password management section and ensure that you are ready to store your passkeys securely. diff --git a/utils/fetched-tags.json b/utils/fetched-tags.json index 94e7693bc..17b10c548 100644 --- a/utils/fetched-tags.json +++ b/utils/fetched-tags.json @@ -486,6 +486,18 @@ "/guides/account-management/vercel": [ "DevOps Accounts" ], + "/guides/endpoint-security/hardware-security-keys": [ + "Security Specialist" + ], + "/guides/endpoint-security/password-manager-endpoint-hardening": [ + "Security Specialist", + "Operations & Strategy", + "Engineer/Developer" + ], + "/guides/endpoint-security/ssh-client-and-key-management-hardening": [ + "Engineer/Developer", + "Security Specialist" + ], "/guides/endpoint-security/zoom-hardening": [ "Security Specialist", "Operations & Strategy" @@ -895,7 +907,8 @@ ], "/opsec/endpoint/overview": [ "Security Specialist", - "Operations & Strategy" + "Operations & Strategy", + "HR" ], "/opsec/google/overview": [ "Community & Marketing", @@ -1269,45 +1282,45 @@ ] }, "sectionMappings": { - "Community Management": "community-management", + "AI Security": "ai-security", "Awareness": "awareness", - "Operational Security": "opsec", - "OpSec Core Concepts": "opsec", - "While Traveling": "opsec", - "Wallet Security": "wallet-security", - "Signing & Verification": "wallet-security", - "Multisig for Protocols": "multisig-for-protocols", - "Multisig Administration": "multisig-for-protocols", - "Operational Runbooks": "multisig-for-protocols", - "For Signers": "multisig-for-protocols", + "Community Management": "community-management", + "DevSecOps": "devsecops", + "Isolation & Sandboxing": "devsecops", + "DPRK IT Workers": "dprk-it-workers", + "Encryption": "encryption", + "ENS": "ens", "External Security Reviews": "external-security-reviews", "Smart Contract Audits": "external-security-reviews", - "Vulnerability Disclosure": "vulnerability-disclosure", - "Infrastructure": "infrastructure", - "Domain & DNS Security": "infrastructure", - "Monitoring": "monitoring", "Front-End/Web Application": "front-end-web-app", + "Governance": "governance", + "Identity and Access Management IAM": "iam", "Incident Management": "incident-management", "Playbooks": "incident-management", "Incident Response Template": "incident-management", "Templates": "incident-management", "Runbooks": "incident-management", - "Threat Modeling": "threat-modeling", - "DPRK IT Workers": "dprk-it-workers", - "Governance": "governance", - "DevSecOps": "devsecops", - "Isolation & Sandboxing": "devsecops", + "Infrastructure": "infrastructure", + "Domain & DNS Security": "infrastructure", + "Monitoring": "monitoring", + "Multisig for Protocols": "multisig-for-protocols", + "Multisig Administration": "multisig-for-protocols", + "Operational Runbooks": "multisig-for-protocols", + "For Signers": "multisig-for-protocols", + "Operational Security": "opsec", + "OpSec Core Concepts": "opsec", + "While Traveling": "opsec", "Privacy": "privacy", - "Supply Chain": "supply-chain", - "Security Automation": "security-automation", - "Identity and Access Management IAM": "iam", + "Safe Harbor": "safe-harbor", "Secure Software Development": "secure-software-development", + "Security Automation": "security-automation", "Security Testing": "security-testing", - "AI Security": "ai-security", - "ENS": "ens", - "Safe Harbor": "safe-harbor", - "Encryption": "encryption", + "Supply Chain": "supply-chain", + "Threat Modeling": "threat-modeling", "Treasury Operations": "treasury-operations", + "Vulnerability Disclosure": "vulnerability-disclosure", + "Wallet Security": "wallet-security", + "Signing & Verification": "wallet-security", "Guides": "guides", "Account Management": "guides", "Endpoint Security": "guides", From e0adc9639af7a8c5282869c893c6a3490bc1cac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Aereal=20Ae=C3=B3n?= <388605+mattaereal@users.noreply.github.com> Date: Tue, 14 Apr 2026 00:03:01 -0300 Subject: [PATCH 08/92] docs: add CC BY-SA 4.0 licensing notice and metadata (#447) Co-authored-by: frameworks-volunteer --- LICENSE | 2 ++ README.md | 4 ++++ docs/pages/intro/introduction.mdx | 4 ++++ package.json | 2 +- 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..c3bc27c66 --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +Security Frameworks © 2023 by Security Alliance, licensed under CC BY-SA 4.0. +To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/4.0/ diff --git a/README.md b/README.md index 255540abe..20c3686c3 100644 --- a/README.md +++ b/README.md @@ -79,3 +79,7 @@ Every contribution helps strengthen the resource and improve security practices See our [Contributing Guide](https://github.com/security-alliance/frameworks/blob/develop/CONTRIBUTING.md) for details on how to get started. + +## License + +Security Frameworks © 2023 by Security Alliance, licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). diff --git a/docs/pages/intro/introduction.mdx b/docs/pages/intro/introduction.mdx index 34547ec42..932cfd897 100644 --- a/docs/pages/intro/introduction.mdx +++ b/docs/pages/intro/introduction.mdx @@ -36,6 +36,10 @@ related to Web3 security. Read our [Contribution Guide](/contribute/contributing) to learn how you can contribute to this project. +## License + +Security Frameworks © 2023 by Security Alliance, licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). + ## Who We Are SEAL is a not-for-profit organization committed to enhancing security awareness, education, and specialized work as a diff --git a/package.json b/package.json index d1ee19218..583c3e6da 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ }, "keywords": [], "author": "", - "license": "ISC", + "license": "CC-BY-SA-4.0", "pnpm": { "onlyBuiltDependencies": [ "esbuild", From 7529920f63e45b5772b9ebae044cca043f67b019 Mon Sep 17 00:00:00 2001 From: Isaac Patka Date: Tue, 21 Apr 2026 11:20:52 -0400 Subject: [PATCH 09/92] Feedback integration certs (#459) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Port cert revisions and add SFC Identity & Accounts Mirrors the revisions applied in the SEAL-Certs-Template repo (see its CHANGELOG.md for full detail). Summary: - sfc-multisig-ops: ms-2.1.2 strengthened from "evaluate" to "implement"; ms-4.1.1 transaction process consolidated 8 to 5 bullets - sfc-treasury-ops: scope note added; per-actor/per-path exposure limits (tro-2.1.3) and privileged access / root account management (tro-3.1.5) added; trusted-parser bullet on tro-4.1.1; various consolidations and softening (session timeouts, impact thresholds, exposure limits) - sfc-devops-infrastructure: di-1.1.4 split into process + di-1.1.5 list; runner hardening on di-3.1.1; network architecture on di-4.1.1; supply chain mention softened; References section added - sfc-dns-registrar: dns-3.1.1 slimmed to reference the new Identity & Accounts cert for account management - sfc-incident-response: four IR controls consolidated (team roles, contacts, alerting, drills); header reference to Identity & Accounts - sfc-identity-accounts (NEW): horizontal cert covering organizational account management (inventory, phishing-resistant MFA, credential management, recovery methods, lifecycle, takeover monitoring, third- party access) Control IDs are stable; no renames. Baseline text changes do not affect workbook import (keyed on control ID). Co-Authored-By: Claude Opus 4.7 (1M context) * Retire SFC Workspace Security; wire in Identity & Accounts The old Workspace Security cert drifted far from SEAL's SME (device management, EDR/MDM, physical/travel security, formal training programs). Its crypto-relevant content (account inventory, phishing-resistant MFA, credential management, account lifecycle, takeover monitoring) is now in the new horizontal Identity & Accounts cert. Generic enterprise IT coverage is better left to ISO 27001 / SOC 2 / CIS. - Delete docs/pages/certs/sfc-workspace-security.mdx - vocs.config.tsx: sidebar updated (add Identity & Accounts, remove Workspace Security) - utils/generate-cert-data.js: CERT_ORDER updated so the overview-page "Export All Certifications" xlsx includes I&A and excludes Workspace - utils/generate-printable-checklists.js: CERT_META updated so the Print button generates an I&A checklist and no longer generates one for Workspace - components/certified-protocols/CertifiedProtocols.tsx: certTypeToName map updated (sfc-ida replaces sfc-ws) - docs/pages/certs/overview.mdx: cert list updated - docs/pages/certs/index.mdx: cert list updated - docs/pages/intro/overview-of-each-framework.mdx: cert list updated The fetched-tags.json and cert-data.json artifacts regenerate at build time. Co-Authored-By: Claude Opus 4.7 (1M context) * Surface control IDs in the control card UI Control IDs (e.g., ms-2.1.2) are already in the data model and used by workbook import/export and aria attributes, but were invisible in the rendered card. Surface them inline next to the title so readers and reviewers have a stable reference they can cite. - ControlCard.tsx: render {control.id} before the title with a muted separator - control.css: .control-id styled muted, monospace, 0.875em; .control-id-sep muted, non-bold No behavioral change; purely additive display. Co-Authored-By: Claude Opus 4.7 (1M context) * Add per-cert version stamps and central changelog Protocols that have certified against an earlier version of a cert need to scope the delta when that cert is revised. Adds explicit versioning so re-certification decisions are data-driven. - Per-cert frontmatter fields: version (semver-ish) and revised (ISO date). Rendered inline near the H1 title: "Revision X.Y · Updated YYYY-MM-DD · Changelog". - New page: docs/pages/certs/changelog.mdx aggregating revision history across all certs with inaugural 2026-04-17 entry covering the feedback-integration-1.1 changes. - vocs.config.tsx: Changelog added to sidebar under SEAL Certifications. All five existing certs stamped at v1.1 (revised 2026-04-17). New Identity & Accounts cert stamped at v1.0. Co-Authored-By: Claude Opus 4.7 (1M context) * Regenerate build artifacts - docs/pages/certs/index.mdx: generate-folder-indexes added Changelog row - utils/fetched-tags.json: tags-fetcher regenerated tag map (added /certs/changelog entry; workspace-security replaced by identity-accounts; sectionMappings sort order shuffled) Co-Authored-By: Claude Opus 4.7 (1M context) * Add ir-2.1.1 threat model to Incident Response Monitoring coverage is only meaningful if it's pointed at the right things. The existing IR cert had team structure, contacts, monitoring, alerting, and playbooks, but no control requiring an explicit threat picture of protocol operations and external dependencies. This control closes that gap and anchors the monitoring and playbook controls to a known threat model. - Insert new ir-2.1.1 (Threat Model for Protocol Operations) at the start of Section 2 (Monitoring, Detection & Alerting) - Existing Section 2 controls shifted: old ir-2.1.1 to ir-2.1.2, ir-2.1.2 to ir-2.1.3, ir-2.1.3 to ir-2.1.4 - Evidence Tracker count 13 to 14 (template repo only) Co-Authored-By: Claude Opus 4.7 (1M context) * Rewrite certs overview to align with program roadmap The previous overview was written during RFC Phase (ended Dec 31, 2025) and framed certifications as "proposed" and "being developed." The framework is now stable, published, and moving into active certification with accredited firms. Rewritten to: - Open with the same framing as the internal program-and-roadmap doc: code audits don't catch operational failures, certifications target that gap - List the six modules (with the new Identity & Accounts and Incident Response updated to include threat modeling) - Condense "How Certification Works" into a five-step engagement flow with EAS attestation - Replace the RFC Phase section with a plain Program Status summary of where the program is now - Trim outdated FAQ items (the "Q1 2026 rollout" question) and update wording throughout - Link to the new /certs/changelog page for revision history Shorter overall; aligned with the roadmap doc without duplicating its operational detail. Co-Authored-By: Claude Opus 4.7 (1M context) * Add back protocol and auditor signup links to certs overview Previous rewrite dropped these along with the RFC Phase framing. Adding them back as a tight "Get Involved" section between Program Status and FAQ. Both entry points currently point at the same typeform (securityalliance.typeform.com/CertsAuditor), matching the original overview page. If protocols and auditors need distinct intake forms later, the URL can be updated. Co-Authored-By: Claude Opus 4.7 (1M context) * Integrate PR feedback from template repo review Mirrors template-repo commits acddf5c, 2d3a777, 5ae8d0f which applied DicksonWu654's review feedback. Since v1.1 hasn't shipped yet, the changelog entry for v1.1 is updated to reflect final state rather than documenting an intra-PR iteration. - sfc-devops-infrastructure: di-1.1.2 drops the supply-chain parenthetical (Section 2 already handles supply chain); di-1.1.4 and di-1.1.5 merged back into a single di-1.1.4 covering both the tool approval process and the approved-tools list; References section at the bottom removed (other certs don't carry References, so the inconsistency wasn't earning its keep) - sfc-identity-accounts: ida-2.1.1 drops the "(subject to SIM-swap and interception)" parenthetical; ida-4.1.1 drops the inline "(coordinated with SFC - Incident Response monitoring)" parenthetical (the trailing IR coordination bullet still carries that point); "Related certs" list in the page body removed (cross-refs live inline in each vertical cert) - sfc-incident-response: ir-2.1.1 threat model gains a baseline bullet on identifying single points of failure and highly centralized components across onchain and offchain layers (cross-chain messaging providers, oracle providers, critical infrastructure dependencies) - changelog.mdx: v1.1 entry updated to reflect final merged state; DevOps control count is now unchanged at 16, workbook compat note flags the shifted IR Section 2 IDs Co-Authored-By: Claude Opus 4.7 (1M context) * Fix protocol signup typeform URL to CertsWaitlist Both the protocol and auditor signup links in the certs overview were pointing at the same auditor form (CertsAuditor). Protocols should land on the waitlist form (CertsWaitlist) instead. Per PR #459 review comment from DicksonWu654. Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.7 (1M context) --- components/cert/ControlCard.tsx | 2 + components/cert/control.css | 13 + .../CertifiedProtocols.tsx | 2 +- docs/pages/certs/changelog.mdx | 88 +++++ docs/pages/certs/index.mdx | 3 +- docs/pages/certs/overview.mdx | 127 +++---- .../pages/certs/sfc-devops-infrastructure.mdx | 48 +-- docs/pages/certs/sfc-dns-registrar.mdx | 16 +- docs/pages/certs/sfc-identity-accounts.mdx | 137 ++++++++ docs/pages/certs/sfc-incident-response.mdx | 100 +++--- docs/pages/certs/sfc-multisig-ops.mdx | 29 +- docs/pages/certs/sfc-treasury-ops.mdx | 153 ++++---- docs/pages/certs/sfc-workspace-security.mdx | 332 ------------------ .../intro/overview-of-each-framework.mdx | 2 +- utils/fetched-tags.json | 13 +- utils/generate-cert-data.js | 2 +- utils/generate-printable-checklists.js | 4 +- vocs.config.tsx | 3 +- 18 files changed, 497 insertions(+), 577 deletions(-) create mode 100644 docs/pages/certs/changelog.mdx create mode 100644 docs/pages/certs/sfc-identity-accounts.mdx delete mode 100644 docs/pages/certs/sfc-workspace-security.mdx diff --git a/components/cert/ControlCard.tsx b/components/cert/ControlCard.tsx index 0a3377f7d..14bddbe6f 100644 --- a/components/cert/ControlCard.tsx +++ b/components/cert/ControlCard.tsx @@ -84,6 +84,8 @@ export const ControlCard = memo(function ControlCard({
+ {control.id} + · {control.title}
diff --git a/components/cert/control.css b/components/cert/control.css index d39f12e63..bf2cd0717 100644 --- a/components/cert/control.css +++ b/components/cert/control.css @@ -81,6 +81,19 @@ color: var(--card-strong); } +.control-id { + color: var(--card-muted); + font-weight: 500; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.875em; +} + +.control-id-sep { + color: var(--card-muted); + font-weight: 400; + margin: 0 2px; +} + .control-description { color: var(--card-muted); } diff --git a/components/certified-protocols/CertifiedProtocols.tsx b/components/certified-protocols/CertifiedProtocols.tsx index aa27793df..37a235031 100644 --- a/components/certified-protocols/CertifiedProtocols.tsx +++ b/components/certified-protocols/CertifiedProtocols.tsx @@ -26,7 +26,7 @@ const certTypeToName: Record = { 'sfc-ms': 'Multisig Operations', 'sfc-dns': 'DNS Registrar', 'sfc-tro': "Treasury Operations", - 'sfc-ws': "Workspace Security", + 'sfc-ida': "Identity & Accounts", }; function ProtocolCard({ protocol }: ProtocolCardProps) { diff --git a/docs/pages/certs/changelog.mdx b/docs/pages/certs/changelog.mdx new file mode 100644 index 000000000..e795af272 --- /dev/null +++ b/docs/pages/certs/changelog.mdx @@ -0,0 +1,88 @@ +--- +title: "SEAL Certifications Changelog | Security Alliance" +description: "Revision history for SEAL certification frameworks. Tracks additions, removals, and control changes across all SFC certs." +tags: + - SEAL/Initiative + - Certifications + - SFC +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' + + + + +# SEAL Certifications Changelog + +Revision history across all SFC certifications. Each cert carries a `version` in its page header; this page aggregates what changed, when, and why so protocols re-certifying after a revision can scope the delta quickly. + +--- + +## 2026-04-17 — Feedback integration round 1 + +### New + +- **[SFC - Identity & Accounts](/certs/sfc-identity-accounts)** (v1.0): new horizontal cert covering organizational account management — inventory (social media, email, SSO, registrar, custody, repo admin, cloud root, SaaS), phishing-resistant MFA, password manager with individual accountability, recovery methods restricted to org channels, account lifecycle, takeover monitoring, third-party access. Addresses a gap where org-account takeover (Twitter, status page, registrar) was not covered despite being a top-tier attack vector for crypto protocols. + +### Retired + +- **SFC - Workspace Security**: retired. Its crypto-relevant content (accounts, credentials, MFA, takeover monitoring) moved to the new Identity & Accounts cert. Content on device management, EDR/MDM, BYOD, physical/travel security, formal training programs with phishing sims, insider threat assessment as standalone, and data classification was intentionally dropped as out of SEAL SME and better covered by ISO 27001 / SOC 2 / CIS. + +### SFC - Multisig Operations (v1.0 → v1.1) + +- `ms-2.1.2` strengthened from "evaluate" to "implement" contract-level security controls. +- `ms-4.1.1` transaction process consolidated from 8 bullets to 5. + +### SFC - Treasury Operations (v1.0 → v1.1) + +- Scope note added distinguishing internal vs professional treasury operations (OTC desks, market-making, custody-as-a-service). +- `tro-1.1.4` "SLAs" replaced with "timeframes". +- `tro-2.1.1` impact thresholds reframed as an example scheme; review trigger broadened. +- `tro-2.1.2` renamed from "fund allocation limits" to "portfolio concentration limits"; wording softened. +- `tro-2.1.3` (NEW): per-actor and per-path exposure limits. +- `tro-3.1.1` session timeout bullet made actionable; geographic restrictions clause dropped. +- `tro-3.1.2` hardware-key MFA added for privileged credential access; owner/admin isolation line moved to `tro-3.1.5`. +- `tro-3.1.5` (NEW): privileged access and root account management. +- `tro-4.1.1` trusted-parser bullet added; consolidated 8 bullets to 5. +- `tro-4.1.2` consolidated 4 bullets to 3. +- `tro-5.1.1` "TVL history" and "insurance coverage" dropped from baseline; exposure limits softened. +- `tro-6.1.1` consolidated 9 bullets to 4. +- `tro-6.1.2` consolidated 7 bullets to 4. +- Header pointer to SFC - Identity & Accounts added for custody platform account management. +- Control count: 20 → 22. + +### SFC - DevOps & Infrastructure (v1.0 → v1.1) + +- `di-1.1.2` supply-chain mention dropped from the baseline (supply chain is already covered substantively in Section 2). +- `di-1.1.4` rewritten to cover both the tool approval process and the maintained approved-tools list in a single control; list review cadence made explicit. +- `di-2.1.1` repo access review cadence tightened; account controls now reference SFC - Identity & Accounts. +- `di-2.1.4` dependencies consolidated from 6 bullets to 3. +- `di-3.1.1` pipeline runner hardening bullet added; consolidated to 5 bullets. +- `di-4.1.1` network architecture bullet added (segmentation, minimal public exposure, firewall/security group rules). +- `di-4.1.2` account controls now reference SFC - Identity & Accounts; break-glass bullets consolidated. +- Control count unchanged: 16. + +### SFC - DNS Registrar (v1.0 → v1.1) + +- `dns-3.1.1` slimmed to reference SFC - Identity & Accounts for account management; DNS-specific registrar RBAC bullet retained. + +### SFC - Incident Response (v1.0 → v1.1) + +- Header pointer to SFC - Identity & Accounts added for org account takeover coordination. +- `ir-1.1.1` IR team roles consolidated from 7 bullets to 3. +- `ir-1.1.2` IR contacts consolidated from 7 bullets to 4. +- `ir-2.1.1` **(NEW)**: threat model for protocol operations, including external dependencies and single points of failure (cross-chain messaging providers, oracle providers, critical infrastructure). Placed before monitoring coverage so monitoring is anchored to a known threat picture. Existing Section 2 controls shifted: previous `ir-2.1.1` → `ir-2.1.2`, `ir-2.1.2` → `ir-2.1.3`, `ir-2.1.3` → `ir-2.1.4`. +- `ir-2.1.3` alerting and paging (previously `ir-2.1.2`) consolidated from 8 bullets to 4. +- `ir-5.1.1` IR drills consolidated from 7 bullets to 4. + +### Cross-cutting + +- Control IDs now rendered next to the title in each control card (UI improvement). +- Account-control pattern (MFA, credential management, access reviews, lifecycle) de-duplicated out of vertical certs; Identity & Accounts is the authoritative source. DNS and DevOps reference it directly; Treasury and Incident Response carry a header pointer and retain domain-specific bullets. + +### Workbook compatibility + +- Control IDs are stable across this revision. No renames. New controls (`tro-2.1.3`, `tro-3.1.5`, `ir-2.1.1`, all `ida-*`) will simply be unpopulated when importing old workbooks, which is expected. Shifted IR Section 2 IDs mean old `ir-2.1.1`/`ir-2.1.2`/`ir-2.1.3` data in workbooks will not align to the new IDs without manual re-mapping. +- Users with saved state for Workspace Security will lose that state (that cert is removed). Other certs retain their localStorage state across the revision. + + diff --git a/docs/pages/certs/index.mdx b/docs/pages/certs/index.mdx index 9754e4aae..53994747d 100644 --- a/docs/pages/certs/index.mdx +++ b/docs/pages/certs/index.mdx @@ -15,9 +15,10 @@ title: "Certs" - [Certified Auditors](/certs/certified-partners) - [SFC: DevOps & Infrastructure](/certs/sfc-devops-infrastructure) - [SFC: DNS Registrar](/certs/sfc-dns-registrar) +- [SFC: Identity & Accounts](/certs/sfc-identity-accounts) - [SFC: Incident Response](/certs/sfc-incident-response) - [SFC: Multisig Operations](/certs/sfc-multisig-ops) - [SFC: Treasury Operations](/certs/sfc-treasury-ops) -- [SFC: Workspace Security](/certs/sfc-workspace-security) - [Certification Guidelines](/certs/certification-guidelines) +- [SEAL Certifications Changelog](/certs/changelog) - [Contributing to SEAL Certifications](/certs/contributions) diff --git a/docs/pages/certs/overview.mdx b/docs/pages/certs/overview.mdx index 368515926..390953000 100644 --- a/docs/pages/certs/overview.mdx +++ b/docs/pages/certs/overview.mdx @@ -1,6 +1,6 @@ --- title: "SEAL Certification Framework | Security Alliance" -description: "SEAL Certification Framework: Modular security certifications for DeFi protocols with standardized evaluation criteria and on-chain attestations." +description: "SEAL Certification Framework: modular operational security certifications for crypto protocols with standardized evaluation criteria and on-chain attestations." tags: - SEAL/Initiative - Certifications @@ -16,143 +16,110 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter, Exp -SEAL Certifications is a certification framework developed by SEAL to provide standardized guidelines and evaluation -criteria for assessing the security of DeFi protocols. SEAL Certifications provides targeted modular certifications -(e.g., [Incident Response](/certs/sfc-incident-response), [Treasury Ops](/certs/sfc-treasury-ops)) that can -independently validate specific aspects of a protocol's security posture. +SEAL Certifications is an open-source operational security certification program for crypto protocols. Smart contract vulnerabilities were once the dominant cause of crypto exploits; operational failures now dominate. Compromised signers, poorly managed multisigs, DNS takeovers, leaked credentials, unmonitored infrastructure, and missing incident response playbooks account for the majority of major incidents. Code audits don't catch these, because the code isn't the issue. -Using SEAL Certifications will help ensure that protocols follow best practices for their security operations, and -provide a standard set of criteria for comparing the security of different protocols. +SEAL Certifications target exactly this gap. The framework evaluates the operational practices that determine whether a protocol can actually defend itself, detect an incident, and respond when things go wrong. -SEAL Certifications is fully open-source and freely available for any protocol to use. +## Certification Modules -## How it Works +The framework covers six modules, each independently scopeable: -Unlike broad certifications like SOC 2 or ISO 27001, SEAL Certifications focus on the highest-impact needs of -protocols, based on what SEAL has observed throughout the industry and in interviews with pilot -protocols. Each certification focuses on a specific area of security and includes controls relevant to that area. -Protocols can use certifications independently to evaluate their security posture through self-assessment, or they can -pursue formal certification through a third-party audit by a SEAL-partnered auditor. After completing a certification -with a third-party auditor, protocols can publicly display their certification status and are issued an on-chain badge -to demonstrate their completion of the certification. + -## Current Status: RFC Phase +- **[Multisig Ops](/certs/sfc-multisig-ops)** — Governance, signer security, transaction verification, emergency procedures +- **[Treasury Ops](/certs/sfc-treasury-ops)** — Treasury architecture, transaction security, custody, DeFi risk management +- **[Incident Response](/certs/sfc-incident-response)** — Threat modeling, monitoring, response playbooks, drills +- **[DevOps & Infrastructure](/certs/sfc-devops-infrastructure)** — Development environment, source code, CI/CD, cloud infrastructure, supply chain +- **[DNS & Registrar](/certs/sfc-dns-registrar)** — Domain management, DNS controls, registrar security, email authentication +- **[Identity & Accounts](/certs/sfc-identity-accounts)** — Organizational account inventory, phishing-resistant MFA, credential management, takeover monitoring -We are currently in a Request for Comment (RFC) Phase until December 31st, 2025. During this period: +See the [changelog](/certs/changelog) for revision history across modules. -- Protocols can begin working through the 5 modular proposed certifications to bring their security standards up to best - practices -- Protocols can sign up for the waitlist for a free 1-hour consultation with the SEAL team to walk through the - certification criteria, identify security gaps, and provide valuable feedback [on this - form](https://securityalliance.typeform.com/CertsAuditor) -- Auditors interested in becoming accredited third-party certification issuers can apply [on this form](https://securityalliance.typeform.com/CertsAuditor) +## How Certification Works -We welcome feedback on the current certifications, suggestions for new modular certifications, and general input on the framework. +SEAL maintains the framework and accredits auditing firms. Accredited firms perform the assessments; SEAL issues the on-chain certification. -## Certifications Being Developed +An engagement runs through five steps: - +1. **Scoping.** Align on which controls apply and what infrastructure is in scope. +2. **Evidence collection.** The protocol team gathers documentation and evidence that their practices meet the framework controls. +3. **Assessment.** The firm reviews evidence against the open-source framework criteria. +4. **Remediation** (if needed). The firm provides recommendations to close any gaps. The protocol team implements fixes. +5. **Certification.** Protocols that meet the standard receive a formal on-chain attestation via the Ethereum Attestation Service (EAS), publicly and cryptographically verifiable. -- **[DevOps & Infrastructure](/certs/sfc-devops-infrastructure.mdx)** - Development environments, CI/CD pipelines, - infrastructure security, supply chain -- **[DNS Security](/certs/sfc-dns-registrar.mdx)** - Domain management, DNS configurations, registrar protection -- **[Incident Response](/certs/sfc-incident-response.mdx)** - Detection, response procedures, team coordination, - emergency operations -- **[Multisig Ops](/certs/sfc-multisig-ops.mdx)** - Governance, signer security, transaction verification -- **[Treasury Ops](/certs/sfc-treasury-ops.mdx)** - Treasury architecture, transaction security, DeFi risk management -- **[Workspace Security](/certs/sfc-workspace-security.mdx)** - Device security, account management, credential - handling, insider threats +A typical engagement runs a few weeks. Protocols can also use the framework independently for self-assessment at any time. -## FAQ +See [Certification Guidelines](/certs/certification-guidelines) for the full process and [Certified Auditors](/certs/certified-partners) for accredited firms. -
-When will Certifications start being issued? +## Program Status -SEAL is currently working with several auditors and protocols to finalize the certification process. We expect to begin -issuing certifications in Q1 2026. +The framework has been validated through pilot analyses with protocols across DeFi, staking, treasury management, and infrastructure, plus feedback sessions with auditing firms and independent security researchers. Controls have been refined through that process and are now published. -In the meantime, protocols are welcome to begin using the SEAL Certifications framework for self-assessments and -internal evaluations. +The program is moving from pilot validation into active certification, starting with supervised first engagements through accredited firms. + +## Get Involved + +- **Protocols** interested in a certification engagement or a consultation with the SEAL team on the framework: [sign up here](https://securityalliance.typeform.com/CertsWaitlist). +- **Auditing firms and independent security researchers** interested in becoming an accredited third-party certification issuer: [apply here](https://securityalliance.typeform.com/CertsAuditor). + +## FAQ -
What's the difference between self-assessments and certified audits? -Self-assessments are completed by the protocol team themselves, using the SEAL Certifications framework as a guide. They -are useful for protocols to internally evaluate their own security posture and identify areas for improvement. -Self-assessments are not verified by a third party and do not result in a formal certification or an endorsed badge. +Self-assessments are completed by the protocol team using the SEAL Certifications framework as a guide. They help protocols internally evaluate their own security posture and identify gaps. Self-assessments are not verified by a third party. -Certified audits are completed by a third-party vendor through SEAL's [partner program](/certs/certified-partners.mdx). -They involve a thorough and independent evaluation of the protocol's security controls against the SEAL Certifications -framework. Upon successful completion of a certified audit, protocols receive a formal attestation on-chain. +Certified audits are completed by a third-party accredited firm through SEAL's [partner program](/certs/certified-partners). The firm independently evaluates the protocol's security controls against the framework. Upon successful completion, protocols receive a formal attestation on-chain.
What is an attestation? -Attestations are certificates issued on-chain through the [Ethereum Attestation Service -(EAS)](https://ethereum.org/en/developers/docs/standards/tokens/eas/) by SEAL to protocols that successfully complete a -certified audit. Attestations serve as verifiable proof that a protocol has met the requirements of a given SEAL -certification. +Attestations are certificates issued on-chain through the [Ethereum Attestation Service (EAS)](https://ethereum.org/en/developers/docs/standards/tokens/eas/) by SEAL to protocols that successfully complete a certified audit. Attestations serve as verifiable proof that a protocol has met the requirements of a given SEAL certification. -Attestations do not indicate that a protocol is completely secure or free from issues. Blockchain security is always -evolving and novel vulnerabilities arise regularly. Instead, attestations demonstrate that a protocol has implemented a -set of standardized best-practices to manage and mitigate security risks. +Attestations do not indicate a protocol is free from security issues. Blockchain security evolves continuously and novel vulnerabilities arise regularly. Attestations demonstrate that a protocol has implemented a set of standardized operational practices to manage and mitigate risk.
What if a protocol doesn't meet all the certification requirements? -Protocols going through a certified audit that don't meet all the certification requirements will receive a report from -the auditor detailing the gaps in their security posture. If the protocol decides to address the gaps, they can work -with the auditor to complete a re-assessment of the controls. +Protocols that don't meet all requirements receive a report from the auditor detailing the gaps. If the protocol addresses the gaps, they can work with the auditor to complete a re-assessment.
-
What kind of evidence is required? -Evidence requirements vary by certification and control. Generally, protocols need to provide documentation, -screenshots, or other artifacts demonstrating the implementation of each control. This might mean a list of signer -addresses for a multisig, incident response playbooks, or screenshots of DNS configurations. +Evidence varies by certification and control. Protocols generally provide documentation, screenshots, or other artifacts demonstrating implementation of each control: a signer registry for multisig, incident response playbooks, DNS configuration records, and so on.
Who can see our attestation? -Attestations are publicly accessible on-chain through the EAS. The detailed audit reports and evidence provided during -the audit process are confidential between the protocol and the auditor. +Attestations are publicly accessible on-chain through EAS. Detailed audit reports and evidence shared during the assessment process are confidential between the protocol and the auditor.
-Can we use the SEAL logo / badge? +Can we use the SEAL badge? -Protocols that successfully complete a certified audit will receive a badge from SEAL. Protocols are welcome to display -this badge on their website or documentation to demonstrate their certification. +Protocols that successfully complete a certified audit receive a badge from SEAL to display on their website or documentation.
Is there a list of certified protocols? -SEAL will maintain a list of protocols that have successfully completed certified audits after the certification program -is fully launched. - -{/* SEAL maintains a list of protocols that have completed certified audits. You can find the list -here: [Certified Protocols](/certs/certified-protocols.mdx). */} +SEAL will maintain a public list of certified protocols as the program launches.
-How can auditors become certified? +How can auditors become accredited? -SEAL works with a group of third-party auditing firms to provide certification audits. For more information on the -process or how to become certified, see our [Certified Auditors](/certs/certified-partners) page. +SEAL accredits third-party auditing firms through a supervised first engagement. See [Certified Auditors](/certs/certified-partners) for the process.
-Can a project lose their certification? +Can a protocol lose its certification? -Yes, certifications can be revoked if a protocol is found to be non-compliant with the certification requirements for an -extended period of time. Certifications are also time-limited and require periodic re-assessment to maintain. +Yes. Certifications can be revoked if a protocol is found to be non-compliant for an extended period. Certifications are also time-limited and require periodic re-assessment.
diff --git a/docs/pages/certs/sfc-devops-infrastructure.mdx b/docs/pages/certs/sfc-devops-infrastructure.mdx index 93c53e483..a08e711c6 100644 --- a/docs/pages/certs/sfc-devops-infrastructure.mdx +++ b/docs/pages/certs/sfc-devops-infrastructure.mdx @@ -1,6 +1,8 @@ --- title: "SFC: DevOps & Infrastructure | Security Alliance" description: "SFC DevOps & Infrastructure certification: development environment security, source code management, supply chain security, CI/CD pipeline controls, infrastructure access, and cloud security monitoring." +version: "1.1" +revised: "2026-04-17" tags: - SEAL/Initiative - Certifications @@ -21,7 +23,7 @@ cert: and infrastructure operations? baselines: - Policy covers environment standards, access controls, deployment procedures, - code management, and supply chain security + and code management - Accessible to all developers and infrastructure operators - Reviewed at least annually and after significant changes (security incidents, technology shifts, organizational restructuring) @@ -39,7 +41,7 @@ cert: title: Development Environment Isolation - id: di-1.1.4 description: Do you evaluate and approve development tools before organizational - use? + use, and maintain an approved tools list? baselines: - Evaluation criteria cover IDEs, extensions, plugins, AI-powered tools, and third-party services @@ -47,8 +49,9 @@ cert: - AI tools assessed for data privacy risks (does the tool send code to third parties for training or analytics?) - Approved tool list maintained; unapproved tools restricted - - Regular reviews of installed tools to identify unused or unrecognized items - title: Development Tools Approval + - List reviewed at least annually, including checks for unused or unrecognized + installed tools + title: Development Tools Approval and Approved List - id: di-2 title: Source Code & Supply Chain Security controls: @@ -59,8 +62,9 @@ cert: - Branch protection rules enforced on main/production branches - Signed commits required for all code changes - Multi-party code review required for merges to protected branches - - MFA required for all repository members - - Repository access reviewed periodically + - Account controls (phishing-resistant MFA, credential management, access + reviews, lifecycle) follow [SFC - Identity & + Accounts](/certs/sfc-identity-accounts) title: Repository Security - id: di-2.1.2 description: Do you scan source code for accidentally committed secrets? @@ -89,13 +93,11 @@ cert: description: Do you verify and manage dependencies to prevent supply chain attacks? baselines: - - Packages obtained from official repositories and trusted sources only - - Package names verified against typosquatting patterns before installation - - Dependencies scanned for known vulnerabilities before deployment - - Dependency version pinning enforced to prevent automatic updates to - compromised versions - - Regular dependency audits for outdated or vulnerable components - - Changelog reviewed for dependency updates to verify expected functionality + - Packages sourced from official repositories, verified against typosquatting + patterns, and pinned to prevent silent updates + - Dependencies scanned for known vulnerabilities before deployment; regular + audits for outdated or vulnerable components + - Changelogs reviewed for dependency updates to verify expected functionality title: Dependency and Supply Chain Security - id: di-3 title: CI/CD Pipeline Security @@ -104,12 +106,12 @@ cert: description: Do you control who can modify and execute your deployment pipelines? baselines: - Pipeline configuration changes require multi-party approval - - Separate service accounts with minimal permissions used for pipeline - execution - - Manual deployment by humans restricted; deployments automated through - controlled pipelines + - Deployments automated through controlled pipelines using separate service + accounts with minimal permissions; manual human deployment restricted - Pipeline and build configurations version-controlled and reviewed - Builds are deterministic with strict dependency sets + - Pipeline runners use pinned, versioned images with minimal scope (ephemeral + where feasible, limited network egress for build steps) title: Pipeline Security Controls - id: di-3.1.2 description: Do you securely manage secrets used in pipelines and applications? @@ -145,19 +147,21 @@ cert: steps required - Infrastructure changes require multi-party approval - IaC security scanning performed before deployment + - "Network architecture defined in code: segmentation (VPCs/subnets), minimal + public exposure, firewall/security group rules" title: Infrastructure as Code - id: di-4.1.2 description: Do you enforce least-privilege access controls for infrastructure? baselines: - - Individual accounts with MFA required; no shared accounts + - Account controls (individual accounts with phishing-resistant MFA, no + shared logins, credential management, access lifecycle) follow + [SFC - Identity & Accounts](/certs/sfc-identity-accounts) - Privileged access is time-limited and requires multi-party approval (JIT access) - Day-to-day operations use minimum necessary permissions (read-only where possible) - Break-glass accounts established for emergency access with individual - accountability - - Break-glass usage triggers immediate alerts to the entire team and requires - post-incident review + accountability; usage triggers immediate alerts and post-incident review - All access activities logged and monitored title: Infrastructure Access Controls - id: di-4.1.3 @@ -192,6 +196,8 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter, Cer # SFC - DevOps & Infrastructure +*Revision {frontmatter.version} · Updated {frontmatter.revised} · [Changelog](/certs/changelog)* + The SEAL Framework Checklist (SFC) for DevOps & Infrastructure provides guidelines for securing development environments, source code management, CI/CD pipelines, and cloud infrastructure. It covers governance, supply chain security, deployment controls, and infrastructure access. diff --git a/docs/pages/certs/sfc-dns-registrar.mdx b/docs/pages/certs/sfc-dns-registrar.mdx index cf6b4a751..d763854be 100644 --- a/docs/pages/certs/sfc-dns-registrar.mdx +++ b/docs/pages/certs/sfc-dns-registrar.mdx @@ -1,6 +1,8 @@ --- title: "SFC: DNS Registrar | Security Alliance" description: "SFC DNS Registrar certification: domain management, registrar access controls, DNSSEC configurations, email authentication (SPF/DKIM/DMARC), monitoring, and incident response." +version: "1.1" +revised: "2026-04-17" tags: - SEAL/Initiative - Certifications @@ -57,12 +59,12 @@ cert: description: Do you control and secure access to domain registrar and DNS management accounts? baselines: - - Documented who is authorized, how access is granted/revoked, and role-based - permissions where available - - Hardware security key MFA (FIDO2/WebAuthn) required for all registrar and - DNS management accounts - - Access reviews conducted at least annually - - Access revoked promptly when no longer needed + - "Registrar and DNS management accounts classified as high-privilege and + managed per [SFC - Identity & Accounts](/certs/sfc-identity-accounts) + (phishing-resistant MFA via hardware keys, credential management, recovery + methods, account lifecycle, periodic access reviews)" + - Registrar account ownership and role-based permissions documented where + the registrar supports them title: Registrar Access Control - id: dns-3.1.2 description: Is your domain security contact email independent of the domains @@ -189,6 +191,8 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter, Cer # SFC - DNS Registrar +*Revision {frontmatter.version} · Updated {frontmatter.revised} · [Changelog](/certs/changelog)* + The SEAL Framework Checklist (SFC) for DNS Registrar provides best practices for securely managing domain names and DNS configurations. For more details on certifications or self-assessments, refer to the [Certification Guidelines](/certs/certification-guidelines). diff --git a/docs/pages/certs/sfc-identity-accounts.mdx b/docs/pages/certs/sfc-identity-accounts.mdx new file mode 100644 index 000000000..fefb439f6 --- /dev/null +++ b/docs/pages/certs/sfc-identity-accounts.mdx @@ -0,0 +1,137 @@ +--- +title: "SFC: Identity & Accounts | Security Alliance" +description: "SFC Identity & Accounts certification: organizational account inventory, phishing-resistant MFA, password management, recovery methods, account lifecycle, and takeover monitoring for social media, email, SSO, registrar, custody, and SaaS accounts." +version: "1.0" +revised: "2026-04-17" +tags: + - SEAL/Initiative + - Certifications + - SFC +cert: +- id: ida-1 + title: Governance & Inventory + controls: + - id: ida-1.1.1 + description: Is there a clearly designated person or team accountable for + organizational account security? + baselines: + - Accountability scope covers account inventory, authentication and credential + standards, access reviews, monitoring, and incident escalation + title: Organizational Account Security Owner + - id: ida-1.1.2 + description: Do you maintain an inventory of organizational accounts with + defined ownership? + baselines: + - Inventory covers social media and public-facing accounts, email domains and + admin accounts, SSO/identity provider, domain registrar, custody platforms, + code repository admin accounts, cloud console root/admin, and critical SaaS + services + - Each account has a documented owner (individual or role) + - Updated within 24 hours for security-critical changes (new accounts, + ownership transfers), 7 days for routine changes + - Reviewed at least annually for completeness + title: Organizational Account Inventory +- id: ida-2 + title: Authentication & Credentials + controls: + - id: ida-2.1.1 + description: Do you enforce phishing-resistant multi-factor authentication on + organizational accounts? + baselines: + - "Hardware security keys (FIDO2/WebAuthn) required for high-privilege + accounts: social media admin, domain registrar, custody platforms, cloud + console root, SSO/IdP, repository admin, email admin" + - MFA required on all organizational accounts at minimum + - SMS and voice-call MFA not used as the primary factor for high-privilege + accounts + - Backup MFA methods configured using independent factors + title: Phishing-Resistant Multi-Factor Authentication + - id: ida-2.1.2 + description: Do you enforce credential management standards with individual + accountability? + baselines: + - Password manager required for all personnel; no passwords stored in plain + text, documents, or browsers + - Unique, strong passwords per account + - Individual accounts required; no shared logins for organizational accounts. + Where shared access is technically unavoidable, it is mediated through the + password manager with per-person audit trails + - Credentials transmitted only through encrypted channels (password manager + sharing, not email or chat) + title: Credential Management and Individual Accountability + - id: ida-2.1.3 + description: Do you restrict account recovery methods to organizational + channels? + baselines: + - Recovery email addresses use organizational domains, not personal email + - Recovery phone numbers use organizational numbers where supported, not + personal numbers + - Backup and recovery codes stored in the password manager or comparable + secure storage (not in personal email, notes, or cloud docs) + - Recovery options reviewed when personnel change and at least annually + title: Recovery Methods Restricted to Organizational Channels +- id: ida-3 + title: Access & Lifecycle + controls: + - id: ida-3.1.1 + description: Do you manage the full lifecycle of organizational accounts, + including provisioning, changes, offboarding, and periodic access review? + baselines: + - Account creation requires documented approval and is provisioned with + least privilege + - Role changes trigger prompt permission adjustments + - Offboarding revokes access across all organizational accounts within 24 + hours of departure, including social media, SaaS, SSO, and shared tools + - Credentials for shared systems the departing person accessed are rotated + - Access reviews conducted at least annually; quarterly for high-privilege + accounts + - Service account and bot account ownership reviewed alongside human accounts + title: Account Lifecycle Management +- id: ida-4 + title: Monitoring & Third-Party + controls: + - id: ida-4.1.1 + description: Do you monitor organizational accounts for takeover, unauthorized + activity, and credential exposure? + baselines: + - Authentication-event monitoring for privileged accounts (unusual logins, + new device enrollments, session anomalies, repeated auth failures) + - Public-facing account monitoring for unauthorized posts or configuration + changes (social media, status pages, external websites) + - Credential exposure detection including dark web, breach databases, and + secret scanning in code + - Alerting and escalation paths documented; critical alerts trigger immediate + investigation + - Response coordinated with the Incident Response plan + title: Organizational Account Takeover Monitoring + - id: ida-4.1.2 + description: Do you manage third-party access to organizational accounts with + time-limited, purpose-specific permissions? + baselines: + - Third-party access documented with scope, purpose, and expiry + - Access limited to minimum necessary systems and time window + - Audit trail maintained for third-party access + - Access revoked at expiry or project completion + - Third-party identity verified before access is granted + title: Third-Party Access Management + +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter, CertList } from '../../../components' + + + + +# SFC - Identity & Accounts + +*Revision {frontmatter.version} · Updated {frontmatter.revised} · [Changelog](/certs/changelog)* + +The SEAL Framework Checklist (SFC) for Identity & Accounts provides guidelines for managing organizational accounts (social media, email, SSO, registrar, SaaS, privileged platforms) such that account compromise cannot redirect users, funds, or trust. + +**Scope**: This is a horizontal cert covering the cross-cutting account-management pattern that applies to multiple surfaces (domains, code, custody, social media). Other certs reference it for account-control concerns while retaining their domain-specific substance (e.g., [SFC - DNS Registrar](/certs/sfc-dns-registrar) handles DNSSEC and registry locks; this cert handles registrar account hygiene). + +For more details on certifications or self-assessments, refer to the [Certification Guidelines](/certs/certification-guidelines). + + + + diff --git a/docs/pages/certs/sfc-incident-response.mdx b/docs/pages/certs/sfc-incident-response.mdx index 0948dc9c0..847d75c56 100644 --- a/docs/pages/certs/sfc-incident-response.mdx +++ b/docs/pages/certs/sfc-incident-response.mdx @@ -1,6 +1,8 @@ --- title: "SFC: Incident Response | Security Alliance" description: "SFC Incident Response certification: team roles, 24/7 monitoring, paging systems, response playbooks, signer coordination, emergency communications, and regular drills." +version: "1.1" +revised: "2026-04-17" tags: - SEAL/Initiative - Certifications @@ -13,33 +15,25 @@ cert: description: Do you have an incident response team with clearly defined roles and responsibilities? baselines: - - Incident commander (with designated backup) who coordinates response, - assigns tasks, and makes time-sensitive decisions - - Subject matter experts identified for key domains (smart contracts, - infrastructure, security) who can analyze attacks and prepare response - strategies - - Scribe role defined for real-time incident documentation - - Communications personnel designated for public information sharing and - record-keeping - - Legal support available with procedures for reviewing response actions, - whitehat engagement, and public communications - - Decision makers defined for high-stakes decisions (system shutdown, public - disclosure, fund recovery) - - Roles, authorities, and escalation measures reviewed at least annually and - after protocol changes, team restructuring, or major incidents + - "Response leadership defined with named backups: incident commander + (coordination, task assignment, time-sensitive decisions) and decision makers + for high-stakes choices (system shutdown, public disclosure, fund recovery)" + - "Supporting roles defined: subject matter experts for key domains (smart + contracts, infrastructure, security), scribe for real-time documentation, + communications lead, and legal support covering response review, whitehat + engagement, and disclosure" + - Roles, authorities, and escalation paths reviewed at least annually and + after major protocol changes, team restructuring, or incidents title: IR Team and Role Assignments - id: ir-1.1.2 description: Do you maintain current contacts and coordination procedures for all parties needed during an incident? baselines: - - Internal coordination procedures between technical (devs, auditors) and - operational teams (security council, communications) - - External protocol contacts for protocols you depend on and protocols that - depend on you - - External expertise contacts including forensics firms, security - consultants, SEAL 911, and auditors - - Legal counsel and communications/PR contacts - - Infrastructure vendor support contacts and escalation procedures + - Internal coordination procedures between technical teams (devs, auditors) + and operational teams (security council, communications) + - External contacts maintained for protocol dependencies (both directions), + external expertise (forensics, consultants, SEAL 911, auditors), legal and + PR, and infrastructure vendor support - Contact list reviewed at least quarterly and after team changes - Escalation order documented for P1 incidents (e.g., SEAL 911 → decision makers → security partners → legal) @@ -48,6 +42,24 @@ cert: title: Monitoring, Detection & Alerting controls: - id: ir-2.1.1 + description: Do you maintain a threat model for protocol operations, including + external dependencies? + baselines: + - Threat model identifies adversaries, attack surfaces, and likely incident + scenarios for protocol operations + - Identifies single points of failure and highly centralized components + across onchain and offchain layers (e.g., cross-chain messaging providers, + oracle providers, critical infrastructure dependencies) + - Covers external dependencies (infrastructure providers, oracles, bridges, + integrations, custody partners) and how their compromise would propagate + - Anchors what the monitoring program detects and what incident playbooks + cover + - Reviewed at least annually and after significant changes (new integrations, + architecture shifts, threat landscape changes) + - Shared with the IR team so detection and response are anchored to the same + threat picture + title: Threat Model for Protocol Operations + - id: ir-2.1.2 description: Do you maintain monitoring coverage for your critical systems, protocols, and external attack surfaces? baselines: @@ -62,23 +74,20 @@ cert: - Monitoring coverage documented — what's covered, what's not, and known gaps title: Monitoring Coverage - - id: ir-2.1.2 + - id: ir-2.1.3 description: Do you have alerting and paging systems that reliably route incidents to available responders? baselines: - - Automated alerting configured for security events and operational issues - - Alerts include embedded triage guidance for distinguishing true incidents - from false positives - - Triage and classification procedures documented with escalation paths - based on severity - - Time-based escalation triggers if initial responder doesn't acknowledge - within defined window - - Management notification requirements for high-severity incidents + - Automated alerting with embedded triage guidance to distinguish true + incidents from false positives + - Triage and classification procedures with severity-based escalation, + including time-based escalation when unacknowledged and management + notification for high-severity incidents - Redundant paging systems with documented failover procedures - - On-call schedules maintained with adequate coverage for operational needs - - Backup procedures when on-call personnel are unreachable + - On-call schedules with adequate coverage and documented backup procedures + when on-call personnel are unreachable title: Alerting, Paging, and Escalation - - id: ir-2.1.3 + - id: ir-2.1.4 description: Do you maintain tamper-evident logs with adequate retention for incident investigation? baselines: @@ -176,17 +185,14 @@ cert: description: Do you conduct regular incident response drills and evaluate the results? baselines: - - Drills conducted at least annually - - Drills cover different incident types across exercises (protocol exploit, - infrastructure failure, key compromise, etc.) - - Tests the full pipeline from monitoring through alerting, paging, triage, - escalation, team coordination, containment, and recovery - - Production alerting pipeline validated end-to-end from event detection - through to responder notification and acknowledgment - - Drill documentation includes date, scenario, participants, response - times, gaps identified, and corrective actions - - Corrective actions tracked to completion with owners and deadlines - - Drill findings incorporated into playbook and procedure updates + - Drills conducted at least annually, covering different incident types + across exercises (protocol exploit, infrastructure failure, key compromise) + - "Tests the full response pipeline end-to-end: monitoring, alerting, paging, + triage, escalation, team coordination, containment, and recovery" + - Drill documentation includes date, scenario, participants, response times, + gaps identified, and corrective actions + - Corrective actions tracked with owners and deadlines; findings incorporated + into playbook and procedure updates title: IR Drills and Testing --- @@ -199,10 +205,14 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter, Cer # SFC - Incident Response +*Revision {frontmatter.version} · Updated {frontmatter.revised} · [Changelog](/certs/changelog)* + The SEAL Framework Checklist (SFC) for Incident Response provides structured guidelines to help teams remain prepared for security incidents affecting blockchain protocols. It covers team structure, monitoring, alerting, and response procedures. +**Related**: Organizational account takeover monitoring and response coordinates with [SFC - Identity & Accounts](/certs/sfc-identity-accounts) (ida-4.1.1). IR handles incident response flow; I&A handles account-control baselines. + For more details on certifications or self-assessments, refer to the [Certification Guidelines](/certs/certification-guidelines). diff --git a/docs/pages/certs/sfc-multisig-ops.mdx b/docs/pages/certs/sfc-multisig-ops.mdx index 6341ca299..0824d2373 100644 --- a/docs/pages/certs/sfc-multisig-ops.mdx +++ b/docs/pages/certs/sfc-multisig-ops.mdx @@ -1,6 +1,8 @@ --- title: "SFC: Multisig Operations | Security Alliance" description: "SFC Multisig Operations certification: governance, signer security, transaction verification, emergency playbooks, communication protocols, and 24/7 paging for critical multisigs." +version: "1.1" +revised: "2026-04-17" tags: - SEAL/Initiative - Certifications @@ -46,12 +48,12 @@ cert: shifts, new products, protocol upgrades, security incidents) title: Multisig Classification and Risk-Based Controls - id: ms-2.1.2 - description: Have you evaluated contract-level security controls that could limit - the impact of a multisig compromise? + description: Do you implement contract-level security controls that limit the + impact of a multisig compromise? baselines: - - Evaluation covers timelocks, modules, guards, address whitelisting, invariant - enforcement, and parameter bounds - - Controls evaluated for each multisig based on classification + - Controls implemented where applicable include timelocks, modules, guards, + address whitelisting, invariant enforcement, and parameter bounds + - Controls selected for each multisig based on classification - Security review required before enabling any module or guard - Decisions documented, including rationale for controls not implemented title: Contract-Level Security Controls @@ -142,16 +144,15 @@ cert: description: Do you have a defined, documented process for how transactions are proposed, verified, and executed? baselines: - - Process covers initiation, approval, simulation, execution, and confirmation - - Defines who is authorized to initiate transactions - - Each signer independently verifies transaction details (chain ID, target - address, calldata, value, nonce, operation type) before signing - - Transaction hashes compared across at least two independent interfaces (e.g., - web UI and CLI, or web and mobile app) + - Process covers initiation (with authorized roles defined), approval, + simulation, execution, and confirmation + - Each signer independently verifies transaction details (chain ID, target, + calldata, value, nonce, operation type) before signing, with hashes + cross-checked across at least two independent interfaces - DELEGATECALL operations to untrusted addresses flagged as high risk - High-risk transactions (large transfers, emergency actions, protocol changes) - require waiting periods where feasible and elevated threshold approval - - High-risk thresholds defined based on classification and reviewed periodically + require waiting periods where feasible and elevated approval; thresholds + defined by classification and reviewed periodically - Private transaction submission used when appropriate to prevent front-running or MEV extraction title: Transaction Handling Process @@ -257,6 +258,8 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter, Cer # SFC - Multisig Operations +*Revision {frontmatter.version} · Updated {frontmatter.revised} · [Changelog](/certs/changelog)* + The SEAL Framework Checklist (SFC) for Multisig Operations provides best practices for managing multisig wallets securely. It covers governance, risk management, signer security, operational procedures, and emergency operations. diff --git a/docs/pages/certs/sfc-treasury-ops.mdx b/docs/pages/certs/sfc-treasury-ops.mdx index cb01d4051..93c83fda3 100644 --- a/docs/pages/certs/sfc-treasury-ops.mdx +++ b/docs/pages/certs/sfc-treasury-ops.mdx @@ -1,6 +1,8 @@ --- title: "SFC: Treasury Operations | Security Alliance" description: "SFC Treasury Operations certification: governance, custody architecture, risk classification, access control, transaction security, protocol deployments, monitoring, incident response, vendor management, and accounting." +version: "1.1" +revised: "2026-04-17" tags: - SEAL/Initiative - Certifications @@ -46,7 +48,7 @@ cert: changes, and protocol integrations - Changes require documented approval before implementation - All changes logged with justification and approver - - Changes reflected in the treasury registry within defined SLAs + - Changes reflected in the treasury registry within defined timeframes - Rollback procedures documented for critical changes title: Treasury Infrastructure Change Management - id: tro-2 @@ -58,27 +60,36 @@ cert: baselines: - Classification considers financial exposure, operational dependency, and regulatory impact - - Impact levels defined (e.g., Low <1%, Medium 1-10%, High 10-25%, Critical - >25% of total assets) + - "Example impact scheme: Low <1%, Medium 1-10%, High 10-25%, Critical >25% + of total assets (organizations may define their own buckets)" - Operational types defined based on transaction frequency and urgency requirements - Each classification maps to specific controls including approval thresholds, MFA requirements, whitelist delays, and monitoring levels - - Classification reviewed when asset values, operational patterns, or risk - profile change significantly + - Classification reviewed on a defined cadence and when treasury conditions + change materially title: Treasury Wallet Risk Classification - id: tro-2.1.2 - description: Do you enforce fund allocation limits and rebalancing triggers - across your treasury? + description: Do you enforce portfolio concentration limits and rebalancing + triggers across your treasury? baselines: - - Maximum allocation defined per wallet, per custody provider, and per chain + - Organization-defined concentration limits per wallet, per custody provider, + and per chain - Rebalancing triggers documented (e.g., when a wallet exceeds its allocation ceiling or falls below operational minimums) - Allocation limits reviewed periodically and after significant treasury changes - - No single wallet or custody account holds more than the organization's - defined maximum concentration - title: Fund Allocation Limits and Rebalancing + title: Portfolio Concentration Limits and Rebalancing + - id: tro-2.1.3 + description: Do you enforce per-actor and per-path exposure limits for treasury + movements? + baselines: + - Maximum value movable by a single person defined and enforced + - Maximum value movable by any automated path (API key, bot, integration) + defined and enforced + - Limits scale with wallet/account classification + - Exceeding limits requires elevated approval or quorum + title: Per-Actor and Per-Path Exposure Limits - id: tro-3 title: Access Control & Platform Security controls: @@ -91,9 +102,9 @@ cert: periods, velocity/spending limits" - Hardware security key MFA required for all approvers on High and Critical accounts - - Session timeouts and re-authentication for sensitive operations - - "Network restrictions: IP whitelisting, VPN requirements where supported, - geographic access restrictions" + - Session timeouts reviewed and configured appropriately for sensitive + operations + - "Network restrictions where supported: IP allowlisting, VPN for admin access" - "Separation of duties enforced: initiators cannot approve their own transactions, admins cannot unilaterally execute withdrawals" - Platform configurations documented and reviewed at least quarterly @@ -102,10 +113,11 @@ cert: description: Do you securely manage all credentials and secrets used in treasury operations? baselines: - - Covers API keys, service accounts, owner/admin credentials, and signing keys + - Covers API keys, service accounts, and signing keys - Credentials stored in dedicated secret management systems, not in code, documents, or shared drives - - Owner and admin credentials isolated from day-to-day operational access + - Hardware security key MFA required for accessing or authorizing privileged + credentials - Credential rotation schedule defined and enforced - Access to credentials logged and monitored title: Credential and Secret Management @@ -131,6 +143,18 @@ cert: capabilities - Mobile devices used as second factors have endpoint security monitoring title: Personnel Operational Security + - id: tro-3.1.5 + description: Do you minimize and control privileged access to custody and + treasury platforms? + baselines: + - No single root user holds all control; where a root account exists, its use + is minimized and audited + - Privileged actions (root account use, owner/admin operations) require quorum + approval where the platform supports it + - Owner and admin credentials isolated from day-to-day operational access + - Hardware security key MFA required for all privileged access + - Privileged-access events logged, alerted on, and reviewed + title: Privileged Access and Root Account Management - id: tro-4 title: Transaction Security controls: @@ -138,31 +162,29 @@ cert: description: Do you have a defined process for verifying and executing treasury transactions? baselines: - - "Pre-execution verification includes: recipient address validation, amount - verification, network confirmation, and transaction simulation" + - "Pre-execution verification covers recipient address (validated through + multiple independent sources, never copied from email/chat/history), amount, + network, and simulation" + - Calldata decoded and simulated via a trusted tool; for high-value operations, + results verified against an independent decoder or simulator - Test transactions required before sending to new addresses - - Address verified through multiple independent sources; never copied from - email, chat, or transaction history - - "Multi-party confirmation required: minimum 2 internal personnel for large - transfers" - - Specific procedures for receiving large incoming transfers (address - generation, bidirectional test, sender coordination) - - Specific procedures for OTC transactions where applicable - - High-value transfers (organization-defined threshold) require video call - verification with liveness checks - - All transaction parameters read aloud and confirmed before execution + - Multi-party confirmation required for high-value transfers, including video + call verification with liveness checks at organization-defined thresholds and + read-aloud parameter confirmation + - Specific procedures for large incoming transfers and OTC transactions where + applicable title: Transaction Verification and Execution - id: tro-4.1.2 description: Are treasury signers and approvers knowledgeable in the security practices relevant to their role? baselines: - - "Knowledge covers: transaction verification procedures, address validation - techniques, social engineering awareness, emergency procedures" + - Knowledge covers transaction verification, address validation, social + engineering awareness, emergency procedures, and custody-platform-specific + workflows - Competence demonstrated before authorization (e.g., verifying a test transaction end-to-end) - - Knowledge refreshed annually; updated within 30 days of significant - procedure changes - - Covers custody-platform-specific workflows and policy engine rules + - Knowledge refreshed annually and within 30 days of significant procedure + changes title: Signer and Approver Knowledge - id: tro-4.1.3 description: Do you have secure communication procedures for treasury operations, @@ -186,13 +208,12 @@ cert: description: Do you evaluate external protocols and enforce exposure limits before deploying treasury funds? baselines: - - "Due diligence process for all protocols before deployment: smart contract - audit status, team reputation, TVL history, insurance coverage" - - Exposure limits defined per protocol, per chain, and per deployment category - (DeFi, staking, liquid staking, etc.) - - Limits reviewed periodically and after significant market or protocol changes - - "Risk re-evaluation triggered by: security incidents, major governance - proposals, significant TVL changes, new vulnerabilities disclosed" + - "Due diligence process for protocols before deployment (e.g., audit status, + team reputation, operational history)" + - Exposure limits defined at organization-chosen granularity (protocol, chain, + category) and reviewed periodically + - Risk re-evaluation triggered by security incidents, major governance + proposals, or newly disclosed vulnerabilities title: Protocol Evaluation and Exposure Limits - id: tro-5.1.2 description: Do you have procedures for managing the lifecycle of your positions @@ -213,40 +234,28 @@ cert: description: Do you monitor your treasury for anomalous activity, external threats, and operational risks? baselines: - - "Transaction monitoring: unusual amounts, unexpected destinations, failed - transactions, policy violations" - - "Account state monitoring: balance changes, configuration modifications, new - device enrollments, authentication anomalies" - - "External threat intelligence: protocol vulnerabilities, DeFi/staking risks, - relevant security incidents in the ecosystem" - - "Vendor/platform monitoring: custody platform status, infrastructure alerts, - service availability" - - "Compliance monitoring: transactions and wallet addresses screened for - sanctions and compliance risk" - - "Protocol and position monitoring: deployed protocol health, governance - changes, TVL shifts, collateral ratios, reward accrual, liquidation risks" - - Alerting with defined severity levels and escalation paths - - Critical alerts (large unexpected transactions, unauthorized access attempts) - trigger immediate investigation - - Monitoring system integrity protected — alerts triggered when monitoring - configurations are changed, disabled, or degraded + - Monitoring scope covers transaction activity, account state, custody + platform and vendor health, and (where applicable) deployed protocol + positions and compliance screening + - External threat intelligence ingested for relevant ecosystem incidents and + vendor advisories + - "Alerting with defined severity levels and escalation paths; critical alerts + trigger immediate investigation" + - Monitoring system integrity protected — alerts triggered when monitoring is + altered, disabled, or degraded title: Monitoring and Threat Awareness - id: tro-6.1.2 description: Do you have an incident response plan for treasury security events, and do you test it? baselines: - - Severity levels defined with escalation procedures specific to treasury - operations - - "Containment procedures: fund protection actions (emergency freeze, transfer - to secure cold storage, policy lockdown)" - - "Covers key scenarios: custody platform compromise, unauthorized transaction, - signer key compromise, vendor breach" - - "Emergency contacts pre-documented: custody provider security team, SEAL 911, - legal counsel" - - Communication plan for stakeholders - - Drills conducted at least annually; after major procedure changes - - "Drill documentation includes: date, participants, response times, issues - identified, improvements made" + - Severity levels defined with escalation paths specific to treasury operations + - "Containment playbook covering fund protection actions (emergency freeze, + transfer to cold storage, policy lockdown) and key scenarios (platform + compromise, unauthorized transaction, signer key compromise, vendor breach)" + - Emergency contacts pre-documented (custody provider security, SEAL 911, + legal counsel) and stakeholder communication plan + - "Drills annually and after major procedure changes; documented with date, + participants, response times, issues, and improvements" title: Incident Response Plan - id: tro-7 title: Vendor & Infrastructure @@ -310,10 +319,16 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter, Cer # SFC - Treasury Operations +*Revision {frontmatter.version} · Updated {frontmatter.revised} · [Changelog](/certs/changelog)* + The SEAL Framework Checklist (SFC) for Treasury Operations provides structured guidelines for securely managing and operating an organization's treasury covering governance, access control, transaction security, monitoring, and vendor management. +**Scope note**: These baselines target internal treasury operations (DAO treasuries, protocol treasuries, operational wallets). Professional treasury operations such as OTC desks, market-making, or custody-as-a-service may warrant additional or differentiated baselines beyond this scope. + +**Related**: Custody platform account management (phishing-resistant MFA, credential management, recovery methods, account lifecycle) is governed by [SFC - Identity & Accounts](/certs/sfc-identity-accounts). Treasury-specific controls below focus on transaction security and platform configuration. + For more details on certifications or self-assessments, refer to the [Certification Guidelines](/certs/certification-guidelines). diff --git a/docs/pages/certs/sfc-workspace-security.mdx b/docs/pages/certs/sfc-workspace-security.mdx deleted file mode 100644 index b70fcfef7..000000000 --- a/docs/pages/certs/sfc-workspace-security.mdx +++ /dev/null @@ -1,332 +0,0 @@ ---- -title: "SFC: Workspace Security | Security Alliance" -description: "SFC Workspace Security certification: device management, account security, credential handling, network security, employee training, and insider threat assessment." -tags: - - SEAL/Initiative - - Certifications - - SFC -cert: -- id: ws-1 - title: Governance & Inventory - controls: - - id: ws-1.1.1 - description: Is there a clearly designated person or team accountable for workspace - security? - baselines: - - Accountability scope covers policy maintenance, device and account standards, - access control oversight, periodic reviews, and incident escalation - title: Workspace Security Owner - - id: ws-1.1.2 - description: Do you maintain a workspace security policy that is accessible and - understood by all personnel? - baselines: - - Policy covers core expectations including device security, account hygiene, - credential management, acceptable use, and incident reporting - - Written in plain language so all personnel can follow it - - Policy reviewed at least annually and after significant changes (security - incidents, technology shifts, organizational restructuring) - title: Workspace Security Policy - - id: ws-1.1.3 - description: Do you maintain an inventory of organizational devices and accounts - with defined ownership? - baselines: - - Scoped to devices and accounts with access to sensitive systems or data - - Device inventory tracks make/model, owner, OS version, encryption status, - and EDR/MDM enrollment - - Account inventory covers organizational accounts (e.g., email, cloud, social media) - with defined ownership - - Updated as devices/accounts are provisioned or decommissioned - title: Asset Inventory - - id: ws-1.1.4 - description: Do you classify systems and data by sensitivity to determine - appropriate security controls? - baselines: - - Classification levels defined (e.g., critical, high, standard) - - Classification determines which controls apply (access restrictions, - monitoring, encryption, backup requirements) - - Classification reviewed when systems, data sensitivity, or organizational - structure change - title: System and Data Classification -- id: ws-2 - title: Device Security - controls: - - id: ws-2.1.1 - description: Do you define and enforce security standards for organizational - devices? - baselines: - - Full disk encryption enabled on all devices - - Automatic OS and application patching enabled or enforced - - Strong authentication required (password/biometric, auto-lock timeouts, - lock screens) - - Administrative privileges separated from daily-use accounts - - Devices procured through verified supply chains and verified for integrity - upon receipt - - Device compliance verified upon provisioning and monitored on an ongoing - basis - - BYOD policy defining what personal devices can access and the security - controls required (e.g., MDM enrollment, separate work profiles) - title: Device Security Standards - - id: ws-2.1.2 - description: Do you have procedures for device loss, theft, and secure - decommissioning? - baselines: - - Remote lock and wipe capability for all organizational devices - - Documented procedures for responding to lost or stolen devices (notification, - remote wipe, credential rotation, incident escalation) - - Secure decommissioning procedures including data sanitization and verified - destruction for storage media - title: Device Lifecycle Management - - id: ws-2.1.3 - description: Do you deploy and monitor endpoint protection on organizational - devices? - baselines: - - EDR or MDM deployed on all organizational devices with documented coverage - - Alert triage and response procedures defined with severity-based escalation - - Compliance enforcement actions documented (e.g., quarantine non-compliant - devices, block access) - - Monitoring coverage includes detection of malware, unauthorized software, - and policy violations - title: Endpoint Protection - - id: ws-2.1.4 - description: Do you maintain physical security requirements for workspaces and - travel? - baselines: - - Physical workspace requirements defined for both on-site and remote work - environments - - Screen privacy and shoulder-surfing awareness enforced - - Travel security procedures documented covering device handling, network - usage, and border crossings - - High-risk travel procedures defined (loaner devices, enhanced controls, - check-in schedules) - - USB and charging security addressed (data blockers, no public USB ports) - - Devices physically secured when not in active use (cable locks, safes, - carry-on luggage for travel) - title: Physical and Travel Security -- id: ws-3 - title: Account, Access & Credential Management - controls: - - id: ws-3.1.1 - description: Do you have procedures for provisioning, modifying, and revoking - user accounts? - baselines: - - Account creation requires documented approval from the account owner's - manager or designated authority - - Accounts provisioned with minimum necessary permissions (least privilege) - - Modification of account permissions requires documented approval - - Account revocation procedures tied to offboarding and role changes - - Service accounts and shared credentials inventoried with defined ownership - title: Account Lifecycle Management - - id: ws-3.1.2 - description: Do you enforce multi-factor authentication across organizational - accounts? - baselines: - - MFA required for all organizational accounts by default - - Hardware security keys required for high-privilege and critical accounts - (admin, infrastructure, custody) - - Phishing-resistant MFA preferred (FIDO2/WebAuthn, hardware keys) over SMS - or TOTP where available - - Exceptions documented with justification, compensating controls, and expiry - date - - Backup MFA methods configured for account recovery - title: Multi-Factor Authentication - - id: ws-3.1.3 - description: Do you maintain security standards for all organizational accounts, - including enterprise platforms and external services? - baselines: - - Security configuration standards defined and applied for enterprise platforms - (Google Workspace, Microsoft 365, collaboration tools) - - Social media and public-facing accounts secured with strong authentication - and defined ownership - - Ownership verified and documented for all organizational accounts - - Recovery methods restricted to organizational channels (no personal recovery - emails or phone numbers on organizational accounts) - - Account configurations reviewed periodically for drift or unauthorized - changes - title: Organizational Account Security - - id: ws-3.1.4 - description: Do you enforce credential management standards, including secure - storage and individual accountability? - baselines: - - Password manager required for all personnel; no passwords stored in plain - text, documents, or browsers - - Unique, strong passwords for every account (minimum length/complexity - standards defined) - - Individual accounts required for all personnel — no sharing of personal - login credentials - - When credentials must be provisioned or shared (e.g., service accounts, API - keys, onboarding), transmission only through encrypted channels (password - manager sharing features, not email or chat) - - Credential rotation schedule defined based on risk; rotation triggered - immediately after suspected compromise - - Enhanced controls for high-privilege credentials (admin accounts, service - accounts, API keys) including stricter rotation, separate storage, and - logged access - - Service account and API key inventory maintained with defined ownership - title: Credential Management Standards - - id: ws-3.1.5 - description: Do you conduct periodic access reviews and promptly adjust - permissions when roles change? - baselines: - - Scheduled access reviews at least quarterly for critical systems, annually - for others - - Access adjusted within defined timelines when employees change roles - - Reviews verify each user still requires their current level of access - - Unnecessary permissions revoked promptly with documented evidence - - Insider threat consideration included — for each role, assess what damage - could be done with current access - title: Access Reviews -- id: ws-4 - title: Software & Application Security - controls: - - id: ws-4.1.1 - description: Do you evaluate and approve software, extensions, and tools before - organizational use? - baselines: - - Evaluation criteria for all software before adoption (browsers, extensions, - IDEs, libraries, AI assistants, SaaS tools) - - Data privacy and leakage risks assessed (does the tool send data to third - parties for training or analytics?) - - Approved software list maintained; unapproved software restricted - - Browser extension approval process defined - - Dependency management includes version pinning, vulnerability scanning, and - update policies - title: Software Evaluation and Approval - - id: ws-4.1.2 - description: Do you secure source code repositories against unauthorized access - and credential exposure? - baselines: - - Role-based access control with least-privilege permissions - - Branch protection rules enforced on critical branches (require reviews, - signed commits) - - Automated secret scanning enabled to detect credentials, API keys, and - private keys in code - - Pre-commit hooks or CI checks to prevent secrets from being committed - - Repository security audited periodically (access permissions, configuration, - open PRs) - title: Source Code and Repository Security -- id: ws-5 - title: Network & Communication - controls: - - id: ws-5.1.1 - description: Do you enforce secure network access for organizational systems? - baselines: - - VPN or zero-trust network access required for accessing internal resources - remotely - - Wi-Fi security standards defined (no auto-connect, avoid public Wi-Fi for - sensitive operations) - - Network segmentation applied where applicable (separate guest networks, - development environments) - - Cellular or personal hotspot preferred over public Wi-Fi for sensitive work - title: Network Security - - id: ws-5.1.2 - description: Do you secure organizational communications and verify identity for - sensitive interactions? - baselines: - - End-to-end encrypted channels used for sensitive communications - - Identity verification procedures established for sensitive requests (e.g., - access changes, financial approvals, credential sharing) - - Anti-impersonation protocols documented (e.g., secondary channel - verification, code words, video confirmation) - - Email security configured (SPF, DKIM, DMARC) to prevent spoofing - - Procedures for channel compromise including switching to backup channels - title: Secure Communications -- id: ws-6 - title: People & Training - controls: - - id: ws-6.1.1 - description: Do you verify employee identity and provide security onboarding - before granting system access? - baselines: - - Identity and authorization verified before any access is provisioned - - Background checks or identity verification appropriate to role sensitivity - - Security onboarding includes device provisioning, account creation, MFA - setup, and initial security training - - New personnel acknowledge security policies before access is granted - - Onboarding checklist documented and consistently applied - title: Security Onboarding - - id: ws-6.1.2 - description: Do you have comprehensive offboarding procedures for departing - personnel? - baselines: - - All account access revoked within 24 hours of departure - - Devices returned and verified for data sanitization - - Credentials and secrets rotated for any shared systems the departing person - accessed - - "Offboarding checklist documented covering: account deprovisioning, device - return, credential rotation, access to organizational repositories and - tools, recovery of company property" - - Involuntary departures trigger immediate access revocation before - notification where feasible - title: Security Offboarding - - id: ws-6.1.3 - description: Do you maintain a security awareness program with regular training - and testing? - baselines: - - "Training covers workspace security topics: phishing, social engineering, - device security, credential hygiene, and incident reporting" - - Phishing simulations conducted at least quarterly - - Follow-up training required for personnel who fail simulations - - Training content updated annually and after significant threats or procedure - changes - - "Covers crypto-specific risks: fake job offers, malicious browser - extensions, clipboard hijacking, social engineering via DMs" - title: Security Awareness and Training -- id: ws-7 - title: Monitoring & Risk Management - controls: - - id: ws-7.1.1 - description: Do you detect and respond to workspace security incidents? - baselines: - - "Monitoring in place for common workspace threats: account takeovers, - unauthorized access, credential leaks, device compromise, data - exfiltration" - - "Response procedures documented for key scenarios: compromised account, - compromised device, data leak, insider threat event" - - Escalation paths defined with severity levels - - Credential leak monitoring (dark web, breach databases, paste sites, code - repositories) - - Incidents documented with timeline, root cause, actions taken, and lessons - learned - title: Workspace Security Monitoring and Incident Response - - id: ws-7.1.2 - description: Do you assess insider threat risks and enforce least-privilege - access for each role? - baselines: - - Damage scenarios documented for each role (what could this person do if - compromised or malicious?) - - Least-privilege access enforced across all systems - - Separation of duties applied for critical operations (e.g., no single - person can deploy to production, execute large transactions, and manage - access controls) - - Assessed during periodic access reviews - title: Insider Threat Assessment - - id: ws-7.1.3 - description: Do you manage third-party access with time-limited, purpose-specific - permissions? - baselines: - - Third-party access requires documented approval with defined scope and - expiry date - - Access limited to minimum necessary systems and data - - Access revoked automatically upon contract expiry or project completion - - Audit trails maintained for all third-party access - - Third-party vendor security evaluated before granting access (security - certifications, incident history) - title: Third-Party Access Management - ---- - -import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter, CertList } from '../../../components' - - - - -# SFC - Workspace Security - -The SEAL Framework Checklist (SFC) for Workspace Security provides guidelines to help secure organizational workspaces -covering device management, account security, communications, and training. - -For more details on certifications or self-assessments, refer to the [Certification Guidelines](/certs/certification-guidelines). - - - - diff --git a/docs/pages/intro/overview-of-each-framework.mdx b/docs/pages/intro/overview-of-each-framework.mdx index 4f9dd56a7..fd0e5803d 100644 --- a/docs/pages/intro/overview-of-each-framework.mdx +++ b/docs/pages/intro/overview-of-each-framework.mdx @@ -254,7 +254,7 @@ types (cold vs hot, custodial vs non-custodial), hardware wallets, signing schem This framework provides a certification system developed by SEAL with standardized guidelines and evaluation criteria for assessing the security of DeFi protocols. It includes modular certifications for specific areas such as DevOps & -Infrastructure, DNS Security, Incident Response, Multisig Ops, Treasury Ops, and Workspace Security. +Infrastructure, DNS Security, Identity & Accounts, Incident Response, Multisig Ops, and Treasury Ops. [Read more about SEAL Certifications →](/certs/overview) diff --git a/utils/fetched-tags.json b/utils/fetched-tags.json index 17b10c548..ed3ecbb4e 100644 --- a/utils/fetched-tags.json +++ b/utils/fetched-tags.json @@ -115,6 +115,11 @@ "SEAL/Initiative", "Certifications" ], + "/certs/changelog": [ + "SEAL/Initiative", + "Certifications", + "SFC" + ], "/certs/contributions": [ "SEAL/Initiative", "Certifications" @@ -133,22 +138,22 @@ "Certifications", "SFC" ], - "/certs/sfc-incident-response": [ + "/certs/sfc-identity-accounts": [ "SEAL/Initiative", "Certifications", "SFC" ], - "/certs/sfc-multisig-ops": [ + "/certs/sfc-incident-response": [ "SEAL/Initiative", "Certifications", "SFC" ], - "/certs/sfc-treasury-ops": [ + "/certs/sfc-multisig-ops": [ "SEAL/Initiative", "Certifications", "SFC" ], - "/certs/sfc-workspace-security": [ + "/certs/sfc-treasury-ops": [ "SEAL/Initiative", "Certifications", "SFC" diff --git a/utils/generate-cert-data.js b/utils/generate-cert-data.js index 989a02973..9c96b8ff0 100644 --- a/utils/generate-cert-data.js +++ b/utils/generate-cert-data.js @@ -17,10 +17,10 @@ const OUTPUT_PATH = path.join(__dirname, '../docs/public/cert-data.json'); const CERT_ORDER = [ { file: 'sfc-devops-infrastructure.mdx', label: 'DevOps & Infrastructure' }, { file: 'sfc-dns-registrar.mdx', label: 'DNS Registrar' }, + { file: 'sfc-identity-accounts.mdx', label: 'Identity & Accounts' }, { file: 'sfc-incident-response.mdx', label: 'Incident Response' }, { file: 'sfc-multisig-ops.mdx', label: 'Multisig Operations' }, { file: 'sfc-treasury-ops.mdx', label: 'Treasury Operations' }, - { file: 'sfc-workspace-security.mdx', label: 'Workspace Security' }, ]; function main() { diff --git a/utils/generate-printable-checklists.js b/utils/generate-printable-checklists.js index 14f940c40..4bd8663a4 100644 --- a/utils/generate-printable-checklists.js +++ b/utils/generate-printable-checklists.js @@ -28,8 +28,8 @@ const CERT_META = { 'sfc-incident-response': { subtitle: 'Detection, response procedures, communication, containment, recovery, and post-incident review.' }, - 'sfc-workspace-security': { - subtitle: 'Access management, device security, network controls, and data protection.' + 'sfc-identity-accounts': { + subtitle: 'Organizational account inventory, phishing-resistant MFA, credential management, account lifecycle, and takeover monitoring.' }, 'sfc-devops-infrastructure': { subtitle: 'CI/CD security, infrastructure hardening, secrets management, and deployment controls.' diff --git a/vocs.config.tsx b/vocs.config.tsx index 55576c952..fa81cbb5f 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -564,13 +564,14 @@ const config = { text: 'SEAL Certification Frameworks', collapsed: true, items: [ { text: 'DevOps & Infrastructure', link: '/certs/sfc-devops-infrastructure' }, { text: 'DNS Registrar', link: '/certs/sfc-dns-registrar' }, + { text: 'Identity & Accounts', link: '/certs/sfc-identity-accounts' }, { text: 'Incident Response', link: '/certs/sfc-incident-response' }, { text: 'Multisig Operations', link: '/certs/sfc-multisig-ops' }, { text: 'Treasury Operations', link: '/certs/sfc-treasury-ops' }, - { text: 'Workspace Security', link: '/certs/sfc-workspace-security' }, ] }, { text: 'Certification Guidelines', link: '/certs/certification-guidelines' }, + { text: 'Changelog', link: '/certs/changelog' }, { text: 'Contributions', link: '/certs/contributions' }, ] }, From 37fa29e1a581038d626156d60e13050d98a4b833 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Tue, 21 Apr 2026 17:26:02 +0200 Subject: [PATCH 10/92] Refreshing stewardship page (#460) --- docs/pages/contribute/index.mdx | 2 +- docs/pages/contribute/stewards.mdx | 199 ++++++++++++++++------------- 2 files changed, 110 insertions(+), 91 deletions(-) diff --git a/docs/pages/contribute/index.mdx b/docs/pages/contribute/index.mdx index 521257bf4..b493fd59e 100644 --- a/docs/pages/contribute/index.mdx +++ b/docs/pages/contribute/index.mdx @@ -13,4 +13,4 @@ title: "Contribute" - [Contributing Guide](/contribute/contributing) - [Spotlight Zone](/contribute/spotlight-zone) -- [Becoming a Framework Steward](/contribute/stewards) +- [Framework Stewardship](/contribute/stewards) diff --git a/docs/pages/contribute/stewards.mdx b/docs/pages/contribute/stewards.mdx index f6be1fb2f..050927bff 100644 --- a/docs/pages/contribute/stewards.mdx +++ b/docs/pages/contribute/stewards.mdx @@ -1,6 +1,6 @@ --- -title: "Becoming a Framework Steward | Security Alliance" -description: "Become a SEAL Framework Steward: champion and caretake a security framework. Rally collaborators, manage contributions, advocate for adoption, and shape Web3 security standards." +title: "Framework Stewardship | Security Alliance" +description: "Learn what it means to be a Security Frameworks steward: how the role is earned, what it involves, and how to get started." --- import { ContributeFooter, TagFilter, TagProvider } from '../../../components' @@ -11,94 +11,113 @@ import { ContributeFooter, TagFilter, TagProvider } from '../../../components' ## What is a Framework Steward? -A framework steward is the champion and caretaker for an individual security framework (most frameworks -here -> [https://frameworks.securityalliance.org](https://frameworks.securityalliance.org) are currently -available for adoption). This role goes beyond casual contribution. -It's about taking ownership and helping guide the framework's development through community engagement. - -## The Steward's Role - -A framework steward is a project management role, responsible for: - -- **Rallying collaborators**: Recruit contributors who share your passion for specific security challenges -- **Managing contributions**: Help triage [GitHub issues](https://github.com/security-alliance/frameworks/issues) and - coordinate improvements -- **Advocating for adoption**: Work with SEAL to promote your framework within your networks and the broader Web3 - community -- **Creating content**: Work with SEAL to write blog posts, host workshops, or share best practices related to your - framework -- **Representing the community**: Be a voice for practitioners who use and rely on these standards - -The core SEAL team will support you throughout this journey, helping you focus on specific challenges rather than -drowning in administrative tasks. - -## Why Become a Steward? - -### Recognition and Growth - -- **Earn achievement badges**: Receive public recognition with roles like Security Framework Ambassador or DAO - Safeguards Steward -- **Build your reputation**: Establish yourself as a thought leader in Web3 security -- **Develop new skills**: Gain experience in open-source governance, technical writing, and community building - -### Tangible Benefits - -- **Access exclusive events**: Receive tickets to security conferences and invite-only Security Alliance gatherings -- **Showcase your expertise**: Get featured through SEAL's official channels, including our - [blog](https://www.securityalliance.org/news) and [social media](https://twitter.com/_SEAL_Org) -- **Connect with peers**: Build relationships with other security professionals who share your interests - -### Lasting Impact - -- **Shape industry standards**: Help develop frameworks that could become foundational to Web3 security -- **Prevent security incidents**: Your work will directly contribute to a safer ecosystem -- **Leave a legacy**: Carve your name into the DNA of Web3 security practices for years to come - -## Stewardship in Action: What It Looks Like - -### Time Commitment - -Being a steward doesn't mean giving up your day job. We're looking for contributors who can dedicate approximately 3 -hours per week to their framework. This might include: - -- Reviewing [pull requests](https://github.com/security-alliance/frameworks/pulls) and GitHub issues -- Participating in sporadic steward meetings -- Creating occasional content or presentations -- Engaging with the community on [Discord](https://discord.gg/securityalliance) - -### Support Structure - -You won't be working alone. You will have: - -- Access to a dedicated channel on our [Discord server](https://discord.gg/securityalliance) -- Coordination calls with the SEAL team as needed -- Documentation templates and contribution guidelines -- Access to technical advisors when needed - -## How to Apply - -Ready to become a framework steward? Here's how to get started: - -1. Review the proposed frameworks at [frameworks.securityalliance.org](https://frameworks.securityalliance.org) and - identify which one aligns with your expertise and interests. -2. Join our [steward candidates Telegram channel](https://t.me/+Yd9OpSt1UvcyMjU5) and introduce yourself to let us know - which Framework you want to adopt. - -We're looking for diverse perspectives and experiences, and you don't need to have decades of experience. Passion, -dedication, and a willingness to learn are just as important. - -## Join Us in Building a Safer Web3 - -The "Adopt a Framework" campaign isn't just about improving documentation. You'll be part of a movement where security -becomes a shared responsibility across the Web3 ecosystem. - -By becoming a steward, you're taking an active role in preventing the next major hack, protecting user funds, and -ensuring that innovation can continue without compromising safety. - -We're just getting started, and we need your expertise. - -Have questions about the stewardship program or ideas for improving it? You can use the [potential stewards Telegram -channel](https://t.me/+Yd9OpSt1UvcyMjU5) for that too! 🙂 +A framework steward is the resident expert for one (or more) of the security domains covered by the +[Security Frameworks](https://frameworks.securityalliance.org). The role is a natural extension +of contribution: stewards are people who have demonstrated genuine depth in a specific area, +through the content they've written or reviewed, and who SEAL trusts to maintain the quality and +direction of that domain going forward. + +It is a technical role, but also a community one. Stewards bring a sense of ownership to their +domain, and being embedded in the contributor community often leads to finding and connecting +with people who care deeply about the same problems. + +## Why become a steward? + +Because what gets written here is what practitioners across Web3 will actually read and rely on. +When you become a steward, your approval is required before anything merges into your framework. +That is real influence over what the community considers best practice, not in a symbolic way, but +in a direct, technical one. + +Beyond that: the people you will meet are worth it. The stewards community is small and +deliberately kept that way. These are security professionals who care enough about the ecosystem +to put time into building something that matters. + +And finally, your work here compounds. The Security Frameworks are a living resource that +practitioners, teams, and organizations will keep coming back to. The depth you bring to your +framework keeps getting read long after the initial contribution. + +## How the role is earned + +Most stewards got here because they genuinely cared about a topic and wrote something useful. +The role is a recognition of that, not a prerequisite for it. + +There is no application form. The typical path looks like this: + +1. A contributor writes or reviews content for a specific framework, either independently or + as part of a broader effort. +2. We notice the quality and depth of that work, or the contributor reaches out expressing + interest in a more active role. +3. If there is mutual trust and the fit is clear, we designate them as steward: they are + added to the [contributors database](https://github.com/security-alliance/frameworks/blob/develop/docs/pages/config/contributors.json) with the `Framework-Steward` badge, and they get their + own entry in the [Spotlight Zone](/contribute/spotlight-zone), the public-facing recognition + page where stewards are listed alongside the frameworks they own. + +If you are interested, the most direct path is to start contributing to the framework you care +about. You can also reach out on [Discord](https://discord.gg/securityalliance) if you want to +discuss it first. + +## Responsibilities + +Once designated as steward for a framework, your responsibilities are focused: + +- **Review incoming contributions**: When a pull request touches your framework, you will be + tagged as a required reviewer. The PR will not be merged without your approval. You become the + quality and accuracy gate for your domain. +- **Help grow the framework**: The Security Frameworks are a work in progress, and many domains + still have room to grow. If you know people with the right expertise, bring them in, whether as + contributors to your framework or to the project more broadly. The goal is to make these + frameworks as broad and battle-tested as possible, and stewards are well-positioned to make + that happen. +- **Advise on structural decisions**: When changes that affect the organization or direction of + the repo are being considered, stewards are consulted as domain experts. Your input carries + weight on decisions that touch your framework. + +## What this looks like in practice + +**On GitHub**: You are tagged as a required reviewer on any PR that touches your framework. +Nothing merges without your sign-off. + +**On Discord**: Our [Discord server](https://discord.gg/securityalliance) has a dedicated +stewards channel. You will be added to it and pinged when a relevant PR comes in, when we +want your perspective on something, or when you want to share something more privately before +it goes to the broader community. It is also where you will meet the other stewards, a small +group of people who are genuinely invested in Web3 security and often worth knowing. + +**On the Spotlight Zone**: Your stewardship is publicly recognized on the +[Spotlight Zone](/contribute/spotlight-zone) page, where your profile appears alongside +the framework you own. + +## Open frameworks + +The following frameworks are currently looking for a steward. If any of these match your +expertise, this is a good place to start: + +- Awareness +- DevSecOps +- DPRK IT Workers +- Encryption +- External Security Reviews +- Front-End & Web App Security +- Governance +- IAM +- Infrastructure +- Multisig for Protocols +- Privacy +- Secure Software Development +- Security Automation +- Supply Chain +- Threat Modeling +- Treasury Operations +- User & Team Security +- Vulnerability Disclosure + +If none of these match your expertise but you see a gap that isn't covered anywhere on the site, +that is worth something too. A new framework can start small, a single page or a rough outline, +and grow from there through iteration. If you have a topic in mind, reach out on +[Discord](https://discord.gg/securityalliance) and we can figure out together whether it makes +sense to build it out. + +Otherwise, start contributing to whichever framework fits your background. Either way works. --- From 4c4f894f350de0909fe1906af0a8f542f055e872 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Tue, 21 Apr 2026 17:27:18 +0200 Subject: [PATCH 11/92] Add footer component to add the license in all the pages (#458) --- docs/footer.tsx | 7 +++++++ docs/styles.css | 9 +++++++++ 2 files changed, 16 insertions(+) create mode 100644 docs/footer.tsx diff --git a/docs/footer.tsx b/docs/footer.tsx new file mode 100644 index 000000000..0ca1dd9cc --- /dev/null +++ b/docs/footer.tsx @@ -0,0 +1,7 @@ +export default function Footer() { + return ( +
+
Security Frameworks © 2023 by Security Alliance, licensed under CC BY-SA 4.0.
+
+ ) +} diff --git a/docs/styles.css b/docs/styles.css index 19ef3d614..ba80044ca 100644 --- a/docs/styles.css +++ b/docs/styles.css @@ -248,3 +248,12 @@ details .vocs_Paragraph { } } +.footer { + align-items: center; + color: var(--vocs-color_text3); + display: flex; + flex-direction: column; + font-size: 14px; + line-height: 1.5em; + width: 100%; +} \ No newline at end of file From 2d9a98960082f03efea269a8a2ad7fa4197e7bd0 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Tue, 21 Apr 2026 17:31:23 +0200 Subject: [PATCH 12/92] making the contributors display more compact to favor UX + sync domains alphabetical order in fetched tags (#441) --- components/contributors/BadgeDisplay.css | 95 +++++++++++ components/contributors/BadgeDisplay.tsx | 156 +++++++++++------- components/contributors/Contributors.css | 200 +++++++++++++++++++---- components/contributors/Contributors.tsx | 62 ++++--- docs/pages/contribute/spotlight-zone.mdx | 2 + 5 files changed, 395 insertions(+), 120 deletions(-) diff --git a/components/contributors/BadgeDisplay.css b/components/contributors/BadgeDisplay.css index b2abcfb4b..8e34c75f5 100644 --- a/components/contributors/BadgeDisplay.css +++ b/components/contributors/BadgeDisplay.css @@ -60,6 +60,101 @@ align-items: stretch; } +/* Overflow chip */ +.badge-overflow-chip { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 48px; + height: 48px; + border-radius: 12px; + border: 2px dashed var(--theme-popup-border); + background: var(--quote-bg, rgba(0, 0, 0, 0.05)); + font-size: 0.6875rem; + font-weight: 700; + color: var(--fg); + opacity: 0.7; + cursor: default; + transition: opacity 0.2s ease, border-color 0.2s ease; + z-index: 1; +} + +.badge-overflow-chip:hover { + opacity: 1; + border-color: var(--vocs-color_codeInlineBg); + z-index: 99999; +} + +.badge-overflow-chip::before { + content: ''; + position: absolute; + bottom: 100%; + left: -8px; + right: -8px; + height: 14px; +} + +.badge-overflow-tooltip { + position: absolute; + bottom: calc(100% + 14px); + left: 50%; + transform: translateX(-50%); + background: linear-gradient(135deg, #1f2937 0%, #111827 100%); + padding: 0.625rem; + border-radius: 10px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(255, 255, 255, 0.1); + z-index: 999999; + opacity: 0; + visibility: hidden; +} + +.badge-overflow-tooltip { width: max-content; } + +.badge-overflow-tooltip::before { + content: ''; + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + border: 7px solid transparent; + border-top-color: #111827; +} + +.badge-overflow-chip:hover .badge-overflow-tooltip, +.badge-overflow-tooltip:hover { + opacity: 1; + visibility: visible; +} + +.badge-overflow-tooltip-grid { + display: grid; + gap: 0.5rem; + justify-items: center; +} + +.badge-overflow-tooltip-grid[data-cols="1"] { grid-template-columns: repeat(1, 48px); } +.badge-overflow-tooltip-grid[data-cols="2"] { grid-template-columns: repeat(2, 48px); } +.badge-overflow-tooltip-grid[data-cols="3"] { grid-template-columns: repeat(3, 48px); } +.badge-overflow-tooltip-grid[data-cols="4"] { grid-template-columns: repeat(4, 48px); } + +.badge-overflow-tooltip .badge-wrapper { + z-index: 1; +} + +.badge-overflow-tooltip .badge-wrapper:hover { + z-index: 99999; +} + +:root:not(.dark) .badge-overflow-tooltip { + background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%); + box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12), 0 0 0 1px rgba(15, 23, 42, 0.08); +} + +:root:not(.dark) .badge-overflow-tooltip::before { + border-top-color: #ffffff; +} + /* Badge Wrapper */ .badge-wrapper { position: relative; diff --git a/components/contributors/BadgeDisplay.tsx b/components/contributors/BadgeDisplay.tsx index a85643b9f..798e49394 100644 --- a/components/contributors/BadgeDisplay.tsx +++ b/components/contributors/BadgeDisplay.tsx @@ -71,6 +71,8 @@ function getBadgeDateLabel(badge: Badge): 'Earned' | 'Last active' { return badge.lastActive ? 'Last active' : 'Earned'; } +const COMPACT_MAX_VISIBLE = 5; + interface BadgeDisplayProps { contributorSlug?: string; badges?: Badge[]; @@ -79,6 +81,66 @@ interface BadgeDisplayProps { layout?: 'grid' | 'stack'; } +function BadgeCard({ badge, index, compact }: { badge: Badge; index: number; compact: boolean }) { + const config = getBadgeConfig(badge.name); + const effectiveDate = getBadgeDate(badge); + const dateLabel = getBadgeDateLabel(badge); + const isNew = isNewlyEarned(effectiveDate); + const badgeDate = formatDate(effectiveDate); + const badgeKey = `${badge.name}-${badge.framework || ''}-${index}`; + const badgeLabel = badge.name === 'Framework-Steward' ? 'Steward' : config.label; + const badgeDescription = badge.framework && badge.name === 'Framework-Steward' + ? `Steward of the ${badge.framework} framework` + : config.description; + + return ( +
+
+ + {isNew && ( +
+ +
+ )} + {!compact && ( +
+ {config.tier === 'legendary' && '👑'} + {config.tier === 'epic' && '💎'} + {config.tier === 'rare' && '⭐'} +
+ )} +
+ +
+
+ {badgeLabel} + + {config.tier?.toUpperCase()} + +
+

{badgeDescription}

+ {badgeDate && ( +
+ + {isNew && ✨ NEW} + {dateLabel} {badgeDate} + +
+ )} +
+
+ ); +} + export function BadgeDisplay({ contributorSlug, badges, @@ -93,7 +155,7 @@ export function BadgeDisplay({ if (badges) { displayBadges = badges.filter(b => b.name && b.name.trim() !== ''); } else if (contributorSlug) { - const contributors = contributorsData as Record; + const contributors = contributorsData as unknown as Record; const contributor = contributors[contributorSlug]; if (contributor?.badges) { displayBadges = contributor.badges.filter(b => b.name && b.name.trim() !== ''); @@ -102,12 +164,22 @@ export function BadgeDisplay({ if (displayBadges.length === 0) return null; - // Sort badges chronologically by date (newest first) - const sortedBadges = [...displayBadges].sort((a, b) => { - const dateA = new Date(getBadgeDate(a) || '1970-01-01').getTime(); - const dateB = new Date(getBadgeDate(b) || '1970-01-01').getTime(); - return dateB - dateA; - }); + const byDateDesc = (a: Badge, b: Badge) => + new Date(getBadgeDate(b) || '1970-01-01').getTime() - new Date(getBadgeDate(a) || '1970-01-01').getTime(); + + let visibleBadges: Badge[]; + let hiddenBadges: Badge[]; + + if (compact) { + const roleBadges = displayBadges.filter(b => getBadgeConfig(b.name).category === 'role').sort(byDateDesc); + const nonRoleBadges = displayBadges.filter(b => getBadgeConfig(b.name).category !== 'role').sort(byDateDesc); + const nonRoleSlots = Math.max(0, COMPACT_MAX_VISIBLE - roleBadges.length); + visibleBadges = [...nonRoleBadges.slice(0, nonRoleSlots), ...roleBadges]; + hiddenBadges = nonRoleBadges.slice(nonRoleSlots); + } else { + visibleBadges = [...displayBadges].sort(byDateDesc); + hiddenBadges = []; + } return (
- {sortedBadges.map((badge, index) => { - const config = getBadgeConfig(badge.name); - const effectiveDate = getBadgeDate(badge); - const dateLabel = getBadgeDateLabel(badge); - const isNew = isNewlyEarned(effectiveDate); - const badgeDate = formatDate(effectiveDate); - const badgeKey = `${badge.name}-${badge.framework || ''}-${index}`; - const badgeLabel = badge.name === 'Framework-Steward' - ? 'Steward' - : config.label; - const badgeDescription = badge.framework && badge.name === 'Framework-Steward' - ? `Steward of the ${badge.framework} framework` - : config.description; - + {visibleBadges.map((badge, index) => ( + + ))} + {hiddenBadges.length > 0 && (() => { + const count = hiddenBadges.length; + const cols = count <= 3 ? count : count === 4 ? 2 : 4; return ( -
-
- - {isNew && ( -
- -
- )} - {!compact && ( -
- {config.tier === 'legendary' && '👑'} - {config.tier === 'epic' && '💎'} - {config.tier === 'rare' && '⭐'} -
- )} -
- -
-
- {badgeLabel} - - {config.tier?.toUpperCase()} - +
+ +{count} +
+
+ {hiddenBadges.map((badge, index) => ( + + ))}
-

{badgeDescription}

- {badgeDate && ( -
- - {isNew && ✨ NEW} - {dateLabel} {badgeDate} - -
- )}
); - })} + })()}
); diff --git a/components/contributors/Contributors.css b/components/contributors/Contributors.css index 2e51111a3..b3dc1e0f9 100644 --- a/components/contributors/Contributors.css +++ b/components/contributors/Contributors.css @@ -122,8 +122,8 @@ /* Grid Layout */ .contributors-page-list { display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); - gap: 1.5rem; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 1rem; padding: 0 0.5rem; max-width: 1400px; } @@ -133,7 +133,7 @@ background: var(--sidebar-bg); border: 1px solid var(--theme-popup-border); border-radius: 6px; - padding: 1rem; + padding: 0.75rem 0.625rem; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); transition: all 0.2s ease, z-index 0s 0.2s; display: flex; @@ -142,8 +142,8 @@ text-align: center; font-size: 0.875rem; position: relative; - min-height: 280px; - min-width: 250px; + min-height: 0; + min-width: 0; isolation: isolate; } @@ -170,11 +170,11 @@ } .contributors-page-avatar { - width: 80px; - height: 80px; + width: 60px; + height: 60px; border-radius: 50%; object-fit: cover; - border: 3px solid var(--theme-popup-border); + border: 2px solid var(--theme-popup-border); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); background-color: var(--sidebar-bg); transition: all 0.3s ease; @@ -184,15 +184,15 @@ .avatar-badge { position: absolute; bottom: -4px; - right: calc(50% - 44px - 8px); - width: 32px; - height: 32px; + right: calc(50% - 34px - 6px); + width: 26px; + height: 26px; display: flex; align-items: center; justify-content: center; - font-size: 1.125rem; + font-size: 0.875rem; background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%); - border: 3px solid var(--sidebar-bg); + border: 2px solid var(--sidebar-bg); border-radius: 50%; box-shadow: 0 4px 12px rgba(251, 191, 36, 0.4); z-index: 3; @@ -207,9 +207,9 @@ .role-badge-overlay { position: absolute; bottom: -4px; - right: calc(50% - 48px); - width: 32px; - height: 32px; + right: calc(50% - 38px); + width: 26px; + height: 26px; z-index: 3; cursor: pointer; } @@ -293,14 +293,14 @@ display: flex; flex-direction: column; align-items: center; - gap: 0.5rem; + gap: 0.25rem; width: 100%; - margin-top: 10px; + margin-top: 6px; } .contributors-page-name { font-weight: 600; - font-size: 1.1rem; + font-size: 0.875rem; word-break: break-word; color: var(--fg); line-height: 1.3; @@ -410,9 +410,9 @@ .contributors-page-social { display: flex; - gap: 0.6rem; + gap: 0.5rem; justify-content: center; - margin: 0.8rem 0 0.3rem; + margin: 0.5rem 0 0.2rem; } .contributor-link { @@ -472,10 +472,28 @@ justify-content: center; } -/* Elevate card when badge tooltip is shown */ -.contributors-page-card:has(.badge-wrapper:hover) { +/* Elevate card when any tooltip/popup is active */ +.contributors-page-card:has(.badge-wrapper:hover), +.contributors-page-card:has(.badge-overflow-chip:hover), +.contributors-page-card:has(.role-badge-overlay:hover), +.contributors-page-card:has(.avatar-wrapper:hover), +.contributors-page-card:has(.contributor-hover-card:hover) { z-index: 99999; transition-delay: 0s; + isolation: auto; +} + + +/* Push role overlay behind tooltips when badges or overflow chip are hovered */ +.contributors-page-card:has(.badge-wrapper:hover) .role-badge-overlay, +.contributors-page-card:has(.badge-overflow-chip:hover) .role-badge-overlay { + z-index: 0; +} + +/* Ensure badges section sits above the avatar section within the card */ +.contributors-page-badges { + position: relative; + z-index: 2; } @@ -521,6 +539,120 @@ border-bottom: 1px solid #ffffff17; } +/* Contributor hover card (shown when hovering avatar) */ +.contributor-hover-card { + position: absolute; + /* sit right next to the avatar: 50% of avatar-wrapper (card center) + half-avatar (30px) */ + left: calc(50% + 50px); + top: 50%; + transform: translateY(-50%); + width: 230px; + background: linear-gradient(135deg, #1f2937 0%, #111827 100%); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 10px; + padding: 0.875rem; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.06); + z-index: 999999; + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity 0.18s ease, visibility 0.18s ease; +} + + +/* Show when avatar is hovered — but suppress if the role badge is specifically hovered */ +.avatar-wrapper:hover:not(:has(.role-badge-overlay:hover)) .contributor-hover-card, +.contributor-hover-card:hover { + opacity: 1; + visibility: visible; + pointer-events: auto; +} + +.hover-card-header { + display: flex; + align-items: center; + gap: 0.625rem; + margin-bottom: 0.625rem; +} + +.hover-card-avatar { + width: 40px; + height: 40px; + border-radius: 50%; + object-fit: cover; + border: 2px solid rgba(255, 255, 255, 0.15); + flex-shrink: 0; +} + +.hover-card-identity { + display: flex; + flex-direction: column; + gap: 0.2rem; + min-width: 0; +} + +.hover-card-name { + font-weight: 700; + font-size: 0.875rem; + color: #f9fafb; + line-height: 1.2; +} + +.hover-card-job-title { + font-size: 0.7rem; + color: #9ca3af; + font-style: italic; + line-height: 1.2; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.hover-card-company { + display: inline-block; + font-size: 0.6875rem; + font-weight: 600; + color: #d1d5db; + background: rgba(255, 255, 255, 0.08); + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 4px; + padding: 0.2rem 0.5rem; + margin-bottom: 0.5rem; +} + +.hover-card-steward { + font-size: 0.6875rem; + font-weight: 600; + color: #60a5fa; + margin-bottom: 0.5rem; +} + +.hover-card-description { + font-size: 0.75rem; + color: #9ca3af; + line-height: 1.5; + margin: 0; + border-top: 1px solid rgba(255, 255, 255, 0.07); + padding-top: 0.5rem; +} + +:root:not(.dark) .contributor-hover-card { + background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%); + border-color: rgba(15, 23, 42, 0.08); + box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12), 0 0 0 1px rgba(15, 23, 42, 0.06); +} + +:root:not(.dark) .hover-card-name { color: #111827; } +:root:not(.dark) .hover-card-job-title { color: #6b7280; } +:root:not(.dark) .hover-card-company { + color: #374151; + background: rgba(15, 23, 42, 0.05); + border-color: rgba(15, 23, 42, 0.1); +} +:root:not(.dark) .hover-card-steward { color: #2563eb; } +:root:not(.dark) .hover-card-description { color: #4b5563; border-top-color: rgba(15, 23, 42, 0.08); } +:root:not(.dark) .hover-card-avatar { border-color: rgba(15, 23, 42, 0.15); } + /* Corner Accent for elite contributors */ .corner-accent { position: absolute; @@ -574,25 +706,25 @@ } .contributors-page-list { - grid-template-columns: 1fr; - gap: 1.5rem; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 0.75rem; padding: 0; } .contributors-page-card { - padding: 1.5rem; + padding: 0.625rem 0.5rem; } .contributors-page-avatar { - width: 72px; - height: 72px; + width: 52px; + height: 52px; } .avatar-badge { - right: calc(50% - 36px - 8px); - width: 28px; - height: 28px; - font-size: 1rem; + right: calc(50% - 30px - 6px); + width: 22px; + height: 22px; + font-size: 0.75rem; } .contributors-page-badges { @@ -603,8 +735,8 @@ @media (min-width: 769px) and (max-width: 1024px) { .contributors-page-list { - grid-template-columns: repeat(2, 1fr); - gap: 1.5rem; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 0.875rem; } .stat-number { diff --git a/components/contributors/Contributors.tsx b/components/contributors/Contributors.tsx index a4cdadc0f..20fbf08bf 100644 --- a/components/contributors/Contributors.tsx +++ b/components/contributors/Contributors.tsx @@ -82,6 +82,39 @@ function RoleBadgeOverlay({ role, badges }: { role: string; badges: Badge[] }) { ); } +function ContributorHoverCard({ contributor }: { contributor: Contributor }) { + const hasContent = contributor.company || contributor.job_title || contributor.description; + if (!hasContent) return null; + const frameworks = getStewardFrameworks(contributor.badges || []); + return ( +
+
+ {contributor.name} +
+
{contributor.name}
+ {contributor.job_title && ( +
{contributor.job_title}
+ )} +
+
+ {contributor.company && ( +
{contributor.company}
+ )} + {frameworks.length > 0 && ( +
Steward of {formatFrameworks(frameworks)}
+ )} + {contributor.description && ( +

{contributor.description}

+ )} +
+ ); +} + // Helper component for steward info function StewardInfo({ badges }: { badges: Badge[] }) { const frameworks = getStewardFrameworks(badges); @@ -160,7 +193,7 @@ function slugify(text: string): string { export function Contributors() { // Convert JSON object to array - const contributors = Object.values(contributorsData as Record); + const contributors = Object.values(contributorsData as unknown as Record); const groups: ContributorGroup[] = [ { @@ -215,7 +248,7 @@ export function Contributors() { id={contributor.name.toLowerCase().replace(/ /g, '-').replace(/[^a-z0-9_-]/g, '')} style={{ '--card-index': index } as React.CSSProperties} > - {/* Avatar with role badge */} + {/* Avatar with role badge and profile hover card */}
)} +
{/* Header with name */} @@ -235,28 +269,10 @@ export function Contributors() {
- {/* Content area - flexible section */} -
- {/* Company */} -
- {contributor.company || '\u00A0'} -
- - {/* Job Title */} -
- {contributor.job_title || '\u00A0'} -
- - {/* Steward info */} + {/* Steward info */} + {contributor.role === 'steward' && ( - - {/* Description - only show for non-stewards */} - {contributor.description && contributor.role !== "steward" && ( -
- {contributor.description} -
- )} -
+ )} {/* Badges display */} {contributor.badges && contributor.badges.length > 0 && ( diff --git a/docs/pages/contribute/spotlight-zone.mdx b/docs/pages/contribute/spotlight-zone.mdx index b7ce23687..66feb1f83 100644 --- a/docs/pages/contribute/spotlight-zone.mdx +++ b/docs/pages/contribute/spotlight-zone.mdx @@ -11,6 +11,8 @@ import { Contributors, ContributeFooter, TagFilter, TagProvider, BadgeLegend } f This is the current list of individuals who have made substantial contributions to the project and deserve recognition. +To understand what each badge represents, refer to the [Badge Legend](#badge-legend) at the bottom of this page. + From 31c808d03db2de00fb3edb3ac4e42bb36f2f926a Mon Sep 17 00:00:00 2001 From: Elliot <34463580+ElliotFriedman@users.noreply.github.com> Date: Tue, 21 Apr 2026 08:35:09 -0700 Subject: [PATCH 13/92] Add Attack Surface Overview page (#435) * Add Attack Surface Overview page with interactive radial threat map Visual security posture dashboard showing 12 attack vectors as a radial diagram. Nodes are color-coded (red/amber/green) by posture state with click-to-toggle and localStorage persistence. Clicking a node opens a detail card with description, attack tags, and framework guide links. Designed for CSOs to quickly assess and communicate security gaps. Co-Authored-By: Claude Opus 4.6 (1M context) * Mark Attack Surface Overview as dev content Adds dev: true flag to sidebar entry per contributing guidelines. Co-Authored-By: Claude Opus 4.6 (1M context) * Replace selection ring with scale + glow effect on selected nodes Selected nodes now scale up 10% and show a soft color-matched glow instead of a detached ring outline. Co-Authored-By: Claude Opus 4.6 (1M context) * Add GitHub Actions reminder for attack surface threat data changes Posts an automated PR comment when threatData.ts is modified, reminding contributors to include all required fields and verify framework links. Follows the same pattern as the existing vocs-config-reminder workflow. Co-Authored-By: Claude Opus 4.6 (1M context) * Address Sara first PR feedback item Co-Authored-By: Claude Opus 4.6 (1M context) * Address Sara second PR feedback item Co-Authored-By: Claude Opus 4.6 (1M context) * Smooth-scroll detail card into view when a threat node is clicked Clicking a node on the radial map showed the detail card below the map, but on typical viewports the card landed below the fold, so it wasn't obvious anything had happened. Use scrollIntoView with block: "nearest" on the card when it mounts or the selected vector changes, respecting prefers-reduced-motion. Third PR feedback item addressed. Co-Authored-By: Claude Opus 4.6 (1M context) * Move attack-surface.mdx into intro/ folder and update links Moved the page to sit alongside the other Introduction pages. Updated sidebar link, internal cross-link in how-to-navigate, and component import path to match the new location. Co-Authored-By: Claude Opus 4.6 (1M context) * Use react-router Link for framework CTA to avoid full page reload The detail card's framework link used a plain tag which caused a full page reload. Switched to react-router-dom's for client-side navigation, consistent with the rest of the site. Co-Authored-By: Claude Opus 4.6 (1M context) * Add standard page components to Attack Surface Overview Adds TagProvider, TagFilter, TagList, and ContributeFooter to match the pattern used by all other Introduction pages. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- components/attack-surface/AttackSurface.css | 405 ++++++++++++++++++ .../attack-surface/AttackSurfaceDashboard.tsx | 343 +++++++++++++++ components/attack-surface/threatData.ts | 259 +++++++++++ components/index.ts | 1 + docs/pages/intro/attack-surface.mdx | 26 ++ .../intro/how-to-navigate-the-website.mdx | 2 + vocs.config.tsx | 1 + 7 files changed, 1037 insertions(+) create mode 100644 components/attack-surface/AttackSurface.css create mode 100644 components/attack-surface/AttackSurfaceDashboard.tsx create mode 100644 components/attack-surface/threatData.ts create mode 100644 docs/pages/intro/attack-surface.mdx diff --git a/components/attack-surface/AttackSurface.css b/components/attack-surface/AttackSurface.css new file mode 100644 index 000000000..32637cf7c --- /dev/null +++ b/components/attack-surface/AttackSurface.css @@ -0,0 +1,405 @@ +/* Attack Surface — Radial Threat Map */ + +.as-wrap { + max-width: 860px; + margin: 0 auto; +} + +/* Summary Bar */ +.as-summary-bar { + display: flex; + align-items: center; + gap: 20px; + flex-wrap: wrap; + margin-bottom: 12px; + font-size: 14px; + color: var(--color-text-muted); +} + +.as-summary-count { + font-weight: 600; + color: var(--color-text-strong); + font-size: 15px; +} + +.as-summary-legend { + display: flex; + gap: 14px; + align-items: center; +} + +.as-legend-item { + display: flex; + align-items: center; + gap: 5px; + font-size: 13px; +} + +.as-legend-dot { + width: 10px; + height: 10px; + border-radius: 50%; + flex-shrink: 0; +} + +/* SVG Map Container — 10% bigger */ +.as-map { + width: 100%; + max-width: 770px; + margin: 0 auto; + display: block; +} + +.as-map svg { + width: 100%; + height: auto; +} + +/* Spokes */ +.as-spoke { + stroke-width: 2; + transition: stroke 0.3s ease; +} + +/* Hub */ +.as-hub { + fill: var(--color-primary); + transition: fill 0.3s ease; +} + +.as-hub-label { + fill: #ffffff; + font-size: 13px; + font-weight: 600; + pointer-events: none; + user-select: none; +} + +/* Nodes */ +.as-node-group { + cursor: pointer; + transition: transform 0.2s ease, filter 0.2s ease; + transform-box: fill-box; + transform-origin: center; +} + +.as-node { + cursor: pointer; + transition: fill 0.3s ease, stroke 0.3s ease; +} + +.as-node-group:hover .as-node { + filter: brightness(1.15); +} + +.as-node-group.selected { + transform: scale(1.1); +} + +.as-node-group.selected.state-no { + filter: drop-shadow(0 0 12px rgba(239, 68, 68, 0.6)); +} + +.as-node-group.selected.state-yes { + filter: drop-shadow(0 0 12px rgba(16, 185, 129, 0.6)); +} + +.as-node-group.selected.state-partial { + filter: drop-shadow(0 0 12px rgba(245, 158, 11, 0.6)); +} + +.as-node-check { + fill: #ffffff; + font-size: 18px; + font-weight: 700; + pointer-events: none; + user-select: none; +} + +.as-node-label { + fill: var(--color-text-primary); + font-size: 11px; + font-weight: 500; + user-select: none; + text-anchor: middle; + cursor: pointer; + transition: fill 0.15s ease; +} + +.as-node-group:hover .as-node-label { + fill: var(--color-primary); +} + +/* State colors */ +.as-node.state-no { fill: #ef4444; } +.as-node.state-yes { fill: #10b981; } +.as-node.state-partial { fill: #f59e0b; } + +.as-spoke.state-no { stroke: rgba(239, 68, 68, 0.3); } +.as-spoke.state-yes { stroke: rgba(16, 185, 129, 0.3); } +.as-spoke.state-partial { stroke: rgba(245, 158, 11, 0.3); } + +/* Critical pulse for unaddressed critical nodes */ +@keyframes as-pulse { + 0%, 100% { filter: drop-shadow(0 0 4px rgba(239, 68, 68, 0.4)); } + 50% { filter: drop-shadow(0 0 12px rgba(239, 68, 68, 0.7)); } +} + +.as-node-group.critical.state-no .as-node { + animation: as-pulse 2.5s ease-in-out infinite; +} + +/* ===== Detail Card ===== */ +.as-detail { + margin-top: 24px; + padding: 24px 28px; + background: var(--bg-card); + border: 1px solid var(--border-card); + border-radius: 8px; + animation: as-slide-in 0.2s ease-out; +} + +@keyframes as-slide-in { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: translateY(0); } +} + +.as-detail-top { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + margin-bottom: 14px; +} + +.as-detail-left { + flex: 1; + min-width: 0; +} + +.as-detail-title { + font-size: 22px; + font-weight: 700; + color: var(--color-text-strong); + margin: 0 0 4px; +} + +.as-detail-sev { + display: flex; + align-items: center; + gap: 6px; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.06em; +} + +.as-detail-sev-dot { + width: 8px; + height: 8px; + border-radius: 50%; +} + +.as-detail-right { + display: flex; + align-items: center; + gap: 12px; + flex-shrink: 0; +} + +/* GAP / IN PROGRESS / SECURED toggle */ +.as-detail-toggle { + display: flex; + border: 1px solid var(--border-card); + border-radius: 4px; + overflow: hidden; +} + +.as-toggle-btn { + padding: 7px 16px; + font-size: 12px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + cursor: pointer; + border: none; + background: transparent; + color: var(--color-text-muted); + transition: all 0.15s ease; +} + +.as-toggle-btn:not(:last-child) { + border-right: 1px solid var(--border-card); +} + +.as-toggle-btn:hover { + background: rgba(255, 255, 255, 0.05); +} + +:root:not(.dark) .as-toggle-btn:hover { + background: rgba(0, 0, 0, 0.03); +} + +.as-toggle-btn.active.state-no { + background: #ef4444; + color: #ffffff; +} + +.as-toggle-btn.active.state-partial { + background: #f59e0b; + color: #ffffff; +} + +.as-toggle-btn.active.state-yes { + background: #10b981; + color: #ffffff; +} + +.as-detail-close { + background: none; + border: none; + color: var(--color-text-muted); + cursor: pointer; + font-size: 22px; + padding: 0 4px; + line-height: 1; + transition: color 0.15s ease; +} + +.as-detail-close:hover { + color: var(--color-text-strong); +} + +/* Description */ +.as-detail-desc { + font-size: 15px; + line-height: 1.6; + color: var(--color-text-muted); + margin: 0 0 16px; +} + +/* Bottom row: tags + CTA link */ +.as-detail-bottom { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 16px; + flex-wrap: wrap; +} + +.as-detail-tags { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.as-attack-tag { + font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; + font-size: 12px; + padding: 4px 12px; + border: 1px solid var(--border-card); + border-radius: 4px; + color: var(--color-text-muted); + white-space: nowrap; +} + +.as-detail-cta { + font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; + font-size: 13px; + padding: 8px 20px; + border: 1px solid var(--color-primary); + border-radius: 4px; + color: var(--color-primary); + text-decoration: none; + white-space: nowrap; + transition: all 0.15s ease; + flex-shrink: 0; +} + +.as-detail-cta:hover { + background: rgba(67, 57, 219, 0.08); +} + +/* ===== Mobile list ===== */ +.as-mobile-list { + display: none; + flex-direction: column; + gap: 8px; +} + +.as-mobile-row { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 14px; + background: var(--bg-card); + border: 1px solid var(--border-card); + border-radius: 8px; + cursor: pointer; + transition: background 0.15s ease; +} + +.as-mobile-row:hover { + background: rgba(67, 57, 219, 0.04); +} + +.as-mobile-row.selected { + border-color: var(--color-text-strong); +} + +.as-mobile-dot { + width: 20px; + height: 20px; + border-radius: 50%; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + font-weight: 700; + color: #ffffff; + cursor: pointer; + transition: background 0.2s ease; +} + +.as-mobile-dot.state-no { background: #ef4444; } +.as-mobile-dot.state-yes { background: #10b981; } +.as-mobile-dot.state-partial { background: #f59e0b; } + +.as-mobile-name { + font-size: 14px; + font-weight: 500; + color: var(--color-text-strong); + flex: 1; +} + +.as-mobile-sev { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + padding: 2px 8px; + border-radius: 4px; + flex-shrink: 0; +} + +@media (max-width: 600px) { + .as-map { display: none; } + .as-mobile-list { display: flex; } + + .as-detail-top { + flex-direction: column; + } + + .as-detail-bottom { + flex-direction: column; + align-items: flex-start; + } +} + +/* Print */ +@media print { + .as-detail-close, + .as-detail-toggle { display: none; } + .as-mobile-dot { print-color-adjust: exact; -webkit-print-color-adjust: exact; } + .as-node { print-color-adjust: exact; -webkit-print-color-adjust: exact; } +} diff --git a/components/attack-surface/AttackSurfaceDashboard.tsx b/components/attack-surface/AttackSurfaceDashboard.tsx new file mode 100644 index 000000000..bc47ece68 --- /dev/null +++ b/components/attack-surface/AttackSurfaceDashboard.tsx @@ -0,0 +1,343 @@ +import { useState, useCallback, useEffect, useMemo, useRef } from "react"; +import { Link } from "react-router-dom"; +import { threatVectors, severityMeta, type PostureState, type ThreatVector } from "./threatData"; +import "./AttackSurface.css"; + +const STORAGE_KEY = "attackSurface-posture"; +const CX = 420; +const CY = 420; +const RADIUS = 286; +const NODE_R = 39; +const HUB_R = 39; + +type PostureMap = Record; + +const nextState: Record = { + no: "yes", + yes: "partial", + partial: "no", +}; + +const stateColors: Record = { + no: "#ef4444", + yes: "#10b981", + partial: "#f59e0b", +}; + +const spokeColors: Record = { + no: "rgba(239, 68, 68, 0.25)", + yes: "rgba(16, 185, 129, 0.25)", + partial: "rgba(245, 158, 11, 0.25)", +}; + +const checkMarks: Record = { + no: "", + yes: "\u2713", + partial: "", +}; + +const postureLabels: Record = { + no: "Gap", + yes: "Secured", + partial: "In Progress", +}; + +function loadPosture(): PostureMap { + try { + const raw = localStorage.getItem(STORAGE_KEY); + return raw ? JSON.parse(raw) : {}; + } catch { + return {}; + } +} + +function savePosture(posture: PostureMap) { + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(posture)); + } catch {} +} + +function wrapLabel(title: string): string[] { + const words = title.split(" "); + const lines: string[] = []; + let current = ""; + for (const word of words) { + if (current && (current + " " + word).length > 16) { + lines.push(current); + current = word; + } else { + current = current ? current + " " + word : word; + } + } + if (current) lines.push(current); + return lines; +} + +function nodePosition(index: number, total: number) { + const angle = ((2 * Math.PI) / total) * index - Math.PI / 2; + return { + x: CX + RADIUS * Math.cos(angle), + y: CY + RADIUS * Math.sin(angle), + }; +} + +export function AttackSurfaceDashboard() { + const [posture, setPosture] = useState({}); + const [selected, setSelected] = useState(null); + + useEffect(() => { + setPosture(loadPosture()); + }, []); + + const handleToggle = useCallback((id: string) => { + setPosture((prev) => { + const current = prev[id] || "no"; + const next = { ...prev, [id]: nextState[current] }; + if (nextState[current] === "no") delete next[id]; + savePosture(next); + return next; + }); + }, []); + + const handleSetPosture = useCallback((id: string, state: PostureState) => { + setPosture((prev) => { + const next = { ...prev, [id]: state }; + if (state === "no") delete next[id]; + savePosture(next); + return next; + }); + }, []); + + const handleSelect = useCallback((id: string) => { + setSelected((prev) => (prev === id ? null : id)); + }, []); + + const counts = useMemo(() => { + const values = threatVectors.map((v) => posture[v.id] || "no"); + return { + yes: values.filter((s) => s === "yes").length, + partial: values.filter((s) => s === "partial").length, + no: values.filter((s) => s === "no").length, + }; + }, [posture]); + + const selectedVector = selected + ? threatVectors.find((v) => v.id === selected) || null + : null; + + return ( +
+ {/* Summary */} +
+ + {counts.yes + counts.partial} of {threatVectors.length} vectors covered + +
+ + + {counts.yes} secured + + + + {counts.partial} in progress + + + + {counts.no} gaps + +
+
+ + {/* SVG Radial Map */} +
+ + {/* Spokes */} + {threatVectors.map((v, i) => { + const pos = nodePosition(i, threatVectors.length); + const state = posture[v.id] || "no"; + return ( + + ); + })} + + {/* Hub */} + + + Your + + + Protocol + + + {/* Nodes */} + {threatVectors.map((v, i) => { + const pos = nodePosition(i, threatVectors.length); + const state = posture[v.id] || "no"; + const isCritical = v.severity === "critical"; + const isSelected = selected === v.id; + const lines = wrapLabel(v.title); + + return ( + handleSelect(v.id)} + style={{ cursor: "pointer" }} + > + {/* Main node */} + + {/* Check mark */} + + {checkMarks[state]} + + {/* Label */} + {lines.map((line, li) => ( + + {line} + + ))} + + ); + })} + +
+ + {/* Mobile list fallback */} +
+ {threatVectors.map((v) => { + const state = posture[v.id] || "no"; + const sev = severityMeta[v.severity]; + return ( +
handleSelect(v.id)} + > +
{ + e.stopPropagation(); + handleToggle(v.id); + }} + > + {checkMarks[state]} +
+ {v.title} + + {sev.label} + +
+ ); + })} +
+ + {/* Detail Card */} + {selectedVector && ( + handleSetPosture(selectedVector.id, state)} + onClose={() => setSelected(null)} + /> + )} +
+ ); +} + +function DetailCard({ + vector, + state, + onSetState, + onClose, +}: { + vector: ThreatVector; + state: PostureState; + onSetState: (state: PostureState) => void; + onClose: () => void; +}) { + const sev = severityMeta[vector.severity]; + const cardRef = useRef(null); + + useEffect(() => { + if (!cardRef.current) return; + const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; + cardRef.current.scrollIntoView({ + behavior: prefersReducedMotion ? "auto" : "smooth", + block: "nearest", + }); + }, [vector.id]); + + return ( +
+
+
+

{vector.title}

+
+ + {sev.label.toUpperCase()} SEVERITY +
+
+
+
+ {(["no", "partial", "yes"] as PostureState[]).map((s) => ( + + ))} +
+ +
+
+ +

{vector.description}

+ +
+
+ {vector.attackTags.map((tag) => ( + {tag} + ))} +
+ + {vector.primaryLinkLabel} → + +
+
+ ); +} diff --git a/components/attack-surface/threatData.ts b/components/attack-surface/threatData.ts new file mode 100644 index 000000000..16af821a1 --- /dev/null +++ b/components/attack-surface/threatData.ts @@ -0,0 +1,259 @@ +export type Severity = "critical" | "high" | "medium"; +export type PostureState = "no" | "yes" | "partial"; +export type Category = "smart-contract" | "operational" | "human" | "infrastructure" | "supply-chain" | "governance"; + +export interface FrameworkLink { + label: string; + href: string; +} + +export interface ThreatVector { + id: string; + title: string; + subtitle: string; + category: Category; + severity: Severity; + description: string; + example?: string; + /** Short attack-type tags shown in the detail card */ + attackTags: string[]; + /** The primary framework page for this vector — label text links here */ + primaryLink: string; + /** Label for the primary link button in the detail card */ + primaryLinkLabel: string; + frameworkLinks: FrameworkLink[]; +} + +export const categoryMeta: Record = { + "smart-contract": { label: "Smart Contract", color: "#ef4444" }, + operational: { label: "Operational", color: "#f97316" }, + human: { label: "Human", color: "#eab308" }, + infrastructure: { label: "Infrastructure", color: "#8b5cf6" }, + "supply-chain": { label: "Supply Chain", color: "#3b82f6" }, + governance: { label: "Governance", color: "#10b981" }, +}; + +export const severityMeta: Record = { + critical: { label: "Critical", color: "#ef4444", bg: "rgba(239, 68, 68, 0.12)" }, + high: { label: "High", color: "#f97316", bg: "rgba(249, 115, 22, 0.12)" }, + medium: { label: "Medium", color: "#eab308", bg: "rgba(234, 179, 8, 0.12)" }, +}; + +export const threatVectors: ThreatVector[] = [ + { + id: "smart-contract-exploits", + title: "Smart Contract Exploits", + subtitle: "Code vulnerabilities in on-chain logic", + category: "smart-contract", + severity: "critical", + description: + "Reentrancy attacks, logic bugs, oracle manipulation, flash loan exploits, and unaudited upgrade paths that drain protocol funds.", + example: "Euler Finance: $197M flash loan exploit", + attackTags: ["Reentrancy", "Flash loan exploit", "Oracle manipulation", "Logic bug"], + primaryLink: "/external-security-reviews/overview", + primaryLinkLabel: "Security Reviews Framework", + frameworkLinks: [ + { label: "Incident Playbooks", href: "/incident-management/playbooks" }, + { label: "DevSecOps", href: "/devsecops/overview" }, + { label: "External Security Reviews", href: "/external-security-reviews/overview" }, + { label: "Security Testing", href: "/security-testing/overview" }, + ], + }, + { + id: "multisig-failures", + title: "Multisig Operational Failures", + subtitle: "Signer and threshold mismanagement", + category: "operational", + severity: "critical", + description: + "Signer unavailability, lost keys, weak thresholds, no rotation policy, and lack of emergency procedures leave protocol treasuries exposed.", + example: "Ronin Bridge: $625M via compromised validator keys", + attackTags: ["Signer unavailability", "Weak threshold", "Key loss", "No rotation policy"], + primaryLink: "/multisig-for-protocols/overview", + primaryLinkLabel: "Multisig Framework", + frameworkLinks: [ + { label: "Multisig Overview", href: "/multisig-for-protocols/overview" }, + { label: "Emergency Procedures", href: "/multisig-for-protocols/emergency-procedures" }, + { label: "Signer Onboarding", href: "/multisig-for-protocols/signer-onboarding" }, + { label: "Multisig Runbooks", href: "/multisig-for-protocols/runbooks/overview" }, + ], + }, + { + id: "dprk-threat-actors", + title: "DPRK / Threat Actor Hiring", + subtitle: "Nation-state infiltration via hiring", + category: "human", + severity: "critical", + description: + "Unknowingly hiring North Korean IT workers or other threat actors as contractors who gain access to codebases, infrastructure, and signing keys.", + example: "Multiple protocols compromised via DPRK contractors", + attackTags: ["Fake identity", "Contractor infiltration", "Code backdoor", "Key theft"], + primaryLink: "/incident-management/playbooks/dprk-it-worker", + primaryLinkLabel: "DPRK Playbook", + frameworkLinks: [ + { label: "DPRK IT Worker Playbook", href: "/incident-management/playbooks/dprk-it-worker" }, + { label: "Insider Threat Mitigation", href: "/opsec/control-domains/people/insider-threat-mitigation" }, + { label: "Personnel Controls", href: "/opsec/control-domains/people/overview" }, + ], + }, + { + id: "leadership-phishing", + title: "Leadership Phishing", + subtitle: "Targeted spearphishing of key personnel", + category: "human", + severity: "critical", + description: + "Spearphishing campaigns targeting founders, executives, and multisig signers to steal credentials, signing keys, or trick them or employees into approving malicious transactions.", + attackTags: ["Spearphishing", "Credential theft", "Malicious signing", "Impersonation"], + primaryLink: "/awareness/understanding-threat-vectors", + primaryLinkLabel: "Threat Vectors Guide", + frameworkLinks: [ + { label: "Understanding Threat Vectors", href: "/awareness/understanding-threat-vectors" }, + { label: "Phishing & Social Engineering", href: "/user-team-security/phishing-social-engineering" }, + { label: "Security Training", href: "/user-team-security/security-training" }, + ], + }, + { + id: "infrastructure-compromise", + title: "Infrastructure Compromise", + subtitle: "Cloud, server, and network breaches", + category: "infrastructure", + severity: "critical", + description: + "Cloud misconfigurations, exposed APIs, server compromise, weak network segmentation, and missing zero-trust architecture leading to full infrastructure takeover.", + attackTags: ["AWS key leak", "RPC manipulation", "Server compromise", "API exfiltration"], + primaryLink: "/infrastructure/overview", + primaryLinkLabel: "Infrastructure Framework", + frameworkLinks: [ + { label: "Infrastructure Overview", href: "/infrastructure/overview" }, + { label: "Cloud Security", href: "/infrastructure/cloud-security" }, + { label: "Network Security", href: "/infrastructure/network-security" }, + { label: "Zero Trust Architecture", href: "/infrastructure/zero-trust-architecture" }, + ], + }, + { + id: "frontend-dns-hijacking", + title: "Frontend / DNS Hijacking", + subtitle: "Website and domain takeover attacks", + category: "infrastructure", + severity: "critical", + description: + "DNS hijacking, compromised frontend deployments, UI spoofing, and registrar account takeovers that redirect users to malicious interfaces draining wallets.", + example: "Curve Finance: DNS hijack redirected users to drainer", + attackTags: ["DNS hijack", "UI spoofing", "Registrar takeover", "Frontend injection"], + primaryLink: "/infrastructure/domain-and-dns-security/overview", + primaryLinkLabel: "DNS Security Framework", + frameworkLinks: [ + { label: "DNS Security", href: "/infrastructure/domain-and-dns-security/overview" }, + { label: "Monitoring & Alerting", href: "/infrastructure/domain-and-dns-security/monitoring-and-alerting" }, + { label: "Front-End Security", href: "/front-end-web-application/overview" }, + { label: "DNS Registrar Cert", href: "/certs/sfc-dns-registrar" }, + ], + }, + { + id: "opsec-failures", + title: "Operational Security Failures", + subtitle: "Day-to-day security hygiene gaps", + category: "operational", + severity: "high", + description: + "Poor device hygiene, leaked credentials, weak access controls, unencrypted communications, and lack of security policies across the team.", + attackTags: ["Leaked credentials", "Weak access controls", "Unencrypted comms", "No MFA"], + primaryLink: "/opsec/overview", + primaryLinkLabel: "OpSec Framework", + frameworkLinks: [ + { label: "OpSec Overview", href: "/opsec/overview" }, + { label: "Technical Controls", href: "/opsec/control-domains/technical/overview" }, + { label: "Device Hardening", href: "/opsec/control-domains/technical/device-hardening" }, + { label: "IAM", href: "/iam/overview" }, + ], + }, + { + id: "supply-chain-attacks", + title: "Supply Chain Attacks", + subtitle: "Compromised dependencies and CI/CD", + category: "supply-chain", + severity: "high", + description: + "Dependency poisoning, typosquatting, compromised CI/CD pipelines, malicious npm/crate packages, and build system backdoors that inject malicious code.", + example: "Ledger Connect Kit: compromised npm package", + attackTags: ["Dependency poisoning", "Typosquatting", "CI/CD backdoors", "Malicious package"], + primaryLink: "/supply-chain/overview", + primaryLinkLabel: "Supply Chain Framework", + frameworkLinks: [ + { label: "Supply Chain Overview", href: "/supply-chain/overview" }, + { label: "DevSecOps", href: "/devsecops/overview" }, + { label: "CI/CD Security", href: "/devsecops/ci-cd-security" }, + { label: "Code Signing", href: "/devsecops/code-signing" }, + ], + }, + { + id: "monitoring-gaps", + title: "Monitoring & Alerting Gaps", + subtitle: "Blind spots in threat detection", + category: "operational", + severity: "high", + description: + "No on-chain monitoring, slow anomaly detection, missing alerts for critical operations, and no automated response — letting attackers operate undetected.", + attackTags: ["No on-chain monitoring", "Slow detection", "Missing alerts", "No auto-response"], + primaryLink: "/monitoring/overview", + primaryLinkLabel: "Monitoring Framework", + frameworkLinks: [ + { label: "Monitoring Overview", href: "/monitoring/overview" }, + { label: "Threat Detection", href: "/security-automation/threat-detection-response" }, + { label: "DNS Monitoring", href: "/infrastructure/domain-and-dns-security/monitoring-and-alerting" }, + ], + }, + { + id: "social-engineering", + title: "Social Engineering", + subtitle: "Manipulation beyond phishing", + category: "human", + severity: "high", + description: + "Impersonation of partners or investors, fake collaboration requests, community manipulation, and trust exploitation to gain access or influence decisions.", + attackTags: ["Impersonation", "Fake partnership", "Community manipulation", "Trust exploit"], + primaryLink: "/awareness/overview", + primaryLinkLabel: "Awareness Framework", + frameworkLinks: [ + { label: "Awareness Overview", href: "/awareness/overview" }, + { label: "Threat Vectors", href: "/awareness/understanding-threat-vectors" }, + { label: "Security Culture", href: "/user-team-security/security-aware-culture" }, + { label: "Community Management", href: "/community-management/overview" }, + ], + }, + { + id: "duress-situations", + title: "Duress Situations", + subtitle: "Physical threats and coercion", + category: "human", + severity: "high", + description: + "Physical threats, kidnapping, extortion, and coercion targeting key personnel to force transaction signing or credential disclosure.", + attackTags: ["Physical threat", "Kidnapping", "Extortion", "Forced signing"], + primaryLink: "/opsec/travel/guide", + primaryLinkLabel: "Travel Security Guide", + frameworkLinks: [ + { label: "Travel Security", href: "/opsec/travel/guide" }, + { label: "Emergency Procedures", href: "/multisig-for-protocols/emergency-procedures" }, + { label: "Physical Controls", href: "/opsec/control-domains/physical-environmental/overview" }, + ], + }, + { + id: "governance-attacks", + title: "Governance Attacks", + subtitle: "Malicious proposals and vote manipulation", + category: "governance", + severity: "medium", + description: + "Proposal manipulation, vote buying, flash loan governance attacks, and unauthorized upgrades that alter protocol behavior or drain treasuries.", + attackTags: ["Proposal manipulation", "Vote buying", "Rogue upgrades"], + primaryLink: "/governance/overview", + primaryLinkLabel: "Governance Framework", + frameworkLinks: [ + { label: "Governance Overview", href: "/governance/overview" }, + { label: "Security Council Best Practices", href: "/governance/security-council-best-practices" }, + { label: "Multisig for Protocols", href: "/multisig-for-protocols/overview" }, + ], + }, +]; diff --git a/components/index.ts b/components/index.ts index 8ee66ac63..c5a48ed3d 100644 --- a/components/index.ts +++ b/components/index.ts @@ -24,3 +24,4 @@ export { CertifiedProtocols } from './certified-protocols/CertifiedProtocols' export { CertifiedProtocolsWrapper } from './certified-protocols/CertifiedProtocolsWrapper' export { BadgeLegend } from './contributors/BadgeLegend' export { DevOnly } from './dev-only/DevOnly' +export { AttackSurfaceDashboard } from './attack-surface/AttackSurfaceDashboard' diff --git a/docs/pages/intro/attack-surface.mdx b/docs/pages/intro/attack-surface.mdx new file mode 100644 index 000000000..889fca0da --- /dev/null +++ b/docs/pages/intro/attack-surface.mdx @@ -0,0 +1,26 @@ +--- +title: "Attack Surface Overview | SEAL Security Frameworks" +description: "Visual overview of the Web3 threat landscape. See how protocols get compromised, assess your security posture, and find remediation guides." +tags: + - Security Specialist + - Operations & Strategy +--- + +import { AttackSurfaceDashboard } from '../../../components' +import { TagList, TagProvider, TagFilter, ContributeFooter } from '../../../components' + + + + +# Attack Surface Overview + + + +See where your protocol is most exposed. Click the checkbox on each vector to track your security posture. Your progress is saved locally in your browser. + + + +--- + + + diff --git a/docs/pages/intro/how-to-navigate-the-website.mdx b/docs/pages/intro/how-to-navigate-the-website.mdx index 5b4205e28..a934cdb24 100644 --- a/docs/pages/intro/how-to-navigate-the-website.mdx +++ b/docs/pages/intro/how-to-navigate-the-website.mdx @@ -11,6 +11,8 @@ import { ContributeFooter, TagFilter, TagProvider } from '../../../components' Navigating the Security Frameworks by SEAL will be designed, in time, to be intuitive and user-friendly. We currently allow users to filter content by role, but we're not quite there yet. +For a visual entry point, try the [Attack Surface Overview](/intro/attack-surface)—it surfaces +common threats as a clickable map, with links into the relevant frameworks for each one. Any feedback on how to improve the usage of frameworks in the future is appreciated. ## Categories diff --git a/vocs.config.tsx b/vocs.config.tsx index fa81cbb5f..8b9b94ba4 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -40,6 +40,7 @@ const config = { { text: 'Introduction to Frameworks', link: '/intro/introduction' }, { text: 'How to Navigate the Website', link: '/intro/how-to-navigate-the-website' }, { text: 'Overview of each Framework', link: '/intro/overview-of-each-framework' }, + { text: 'Attack Surface Overview', link: '/intro/attack-surface', dev: true }, ] }, { From bb48f7d4080f55b25adeedcff05925847dc95b4b Mon Sep 17 00:00:00 2001 From: Tim Sh Date: Tue, 21 Apr 2026 17:40:12 +0100 Subject: [PATCH 14/92] content: clarify phishing awareness guidance on account claims and channel scope (#445) Clarify that profile labels and platform status indicators are not proof of legitimacy, and state that unsupported platforms should be explicitly identified. --- docs/pages/community-management/overview.mdx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/pages/community-management/overview.mdx b/docs/pages/community-management/overview.mdx index b0479081a..2acb2a587 100644 --- a/docs/pages/community-management/overview.mdx +++ b/docs/pages/community-management/overview.mdx @@ -54,8 +54,11 @@ platform-specific recommendations in more depth. - Educate members on recognizing and reporting phishing attempts. - Clearly communicate to community members that your team will never send the first direct message to them. This is important because attackers often impersonate team members and initiate direct messages to trick users into believing - they are legitimate, thereby gaining their trust and potentially compromising their security. -- Publicly define all official communication channels used by your organization. + they are legitimate, thereby gaining their trust and potentially compromising their security. + However, statements such as “will never DM first” or labels like “Official,” “Support,” or platform status indicators (e.g., premium badges) must not be treated as proof of legitimacy. +- Publicly define all official communication channels and clearly state which platforms are not used. +If a platform is unsupported, declare this alongside official links (e.g., “We do not operate a Telegram community”). +Where possible, reserve common impersonation handles and maintain placeholder accounts that redirect users to official channels. Refer to the [**Security Awareness framework**](/awareness/overview) to learn more about social engineering techniques and security training best practices. From 8368fc3d3d4e9edb8c846b6cc9300bba9e93423b Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Tue, 21 Apr 2026 18:41:17 +0200 Subject: [PATCH 15/92] bump dependencies after security alerts (#453) --- package.json | 9 +- pnpm-lock.yaml | 2403 ++++++++++++++++++++++++------------------------ 2 files changed, 1209 insertions(+), 1203 deletions(-) diff --git a/package.json b/package.json index 583c3e6da..6dfec677e 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,9 @@ "description": "Official repository to the Security Frameworks by SEAL. This repository contains the entire structure and contents of the frameworks. Feel free to suggest from new categories to grammar corrections. Collaboration is open to everyone. **This is a work in progress.**", "main": "index.js", "devDependencies": { - "@types/exceljs": "^1.3.2", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", - "cspell": "^9.4.0", + "cspell": "9.8.0", "tailwindcss": "4.0.7", "vocs": "1.2.2" }, @@ -34,9 +33,9 @@ }, "packageManager": "pnpm@10.15.0", "dependencies": { - "@aws-sdk/client-s3": "^3.968.0", - "@aws-sdk/lib-storage": "^3.967.0", - "axios": "^1.13.2", + "@aws-sdk/client-s3": "3.1024.0", + "@aws-sdk/lib-storage": "3.1024.0", + "axios": "1.14.0", "exceljs": "^4.4.0", "gray-matter": "^4.0.3", "just-install": "^2.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index abf30ed74..119295410 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,14 +9,14 @@ importers: .: dependencies: '@aws-sdk/client-s3': - specifier: ^3.968.0 - version: 3.971.0 + specifier: 3.1024.0 + version: 3.1024.0 '@aws-sdk/lib-storage': - specifier: ^3.967.0 - version: 3.971.0(@aws-sdk/client-s3@3.971.0) + specifier: 3.1024.0 + version: 3.1024.0(@aws-sdk/client-s3@3.1024.0) axios: - specifier: ^1.13.2 - version: 1.13.2 + specifier: 1.14.0 + version: 1.14.0 exceljs: specifier: ^4.4.0 version: 4.4.0 @@ -51,9 +51,6 @@ importers: specifier: ^13.0.0 version: 13.0.0 devDependencies: - '@types/exceljs': - specifier: ^1.3.2 - version: 1.3.2 '@types/react': specifier: ^19.2.7 version: 19.2.7 @@ -61,8 +58,8 @@ importers: specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.7) cspell: - specifier: ^9.4.0 - version: 9.4.0 + specifier: 9.8.0 + version: 9.8.0 tailwindcss: specifier: 4.0.7 version: 4.0.7 @@ -98,137 +95,133 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.971.0': - resolution: {integrity: sha512-BBUne390fKa4C4QvZlUZ5gKcu+Uyid4IyQ20N4jl0vS7SK2xpfXlJcgKqPW5ts6kx6hWTQBk6sH5Lf12RvuJxg==} + '@aws-sdk/client-s3@3.1024.0': + resolution: {integrity: sha512-8qdO5aLCzaf9l0RdrSBW1iIroRKP2QBqtZ6lkrtHKiaaH0B18xEn+lrEgiN/eCf3uRAYk4cqbnI2XcWzm+7dDQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/client-sso@3.971.0': - resolution: {integrity: sha512-Xx+w6DQqJxDdymYyIxyKJnRzPvVJ4e/Aw0czO7aC9L/iraaV7AG8QtRe93OGW6aoHSh72CIiinnpJJfLsQqP4g==} + '@aws-sdk/core@3.973.27': + resolution: {integrity: sha512-CUZ5m8hwMCH6OYI4Li/WgMfIEx10Q2PLI9Y3XOUTPGZJ53aZ0007jCv+X/ywsaERyKPdw5MRZWk877roQksQ4A==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.970.0': - resolution: {integrity: sha512-klpzObldOq8HXzDjDlY6K8rMhYZU6mXRz6P9F9N+tWnjoYFfeBMra8wYApydElTUYQKP1O7RLHwH1OKFfKcqIA==} + '@aws-sdk/crc64-nvme@3.972.6': + resolution: {integrity: sha512-NMbiqKdruhwwgI6nzBVe2jWMkXjaoQz2YOs3rFX+2F3gGyrJDkDPwMpV/RsTFeq2vAQ055wZNtOXFK4NYSkM8g==} engines: {node: '>=20.0.0'} - '@aws-sdk/crc64-nvme@3.969.0': - resolution: {integrity: sha512-IGNkP54HD3uuLnrPCYsv3ZD478UYq+9WwKrIVJ9Pdi3hxPg8562CH3ZHf8hEgfePN31P9Kj+Zu9kq2Qcjjt61A==} + '@aws-sdk/credential-provider-env@3.972.25': + resolution: {integrity: sha512-6QfI0wv4jpG5CrdO/AO0JfZ2ux+tKwJPrUwmvxXF50vI5KIypKVGNF6b4vlkYEnKumDTI1NX2zUBi8JoU5QU3A==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.970.0': - resolution: {integrity: sha512-rtVzXzEtAfZBfh+lq3DAvRar4c3jyptweOAJR2DweyXx71QSMY+O879hjpMwES7jl07a3O1zlnFIDo4KP/96kQ==} + '@aws-sdk/credential-provider-http@3.972.27': + resolution: {integrity: sha512-3V3Usj9Gs93h865DqN4M2NWJhC5kXU9BvZskfN3+69omuYlE3TZxOEcVQtBGLOloJB7BVfJKXVLqeNhOzHqSlQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.970.0': - resolution: {integrity: sha512-CjDbWL7JxjLc9ZxQilMusWSw05yRvUJKRpz59IxDpWUnSMHC9JMMUUkOy5Izk8UAtzi6gupRWArp4NG4labt9Q==} + '@aws-sdk/credential-provider-ini@3.972.29': + resolution: {integrity: sha512-SiBuAnXecCbT/OpAf3vqyI/AVE3mTaYr9ShXLybxZiPLBiPCCOIWSGAtYYGQWMRvobBTiqOewaB+wcgMMZI2Aw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.971.0': - resolution: {integrity: sha512-c0TGJG4xyfTZz3SInXfGU8i5iOFRrLmy4Bo7lMyH+IpngohYMYGYl61omXqf2zdwMbDv+YJ9AviQTcCaEUKi8w==} + '@aws-sdk/credential-provider-login@3.972.29': + resolution: {integrity: sha512-OGOslTbOlxXexKMqhxCEbBQbUIfuhGxU5UXw3Fm56ypXHvrXH4aTt/xb5Y884LOoteP1QST1lVZzHfcTnWhiPQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.971.0': - resolution: {integrity: sha512-yhbzmDOsk0RXD3rTPhZra4AWVnVAC4nFWbTp+sUty1hrOPurUmhuz8bjpLqYTHGnlMbJp+UqkQONhS2+2LzW2g==} + '@aws-sdk/credential-provider-node@3.972.30': + resolution: {integrity: sha512-FMnAnWxc8PG+ZrZ2OBKzY4luCUJhe9CG0B9YwYr4pzrYGLXBS2rl+UoUvjGbAwiptxRL6hyA3lFn03Bv1TLqTw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.971.0': - resolution: {integrity: sha512-epUJBAKivtJqalnEBRsYIULKYV063o/5mXNJshZfyvkAgNIzc27CmmKRXTN4zaNOZg8g/UprFp25BGsi19x3nQ==} + '@aws-sdk/credential-provider-process@3.972.25': + resolution: {integrity: sha512-HR7ynNRdNhNsdVCOCegy1HsfsRzozCOPtD3RzzT1JouuaHobWyRfJzCBue/3jP7gECHt+kQyZUvwg/cYLWurNQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.970.0': - resolution: {integrity: sha512-0XeT8OaT9iMA62DFV9+m6mZfJhrD0WNKf4IvsIpj2Z7XbaYfz3CoDDvNoALf3rPY9NzyMHgDxOspmqdvXP00mw==} + '@aws-sdk/credential-provider-sso@3.972.29': + resolution: {integrity: sha512-HWv4SEq3jZDYPlwryZVef97+U8CxxRos5mK8sgGO1dQaFZpV5giZLzqGE5hkDmh2csYcBO2uf5XHjPTpZcJlig==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.971.0': - resolution: {integrity: sha512-dY0hMQ7dLVPQNJ8GyqXADxa9w5wNfmukgQniLxGVn+dMRx3YLViMp5ZpTSQpFhCWNF0oKQrYAI5cHhUJU1hETw==} + '@aws-sdk/credential-provider-web-identity@3.972.29': + resolution: {integrity: sha512-PdMBza1WEKEUPFEmMGCfnU2RYCz9MskU2e8JxjyUOsMKku7j9YaDKvbDi2dzC0ihFoM6ods2SbhfAAro+Gwlew==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.971.0': - resolution: {integrity: sha512-F1AwfNLr7H52T640LNON/h34YDiMuIqW/ZreGzhRR6vnFGaSPtNSKAKB2ssAMkLM8EVg8MjEAYD3NCUiEo+t/w==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/lib-storage@3.971.0': - resolution: {integrity: sha512-THTCXZiYjuAU2kPD8rIuvtYRT83BxEzbv4uayPlQJ8v5bybLTYDbNEbpfZGilyAqUAdSGTMOkoLu9ROryCJ3/g==} + '@aws-sdk/lib-storage@3.1024.0': + resolution: {integrity: sha512-5zZSYI8VncyiatvEn+UqElvgX14NKEc+3a+aeMyMzqKuYgDCFd97KGdbqAXEgnfEiTNK0QgSecFC6PUMAsE3xg==} engines: {node: '>=20.0.0'} peerDependencies: - '@aws-sdk/client-s3': 3.971.0 + '@aws-sdk/client-s3': ^3.1024.0 - '@aws-sdk/middleware-bucket-endpoint@3.969.0': - resolution: {integrity: sha512-MlbrlixtkTVhYhoasblKOkr7n2yydvUZjjxTnBhIuHmkyBS1619oGnTfq/uLeGYb4NYXdeQ5OYcqsRGvmWSuTw==} + '@aws-sdk/middleware-bucket-endpoint@3.972.9': + resolution: {integrity: sha512-COToYKgquDyligbcAep7ygs48RK+mwe/IYprq4+TSrVFzNOYmzWvHf6werpnKV5VYpRiwdn+Wa5ZXkPqLVwcTg==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-expect-continue@3.969.0': - resolution: {integrity: sha512-qXygzSi8osok7tH9oeuS3HoKw6jRfbvg5Me/X5RlHOvSSqQz8c5O9f3MjUApaCUSwbAU92KrbZWasw2PKiaVHg==} + '@aws-sdk/middleware-expect-continue@3.972.9': + resolution: {integrity: sha512-V/FNCjFxnh4VGu+HdSiW4Yg5GELihA1MIDSAdsEPvuayXBVmr0Jaa6jdLAZLH38KYXl/vVjri9DQJWnTAujHEA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.971.0': - resolution: {integrity: sha512-+hGUDUxeIw8s2kkjfeXym0XZxdh0cqkHkDpEanWYdS1gnWkIR+gf9u/DKbKqGHXILPaqHXhWpLTQTVlaB4sI7Q==} + '@aws-sdk/middleware-flexible-checksums@3.974.7': + resolution: {integrity: sha512-uU4/ch2CLHB8Phu1oTKnnQ4e8Ujqi49zEnQYBhWYT53zfFvtJCdGsaOoypBr8Fm/pmCBssRmGoIQ4sixgdLP9w==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-host-header@3.969.0': - resolution: {integrity: sha512-AWa4rVsAfBR4xqm7pybQ8sUNJYnjyP/bJjfAw34qPuh3M9XrfGbAHG0aiAfQGrBnmS28jlO6Kz69o+c6PRw1dw==} + '@aws-sdk/middleware-host-header@3.972.9': + resolution: {integrity: sha512-je5vRdNw4SkuTnmRbFZLdye4sQ0faLt8kwka5wnnSU30q1mHO4X+idGEJOOE+Tn1ME7Oryn05xxkDvIb3UaLaQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-location-constraint@3.969.0': - resolution: {integrity: sha512-zH7pDfMLG/C4GWMOpvJEoYcSpj7XsNP9+irlgqwi667sUQ6doHQJ3yyDut3yiTk0maq1VgmriPFELyI9lrvH/g==} + '@aws-sdk/middleware-location-constraint@3.972.9': + resolution: {integrity: sha512-TyfOi2XNdOZpNKeTJwRUsVAGa+14nkyMb2VVGG+eDgcWG/ed6+NUo72N3hT6QJioxym80NSinErD+LBRF0Ir1w==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-logger@3.969.0': - resolution: {integrity: sha512-xwrxfip7Y2iTtCMJ+iifN1E1XMOuhxIHY9DreMCvgdl4r7+48x2S1bCYPWH3eNY85/7CapBWdJ8cerpEl12sQQ==} + '@aws-sdk/middleware-logger@3.972.9': + resolution: {integrity: sha512-HsVgDrruhqI28RkaXALm8grJ7Agc1wF6Et0xh6pom8NdO2VdO/SD9U/tPwUjewwK/pVoka+EShBxyCvgsPCtog==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.969.0': - resolution: {integrity: sha512-2r3PuNquU3CcS1Am4vn/KHFwLi8QFjMdA/R+CRDXT4AFO/0qxevF/YStW3gAKntQIgWgQV8ZdEtKAoJvLI4UWg==} + '@aws-sdk/middleware-recursion-detection@3.972.10': + resolution: {integrity: sha512-RVQQbq5orQ/GHUnXvqEOj2HHPBJm+mM+ySwZKS5UaLBwra5ugRtiH09PLUoOZRl7a1YzaOzXSuGbn9iD5j60WQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-sdk-s3@3.970.0': - resolution: {integrity: sha512-v/Y5F1lbFFY7vMeG5yYxuhnn0CAshz6KMxkz1pDyPxejNE9HtA0w8R6OTBh/bVdIm44QpjhbI7qeLdOE/PLzXQ==} + '@aws-sdk/middleware-sdk-s3@3.972.28': + resolution: {integrity: sha512-qJHcJQH9UNPUrnPlRtCozKjtqAaypQ5IgQxTNoPsVYIQeuwNIA8Rwt3NvGij1vCDYDfCmZaPLpnJEHlZXeFqmg==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-ssec@3.971.0': - resolution: {integrity: sha512-QGVhvRveYG64ZhnS/b971PxXM6N2NU79Fxck4EfQ7am8v1Br0ctoeDDAn9nXNblLGw87we9Z65F7hMxxiFHd3w==} + '@aws-sdk/middleware-ssec@3.972.9': + resolution: {integrity: sha512-wSA2BR7L0CyBNDJeSrleIIzC+DzL93YNTdfU0KPGLiocK6YsRv1nPAzPF+BFSdcs0Qa5ku5Kcf4KvQcWwKGenQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.970.0': - resolution: {integrity: sha512-dnSJGGUGSFGEX2NzvjwSefH+hmZQ347AwbLhAsi0cdnISSge+pcGfOFrJt2XfBIypwFe27chQhlfuf/gWdzpZg==} + '@aws-sdk/middleware-user-agent@3.972.29': + resolution: {integrity: sha512-f/sIRzuTfEjg6NsbMYvye2VsmnQoNgntntleQyx5uGacUYzszbfIlO3GcI6G6daWUmTm0IDZc11qMHWwF0o0mQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.971.0': - resolution: {integrity: sha512-TWaILL8GyYlhGrxxnmbkazM4QsXatwQgoWUvo251FXmUOsiXDFDVX3hoGIfB3CaJhV2pJPfebHUNJtY6TjZ11g==} + '@aws-sdk/nested-clients@3.996.19': + resolution: {integrity: sha512-uFkmCDXvmQYLanlYdOFS0+MQWkrj9wPMt/ZCc/0J0fjPim6F5jBVBmEomvGY/j77ILW6GTPwN22Jc174Mhkw6Q==} engines: {node: '>=20.0.0'} - '@aws-sdk/region-config-resolver@3.969.0': - resolution: {integrity: sha512-scj9OXqKpcjJ4jsFLtqYWz3IaNvNOQTFFvEY8XMJXTv+3qF5I7/x9SJtKzTRJEBF3spjzBUYPtGFbs9sj4fisQ==} + '@aws-sdk/region-config-resolver@3.972.11': + resolution: {integrity: sha512-6Q8B1dcx6BBqUTY1Mc/eROKA0FImEEY5VPSd6AGPEUf0ErjExz4snVqa9kNJSoVDV1rKaNf3qrWojgcKW+SdDg==} engines: {node: '>=20.0.0'} - '@aws-sdk/signature-v4-multi-region@3.970.0': - resolution: {integrity: sha512-z3syXfuK/x/IsKf/AeYmgc2NT7fcJ+3fHaGO+fkghkV9WEba3fPyOwtTBX4KpFMNb2t50zDGZwbzW1/5ighcUQ==} + '@aws-sdk/signature-v4-multi-region@3.996.16': + resolution: {integrity: sha512-EMdXYB4r/k5RWq86fugjRhid5JA+Z6MpS7n4sij4u5/C+STrkvuf9aFu41rJA9MjUzxCLzv8U2XL8cH2GSRYpQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.971.0': - resolution: {integrity: sha512-4hKGWZbmuDdONMJV0HJ+9jwTDb0zLfKxcCLx2GEnBY31Gt9GeyIQ+DZ97Bb++0voawj6pnZToFikXTyrEq2x+w==} + '@aws-sdk/token-providers@3.1026.0': + resolution: {integrity: sha512-Ieq/HiRrbEtrYP387Nes0XlR7H1pJiJOZKv+QyQzMYpvTiDs0VKy2ZB3E2Zf+aFovWmeE7lRE4lXyF7dYM6GgA==} engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.969.0': - resolution: {integrity: sha512-7IIzM5TdiXn+VtgPdVLjmE6uUBUtnga0f4RiSEI1WW10RPuNvZ9U+pL3SwDiRDAdoGrOF9tSLJOFZmfuwYuVYQ==} + '@aws-sdk/types@3.973.7': + resolution: {integrity: sha512-reXRwoJ6CfChoqAsBszUYajAF8Z2LRE+CRcKocvFSMpIiLOtYU3aJ9trmn6VVPAzbbY5LXF+FfmUslbXk1SYFg==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-arn-parser@3.968.0': - resolution: {integrity: sha512-gqqvYcitIIM2K4lrDX9de9YvOfXBcVdxfT/iLnvHJd4YHvSXlt+gs+AsL4FfPCxG4IG9A+FyulP9Sb1MEA75vw==} + '@aws-sdk/util-arn-parser@3.972.3': + resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-endpoints@3.970.0': - resolution: {integrity: sha512-TZNZqFcMUtjvhZoZRtpEGQAdULYiy6rcGiXAbLU7e9LSpIYlRqpLa207oMNfgbzlL2PnHko+eVg8rajDiSOYCg==} + '@aws-sdk/util-endpoints@3.996.6': + resolution: {integrity: sha512-2nUQ+2ih7CShuKHpGSIYvvAIOHy52dOZguYG36zptBukhw6iFwcvGfG0tes0oZFWQqEWvgZe9HLWaNlvXGdOrg==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-locate-window@3.965.2': - resolution: {integrity: sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q==} + '@aws-sdk/util-locate-window@3.965.5': + resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-user-agent-browser@3.969.0': - resolution: {integrity: sha512-bpJGjuKmFr0rA6UKUCmN8D19HQFMLXMx5hKBXqBlPFdalMhxJSjcxzX9DbQh0Fn6bJtxCguFmRGOBdQqNOt49g==} + '@aws-sdk/util-user-agent-browser@3.972.9': + resolution: {integrity: sha512-sn/LMzTbGjYqCCF24390WxPd6hkpoSptiUn5DzVp4cD71yqw+yGEGm1YCxyEoPXyc8qciM8UzLJcZBFslxo5Uw==} - '@aws-sdk/util-user-agent-node@3.971.0': - resolution: {integrity: sha512-Eygjo9mFzQYjbGY3MYO6CsIhnTwAMd3WmuFalCykqEmj2r5zf0leWrhPaqvA5P68V5JdGfPYgj7vhNOd6CtRBQ==} + '@aws-sdk/util-user-agent-node@3.973.15': + resolution: {integrity: sha512-fYn3s9PtKdgQkczGZCFMgkNEe8aq1JCVbnRqjqN9RSVW43xn2RV9xdcZ3z01a48Jpkuh/xCmBKJxdLOo4Ozg7w==} engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -236,12 +229,12 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.969.0': - resolution: {integrity: sha512-BSe4Lx/qdRQQdX8cSSI7Et20vqBspzAjBy8ZmXVoyLkol3y4sXBXzn+BiLtR+oh60ExQn6o2DU4QjdOZbXaKIQ==} + '@aws-sdk/xml-builder@3.972.17': + resolution: {integrity: sha512-Ra7hjqAZf1OXRRMueB13qex7mFJRDK/pgCvdSFemXBT8KCGnQDPoKzHY1SjN+TjJVmnpSF14W5tJ1vDamFu+Gg==} engines: {node: '>=20.0.0'} - '@aws/lambda-invoke-store@0.2.3': - resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} + '@aws/lambda-invoke-store@0.2.4': + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} engines: {node: '>=18.0.0'} '@babel/code-frame@7.27.1': @@ -363,107 +356,115 @@ packages: bundledDependencies: - is-unicode-supported - '@cspell/cspell-bundled-dicts@9.4.0': - resolution: {integrity: sha512-Hm2gpMg/lRv4fKtiO2NfBiaJdFZVVb1V1a+IVhlD9qCuObLhCt60Oze2kD1dQzhbaIX756cs/eyxa5bQ5jihhQ==} + '@cspell/cspell-bundled-dicts@9.8.0': + resolution: {integrity: sha512-MpXFpVyBPfJQ1YuVotljqUaGf6lWuf+fuWBBgs0PHFYTSjRPWuIxviAaCDnup/CJLLH60xQL4IlcQe4TOjzljw==} engines: {node: '>=20'} - '@cspell/cspell-json-reporter@9.4.0': - resolution: {integrity: sha512-TpHY7t13xNhcZF9bwOfgVIhcyPDamMnxU/TBYhf4mPtXPLrZ5gBTg3UZh0/9Zn3naMjmJtngdsLvB2wai9xBlQ==} + '@cspell/cspell-json-reporter@9.8.0': + resolution: {integrity: sha512-nqUaSo9T7l8KrE22gc7ZIs+zvP7ak1i7JqGdRs8sGvh2Ijqj43qYQLePgb1b/vm8a1bavnc51m+vf05hpd3g3Q==} engines: {node: '>=20'} - '@cspell/cspell-pipe@9.4.0': - resolution: {integrity: sha512-cI0sUe7SB99hJB1T6PhH/MpSrnml1kOekTCE+VH3Eb7zkVP5/mwJXs8BlufdvwBona+Cgkx6jeWlhFpxLc39Yg==} + '@cspell/cspell-performance-monitor@9.8.0': + resolution: {integrity: sha512-IsrXYzn23yJICIQ915ACdf+2lNEcFNTu5BIQt3khHOsGVvZ9/AZYpu9Dk825vUyZG7RHg2Oi6dYNiJtULG4ouQ==} + engines: {node: '>=20.18'} + + '@cspell/cspell-pipe@9.8.0': + resolution: {integrity: sha512-ISEUD8PHYkd2Ktafc6hFfIXdGKYUvthA09NbwwZsWmOqYyk4wWKHZKqyyxD+BcrFwOyMOJcD8OEvIjkRQp2SJw==} engines: {node: '>=20'} - '@cspell/cspell-resolver@9.4.0': - resolution: {integrity: sha512-o9gbbdXlhxG2rqtGqQ7xZ8MGDDsPLbskBnTeuA++ix4Ch/HdjrBNmKReIGAEqJPfP+JGgoEKqFISHUDKAJ/ygQ==} + '@cspell/cspell-resolver@9.8.0': + resolution: {integrity: sha512-PZJj56BZpKfMxOzWkyt7b+aIXObe+8Ku/zLI4xDXPSuQPENbHBFHfPIZx68CyGEkanKxZ1ewKVx/FT1FUy+wDA==} engines: {node: '>=20'} - '@cspell/cspell-service-bus@9.4.0': - resolution: {integrity: sha512-UottRlFPN6FGUfojx5HtUPZTeYXg2rf2HvO/HLh0KicirVYO16vFxTevg9MyOvw1EXSsDRz8ECANjiE7fnzBCQ==} + '@cspell/cspell-service-bus@9.8.0': + resolution: {integrity: sha512-P45sd2nqwcqhulBBbQnZB/JNcobecTrP4Ky3vmEq0cprsvavc+ZoHF9U2Ql5ghMSUzjrF2n1aNzZ8cH4IlsnKg==} engines: {node: '>=20'} - '@cspell/cspell-types@9.4.0': - resolution: {integrity: sha512-vSpd50OfmthBH0aRFRLA2zJFtwli3ntHA0WAOJ8tIMLUCJgF3udooRXFeX3wR8ri69C9mc3864LC4inyRC/E9w==} + '@cspell/cspell-types@9.8.0': + resolution: {integrity: sha512-7Ge4UD6SCA49Tcc3+GTlz3Xn4cqVUAXtDO0u9IeHvJgkN3Me2Rw2GB/CtGmhKST3YeEeZMX7ww09TdHMUJlehw==} engines: {node: '>=20'} + '@cspell/cspell-worker@9.8.0': + resolution: {integrity: sha512-W8FLdE3MXPLbWtAXciILQhk9CHd6Mt+HRjZHM8m+dwE1Bc2TAjUai8kIxsdhHUq58p7gYY2ekr5sg1uYOUgTAA==} + engines: {node: '>=20.18'} + '@cspell/dict-ada@4.1.1': resolution: {integrity: sha512-E+0YW9RhZod/9Qy2gxfNZiHJjCYFlCdI69br1eviQQWB8yOTJX0JHXLs79kOYhSW0kINPVUdvddEBe6Lu6CjGQ==} '@cspell/dict-al@1.1.1': resolution: {integrity: sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==} - '@cspell/dict-aws@4.0.16': - resolution: {integrity: sha512-a681zShZbtTo947NvTYGLer95ZDQw1ROKvIFydak1e0OlfFCsNdtcYTupn0nbbYs53c9AO7G2DU8AcNEAnwXPA==} + '@cspell/dict-aws@4.0.17': + resolution: {integrity: sha512-ORcblTWcdlGjIbWrgKF+8CNEBQiLVKdUOFoTn0KPNkAYnFcdPP0muT4892h7H4Xafh3j72wqB4/loQ6Nti9E/w==} '@cspell/dict-bash@4.2.2': resolution: {integrity: sha512-kyWbwtX3TsCf5l49gGQIZkRLaB/P8g73GDRm41Zu8Mv51kjl2H7Au0TsEvHv7jzcsRLS6aUYaZv6Zsvk1fOz+Q==} - '@cspell/dict-companies@3.2.7': - resolution: {integrity: sha512-fEyr3LmpFKTaD0LcRhB4lfW1AmULYBqzg4gWAV0dQCv06l+TsA+JQ+3pZJbUcoaZirtgsgT3dL3RUjmGPhUH0A==} + '@cspell/dict-companies@3.2.11': + resolution: {integrity: sha512-0cmafbcz2pTHXLd59eLR1gvDvN6aWAOM0+cIL4LLF9GX9yB2iKDNrKsvs4tJRqutoaTdwNFBbV0FYv+6iCtebQ==} - '@cspell/dict-cpp@6.0.15': - resolution: {integrity: sha512-N7MKK3llRNoBncygvrnLaGvmjo4xzVr5FbtAc9+MFGHK6/LeSySBupr1FM72XDaVSIsmBEe7sDYCHHwlI9Jb2w==} + '@cspell/dict-cpp@7.0.2': + resolution: {integrity: sha512-dfbeERiVNeqmo/npivdR6rDiBCqZi3QtjH2Z0HFcXwpdj6i97dX1xaKyK2GUsO/p4u1TOv63Dmj5Vm48haDpuA==} '@cspell/dict-cryptocurrencies@5.0.5': resolution: {integrity: sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==} - '@cspell/dict-csharp@4.0.7': - resolution: {integrity: sha512-H16Hpu8O/1/lgijFt2lOk4/nnldFtQ4t8QHbyqphqZZVE5aS4J/zD/WvduqnLY21aKhZS6jo/xF5PX9jyqPKUA==} + '@cspell/dict-csharp@4.0.8': + resolution: {integrity: sha512-qmk45pKFHSxckl5mSlbHxmDitSsGMlk/XzFgt7emeTJWLNSTUK//MbYAkBNRtfzB4uD7pAFiKgpKgtJrTMRnrQ==} - '@cspell/dict-css@4.0.18': - resolution: {integrity: sha512-EF77RqROHL+4LhMGW5NTeKqfUd/e4OOv6EDFQ/UQQiFyWuqkEKyEz0NDILxOFxWUEVdjT2GQ2cC7t12B6pESwg==} + '@cspell/dict-css@4.1.1': + resolution: {integrity: sha512-y/Vgo6qY08e1t9OqR56qjoFLBCpi4QfWMf2qzD1l9omRZwvSMQGRPz4x0bxkkkU4oocMAeztjzCsmLew//c/8w==} - '@cspell/dict-dart@2.3.1': - resolution: {integrity: sha512-xoiGnULEcWdodXI6EwVyqpZmpOoh8RA2Xk9BNdR7DLamV/QMvEYn8KJ7NlRiTSauJKPNkHHQ5EVHRM6sTS7jdg==} + '@cspell/dict-dart@2.3.2': + resolution: {integrity: sha512-sUiLW56t9gfZcu8iR/5EUg+KYyRD83Cjl3yjDEA2ApVuJvK1HhX+vn4e4k4YfjpUQMag8XO2AaRhARE09+/rqw==} - '@cspell/dict-data-science@2.0.12': - resolution: {integrity: sha512-vI/mg6cI28IkFcpeINS7cm5M9HWemmXSTnxJiu3nmc4VAGx35SXIEyuLGBcsVzySvDablFYf4hsEpmg1XpVsUQ==} + '@cspell/dict-data-science@2.0.13': + resolution: {integrity: sha512-l1HMEhBJkPmw4I2YGVu2eBSKM89K9pVF+N6qIr5Uo5H3O979jVodtuwP8I7LyPrJnC6nz28oxeGRCLh9xC5CVA==} - '@cspell/dict-django@4.1.5': - resolution: {integrity: sha512-AvTWu99doU3T8ifoMYOMLW2CXKvyKLukPh1auOPwFGHzueWYvBBN+OxF8wF7XwjTBMMeRleVdLh3aWCDEX/ZWg==} + '@cspell/dict-django@4.1.6': + resolution: {integrity: sha512-SdbSFDGy9ulETqNz15oWv2+kpWLlk8DJYd573xhIkeRdcXOjskRuxjSZPKfW7O3NxN/KEf3gm3IevVOiNuFS+w==} - '@cspell/dict-docker@1.1.16': - resolution: {integrity: sha512-UiVQ5RmCg6j0qGIxrBnai3pIB+aYKL3zaJGvXk1O/ertTKJif9RZikKXCEgqhaCYMweM4fuLqWSVmw3hU164Iw==} + '@cspell/dict-docker@1.1.17': + resolution: {integrity: sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ==} - '@cspell/dict-dotnet@5.0.10': - resolution: {integrity: sha512-ooar8BP/RBNP1gzYfJPStKEmpWy4uv/7JCq6FOnJLeD1yyfG3d/LFMVMwiJo+XWz025cxtkM3wuaikBWzCqkmg==} + '@cspell/dict-dotnet@5.0.13': + resolution: {integrity: sha512-xPp7jMnFpOri7tzmqmm/dXMolXz1t2bhNqxYkOyMqXhvs08oc7BFs+EsbDY0X7hqiISgeFZGNqn0dOCr+ncPYw==} '@cspell/dict-elixir@4.0.8': resolution: {integrity: sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==} - '@cspell/dict-en-common-misspellings@2.1.8': - resolution: {integrity: sha512-vDsjRFPQGuAADAiitf82z9Mz3DcqKZi6V5hPAEIFkLLKjFVBcjUsSq59SfL59ElIFb76MtBO0BLifdEbBj+DoQ==} + '@cspell/dict-en-common-misspellings@2.1.12': + resolution: {integrity: sha512-14Eu6QGqyksqOd4fYPuRb58lK1Va7FQK9XxFsRKnZU8LhL3N+kj7YKDW+7aIaAN/0WGEqslGP6lGbQzNti8Akw==} - '@cspell/dict-en-gb-mit@3.1.14': - resolution: {integrity: sha512-b+vEerlHP6rnNf30tmTJb7JZnOq4WAslYUvexOz/L3gDna9YJN3bAnwRJ3At3bdcOcMG7PTv3Pi+C73IR22lNg==} + '@cspell/dict-en-gb-mit@3.1.22': + resolution: {integrity: sha512-xE5Vg6gGdMkZ1Ep6z9SJMMioGkkT1GbxS5Mm0U3Ey1/H68P0G7cJcyiVr1CARxFbLqKE4QUpoV1o6jz1Z5Yl9Q==} - '@cspell/dict-en_us@4.4.24': - resolution: {integrity: sha512-JE+/H2YicHJTneRmgH4GSI21rS+1yGZVl1jfOQgl8iHLC+yTTMtCvueNDMK94CgJACzYAoCsQB70MqiFJJfjLQ==} + '@cspell/dict-en_us@4.4.33': + resolution: {integrity: sha512-zWftVqfUStDA37wO1ZNDN1qMJOfcxELa8ucHW8W8wBAZY3TK5Nb6deLogCK/IJi/Qljf30dwwuqqv84Qqle9Tw==} - '@cspell/dict-filetypes@3.0.14': - resolution: {integrity: sha512-KSXaSMYYNMLLdHEnju1DyRRH3eQWPRYRnOXpuHUdOh2jC44VgQoxyMU7oB3NAhDhZKBPCihabzECsAGFbdKfEA==} + '@cspell/dict-filetypes@3.0.18': + resolution: {integrity: sha512-yU7RKD/x1IWmDLzWeiItMwgV+6bUcU/af23uS0+uGiFUbsY1qWV/D4rxlAAO6Z7no3J2z8aZOkYIOvUrJq0Rcw==} '@cspell/dict-flutter@1.1.1': resolution: {integrity: sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==} - '@cspell/dict-fonts@4.0.5': - resolution: {integrity: sha512-BbpkX10DUX/xzHs6lb7yzDf/LPjwYIBJHJlUXSBXDtK/1HaeS+Wqol4Mlm2+NAgZ7ikIE5DQMViTgBUY3ezNoQ==} + '@cspell/dict-fonts@4.0.6': + resolution: {integrity: sha512-aR/0csY01dNb0A1tw/UmN9rKgHruUxsYsvXu6YlSBJFu60s26SKr/k1o4LavpHTQ+lznlYMqAvuxGkE4Flliqw==} '@cspell/dict-fsharp@1.1.1': resolution: {integrity: sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==} - '@cspell/dict-fullstack@3.2.7': - resolution: {integrity: sha512-IxEk2YAwAJKYCUEgEeOg3QvTL4XLlyArJElFuMQevU1dPgHgzWElFevN5lsTFnvMFA1riYsVinqJJX0BanCFEg==} + '@cspell/dict-fullstack@3.2.9': + resolution: {integrity: sha512-diZX+usW5aZ4/b2T0QM/H/Wl9aNMbdODa1Jq0ReBr/jazmNeWjd+PyqeVgzd1joEaHY+SAnjrf/i9CwKd2ZtWQ==} '@cspell/dict-gaming-terms@1.1.2': resolution: {integrity: sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==} - '@cspell/dict-git@3.0.7': - resolution: {integrity: sha512-odOwVKgfxCQfiSb+nblQZc4ErXmnWEnv8XwkaI4sNJ7cNmojnvogYVeMqkXPjvfrgEcizEEA4URRD2Ms5PDk1w==} + '@cspell/dict-git@3.1.0': + resolution: {integrity: sha512-KEt9zGkxqGy2q1nwH4CbyqTSv5nadpn8BAlDnzlRcnL0Xb3LX9xTgSGShKvzb0bw35lHoYyLWN2ZKAqbC4pgGQ==} - '@cspell/dict-golang@6.0.24': - resolution: {integrity: sha512-rY7PlC3MsHozmjrZWi0HQPUl0BVCV0+mwK0rnMT7pOIXqOe4tWCYMULDIsEk4F0gbIxb5badd2dkCPDYjLnDgA==} + '@cspell/dict-golang@6.0.26': + resolution: {integrity: sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg==} '@cspell/dict-google@1.0.9': resolution: {integrity: sha512-biL65POqialY0i4g6crj7pR6JnBkbsPovB2WDYkj3H4TuC/QXv7Pu5pdPxeUJA6TSCHI7T5twsO4VSVyRxD9CA==} @@ -471,11 +472,11 @@ packages: '@cspell/dict-haskell@4.0.6': resolution: {integrity: sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==} - '@cspell/dict-html-symbol-entities@4.0.4': - resolution: {integrity: sha512-afea+0rGPDeOV9gdO06UW183Qg6wRhWVkgCFwiO3bDupAoyXRuvupbb5nUyqSTsLXIKL8u8uXQlJ9pkz07oVXw==} + '@cspell/dict-html-symbol-entities@4.0.5': + resolution: {integrity: sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA==} - '@cspell/dict-html@4.0.13': - resolution: {integrity: sha512-vHzk2xfqQYPvoXtQtywa6ekIonPrUEwe2uftjry3UNRNl89TtzLJVSkiymKJ3WMb+W/DwKXKIb1tKzcIS8ccIg==} + '@cspell/dict-html@4.0.15': + resolution: {integrity: sha512-GJYnYKoD9fmo2OI0aySEGZOjThnx3upSUvV7mmqUu8oG+mGgzqm82P/f7OqsuvTaInZZwZbo+PwJQd/yHcyFIw==} '@cspell/dict-java@5.0.12': resolution: {integrity: sha512-qPSNhTcl7LGJ5Qp6VN71H8zqvRQK04S08T67knMq9hTA8U7G1sTKzLmBaDOFhq17vNX/+rT+rbRYp+B5Nwza1A==} @@ -489,8 +490,8 @@ packages: '@cspell/dict-kotlin@1.1.1': resolution: {integrity: sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==} - '@cspell/dict-latex@4.0.4': - resolution: {integrity: sha512-YdTQhnTINEEm/LZgTzr9Voz4mzdOXH7YX+bSFs3hnkUHCUUtX/mhKgf1CFvZ0YNM2afjhQcmLaR9bDQVyYBvpA==} + '@cspell/dict-latex@5.1.0': + resolution: {integrity: sha512-qxT4guhysyBt0gzoliXYEBYinkAdEtR2M7goRaUH0a7ltCsoqqAeEV8aXYRIdZGcV77gYSobvu3jJL038tlPAw==} '@cspell/dict-lorem-ipsum@4.0.5': resolution: {integrity: sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==} @@ -501,52 +502,52 @@ packages: '@cspell/dict-makefile@1.0.5': resolution: {integrity: sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==} - '@cspell/dict-markdown@2.0.13': - resolution: {integrity: sha512-rFeGikf+lVlywEp7giATUfi8myFeee6jqgbUgtdIdl/OBmRBPe5m7mKNk7yMItMZe8ICrwMxFwJy5OeTnrr6QA==} + '@cspell/dict-markdown@2.0.16': + resolution: {integrity: sha512-976RRqKv6cwhrxdFCQP2DdnBVB86BF57oQtPHy4Zbf4jF/i2Oy29MCrxirnOBalS1W6KQeto7NdfDXRAwkK4PQ==} peerDependencies: - '@cspell/dict-css': ^4.0.18 - '@cspell/dict-html': ^4.0.13 - '@cspell/dict-html-symbol-entities': ^4.0.4 + '@cspell/dict-css': ^4.1.1 + '@cspell/dict-html': ^4.0.15 + '@cspell/dict-html-symbol-entities': ^4.0.5 '@cspell/dict-typescript': ^3.2.3 - '@cspell/dict-monkeyc@1.0.11': - resolution: {integrity: sha512-7Q1Ncu0urALI6dPTrEbSTd//UK0qjRBeaxhnm8uY5fgYNFYAG+u4gtnTIo59S6Bw5P++4H3DiIDYoQdY/lha8w==} + '@cspell/dict-monkeyc@1.0.12': + resolution: {integrity: sha512-MN7Vs11TdP5mbdNFQP5x2Ac8zOBm97ARg6zM5Sb53YQt/eMvXOMvrep7+/+8NJXs0jkp70bBzjqU4APcqBFNAw==} - '@cspell/dict-node@5.0.8': - resolution: {integrity: sha512-AirZcN2i84ynev3p2/1NCPEhnNsHKMz9zciTngGoqpdItUb2bDt1nJBjwlsrFI78GZRph/VaqTVFwYikmncpXg==} + '@cspell/dict-node@5.0.9': + resolution: {integrity: sha512-hO+ga+uYZ/WA4OtiMEyKt5rDUlUyu3nXMf8KVEeqq2msYvAPdldKBGH7lGONg6R/rPhv53Rb+0Y1SLdoK1+7wQ==} - '@cspell/dict-npm@5.2.25': - resolution: {integrity: sha512-jxhVxM3+ilxbum/N2ejAvVuvet1OrGeW1fD7GagAkHU/2zlzINZkJLDtXk6v1WHUjigfhiAsois3puobv/2A1A==} + '@cspell/dict-npm@5.2.38': + resolution: {integrity: sha512-21ucGRPYYhr91C2cDBoMPTrcIOStQv33xOqJB0JLoC5LAs2Sfj9EoPGhGb+gIFVHz6Ia7JQWE2SJsOVFJD1wmg==} - '@cspell/dict-php@4.1.0': - resolution: {integrity: sha512-dTDeabyOj7eFvn2Q4Za3uVXM2+SzeFMqX8ly2P0XTo4AzbCmI2hulFD/QIADwWmwiRrInbbf8cxwFHNIYrXl4w==} + '@cspell/dict-php@4.1.1': + resolution: {integrity: sha512-EXelI+4AftmdIGtA8HL8kr4WlUE11OqCSVlnIgZekmTkEGSZdYnkFdiJ5IANSALtlQ1mghKjz+OFqVs6yowgWA==} '@cspell/dict-powershell@5.0.15': resolution: {integrity: sha512-l4S5PAcvCFcVDMJShrYD0X6Huv9dcsQPlsVsBGbH38wvuN7gS7+GxZFAjTNxDmTY1wrNi1cCatSg6Pu2BW4rgg==} - '@cspell/dict-public-licenses@2.0.15': - resolution: {integrity: sha512-cJEOs901H13Pfy0fl4dCD1U+xpWIMaEPq8MeYU83FfDZvellAuSo4GqWCripfIqlhns/L6+UZEIJSOZnjgy7Wg==} + '@cspell/dict-public-licenses@2.0.16': + resolution: {integrity: sha512-EQRrPvEOmwhwWezV+W7LjXbIBjiy6y/shrET6Qcpnk3XANTzfvWflf9PnJ5kId/oKWvihFy0za0AV1JHd03pSQ==} - '@cspell/dict-python@4.2.23': - resolution: {integrity: sha512-c0C//tmG4PZWeONtTBPXa6q0ylfz3/BgEcHAR1L0BPWjNUIzTyx9J+hEIUCPYf7eAPeYjaDuTvYlg11igXXE4Q==} + '@cspell/dict-python@4.2.26': + resolution: {integrity: sha512-hbjN6BjlSgZOG2dA2DtvYNGBM5Aq0i0dHaZjMOI9K/9vRicVvKbcCiBSSrR3b+jwjhQL5ff7HwG5xFaaci0GQA==} '@cspell/dict-r@2.1.1': resolution: {integrity: sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==} - '@cspell/dict-ruby@5.0.9': - resolution: {integrity: sha512-H2vMcERMcANvQshAdrVx0XoWaNX8zmmiQN11dZZTQAZaNJ0xatdJoSqY8C8uhEMW89bfgpN+NQgGuDXW2vmXEw==} + '@cspell/dict-ruby@5.1.1': + resolution: {integrity: sha512-LHrp84oEV6q1ZxPPyj4z+FdKyq1XAKYPtmGptrd+uwHbrF/Ns5+fy6gtSi7pS+uc0zk3JdO9w/tPK+8N1/7WUA==} - '@cspell/dict-rust@4.0.12': - resolution: {integrity: sha512-z2QiH+q9UlNhobBJArvILRxV8Jz0pKIK7gqu4TgmEYyjiu1TvnGZ1tbYHeu9w3I/wOP6UMDoCBTty5AlYfW0mw==} + '@cspell/dict-rust@4.1.2': + resolution: {integrity: sha512-O1FHrumYcO+HZti3dHfBPUdnDFkI+nbYK3pxYmiM1sr+G0ebOd6qchmswS0Wsc6ZdEVNiPYJY/gZQR6jfW3uOg==} - '@cspell/dict-scala@5.0.8': - resolution: {integrity: sha512-YdftVmumv8IZq9zu1gn2U7A4bfM2yj9Vaupydotyjuc+EEZZSqAafTpvW/jKLWji2TgybM1L2IhmV0s/Iv9BTw==} + '@cspell/dict-scala@5.0.9': + resolution: {integrity: sha512-AjVcVAELgllybr1zk93CJ5wSUNu/Zb5kIubymR/GAYkMyBdYFCZ3Zbwn4Zz8GJlFFAbazABGOu0JPVbeY59vGg==} '@cspell/dict-shell@1.1.2': resolution: {integrity: sha512-WqOUvnwcHK1X61wAfwyXq04cn7KYyskg90j4lLg3sGGKMW9Sq13hs91pqrjC44Q+lQLgCobrTkMDw9Wyl9nRFA==} - '@cspell/dict-software-terms@5.1.15': - resolution: {integrity: sha512-93VqazVvVtHuKY7seGxbfdtrnPBgZ/hZ/NmFFkBRhkRL6NavaQ6U2QsHpnlVEZN5km3DmaQy1X4ZcvNoSTK/ZQ==} + '@cspell/dict-software-terms@5.2.2': + resolution: {integrity: sha512-0CaYd6TAsKtEoA7tNswm1iptEblTzEe3UG8beG2cpSTHk7afWIVMtJLgXDv0f/Li67Lf3Z1Jf3JeXR7GsJ2TRw==} '@cspell/dict-sql@2.2.1': resolution: {integrity: sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==} @@ -569,20 +570,24 @@ packages: '@cspell/dict-zig@1.0.0': resolution: {integrity: sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ==} - '@cspell/dynamic-import@9.4.0': - resolution: {integrity: sha512-d2fjLjzrKGUIn5hWK8gMuyAh2pqXSxBqOHpU1jR3jxbrO3MilunKNijaSstv7CZn067Jpc36VfaKQodaXNZzUA==} + '@cspell/dynamic-import@9.8.0': + resolution: {integrity: sha512-wMgb32lqG9g6lCipUQsY9Bk5idXPDz7wvzOqEsU1M2HmNYmdE1wfPoRpfQfsVL965iG3+6h8QLr2+8FKpweFEQ==} engines: {node: '>=20'} - '@cspell/filetypes@9.4.0': - resolution: {integrity: sha512-RMrYHkvPF0tHVFM+T4voEhX9sfYQrd/mnNbf6+O4CWUyLCz4NQ5H9yOgEIJwEcLu4y3NESGXFef/Jn5xo0CUfg==} + '@cspell/filetypes@9.8.0': + resolution: {integrity: sha512-yHvtYn9qt6zykua77sNzTcf7HrG/dpo/+2pCMGSrfSrQypSNT6FUFvMS04W7kwhP86U1GkCjppNykXuoH3cqug==} engines: {node: '>=20'} - '@cspell/strong-weak-map@9.4.0': - resolution: {integrity: sha512-ui7mlXYmqElS/SmRubPBNWdkQVWgWbB6rjCurc+0owYXlnweItAMHTxC8mCWM/Au22SF1dB/JR8QBELFXLkTjQ==} + '@cspell/rpc@9.8.0': + resolution: {integrity: sha512-t4lHEa254W+PePXNQ1noW7QhQxz/mhsJ9X8LEt0ILzBbPWCJzN+JuaM7EiolIPiwxtfxpMwKx9482kt4eTja7A==} + engines: {node: '>=20.18'} + + '@cspell/strong-weak-map@9.8.0': + resolution: {integrity: sha512-HocksAqZ0JcWA5oWO7TIlOCftXVGkPGzbeFlCRRrjJpZmYQH+4NdeEXyQC6T89NGocp45td/CgyBcAaFMy1N9w==} engines: {node: '>=20'} - '@cspell/url@9.4.0': - resolution: {integrity: sha512-nt88P6m20AaVbqMxsyPf8KqyWPaFEW2UANi0ijBxc2xTkD2KiUovxfZUYW6NMU9XBYZlovT5LztkEhst2yBcSA==} + '@cspell/url@9.8.0': + resolution: {integrity: sha512-LY1lFiZLTQF/ma1ilfKmRmFmEOw0RfYhyl0UMhY7/d93b+kiDMhxP/9Qir4+5LyiRncaE3++ZcWno9Hya+ssRg==} engines: {node: '>=20'} '@emnapi/runtime@1.8.1': @@ -591,23 +596,17 @@ packages: '@emotion/hash@0.9.2': resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} - '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.27.0': resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} - cpu: [arm64] - os: [android] + cpu: [ppc64] + os: [aix] '@esbuild/android-arm64@0.27.0': resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} @@ -615,10 +614,10 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} engines: {node: '>=18'} - cpu: [arm] + cpu: [arm64] os: [android] '@esbuild/android-arm@0.27.0': @@ -627,10 +626,10 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm] os: [android] '@esbuild/android-x64@0.27.0': @@ -639,11 +638,11 @@ packages: cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] + cpu: [x64] + os: [android] '@esbuild/darwin-arm64@0.27.0': resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} @@ -651,10 +650,10 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.27.0': @@ -663,11 +662,11 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] + cpu: [x64] + os: [darwin] '@esbuild/freebsd-arm64@0.27.0': resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==} @@ -675,10 +674,10 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.27.0': @@ -687,11 +686,11 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} engines: {node: '>=18'} - cpu: [arm64] - os: [linux] + cpu: [x64] + os: [freebsd] '@esbuild/linux-arm64@0.27.0': resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} @@ -699,10 +698,10 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} engines: {node: '>=18'} - cpu: [arm] + cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.27.0': @@ -711,10 +710,10 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} engines: {node: '>=18'} - cpu: [ia32] + cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.27.0': @@ -723,10 +722,10 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} engines: {node: '>=18'} - cpu: [loong64] + cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.27.0': @@ -735,10 +734,10 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} engines: {node: '>=18'} - cpu: [mips64el] + cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.27.0': @@ -747,10 +746,10 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} engines: {node: '>=18'} - cpu: [ppc64] + cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.27.0': @@ -759,10 +758,10 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} engines: {node: '>=18'} - cpu: [riscv64] + cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.27.0': @@ -771,10 +770,10 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} engines: {node: '>=18'} - cpu: [s390x] + cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.27.0': @@ -783,10 +782,10 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} engines: {node: '>=18'} - cpu: [x64] + cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.27.0': @@ -795,11 +794,11 @@ packages: cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] + cpu: [x64] + os: [linux] '@esbuild/netbsd-arm64@0.27.0': resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} @@ -807,10 +806,10 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.27.0': @@ -819,11 +818,11 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] + cpu: [x64] + os: [netbsd] '@esbuild/openbsd-arm64@0.27.0': resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} @@ -831,10 +830,10 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.27.0': @@ -843,11 +842,11 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] + cpu: [x64] + os: [openbsd] '@esbuild/openharmony-arm64@0.27.0': resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==} @@ -855,11 +854,11 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} engines: {node: '>=18'} - cpu: [x64] - os: [sunos] + cpu: [arm64] + os: [openharmony] '@esbuild/sunos-x64@0.27.0': resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} @@ -867,11 +866,11 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} engines: {node: '>=18'} - cpu: [arm64] - os: [win32] + cpu: [x64] + os: [sunos] '@esbuild/win32-arm64@0.27.0': resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} @@ -879,10 +878,10 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} engines: {node: '>=18'} - cpu: [ia32] + cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.27.0': @@ -891,10 +890,10 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} engines: {node: '>=18'} - cpu: [x64] + cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.27.0': @@ -903,6 +902,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@fast-csv/format@4.3.5': resolution: {integrity: sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==} @@ -934,8 +939,8 @@ packages: resolution: {integrity: sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==} engines: {node: '>=6'} - '@hono/node-server@1.19.6': - resolution: {integrity: sha512-Shz/KjlIeAhfiuE93NDKVdZ7HdBVLQAfdbaXEaoAVO3ic9ibRSLGIQGkcBbFyuLr+7/1D5ZCINM8B+6IvXeMtw==} + '@hono/node-server@1.19.13': + resolution: {integrity: sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 @@ -2020,220 +2025,216 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - '@smithy/abort-controller@4.2.8': - resolution: {integrity: sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==} + '@smithy/chunked-blob-reader-native@4.2.3': + resolution: {integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==} engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader-native@4.2.1': - resolution: {integrity: sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==} + '@smithy/chunked-blob-reader@5.2.2': + resolution: {integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==} engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader@5.2.0': - resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==} + '@smithy/config-resolver@4.4.14': + resolution: {integrity: sha512-N55f8mPEccpzKetUagdvmAy8oohf0J5cuj9jLI1TaSceRlq0pJsIZepY3kmAXAhyxqXPV6hDerDQhqQPKWgAoQ==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.4.6': - resolution: {integrity: sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==} + '@smithy/core@3.23.14': + resolution: {integrity: sha512-vJ0IhpZxZAkFYOegMKSrxw7ujhhT2pass/1UEcZ4kfl5srTAqtPU5I7MdYQoreVas3204ykCiNhY1o7Xlz6Yyg==} engines: {node: '>=18.0.0'} - '@smithy/core@3.20.7': - resolution: {integrity: sha512-aO7jmh3CtrmPsIJxUwYIzI5WVlMK8BMCPQ4D4nTzqTqBhbzvxHNzBMGcEg13yg/z9R2Qsz49NUFl0F0lVbTVFw==} + '@smithy/credential-provider-imds@4.2.13': + resolution: {integrity: sha512-wboCPijzf6RJKLOvnjDAiBxGSmSnGXj35o5ZAWKDaHa/cvQ5U3ZJ13D4tMCE8JG4dxVAZFy/P0x/V9CwwdfULQ==} engines: {node: '>=18.0.0'} - '@smithy/credential-provider-imds@4.2.8': - resolution: {integrity: sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==} + '@smithy/eventstream-codec@4.2.13': + resolution: {integrity: sha512-vYahwBAtRaAcFbOmE9aLr12z7RiHYDSLcnogSdxfm7kKfsNa3wH+NU5r7vTeB5rKvLsWyPjVX8iH94brP7umiQ==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-codec@4.2.8': - resolution: {integrity: sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==} + '@smithy/eventstream-serde-browser@4.2.13': + resolution: {integrity: sha512-wwybfcOX0tLqCcBP378TIU9IqrDuZq/tDV48LlZNydMpCnqnYr+hWBAYbRE+rFFf/p7IkDJySM3bgiMKP2ihPg==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-browser@4.2.8': - resolution: {integrity: sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==} + '@smithy/eventstream-serde-config-resolver@4.3.13': + resolution: {integrity: sha512-ied1lO559PtAsMJzg2TKRlctLnEi1PfkNeMMpdwXDImk1zV9uvS/Oxoy/vcy9uv1GKZAjDAB5xT6ziE9fzm5wA==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-config-resolver@4.3.8': - resolution: {integrity: sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==} + '@smithy/eventstream-serde-node@4.2.13': + resolution: {integrity: sha512-hFyK+ORJrxAN3RYoaD6+gsGDQjeix8HOEkosoajvXYZ4VeqonM3G4jd9IIRm/sWGXUKmudkY9KdYjzosUqdM8A==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-node@4.2.8': - resolution: {integrity: sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==} + '@smithy/eventstream-serde-universal@4.2.13': + resolution: {integrity: sha512-kRrq4EKLGeOxhC2CBEhRNcu1KSzNJzYY7RK3S7CxMPgB5dRrv55WqQOtRwQxQLC04xqORFLUgnDlc6xrNUULaA==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-universal@4.2.8': - resolution: {integrity: sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==} + '@smithy/fetch-http-handler@5.3.16': + resolution: {integrity: sha512-nYDRUIvNd4mFmuXraRWt6w5UsZTNqtj4hXJA/iiOD4tuseIdLP9Lq38teH/SZTcIFCa2f+27o7hYpIsWktJKEQ==} engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@5.3.9': - resolution: {integrity: sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==} + '@smithy/hash-blob-browser@4.2.14': + resolution: {integrity: sha512-rtQ5es8r/5v4rav7q5QTsfx9CtCyzrz/g7ZZZBH2xtMmd6G/KQrLOWfSHTvFOUPlVy59RQvxeBYJaLRoybMEyA==} engines: {node: '>=18.0.0'} - '@smithy/hash-blob-browser@4.2.9': - resolution: {integrity: sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==} + '@smithy/hash-node@4.2.13': + resolution: {integrity: sha512-4/oy9h0jjmY80a2gOIo75iLl8TOPhmtx4E2Hz+PfMjvx/vLtGY4TMU/35WRyH2JHPfT5CVB38u4JRow7gnmzJA==} engines: {node: '>=18.0.0'} - '@smithy/hash-node@4.2.8': - resolution: {integrity: sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==} + '@smithy/hash-stream-node@4.2.13': + resolution: {integrity: sha512-WdQ7HwUjINXETeh6dqUeob1UHIYx8kAn9PSp1HhM2WWegiZBYVy2WXIs1lB07SZLan/udys9SBnQGt9MQbDpdg==} engines: {node: '>=18.0.0'} - '@smithy/hash-stream-node@4.2.8': - resolution: {integrity: sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==} - engines: {node: '>=18.0.0'} - - '@smithy/invalid-dependency@4.2.8': - resolution: {integrity: sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==} + '@smithy/invalid-dependency@4.2.13': + resolution: {integrity: sha512-jvC0RB/8BLj2SMIkY0Npl425IdnxZJxInpZJbu563zIRnVjpDMXevU3VMCRSabaLB0kf/eFIOusdGstrLJ8IDg==} engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} - '@smithy/is-array-buffer@4.2.0': - resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} engines: {node: '>=18.0.0'} - '@smithy/md5-js@4.2.8': - resolution: {integrity: sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==} + '@smithy/md5-js@4.2.13': + resolution: {integrity: sha512-cNm7I9NXolFxtS20ojROddOEpSAeI1Obq6pd1Kj5HtHws3s9Fkk8DdHDfQSs5KuxCewZuVK6UqrJnfJmiMzDuQ==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@4.2.8': - resolution: {integrity: sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==} + '@smithy/middleware-content-length@4.2.13': + resolution: {integrity: sha512-IPMLm/LE4AZwu6qiE8Rr8vJsWhs9AtOdySRXrOM7xnvclp77Tyh7hMs/FRrMf26kgIe67vFJXXOSmVxS7oKeig==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.8': - resolution: {integrity: sha512-TV44qwB/T0OMMzjIuI+JeS0ort3bvlPJ8XIH0MSlGADraXpZqmyND27ueuAL3E14optleADWqtd7dUgc2w+qhQ==} + '@smithy/middleware-endpoint@4.4.29': + resolution: {integrity: sha512-R9Q/58U+qBiSARGWbAbFLczECg/RmysRksX6Q8BaQEpt75I7LI6WGDZnjuC9GXSGKljEbA7N118LhGaMbfrTXw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.24': - resolution: {integrity: sha512-yiUY1UvnbUFfP5izoKLtfxDSTRv724YRRwyiC/5HYY6vdsVDcDOXKSXmkJl/Hovcxt5r+8tZEUAdrOaCJwrl9Q==} + '@smithy/middleware-retry@4.5.0': + resolution: {integrity: sha512-/NzISn4grj/BRFVua/xnQwF+7fakYZgimpw2dfmlPgcqecBMKxpB9g5mLYRrmBD5OrPoODokw4Vi1hrSR4zRyw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.2.9': - resolution: {integrity: sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==} + '@smithy/middleware-serde@4.2.17': + resolution: {integrity: sha512-0T2mcaM6v9W1xku86Dk0bEW7aEseG6KenFkPK98XNw0ZhOqOiD1MrMsdnQw9QsL3/Oa85T53iSMlm0SZdSuIEQ==} engines: {node: '>=18.0.0'} - '@smithy/middleware-stack@4.2.8': - resolution: {integrity: sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==} + '@smithy/middleware-stack@4.2.13': + resolution: {integrity: sha512-g72jN/sGDLyTanrCLH9fhg3oysO3f7tQa6eWWsMyn2BiYNCgjF24n4/I9wff/5XidFvjj9ilipAoQrurTUrLvw==} engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@4.3.8': - resolution: {integrity: sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==} + '@smithy/node-config-provider@4.3.13': + resolution: {integrity: sha512-iGxQ04DsKXLckbgnX4ipElrOTk+IHgTyu0q0WssZfYhDm9CQWHmu6cOeI5wmWRxpXbBDhIIfXMWz5tPEtcVqbw==} engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.4.8': - resolution: {integrity: sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==} + '@smithy/node-http-handler@4.5.2': + resolution: {integrity: sha512-/oD7u8M0oj2ZTFw7GkuuHWpIxtWdLlnyNkbrWcyVYhd5RJNDuczdkb0wfnQICyNFrVPlr8YHOhamjNy3zidhmA==} engines: {node: '>=18.0.0'} - '@smithy/property-provider@4.2.8': - resolution: {integrity: sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==} + '@smithy/property-provider@4.2.13': + resolution: {integrity: sha512-bGzUCthxRmezuxkbu9wD33wWg9KX3hJpCXpQ93vVkPrHn9ZW6KNNdY5xAUWNuRCwQ+VyboFuWirG1lZhhkcyRQ==} engines: {node: '>=18.0.0'} - '@smithy/protocol-http@5.3.8': - resolution: {integrity: sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==} + '@smithy/protocol-http@5.3.13': + resolution: {integrity: sha512-+HsmuJUF4u8POo6s8/a2Yb/AQ5t/YgLovCuHF9oxbocqv+SZ6gd8lC2duBFiCA/vFHoHQhoq7QjqJqZC6xOxxg==} engines: {node: '>=18.0.0'} - '@smithy/querystring-builder@4.2.8': - resolution: {integrity: sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==} + '@smithy/querystring-builder@4.2.13': + resolution: {integrity: sha512-tG4aOYFCZdPMjbgfhnIQ322H//ojujldp1SrHPHpBSb3NqgUp3dwiUGRJzie87hS1DYwWGqDuPaowoDF+rYCbQ==} engines: {node: '>=18.0.0'} - '@smithy/querystring-parser@4.2.8': - resolution: {integrity: sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==} + '@smithy/querystring-parser@4.2.13': + resolution: {integrity: sha512-hqW3Q4P+CDzUyQ87GrboGMeD7XYNMOF+CuTwu936UQRB/zeYn3jys8C3w+wMkDfY7CyyyVwZQ5cNFoG0x1pYmA==} engines: {node: '>=18.0.0'} - '@smithy/service-error-classification@4.2.8': - resolution: {integrity: sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==} + '@smithy/service-error-classification@4.2.13': + resolution: {integrity: sha512-a0s8XZMfOC/qpqq7RCPvJlk93rWFrElH6O++8WJKz0FqnA4Y7fkNi/0mnGgSH1C4x6MFsuBA8VKu4zxFrMe5Vw==} engines: {node: '>=18.0.0'} - '@smithy/shared-ini-file-loader@4.4.3': - resolution: {integrity: sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==} + '@smithy/shared-ini-file-loader@4.4.8': + resolution: {integrity: sha512-VZCZx2bZasxdqxVgEAhREvDSlkatTPnkdWy1+Kiy8w7kYPBosW0V5IeDwzDUMvWBt56zpK658rx1cOBFOYaPaw==} engines: {node: '>=18.0.0'} - '@smithy/signature-v4@5.3.8': - resolution: {integrity: sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==} + '@smithy/signature-v4@5.3.13': + resolution: {integrity: sha512-YpYSyM0vMDwKbHD/JA7bVOF6kToVRpa+FM5ateEVRpsTNu564g1muBlkTubXhSKKYXInhpADF46FPyrZcTLpXg==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.10.9': - resolution: {integrity: sha512-Je0EvGXVJ0Vrrr2lsubq43JGRIluJ/hX17aN/W/A0WfE+JpoMdI8kwk2t9F0zTX9232sJDGcoH4zZre6m6f/sg==} + '@smithy/smithy-client@4.12.9': + resolution: {integrity: sha512-ovaLEcTU5olSeHcRXcxV6viaKtpkHZumn6Ps0yn7dRf2rRSfy794vpjOtrWDO0d1auDSvAqxO+lyhERSXQ03EQ==} engines: {node: '>=18.0.0'} - '@smithy/types@4.12.0': - resolution: {integrity: sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==} + '@smithy/types@4.14.0': + resolution: {integrity: sha512-OWgntFLW88kx2qvf/c/67Vno1yuXm/f9M7QFAtVkkO29IJXGBIg0ycEaBTH0kvCtwmvZxRujrgP5a86RvsXJAQ==} engines: {node: '>=18.0.0'} - '@smithy/url-parser@4.2.8': - resolution: {integrity: sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==} + '@smithy/url-parser@4.2.13': + resolution: {integrity: sha512-2G03yoboIRZlZze2+PT4GZEjgwQsJjUgn6iTsvxA02bVceHR6vp4Cuk7TUnPFWKF+ffNUk3kj4COwkENS2K3vw==} engines: {node: '>=18.0.0'} - '@smithy/util-base64@4.3.0': - resolution: {integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==} + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-browser@4.2.0': - resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-node@4.2.1': - resolution: {integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==} + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} engines: {node: '>=14.0.0'} - '@smithy/util-buffer-from@4.2.0': - resolution: {integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==} + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} engines: {node: '>=18.0.0'} - '@smithy/util-config-provider@4.2.0': - resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.3.23': - resolution: {integrity: sha512-mMg+r/qDfjfF/0psMbV4zd7F/i+rpyp7Hjh0Wry7eY15UnzTEId+xmQTGDU8IdZtDfbGQxuWNfgBZKBj+WuYbA==} + '@smithy/util-defaults-mode-browser@4.3.45': + resolution: {integrity: sha512-ag9sWc6/nWZAuK3Wm9KlFJUnRkXLrXn33RFjIAmCTFThqLHY+7wCst10BGq56FxslsDrjhSie46c8OULS+BiIw==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.26': - resolution: {integrity: sha512-EQqe/WkbCinah0h1lMWh9ICl0Ob4lyl20/10WTB35SC9vDQfD8zWsOT+x2FIOXKAoZQ8z/y0EFMoodbcqWJY/w==} + '@smithy/util-defaults-mode-node@4.2.49': + resolution: {integrity: sha512-jlN6vHwE8gY5AfiFBavtD3QtCX2f7lM3BKkz7nFKSNfFR5nXLXLg6sqXTJEEyDwtxbztIDBQCfjsGVXlIru2lQ==} engines: {node: '>=18.0.0'} - '@smithy/util-endpoints@3.2.8': - resolution: {integrity: sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==} + '@smithy/util-endpoints@3.3.4': + resolution: {integrity: sha512-BKoR/ubPp9KNKFxPpg1J28N1+bgu8NGAtJblBP7yHy8yQPBWhIAv9+l92SlQLpolGm71CVO+btB60gTgzT0wog==} engines: {node: '>=18.0.0'} - '@smithy/util-hex-encoding@4.2.0': - resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} engines: {node: '>=18.0.0'} - '@smithy/util-middleware@4.2.8': - resolution: {integrity: sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==} + '@smithy/util-middleware@4.2.13': + resolution: {integrity: sha512-GTooyrlmRTqvUen4eK7/K1p6kryF7bnDfq6XsAbIsf2mo51B/utaH+XThY6dKgNCWzMAaH/+OLmqaBuLhLWRow==} engines: {node: '>=18.0.0'} - '@smithy/util-retry@4.2.8': - resolution: {integrity: sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==} + '@smithy/util-retry@4.3.0': + resolution: {integrity: sha512-tSOPQNT/4KfbvqeMovWC3g23KSYy8czHd3tlN+tOYVNIDLSfxIsrPJihYi5TpNcoV789KWtgChUVedh2y6dDPg==} engines: {node: '>=18.0.0'} - '@smithy/util-stream@4.5.10': - resolution: {integrity: sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==} + '@smithy/util-stream@4.5.22': + resolution: {integrity: sha512-3H8iq/0BfQjUs2/4fbHZ9aG9yNzcuZs24LPkcX1Q7Z+qpqaGM8+qbGmE8zo9m2nCRgamyvS98cHdcWvR6YUsew==} engines: {node: '>=18.0.0'} - '@smithy/util-uri-escape@4.2.0': - resolution: {integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==} + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} engines: {node: '>=18.0.0'} '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} - '@smithy/util-utf8@4.2.0': - resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} engines: {node: '>=18.0.0'} - '@smithy/util-waiter@4.2.8': - resolution: {integrity: sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==} + '@smithy/util-waiter@4.2.15': + resolution: {integrity: sha512-oUt9o7n8hBv3BL56sLSneL0XeigZSuem0Hr78JaoK33D9oKieyCvVP8eTSe3j7g2mm/S1DvzxKieG7JEWNJUNg==} engines: {node: '>=18.0.0'} - '@smithy/uuid@1.1.0': - resolution: {integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==} + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} engines: {node: '>=18.0.0'} '@standard-schema/spec@1.0.0': @@ -2443,10 +2444,6 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/exceljs@1.3.2': - resolution: {integrity: sha512-etq4LpmL5vZKzf29U5u7cWjoCWGc67hTc2WrvwmcBUyRiHydUPg5ZXGFwBHcEkTLinyT6fRmJiVSRwf4RdnjUA==} - deprecated: This is a stub types definition. exceljs provides its own type definitions, so you do not need this installed. - '@types/geojson@7946.0.16': resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} @@ -2578,8 +2575,8 @@ packages: peerDependencies: postcss: ^8.1.0 - axios@1.13.2: - resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + axios@1.14.0: + resolution: {integrity: sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==} bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -2616,14 +2613,14 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - bowser@2.13.1: - resolution: {integrity: sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==} + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@1.1.13: + resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@2.0.3: + resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} browserslist@4.28.1: resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} @@ -2732,8 +2729,8 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - commander@14.0.2: - resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} commander@7.2.0: @@ -2744,8 +2741,8 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - comment-json@4.4.1: - resolution: {integrity: sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==} + comment-json@4.6.2: + resolution: {integrity: sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==} engines: {node: '>= 6'} compress-commons@4.1.2: @@ -2799,43 +2796,45 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - cspell-config-lib@9.4.0: - resolution: {integrity: sha512-CvQKSmK/DRIf3LpNx2sZth65pHW2AHngZqLkH3DTwnAPbiCAsE0XvCrVhvDfCNu/6uJIaa+NVHSs8GOf//DHBQ==} + cspell-config-lib@9.8.0: + resolution: {integrity: sha512-gMJBAgYPvvO+uDFLUcGWaTu6/e+r8mm4GD4rQfWa/yV4F9fj+yOYLIMZqLWRvT1moHZX1FxyVvUbJcmZ1gfebg==} engines: {node: '>=20'} - cspell-dictionary@9.4.0: - resolution: {integrity: sha512-c2qscanRZChoHZFYI7KpvBMdy8i6wNwl2EflcNRrFiFOq67t9CgxLe54PafaqhrHGpBc8nElaZKciLvjj6Uscw==} + cspell-dictionary@9.8.0: + resolution: {integrity: sha512-QW4hdkWcrxZA1QNqi26U0S/U3/V+tKCm7JaaesEJW2F6Ao+23AbHVwidyAVtXaEhGkn6PxB+epKrrAa6nE69qA==} engines: {node: '>=20'} - cspell-gitignore@9.4.0: - resolution: {integrity: sha512-HMrzLmJBUMSpaMMkltlTAz/aVOrHxixyhKfg5WbFCJ5JYZO6Qu3/JU3wRoOFoud9449wRjLkvrGmbbL2+vO6Lw==} + cspell-gitignore@9.8.0: + resolution: {integrity: sha512-SDUa1DmSfT20+JH7XtyzcEL9KfurneoR/XbmlrtPQZP/LUHXh3yz4x/0vFIkEFXNWdSckY0QdWTz8DaxClCf4Q==} engines: {node: '>=20'} hasBin: true - cspell-glob@9.4.0: - resolution: {integrity: sha512-Q87Suj9oXrhoKck15qWorCizBjMNxG/k3NjnhKIAMrF+PdUa1Mpl0MOD+hqV1Wvwh1UHcIMYCP3bR3XpBbNx+Q==} + cspell-glob@9.8.0: + resolution: {integrity: sha512-Uvj/iHXs+jpsJyIEnhEoJTWXb1GVyZ9T05L5JFtZfsQNXrh8SRDQPscjxbg4okKr63N7WevfioQum/snHNYvmw==} engines: {node: '>=20'} - cspell-grammar@9.4.0: - resolution: {integrity: sha512-ie7OQ4Neflo+61bMzoLR7GtlZfMBAm2KL1U4iNqh15wUE5fDbvXeN15H5lu+gcO8BwYvC5wxZknw1x62/J8+3Q==} + cspell-grammar@9.8.0: + resolution: {integrity: sha512-01XMq2vhPS0Gvxnfed9uvOwH+3cXddHYxW0PwCE+SZdcC6TN8yM6glByuLt1qFustAmQVE5GSr7uAY9o4pZQRg==} engines: {node: '>=20'} hasBin: true - cspell-io@9.4.0: - resolution: {integrity: sha512-8w30dqlO54H9w6WGlvZhHI5kytVbF3bYPqKJAZLWKEO36L2mdpf6/abx/FA4yVLJ56wmH1x0N0ZK32wNRl5C6A==} + cspell-io@9.8.0: + resolution: {integrity: sha512-JINaEWQEzR4f2upwdZOFcft+nBvQgizJfrOLszxG3p+BIzljnGklqE/nUtLFZpBu0oMJvuM/Fd+GsWor0yP7Xw==} engines: {node: '>=20'} - cspell-lib@9.4.0: - resolution: {integrity: sha512-ajjioE59IEDNUPawfaBpiMfGC32iKPkuYd4T9ftguuef8VvyKRifniiUi1nxwGgAhzSfxHvWs7qdT+29Pp5TMQ==} + cspell-lib@9.8.0: + resolution: {integrity: sha512-G2TtPcye5QE5ev3YgWq42UOJLpTZ6naO/47oIm+jmeSYbgnbcOSThnEE7uMycx+TTNOz/vJVFpZmQyt0bWCftw==} engines: {node: '>=20'} - cspell-trie-lib@9.4.0: - resolution: {integrity: sha512-bySJTm8XDiJAoC1MDo4lE/KpSNxydo13ZETC8TF7Hb3rbWI1c6o5eZ4+i/tkG3M94OvKV91+MeAvoMCe7GGgAw==} + cspell-trie-lib@9.8.0: + resolution: {integrity: sha512-GXIyqxya8QLp6SjKsAN9w3apvt1Ww7GKcZvTBaP76OfLoyb1QC6unwmObY2cZs1manCntGwHrgU6vFNuXnTzpw==} engines: {node: '>=20'} + peerDependencies: + '@cspell/cspell-types': 9.8.0 - cspell@9.4.0: - resolution: {integrity: sha512-ZvXO+EY/G0/msu7jwRiVk0sXL/zB7DMJLBvjSUrK82uVbDoDxHwXxUuOz2UVnk2+J61//ldIZrjxVK8KMvaJlg==} - engines: {node: '>=20'} + cspell@9.8.0: + resolution: {integrity: sha512-qL0VErMSn8BDxaPxcV+9uenffgjPS+5Jfz+m4rCsvYjzLwr7AaaJBWWSV2UiAe/4cturae8n8qzxiGnbbazkRw==} + engines: {node: '>=20.18'} hasBin: true css-selector-parser@3.2.0: @@ -3088,8 +3087,8 @@ packages: resolution: {integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==} hasBin: true - dompurify@3.3.0: - resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==} + dompurify@3.3.3: + resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} @@ -3132,9 +3131,9 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - env-paths@3.0.0: - resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + env-paths@4.0.0: + resolution: {integrity: sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw==} + engines: {node: '>=20'} es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} @@ -3161,13 +3160,13 @@ packages: esast-util-from-js@2.0.1: resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + esbuild@0.27.0: + resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} engines: {node: '>=18'} hasBin: true - esbuild@0.27.0: - resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} engines: {node: '>=18'} hasBin: true @@ -3250,15 +3249,18 @@ packages: resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==} engines: {node: '>=10.0.0'} - fast-equals@5.3.3: - resolution: {integrity: sha512-/boTcHZeIAQ2r/tL11voclBHDeP9WPxLt+tyAbVSyyXuUFyh0Tne7gJZTqGbxnvj79TjLdCXLOY7UIPhyG5MTw==} + fast-equals@6.0.0: + resolution: {integrity: sha512-PFhhIGgdM79r5Uztdj9Zb6Tt1zKafqVfdMGwVca1z5z6fbX7DmsySSuJd8HiP6I1j505DCS83cLxo5rmSNeVEA==} engines: {node: '>=6.0.0'} fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - fast-xml-parser@5.2.5: - resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} + fast-xml-builder@1.1.4: + resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} + + fast-xml-parser@5.5.8: + resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==} hasBin: true fault@2.0.1: @@ -3284,8 +3286,8 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} @@ -3378,9 +3380,9 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - global-directory@4.0.1: - resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} - engines: {node: '>=18'} + global-directory@5.0.0: + resolution: {integrity: sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==} + engines: {node: '>=20'} gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} @@ -3462,8 +3464,8 @@ packages: hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} - hono@4.12.3: - resolution: {integrity: sha512-SFsVSjp8sj5UumXOOFlkZOG6XS9SJDKw0TbwFeV+AJ8xlST8kxK5Z/5EYa111UY8732lK2S/xB653ceuaoGwpg==} + hono@4.12.12: + resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==} engines: {node: '>=16.9.0'} html-void-elements@3.0.0: @@ -3501,9 +3503,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - ini@4.1.1: - resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -3539,6 +3541,10 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-safe-filename@0.1.1: + resolution: {integrity: sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g==} + engines: {node: '>=20'} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -4132,6 +4138,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-expression-matcher@1.4.0: + resolution: {integrity: sha512-s4DQMxIdhj3jLFWd9LxHOplj4p9yQ4ffMGowFf3cpEgrrJjEhN0V5nxw4Ye1EViAGDoL4/1AeO6qHpqYPOzE4Q==} + engines: {node: '>=14.0.0'} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -4149,8 +4159,8 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} pkg-types@1.3.1: @@ -4188,8 +4198,9 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} @@ -4420,6 +4431,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + send@0.19.0: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} @@ -4458,8 +4474,8 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - smol-toml@1.5.2: - resolution: {integrity: sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==} + smol-toml@1.6.1: + resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} source-map-js@1.2.1: @@ -4512,8 +4528,8 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - strnum@2.1.2: - resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} + strnum@2.2.3: + resolution: {integrity: sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==} style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -4549,6 +4565,10 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + tmp@0.2.5: resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} @@ -4711,8 +4731,8 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@7.2.6: - resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} + vite@7.3.2: + resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -4812,6 +4832,11 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + engines: {node: '>= 14.6'} + hasBin: true + yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} @@ -4840,21 +4865,21 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.969.0 + '@aws-sdk/types': 3.973.7 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.969.0 + '@aws-sdk/types': 3.973.7 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.969.0 - '@aws-sdk/util-locate-window': 3.965.2 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-locate-window': 3.965.5 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -4863,15 +4888,15 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.969.0 - '@aws-sdk/util-locate-window': 3.965.2 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-locate-window': 3.965.5 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.969.0 + '@aws-sdk/types': 3.973.7 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -4880,458 +4905,418 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.969.0 + '@aws-sdk/types': 3.973.7 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.971.0': + '@aws-sdk/client-s3@3.1024.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.970.0 - '@aws-sdk/credential-provider-node': 3.971.0 - '@aws-sdk/middleware-bucket-endpoint': 3.969.0 - '@aws-sdk/middleware-expect-continue': 3.969.0 - '@aws-sdk/middleware-flexible-checksums': 3.971.0 - '@aws-sdk/middleware-host-header': 3.969.0 - '@aws-sdk/middleware-location-constraint': 3.969.0 - '@aws-sdk/middleware-logger': 3.969.0 - '@aws-sdk/middleware-recursion-detection': 3.969.0 - '@aws-sdk/middleware-sdk-s3': 3.970.0 - '@aws-sdk/middleware-ssec': 3.971.0 - '@aws-sdk/middleware-user-agent': 3.970.0 - '@aws-sdk/region-config-resolver': 3.969.0 - '@aws-sdk/signature-v4-multi-region': 3.970.0 - '@aws-sdk/types': 3.969.0 - '@aws-sdk/util-endpoints': 3.970.0 - '@aws-sdk/util-user-agent-browser': 3.969.0 - '@aws-sdk/util-user-agent-node': 3.971.0 - '@smithy/config-resolver': 4.4.6 - '@smithy/core': 3.20.7 - '@smithy/eventstream-serde-browser': 4.2.8 - '@smithy/eventstream-serde-config-resolver': 4.3.8 - '@smithy/eventstream-serde-node': 4.2.8 - '@smithy/fetch-http-handler': 5.3.9 - '@smithy/hash-blob-browser': 4.2.9 - '@smithy/hash-node': 4.2.8 - '@smithy/hash-stream-node': 4.2.8 - '@smithy/invalid-dependency': 4.2.8 - '@smithy/md5-js': 4.2.8 - '@smithy/middleware-content-length': 4.2.8 - '@smithy/middleware-endpoint': 4.4.8 - '@smithy/middleware-retry': 4.4.24 - '@smithy/middleware-serde': 4.2.9 - '@smithy/middleware-stack': 4.2.8 - '@smithy/node-config-provider': 4.3.8 - '@smithy/node-http-handler': 4.4.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/smithy-client': 4.10.9 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.23 - '@smithy/util-defaults-mode-node': 4.2.26 - '@smithy/util-endpoints': 3.2.8 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-retry': 4.2.8 - '@smithy/util-stream': 4.5.10 - '@smithy/util-utf8': 4.2.0 - '@smithy/util-waiter': 4.2.8 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/credential-provider-node': 3.972.30 + '@aws-sdk/middleware-bucket-endpoint': 3.972.9 + '@aws-sdk/middleware-expect-continue': 3.972.9 + '@aws-sdk/middleware-flexible-checksums': 3.974.7 + '@aws-sdk/middleware-host-header': 3.972.9 + '@aws-sdk/middleware-location-constraint': 3.972.9 + '@aws-sdk/middleware-logger': 3.972.9 + '@aws-sdk/middleware-recursion-detection': 3.972.10 + '@aws-sdk/middleware-sdk-s3': 3.972.28 + '@aws-sdk/middleware-ssec': 3.972.9 + '@aws-sdk/middleware-user-agent': 3.972.29 + '@aws-sdk/region-config-resolver': 3.972.11 + '@aws-sdk/signature-v4-multi-region': 3.996.16 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-endpoints': 3.996.6 + '@aws-sdk/util-user-agent-browser': 3.972.9 + '@aws-sdk/util-user-agent-node': 3.973.15 + '@smithy/config-resolver': 4.4.14 + '@smithy/core': 3.23.14 + '@smithy/eventstream-serde-browser': 4.2.13 + '@smithy/eventstream-serde-config-resolver': 4.3.13 + '@smithy/eventstream-serde-node': 4.2.13 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/hash-blob-browser': 4.2.14 + '@smithy/hash-node': 4.2.13 + '@smithy/hash-stream-node': 4.2.13 + '@smithy/invalid-dependency': 4.2.13 + '@smithy/md5-js': 4.2.13 + '@smithy/middleware-content-length': 4.2.13 + '@smithy/middleware-endpoint': 4.4.29 + '@smithy/middleware-retry': 4.5.0 + '@smithy/middleware-serde': 4.2.17 + '@smithy/middleware-stack': 4.2.13 + '@smithy/node-config-provider': 4.3.13 + '@smithy/node-http-handler': 4.5.2 + '@smithy/protocol-http': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.45 + '@smithy/util-defaults-mode-node': 4.2.49 + '@smithy/util-endpoints': 3.3.4 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-retry': 4.3.0 + '@smithy/util-stream': 4.5.22 + '@smithy/util-utf8': 4.2.2 + '@smithy/util-waiter': 4.2.15 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.971.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.970.0 - '@aws-sdk/middleware-host-header': 3.969.0 - '@aws-sdk/middleware-logger': 3.969.0 - '@aws-sdk/middleware-recursion-detection': 3.969.0 - '@aws-sdk/middleware-user-agent': 3.970.0 - '@aws-sdk/region-config-resolver': 3.969.0 - '@aws-sdk/types': 3.969.0 - '@aws-sdk/util-endpoints': 3.970.0 - '@aws-sdk/util-user-agent-browser': 3.969.0 - '@aws-sdk/util-user-agent-node': 3.971.0 - '@smithy/config-resolver': 4.4.6 - '@smithy/core': 3.20.7 - '@smithy/fetch-http-handler': 5.3.9 - '@smithy/hash-node': 4.2.8 - '@smithy/invalid-dependency': 4.2.8 - '@smithy/middleware-content-length': 4.2.8 - '@smithy/middleware-endpoint': 4.4.8 - '@smithy/middleware-retry': 4.4.24 - '@smithy/middleware-serde': 4.2.9 - '@smithy/middleware-stack': 4.2.8 - '@smithy/node-config-provider': 4.3.8 - '@smithy/node-http-handler': 4.4.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/smithy-client': 4.10.9 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.23 - '@smithy/util-defaults-mode-node': 4.2.26 - '@smithy/util-endpoints': 3.2.8 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-retry': 4.2.8 - '@smithy/util-utf8': 4.2.0 + '@aws-sdk/core@3.973.27': + dependencies: + '@aws-sdk/types': 3.973.7 + '@aws-sdk/xml-builder': 3.972.17 + '@smithy/core': 3.23.14 + '@smithy/node-config-provider': 4.3.13 + '@smithy/property-provider': 4.2.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/signature-v4': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/core@3.970.0': - dependencies: - '@aws-sdk/types': 3.969.0 - '@aws-sdk/xml-builder': 3.969.0 - '@smithy/core': 3.20.7 - '@smithy/node-config-provider': 4.3.8 - '@smithy/property-provider': 4.2.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/signature-v4': 5.3.8 - '@smithy/smithy-client': 4.10.9 - '@smithy/types': 4.12.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@aws-sdk/crc64-nvme@3.969.0': + '@aws-sdk/crc64-nvme@3.972.6': dependencies: - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.970.0': + '@aws-sdk/credential-provider-env@3.972.25': dependencies: - '@aws-sdk/core': 3.970.0 - '@aws-sdk/types': 3.969.0 - '@smithy/property-provider': 4.2.8 - '@smithy/types': 4.12.0 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.970.0': - dependencies: - '@aws-sdk/core': 3.970.0 - '@aws-sdk/types': 3.969.0 - '@smithy/fetch-http-handler': 5.3.9 - '@smithy/node-http-handler': 4.4.8 - '@smithy/property-provider': 4.2.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/smithy-client': 4.10.9 - '@smithy/types': 4.12.0 - '@smithy/util-stream': 4.5.10 + '@aws-sdk/credential-provider-http@3.972.27': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/types': 3.973.7 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/node-http-handler': 4.5.2 + '@smithy/property-provider': 4.2.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/util-stream': 4.5.22 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.971.0': - dependencies: - '@aws-sdk/core': 3.970.0 - '@aws-sdk/credential-provider-env': 3.970.0 - '@aws-sdk/credential-provider-http': 3.970.0 - '@aws-sdk/credential-provider-login': 3.971.0 - '@aws-sdk/credential-provider-process': 3.970.0 - '@aws-sdk/credential-provider-sso': 3.971.0 - '@aws-sdk/credential-provider-web-identity': 3.971.0 - '@aws-sdk/nested-clients': 3.971.0 - '@aws-sdk/types': 3.969.0 - '@smithy/credential-provider-imds': 4.2.8 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 + '@aws-sdk/credential-provider-ini@3.972.29': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/credential-provider-env': 3.972.25 + '@aws-sdk/credential-provider-http': 3.972.27 + '@aws-sdk/credential-provider-login': 3.972.29 + '@aws-sdk/credential-provider-process': 3.972.25 + '@aws-sdk/credential-provider-sso': 3.972.29 + '@aws-sdk/credential-provider-web-identity': 3.972.29 + '@aws-sdk/nested-clients': 3.996.19 + '@aws-sdk/types': 3.973.7 + '@smithy/credential-provider-imds': 4.2.13 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.971.0': + '@aws-sdk/credential-provider-login@3.972.29': dependencies: - '@aws-sdk/core': 3.970.0 - '@aws-sdk/nested-clients': 3.971.0 - '@aws-sdk/types': 3.969.0 - '@smithy/property-provider': 4.2.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/nested-clients': 3.996.19 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.971.0': - dependencies: - '@aws-sdk/credential-provider-env': 3.970.0 - '@aws-sdk/credential-provider-http': 3.970.0 - '@aws-sdk/credential-provider-ini': 3.971.0 - '@aws-sdk/credential-provider-process': 3.970.0 - '@aws-sdk/credential-provider-sso': 3.971.0 - '@aws-sdk/credential-provider-web-identity': 3.971.0 - '@aws-sdk/types': 3.969.0 - '@smithy/credential-provider-imds': 4.2.8 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 + '@aws-sdk/credential-provider-node@3.972.30': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.25 + '@aws-sdk/credential-provider-http': 3.972.27 + '@aws-sdk/credential-provider-ini': 3.972.29 + '@aws-sdk/credential-provider-process': 3.972.25 + '@aws-sdk/credential-provider-sso': 3.972.29 + '@aws-sdk/credential-provider-web-identity': 3.972.29 + '@aws-sdk/types': 3.973.7 + '@smithy/credential-provider-imds': 4.2.13 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.970.0': + '@aws-sdk/credential-provider-process@3.972.25': dependencies: - '@aws-sdk/core': 3.970.0 - '@aws-sdk/types': 3.969.0 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.971.0': + '@aws-sdk/credential-provider-sso@3.972.29': dependencies: - '@aws-sdk/client-sso': 3.971.0 - '@aws-sdk/core': 3.970.0 - '@aws-sdk/token-providers': 3.971.0 - '@aws-sdk/types': 3.969.0 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/nested-clients': 3.996.19 + '@aws-sdk/token-providers': 3.1026.0 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.971.0': + '@aws-sdk/credential-provider-web-identity@3.972.29': dependencies: - '@aws-sdk/core': 3.970.0 - '@aws-sdk/nested-clients': 3.971.0 - '@aws-sdk/types': 3.969.0 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/nested-clients': 3.996.19 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/lib-storage@3.971.0(@aws-sdk/client-s3@3.971.0)': + '@aws-sdk/lib-storage@3.1024.0(@aws-sdk/client-s3@3.1024.0)': dependencies: - '@aws-sdk/client-s3': 3.971.0 - '@smithy/abort-controller': 4.2.8 - '@smithy/middleware-endpoint': 4.4.8 - '@smithy/smithy-client': 4.10.9 + '@aws-sdk/client-s3': 3.1024.0 + '@smithy/middleware-endpoint': 4.4.29 + '@smithy/protocol-http': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 buffer: 5.6.0 events: 3.3.0 stream-browserify: 3.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-bucket-endpoint@3.969.0': + '@aws-sdk/middleware-bucket-endpoint@3.972.9': dependencies: - '@aws-sdk/types': 3.969.0 - '@aws-sdk/util-arn-parser': 3.968.0 - '@smithy/node-config-provider': 4.3.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-config-provider': 4.2.0 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-arn-parser': 3.972.3 + '@smithy/node-config-provider': 4.3.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-config-provider': 4.2.2 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.969.0': + '@aws-sdk/middleware-expect-continue@3.972.9': dependencies: - '@aws-sdk/types': 3.969.0 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 + '@aws-sdk/types': 3.973.7 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.971.0': + '@aws-sdk/middleware-flexible-checksums@3.974.7': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.970.0 - '@aws-sdk/crc64-nvme': 3.969.0 - '@aws-sdk/types': 3.969.0 - '@smithy/is-array-buffer': 4.2.0 - '@smithy/node-config-provider': 4.3.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-stream': 4.5.10 - '@smithy/util-utf8': 4.2.0 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/crc64-nvme': 3.972.6 + '@aws-sdk/types': 3.973.7 + '@smithy/is-array-buffer': 4.2.2 + '@smithy/node-config-provider': 4.3.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-stream': 4.5.22 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.969.0': + '@aws-sdk/middleware-host-header@3.972.9': dependencies: - '@aws-sdk/types': 3.969.0 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 + '@aws-sdk/types': 3.973.7 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/middleware-location-constraint@3.969.0': + '@aws-sdk/middleware-location-constraint@3.972.9': dependencies: - '@aws-sdk/types': 3.969.0 - '@smithy/types': 4.12.0 + '@aws-sdk/types': 3.973.7 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.969.0': + '@aws-sdk/middleware-logger@3.972.9': dependencies: - '@aws-sdk/types': 3.969.0 - '@smithy/types': 4.12.0 + '@aws-sdk/types': 3.973.7 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.969.0': + '@aws-sdk/middleware-recursion-detection@3.972.10': dependencies: - '@aws-sdk/types': 3.969.0 - '@aws/lambda-invoke-store': 0.2.3 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 + '@aws-sdk/types': 3.973.7 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.970.0': - dependencies: - '@aws-sdk/core': 3.970.0 - '@aws-sdk/types': 3.969.0 - '@aws-sdk/util-arn-parser': 3.968.0 - '@smithy/core': 3.20.7 - '@smithy/node-config-provider': 4.3.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/signature-v4': 5.3.8 - '@smithy/smithy-client': 4.10.9 - '@smithy/types': 4.12.0 - '@smithy/util-config-provider': 4.2.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-stream': 4.5.10 - '@smithy/util-utf8': 4.2.0 + '@aws-sdk/middleware-sdk-s3@3.972.28': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-arn-parser': 3.972.3 + '@smithy/core': 3.23.14 + '@smithy/node-config-provider': 4.3.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/signature-v4': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-stream': 4.5.22 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/middleware-ssec@3.971.0': + '@aws-sdk/middleware-ssec@3.972.9': dependencies: - '@aws-sdk/types': 3.969.0 - '@smithy/types': 4.12.0 + '@aws-sdk/types': 3.973.7 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.970.0': + '@aws-sdk/middleware-user-agent@3.972.29': dependencies: - '@aws-sdk/core': 3.970.0 - '@aws-sdk/types': 3.969.0 - '@aws-sdk/util-endpoints': 3.970.0 - '@smithy/core': 3.20.7 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-endpoints': 3.996.6 + '@smithy/core': 3.23.14 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-retry': 4.3.0 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.971.0': + '@aws-sdk/nested-clients@3.996.19': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.970.0 - '@aws-sdk/middleware-host-header': 3.969.0 - '@aws-sdk/middleware-logger': 3.969.0 - '@aws-sdk/middleware-recursion-detection': 3.969.0 - '@aws-sdk/middleware-user-agent': 3.970.0 - '@aws-sdk/region-config-resolver': 3.969.0 - '@aws-sdk/types': 3.969.0 - '@aws-sdk/util-endpoints': 3.970.0 - '@aws-sdk/util-user-agent-browser': 3.969.0 - '@aws-sdk/util-user-agent-node': 3.971.0 - '@smithy/config-resolver': 4.4.6 - '@smithy/core': 3.20.7 - '@smithy/fetch-http-handler': 5.3.9 - '@smithy/hash-node': 4.2.8 - '@smithy/invalid-dependency': 4.2.8 - '@smithy/middleware-content-length': 4.2.8 - '@smithy/middleware-endpoint': 4.4.8 - '@smithy/middleware-retry': 4.4.24 - '@smithy/middleware-serde': 4.2.9 - '@smithy/middleware-stack': 4.2.8 - '@smithy/node-config-provider': 4.3.8 - '@smithy/node-http-handler': 4.4.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/smithy-client': 4.10.9 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.23 - '@smithy/util-defaults-mode-node': 4.2.26 - '@smithy/util-endpoints': 3.2.8 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-retry': 4.2.8 - '@smithy/util-utf8': 4.2.0 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/middleware-host-header': 3.972.9 + '@aws-sdk/middleware-logger': 3.972.9 + '@aws-sdk/middleware-recursion-detection': 3.972.10 + '@aws-sdk/middleware-user-agent': 3.972.29 + '@aws-sdk/region-config-resolver': 3.972.11 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-endpoints': 3.996.6 + '@aws-sdk/util-user-agent-browser': 3.972.9 + '@aws-sdk/util-user-agent-node': 3.973.15 + '@smithy/config-resolver': 4.4.14 + '@smithy/core': 3.23.14 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/hash-node': 4.2.13 + '@smithy/invalid-dependency': 4.2.13 + '@smithy/middleware-content-length': 4.2.13 + '@smithy/middleware-endpoint': 4.4.29 + '@smithy/middleware-retry': 4.5.0 + '@smithy/middleware-serde': 4.2.17 + '@smithy/middleware-stack': 4.2.13 + '@smithy/node-config-provider': 4.3.13 + '@smithy/node-http-handler': 4.5.2 + '@smithy/protocol-http': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.45 + '@smithy/util-defaults-mode-node': 4.2.49 + '@smithy/util-endpoints': 3.3.4 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-retry': 4.3.0 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.969.0': + '@aws-sdk/region-config-resolver@3.972.11': dependencies: - '@aws-sdk/types': 3.969.0 - '@smithy/config-resolver': 4.4.6 - '@smithy/node-config-provider': 4.3.8 - '@smithy/types': 4.12.0 + '@aws-sdk/types': 3.973.7 + '@smithy/config-resolver': 4.4.14 + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.970.0': + '@aws-sdk/signature-v4-multi-region@3.996.16': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.970.0 - '@aws-sdk/types': 3.969.0 - '@smithy/protocol-http': 5.3.8 - '@smithy/signature-v4': 5.3.8 - '@smithy/types': 4.12.0 + '@aws-sdk/middleware-sdk-s3': 3.972.28 + '@aws-sdk/types': 3.973.7 + '@smithy/protocol-http': 5.3.13 + '@smithy/signature-v4': 5.3.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.971.0': + '@aws-sdk/token-providers@3.1026.0': dependencies: - '@aws-sdk/core': 3.970.0 - '@aws-sdk/nested-clients': 3.971.0 - '@aws-sdk/types': 3.969.0 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/nested-clients': 3.996.19 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.969.0': + '@aws-sdk/types@3.973.7': dependencies: - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@aws-sdk/util-arn-parser@3.968.0': + '@aws-sdk/util-arn-parser@3.972.3': dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.970.0': + '@aws-sdk/util-endpoints@3.996.6': dependencies: - '@aws-sdk/types': 3.969.0 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - '@smithy/util-endpoints': 3.2.8 + '@aws-sdk/types': 3.973.7 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-endpoints': 3.3.4 tslib: 2.8.1 - '@aws-sdk/util-locate-window@3.965.2': + '@aws-sdk/util-locate-window@3.965.5': dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.969.0': + '@aws-sdk/util-user-agent-browser@3.972.9': dependencies: - '@aws-sdk/types': 3.969.0 - '@smithy/types': 4.12.0 - bowser: 2.13.1 + '@aws-sdk/types': 3.973.7 + '@smithy/types': 4.14.0 + bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.971.0': + '@aws-sdk/util-user-agent-node@3.973.15': dependencies: - '@aws-sdk/middleware-user-agent': 3.970.0 - '@aws-sdk/types': 3.969.0 - '@smithy/node-config-provider': 4.3.8 - '@smithy/types': 4.12.0 + '@aws-sdk/middleware-user-agent': 3.972.29 + '@aws-sdk/types': 3.973.7 + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-config-provider': 4.2.2 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.969.0': + '@aws-sdk/xml-builder@3.972.17': dependencies: - '@smithy/types': 4.12.0 - fast-xml-parser: 5.2.5 + '@smithy/types': 4.14.0 + fast-xml-parser: 5.5.8 tslib: 2.8.1 - '@aws/lambda-invoke-store@0.2.3': {} + '@aws/lambda-invoke-store@0.2.4': {} '@babel/code-frame@7.27.1': dependencies: @@ -5482,60 +5467,60 @@ snapshots: picocolors: 1.1.1 sisteransi: 1.0.5 - '@cspell/cspell-bundled-dicts@9.4.0': + '@cspell/cspell-bundled-dicts@9.8.0': dependencies: '@cspell/dict-ada': 4.1.1 '@cspell/dict-al': 1.1.1 - '@cspell/dict-aws': 4.0.16 + '@cspell/dict-aws': 4.0.17 '@cspell/dict-bash': 4.2.2 - '@cspell/dict-companies': 3.2.7 - '@cspell/dict-cpp': 6.0.15 + '@cspell/dict-companies': 3.2.11 + '@cspell/dict-cpp': 7.0.2 '@cspell/dict-cryptocurrencies': 5.0.5 - '@cspell/dict-csharp': 4.0.7 - '@cspell/dict-css': 4.0.18 - '@cspell/dict-dart': 2.3.1 - '@cspell/dict-data-science': 2.0.12 - '@cspell/dict-django': 4.1.5 - '@cspell/dict-docker': 1.1.16 - '@cspell/dict-dotnet': 5.0.10 + '@cspell/dict-csharp': 4.0.8 + '@cspell/dict-css': 4.1.1 + '@cspell/dict-dart': 2.3.2 + '@cspell/dict-data-science': 2.0.13 + '@cspell/dict-django': 4.1.6 + '@cspell/dict-docker': 1.1.17 + '@cspell/dict-dotnet': 5.0.13 '@cspell/dict-elixir': 4.0.8 - '@cspell/dict-en-common-misspellings': 2.1.8 - '@cspell/dict-en-gb-mit': 3.1.14 - '@cspell/dict-en_us': 4.4.24 - '@cspell/dict-filetypes': 3.0.14 + '@cspell/dict-en-common-misspellings': 2.1.12 + '@cspell/dict-en-gb-mit': 3.1.22 + '@cspell/dict-en_us': 4.4.33 + '@cspell/dict-filetypes': 3.0.18 '@cspell/dict-flutter': 1.1.1 - '@cspell/dict-fonts': 4.0.5 + '@cspell/dict-fonts': 4.0.6 '@cspell/dict-fsharp': 1.1.1 - '@cspell/dict-fullstack': 3.2.7 + '@cspell/dict-fullstack': 3.2.9 '@cspell/dict-gaming-terms': 1.1.2 - '@cspell/dict-git': 3.0.7 - '@cspell/dict-golang': 6.0.24 + '@cspell/dict-git': 3.1.0 + '@cspell/dict-golang': 6.0.26 '@cspell/dict-google': 1.0.9 '@cspell/dict-haskell': 4.0.6 - '@cspell/dict-html': 4.0.13 - '@cspell/dict-html-symbol-entities': 4.0.4 + '@cspell/dict-html': 4.0.15 + '@cspell/dict-html-symbol-entities': 4.0.5 '@cspell/dict-java': 5.0.12 '@cspell/dict-julia': 1.1.1 '@cspell/dict-k8s': 1.0.12 '@cspell/dict-kotlin': 1.1.1 - '@cspell/dict-latex': 4.0.4 + '@cspell/dict-latex': 5.1.0 '@cspell/dict-lorem-ipsum': 4.0.5 '@cspell/dict-lua': 4.0.8 '@cspell/dict-makefile': 1.0.5 - '@cspell/dict-markdown': 2.0.13(@cspell/dict-css@4.0.18)(@cspell/dict-html-symbol-entities@4.0.4)(@cspell/dict-html@4.0.13)(@cspell/dict-typescript@3.2.3) - '@cspell/dict-monkeyc': 1.0.11 - '@cspell/dict-node': 5.0.8 - '@cspell/dict-npm': 5.2.25 - '@cspell/dict-php': 4.1.0 + '@cspell/dict-markdown': 2.0.16(@cspell/dict-css@4.1.1)(@cspell/dict-html-symbol-entities@4.0.5)(@cspell/dict-html@4.0.15)(@cspell/dict-typescript@3.2.3) + '@cspell/dict-monkeyc': 1.0.12 + '@cspell/dict-node': 5.0.9 + '@cspell/dict-npm': 5.2.38 + '@cspell/dict-php': 4.1.1 '@cspell/dict-powershell': 5.0.15 - '@cspell/dict-public-licenses': 2.0.15 - '@cspell/dict-python': 4.2.23 + '@cspell/dict-public-licenses': 2.0.16 + '@cspell/dict-python': 4.2.26 '@cspell/dict-r': 2.1.1 - '@cspell/dict-ruby': 5.0.9 - '@cspell/dict-rust': 4.0.12 - '@cspell/dict-scala': 5.0.8 + '@cspell/dict-ruby': 5.1.1 + '@cspell/dict-rust': 4.1.2 + '@cspell/dict-scala': 5.0.9 '@cspell/dict-shell': 1.1.2 - '@cspell/dict-software-terms': 5.1.15 + '@cspell/dict-software-terms': 5.2.2 '@cspell/dict-sql': 2.2.1 '@cspell/dict-svelte': 1.0.7 '@cspell/dict-swift': 2.0.6 @@ -5544,81 +5529,87 @@ snapshots: '@cspell/dict-vue': 3.0.5 '@cspell/dict-zig': 1.0.0 - '@cspell/cspell-json-reporter@9.4.0': + '@cspell/cspell-json-reporter@9.8.0': dependencies: - '@cspell/cspell-types': 9.4.0 + '@cspell/cspell-types': 9.8.0 + + '@cspell/cspell-performance-monitor@9.8.0': {} - '@cspell/cspell-pipe@9.4.0': {} + '@cspell/cspell-pipe@9.8.0': {} - '@cspell/cspell-resolver@9.4.0': + '@cspell/cspell-resolver@9.8.0': dependencies: - global-directory: 4.0.1 + global-directory: 5.0.0 - '@cspell/cspell-service-bus@9.4.0': {} + '@cspell/cspell-service-bus@9.8.0': {} - '@cspell/cspell-types@9.4.0': {} + '@cspell/cspell-types@9.8.0': {} + + '@cspell/cspell-worker@9.8.0': + dependencies: + cspell-lib: 9.8.0 '@cspell/dict-ada@4.1.1': {} '@cspell/dict-al@1.1.1': {} - '@cspell/dict-aws@4.0.16': {} + '@cspell/dict-aws@4.0.17': {} '@cspell/dict-bash@4.2.2': dependencies: '@cspell/dict-shell': 1.1.2 - '@cspell/dict-companies@3.2.7': {} + '@cspell/dict-companies@3.2.11': {} - '@cspell/dict-cpp@6.0.15': {} + '@cspell/dict-cpp@7.0.2': {} '@cspell/dict-cryptocurrencies@5.0.5': {} - '@cspell/dict-csharp@4.0.7': {} + '@cspell/dict-csharp@4.0.8': {} - '@cspell/dict-css@4.0.18': {} + '@cspell/dict-css@4.1.1': {} - '@cspell/dict-dart@2.3.1': {} + '@cspell/dict-dart@2.3.2': {} - '@cspell/dict-data-science@2.0.12': {} + '@cspell/dict-data-science@2.0.13': {} - '@cspell/dict-django@4.1.5': {} + '@cspell/dict-django@4.1.6': {} - '@cspell/dict-docker@1.1.16': {} + '@cspell/dict-docker@1.1.17': {} - '@cspell/dict-dotnet@5.0.10': {} + '@cspell/dict-dotnet@5.0.13': {} '@cspell/dict-elixir@4.0.8': {} - '@cspell/dict-en-common-misspellings@2.1.8': {} + '@cspell/dict-en-common-misspellings@2.1.12': {} - '@cspell/dict-en-gb-mit@3.1.14': {} + '@cspell/dict-en-gb-mit@3.1.22': {} - '@cspell/dict-en_us@4.4.24': {} + '@cspell/dict-en_us@4.4.33': {} - '@cspell/dict-filetypes@3.0.14': {} + '@cspell/dict-filetypes@3.0.18': {} '@cspell/dict-flutter@1.1.1': {} - '@cspell/dict-fonts@4.0.5': {} + '@cspell/dict-fonts@4.0.6': {} '@cspell/dict-fsharp@1.1.1': {} - '@cspell/dict-fullstack@3.2.7': {} + '@cspell/dict-fullstack@3.2.9': {} '@cspell/dict-gaming-terms@1.1.2': {} - '@cspell/dict-git@3.0.7': {} + '@cspell/dict-git@3.1.0': {} - '@cspell/dict-golang@6.0.24': {} + '@cspell/dict-golang@6.0.26': {} '@cspell/dict-google@1.0.9': {} '@cspell/dict-haskell@4.0.6': {} - '@cspell/dict-html-symbol-entities@4.0.4': {} + '@cspell/dict-html-symbol-entities@4.0.5': {} - '@cspell/dict-html@4.0.13': {} + '@cspell/dict-html@4.0.15': {} '@cspell/dict-java@5.0.12': {} @@ -5628,7 +5619,7 @@ snapshots: '@cspell/dict-kotlin@1.1.1': {} - '@cspell/dict-latex@4.0.4': {} + '@cspell/dict-latex@5.1.0': {} '@cspell/dict-lorem-ipsum@4.0.5': {} @@ -5636,40 +5627,40 @@ snapshots: '@cspell/dict-makefile@1.0.5': {} - '@cspell/dict-markdown@2.0.13(@cspell/dict-css@4.0.18)(@cspell/dict-html-symbol-entities@4.0.4)(@cspell/dict-html@4.0.13)(@cspell/dict-typescript@3.2.3)': + '@cspell/dict-markdown@2.0.16(@cspell/dict-css@4.1.1)(@cspell/dict-html-symbol-entities@4.0.5)(@cspell/dict-html@4.0.15)(@cspell/dict-typescript@3.2.3)': dependencies: - '@cspell/dict-css': 4.0.18 - '@cspell/dict-html': 4.0.13 - '@cspell/dict-html-symbol-entities': 4.0.4 + '@cspell/dict-css': 4.1.1 + '@cspell/dict-html': 4.0.15 + '@cspell/dict-html-symbol-entities': 4.0.5 '@cspell/dict-typescript': 3.2.3 - '@cspell/dict-monkeyc@1.0.11': {} + '@cspell/dict-monkeyc@1.0.12': {} - '@cspell/dict-node@5.0.8': {} + '@cspell/dict-node@5.0.9': {} - '@cspell/dict-npm@5.2.25': {} + '@cspell/dict-npm@5.2.38': {} - '@cspell/dict-php@4.1.0': {} + '@cspell/dict-php@4.1.1': {} '@cspell/dict-powershell@5.0.15': {} - '@cspell/dict-public-licenses@2.0.15': {} + '@cspell/dict-public-licenses@2.0.16': {} - '@cspell/dict-python@4.2.23': + '@cspell/dict-python@4.2.26': dependencies: - '@cspell/dict-data-science': 2.0.12 + '@cspell/dict-data-science': 2.0.13 '@cspell/dict-r@2.1.1': {} - '@cspell/dict-ruby@5.0.9': {} + '@cspell/dict-ruby@5.1.1': {} - '@cspell/dict-rust@4.0.12': {} + '@cspell/dict-rust@4.1.2': {} - '@cspell/dict-scala@5.0.8': {} + '@cspell/dict-scala@5.0.9': {} '@cspell/dict-shell@1.1.2': {} - '@cspell/dict-software-terms@5.1.15': {} + '@cspell/dict-software-terms@5.2.2': {} '@cspell/dict-sql@2.2.1': {} @@ -5685,16 +5676,18 @@ snapshots: '@cspell/dict-zig@1.0.0': {} - '@cspell/dynamic-import@9.4.0': + '@cspell/dynamic-import@9.8.0': dependencies: - '@cspell/url': 9.4.0 + '@cspell/url': 9.8.0 import-meta-resolve: 4.2.0 - '@cspell/filetypes@9.4.0': {} + '@cspell/filetypes@9.8.0': {} - '@cspell/strong-weak-map@9.4.0': {} + '@cspell/rpc@9.8.0': {} - '@cspell/url@9.4.0': {} + '@cspell/strong-weak-map@9.8.0': {} + + '@cspell/url@9.8.0': {} '@emnapi/runtime@1.8.1': dependencies: @@ -5703,162 +5696,162 @@ snapshots: '@emotion/hash@0.9.2': {} - '@esbuild/aix-ppc64@0.25.12': - optional: true - '@esbuild/aix-ppc64@0.27.0': optional: true - '@esbuild/android-arm64@0.25.12': + '@esbuild/aix-ppc64@0.27.7': optional: true '@esbuild/android-arm64@0.27.0': optional: true - '@esbuild/android-arm@0.25.12': + '@esbuild/android-arm64@0.27.7': optional: true '@esbuild/android-arm@0.27.0': optional: true - '@esbuild/android-x64@0.25.12': + '@esbuild/android-arm@0.27.7': optional: true '@esbuild/android-x64@0.27.0': optional: true - '@esbuild/darwin-arm64@0.25.12': + '@esbuild/android-x64@0.27.7': optional: true '@esbuild/darwin-arm64@0.27.0': optional: true - '@esbuild/darwin-x64@0.25.12': + '@esbuild/darwin-arm64@0.27.7': optional: true '@esbuild/darwin-x64@0.27.0': optional: true - '@esbuild/freebsd-arm64@0.25.12': + '@esbuild/darwin-x64@0.27.7': optional: true '@esbuild/freebsd-arm64@0.27.0': optional: true - '@esbuild/freebsd-x64@0.25.12': + '@esbuild/freebsd-arm64@0.27.7': optional: true '@esbuild/freebsd-x64@0.27.0': optional: true - '@esbuild/linux-arm64@0.25.12': + '@esbuild/freebsd-x64@0.27.7': optional: true '@esbuild/linux-arm64@0.27.0': optional: true - '@esbuild/linux-arm@0.25.12': + '@esbuild/linux-arm64@0.27.7': optional: true '@esbuild/linux-arm@0.27.0': optional: true - '@esbuild/linux-ia32@0.25.12': + '@esbuild/linux-arm@0.27.7': optional: true '@esbuild/linux-ia32@0.27.0': optional: true - '@esbuild/linux-loong64@0.25.12': + '@esbuild/linux-ia32@0.27.7': optional: true '@esbuild/linux-loong64@0.27.0': optional: true - '@esbuild/linux-mips64el@0.25.12': + '@esbuild/linux-loong64@0.27.7': optional: true '@esbuild/linux-mips64el@0.27.0': optional: true - '@esbuild/linux-ppc64@0.25.12': + '@esbuild/linux-mips64el@0.27.7': optional: true '@esbuild/linux-ppc64@0.27.0': optional: true - '@esbuild/linux-riscv64@0.25.12': + '@esbuild/linux-ppc64@0.27.7': optional: true '@esbuild/linux-riscv64@0.27.0': optional: true - '@esbuild/linux-s390x@0.25.12': + '@esbuild/linux-riscv64@0.27.7': optional: true '@esbuild/linux-s390x@0.27.0': optional: true - '@esbuild/linux-x64@0.25.12': + '@esbuild/linux-s390x@0.27.7': optional: true '@esbuild/linux-x64@0.27.0': optional: true - '@esbuild/netbsd-arm64@0.25.12': + '@esbuild/linux-x64@0.27.7': optional: true '@esbuild/netbsd-arm64@0.27.0': optional: true - '@esbuild/netbsd-x64@0.25.12': + '@esbuild/netbsd-arm64@0.27.7': optional: true '@esbuild/netbsd-x64@0.27.0': optional: true - '@esbuild/openbsd-arm64@0.25.12': + '@esbuild/netbsd-x64@0.27.7': optional: true '@esbuild/openbsd-arm64@0.27.0': optional: true - '@esbuild/openbsd-x64@0.25.12': + '@esbuild/openbsd-arm64@0.27.7': optional: true '@esbuild/openbsd-x64@0.27.0': optional: true - '@esbuild/openharmony-arm64@0.25.12': + '@esbuild/openbsd-x64@0.27.7': optional: true '@esbuild/openharmony-arm64@0.27.0': optional: true - '@esbuild/sunos-x64@0.25.12': + '@esbuild/openharmony-arm64@0.27.7': optional: true '@esbuild/sunos-x64@0.27.0': optional: true - '@esbuild/win32-arm64@0.25.12': + '@esbuild/sunos-x64@0.27.7': optional: true '@esbuild/win32-arm64@0.27.0': optional: true - '@esbuild/win32-ia32@0.25.12': + '@esbuild/win32-arm64@0.27.7': optional: true '@esbuild/win32-ia32@0.27.0': optional: true - '@esbuild/win32-x64@0.25.12': + '@esbuild/win32-ia32@0.27.7': optional: true '@esbuild/win32-x64@0.27.0': optional: true + '@esbuild/win32-x64@0.27.7': + optional: true + '@fast-csv/format@4.3.5': dependencies: '@types/node': 14.18.63 @@ -5905,9 +5898,9 @@ snapshots: '@fortawesome/fontawesome-free@6.7.2': {} - '@hono/node-server@1.19.6(hono@4.12.3)': + '@hono/node-server@1.19.13(hono@4.12.12)': dependencies: - hono: 4.12.3 + hono: 4.12.12 '@iconify/types@2.0.0': {} @@ -6868,7 +6861,7 @@ snapshots: dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 - picomatch: 4.0.3 + picomatch: 4.0.4 optionalDependencies: rollup: 4.59.0 @@ -7005,254 +6998,250 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} - '@smithy/abort-controller@4.2.8': + '@smithy/chunked-blob-reader-native@4.2.3': dependencies: - '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.2 tslib: 2.8.1 - '@smithy/chunked-blob-reader-native@4.2.1': + '@smithy/chunked-blob-reader@5.2.2': dependencies: - '@smithy/util-base64': 4.3.0 tslib: 2.8.1 - '@smithy/chunked-blob-reader@5.2.0': + '@smithy/config-resolver@4.4.14': dependencies: + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.4 + '@smithy/util-middleware': 4.2.13 tslib: 2.8.1 - '@smithy/config-resolver@4.4.6': - dependencies: - '@smithy/node-config-provider': 4.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-config-provider': 4.2.0 - '@smithy/util-endpoints': 3.2.8 - '@smithy/util-middleware': 4.2.8 + '@smithy/core@3.23.14': + dependencies: + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-stream': 4.5.22 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/core@3.20.7': - dependencies: - '@smithy/middleware-serde': 4.2.9 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-stream': 4.5.10 - '@smithy/util-utf8': 4.2.0 - '@smithy/uuid': 1.1.0 - tslib: 2.8.1 - - '@smithy/credential-provider-imds@4.2.8': + '@smithy/credential-provider-imds@4.2.13': dependencies: - '@smithy/node-config-provider': 4.3.8 - '@smithy/property-provider': 4.2.8 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 + '@smithy/node-config-provider': 4.3.13 + '@smithy/property-provider': 4.2.13 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 tslib: 2.8.1 - '@smithy/eventstream-codec@4.2.8': + '@smithy/eventstream-codec@4.2.13': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.12.0 - '@smithy/util-hex-encoding': 4.2.0 + '@smithy/types': 4.14.0 + '@smithy/util-hex-encoding': 4.2.2 tslib: 2.8.1 - '@smithy/eventstream-serde-browser@4.2.8': + '@smithy/eventstream-serde-browser@4.2.13': dependencies: - '@smithy/eventstream-serde-universal': 4.2.8 - '@smithy/types': 4.12.0 + '@smithy/eventstream-serde-universal': 4.2.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/eventstream-serde-config-resolver@4.3.8': + '@smithy/eventstream-serde-config-resolver@4.3.13': dependencies: - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/eventstream-serde-node@4.2.8': + '@smithy/eventstream-serde-node@4.2.13': dependencies: - '@smithy/eventstream-serde-universal': 4.2.8 - '@smithy/types': 4.12.0 + '@smithy/eventstream-serde-universal': 4.2.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/eventstream-serde-universal@4.2.8': + '@smithy/eventstream-serde-universal@4.2.13': dependencies: - '@smithy/eventstream-codec': 4.2.8 - '@smithy/types': 4.12.0 + '@smithy/eventstream-codec': 4.2.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/fetch-http-handler@5.3.9': + '@smithy/fetch-http-handler@5.3.16': dependencies: - '@smithy/protocol-http': 5.3.8 - '@smithy/querystring-builder': 4.2.8 - '@smithy/types': 4.12.0 - '@smithy/util-base64': 4.3.0 + '@smithy/protocol-http': 5.3.13 + '@smithy/querystring-builder': 4.2.13 + '@smithy/types': 4.14.0 + '@smithy/util-base64': 4.3.2 tslib: 2.8.1 - '@smithy/hash-blob-browser@4.2.9': + '@smithy/hash-blob-browser@4.2.14': dependencies: - '@smithy/chunked-blob-reader': 5.2.0 - '@smithy/chunked-blob-reader-native': 4.2.1 - '@smithy/types': 4.12.0 + '@smithy/chunked-blob-reader': 5.2.2 + '@smithy/chunked-blob-reader-native': 4.2.3 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/hash-node@4.2.8': + '@smithy/hash-node@4.2.13': dependencies: - '@smithy/types': 4.12.0 - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/types': 4.14.0 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/hash-stream-node@4.2.8': + '@smithy/hash-stream-node@4.2.13': dependencies: - '@smithy/types': 4.12.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/types': 4.14.0 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/invalid-dependency@4.2.8': + '@smithy/invalid-dependency@4.2.13': dependencies: - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 - '@smithy/is-array-buffer@4.2.0': + '@smithy/is-array-buffer@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/md5-js@4.2.8': + '@smithy/md5-js@4.2.13': dependencies: - '@smithy/types': 4.12.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/types': 4.14.0 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/middleware-content-length@4.2.8': + '@smithy/middleware-content-length@4.2.13': dependencies: - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.4.8': + '@smithy/middleware-endpoint@4.4.29': dependencies: - '@smithy/core': 3.20.7 - '@smithy/middleware-serde': 4.2.9 - '@smithy/node-config-provider': 4.3.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - '@smithy/util-middleware': 4.2.8 + '@smithy/core': 3.23.14 + '@smithy/middleware-serde': 4.2.17 + '@smithy/node-config-provider': 4.3.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-middleware': 4.2.13 tslib: 2.8.1 - '@smithy/middleware-retry@4.4.24': - dependencies: - '@smithy/node-config-provider': 4.3.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/service-error-classification': 4.2.8 - '@smithy/smithy-client': 4.10.9 - '@smithy/types': 4.12.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-retry': 4.2.8 - '@smithy/uuid': 1.1.0 + '@smithy/middleware-retry@4.5.0': + dependencies: + '@smithy/core': 3.23.14 + '@smithy/node-config-provider': 4.3.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/service-error-classification': 4.2.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-retry': 4.3.0 + '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/middleware-serde@4.2.9': + '@smithy/middleware-serde@4.2.17': dependencies: - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 + '@smithy/core': 3.23.14 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/middleware-stack@4.2.8': + '@smithy/middleware-stack@4.2.13': dependencies: - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/node-config-provider@4.3.8': + '@smithy/node-config-provider@4.3.13': dependencies: - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/node-http-handler@4.4.8': + '@smithy/node-http-handler@4.5.2': dependencies: - '@smithy/abort-controller': 4.2.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/querystring-builder': 4.2.8 - '@smithy/types': 4.12.0 + '@smithy/protocol-http': 5.3.13 + '@smithy/querystring-builder': 4.2.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/property-provider@4.2.8': + '@smithy/property-provider@4.2.13': dependencies: - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/protocol-http@5.3.8': + '@smithy/protocol-http@5.3.13': dependencies: - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/querystring-builder@4.2.8': + '@smithy/querystring-builder@4.2.13': dependencies: - '@smithy/types': 4.12.0 - '@smithy/util-uri-escape': 4.2.0 + '@smithy/types': 4.14.0 + '@smithy/util-uri-escape': 4.2.2 tslib: 2.8.1 - '@smithy/querystring-parser@4.2.8': + '@smithy/querystring-parser@4.2.13': dependencies: - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/service-error-classification@4.2.8': + '@smithy/service-error-classification@4.2.13': dependencies: - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 - '@smithy/shared-ini-file-loader@4.4.3': + '@smithy/shared-ini-file-loader@4.4.8': dependencies: - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/signature-v4@5.3.8': + '@smithy/signature-v4@5.3.13': dependencies: - '@smithy/is-array-buffer': 4.2.0 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-hex-encoding': 4.2.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-uri-escape': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/smithy-client@4.10.9': + '@smithy/smithy-client@4.12.9': dependencies: - '@smithy/core': 3.20.7 - '@smithy/middleware-endpoint': 4.4.8 - '@smithy/middleware-stack': 4.2.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-stream': 4.5.10 + '@smithy/core': 3.23.14 + '@smithy/middleware-endpoint': 4.4.29 + '@smithy/middleware-stack': 4.2.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-stream': 4.5.22 tslib: 2.8.1 - '@smithy/types@4.12.0': + '@smithy/types@4.14.0': dependencies: tslib: 2.8.1 - '@smithy/url-parser@4.2.8': + '@smithy/url-parser@4.2.13': dependencies: - '@smithy/querystring-parser': 4.2.8 - '@smithy/types': 4.12.0 + '@smithy/querystring-parser': 4.2.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/util-base64@4.3.0': + '@smithy/util-base64@4.3.2': dependencies: - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/util-body-length-browser@4.2.0': + '@smithy/util-body-length-browser@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/util-body-length-node@4.2.1': + '@smithy/util-body-length-node@4.2.3': dependencies: tslib: 2.8.1 @@ -7261,65 +7250,65 @@ snapshots: '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 - '@smithy/util-buffer-from@4.2.0': + '@smithy/util-buffer-from@4.2.2': dependencies: - '@smithy/is-array-buffer': 4.2.0 + '@smithy/is-array-buffer': 4.2.2 tslib: 2.8.1 - '@smithy/util-config-provider@4.2.0': + '@smithy/util-config-provider@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.23': + '@smithy/util-defaults-mode-browser@4.3.45': dependencies: - '@smithy/property-provider': 4.2.8 - '@smithy/smithy-client': 4.10.9 - '@smithy/types': 4.12.0 + '@smithy/property-provider': 4.2.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.26': + '@smithy/util-defaults-mode-node@4.2.49': dependencies: - '@smithy/config-resolver': 4.4.6 - '@smithy/credential-provider-imds': 4.2.8 - '@smithy/node-config-provider': 4.3.8 - '@smithy/property-provider': 4.2.8 - '@smithy/smithy-client': 4.10.9 - '@smithy/types': 4.12.0 + '@smithy/config-resolver': 4.4.14 + '@smithy/credential-provider-imds': 4.2.13 + '@smithy/node-config-provider': 4.3.13 + '@smithy/property-provider': 4.2.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/util-endpoints@3.2.8': + '@smithy/util-endpoints@3.3.4': dependencies: - '@smithy/node-config-provider': 4.3.8 - '@smithy/types': 4.12.0 + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/util-hex-encoding@4.2.0': + '@smithy/util-hex-encoding@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/util-middleware@4.2.8': + '@smithy/util-middleware@4.2.13': dependencies: - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/util-retry@4.2.8': + '@smithy/util-retry@4.3.0': dependencies: - '@smithy/service-error-classification': 4.2.8 - '@smithy/types': 4.12.0 + '@smithy/service-error-classification': 4.2.13 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/util-stream@4.5.10': + '@smithy/util-stream@4.5.22': dependencies: - '@smithy/fetch-http-handler': 5.3.9 - '@smithy/node-http-handler': 4.4.8 - '@smithy/types': 4.12.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-hex-encoding': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/node-http-handler': 4.5.2 + '@smithy/types': 4.14.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/util-uri-escape@4.2.0': + '@smithy/util-uri-escape@4.2.2': dependencies: tslib: 2.8.1 @@ -7328,18 +7317,17 @@ snapshots: '@smithy/util-buffer-from': 2.2.0 tslib: 2.8.1 - '@smithy/util-utf8@4.2.0': + '@smithy/util-utf8@4.2.2': dependencies: - '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-buffer-from': 4.2.2 tslib: 2.8.1 - '@smithy/util-waiter@4.2.8': + '@smithy/util-waiter@4.2.15': dependencies: - '@smithy/abort-controller': 4.2.8 - '@smithy/types': 4.12.0 + '@smithy/types': 4.14.0 tslib: 2.8.1 - '@smithy/uuid@1.1.0': + '@smithy/uuid@1.1.2': dependencies: tslib: 2.8.1 @@ -7406,12 +7394,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.15 '@tailwindcss/oxide-win32-x64-msvc': 4.1.15 - '@tailwindcss/vite@4.1.15(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@tailwindcss/vite@4.1.15(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': dependencies: '@tailwindcss/node': 4.1.15 '@tailwindcss/oxide': 4.1.15 tailwindcss: 4.1.15 - vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) '@types/babel__core@7.20.5': dependencies: @@ -7561,10 +7549,6 @@ snapshots: '@types/estree@1.0.8': {} - '@types/exceljs@1.3.2': - dependencies: - exceljs: 4.4.0 - '@types/geojson@7946.0.16': {} '@types/hast@3.0.4': @@ -7624,7 +7608,7 @@ snapshots: dependencies: '@vanilla-extract/css': 1.17.5 '@vanilla-extract/integration': 8.0.6 - vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) vite-node: 3.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' @@ -7680,11 +7664,11 @@ snapshots: '@vanilla-extract/private@1.0.9': {} - '@vanilla-extract/vite-plugin@5.1.3(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(yaml@2.8.2)': + '@vanilla-extract/vite-plugin@5.1.3(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(yaml@2.8.2)': dependencies: '@vanilla-extract/compiler': 0.3.3(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) '@vanilla-extract/integration': 8.0.6 - vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -7700,7 +7684,7 @@ snapshots: - tsx - yaml - '@vitejs/plugin-react@5.1.1(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + '@vitejs/plugin-react@5.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) @@ -7708,7 +7692,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.47 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -7782,11 +7766,11 @@ snapshots: postcss: 8.5.6 postcss-value-parser: 4.2.0 - axios@1.13.2: + axios@1.14.0: dependencies: follow-redirects: 1.15.11 form-data: 4.0.5 - proxy-from-env: 1.1.0 + proxy-from-env: 2.1.0 transitivePeerDependencies: - debug @@ -7823,14 +7807,14 @@ snapshots: boolbase@1.0.0: {} - bowser@2.13.1: {} + bowser@2.14.1: {} - brace-expansion@1.1.12: + brace-expansion@1.1.13: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: + brace-expansion@2.0.3: dependencies: balanced-match: 1.0.2 @@ -7933,16 +7917,15 @@ snapshots: comma-separated-tokens@2.0.3: {} - commander@14.0.2: {} + commander@14.0.3: {} commander@7.2.0: {} commander@8.3.0: {} - comment-json@4.4.1: + comment-json@4.6.2: dependencies: array-timsort: 1.0.3 - core-util-is: 1.0.3 esprima: 4.0.1 compress-commons@4.1.2: @@ -8007,59 +7990,62 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - cspell-config-lib@9.4.0: + cspell-config-lib@9.8.0: dependencies: - '@cspell/cspell-types': 9.4.0 - comment-json: 4.4.1 - smol-toml: 1.5.2 - yaml: 2.8.2 + '@cspell/cspell-types': 9.8.0 + comment-json: 4.6.2 + smol-toml: 1.6.1 + yaml: 2.8.3 - cspell-dictionary@9.4.0: + cspell-dictionary@9.8.0: dependencies: - '@cspell/cspell-pipe': 9.4.0 - '@cspell/cspell-types': 9.4.0 - cspell-trie-lib: 9.4.0 - fast-equals: 5.3.3 + '@cspell/cspell-performance-monitor': 9.8.0 + '@cspell/cspell-pipe': 9.8.0 + '@cspell/cspell-types': 9.8.0 + cspell-trie-lib: 9.8.0(@cspell/cspell-types@9.8.0) + fast-equals: 6.0.0 - cspell-gitignore@9.4.0: + cspell-gitignore@9.8.0: dependencies: - '@cspell/url': 9.4.0 - cspell-glob: 9.4.0 - cspell-io: 9.4.0 + '@cspell/url': 9.8.0 + cspell-glob: 9.8.0 + cspell-io: 9.8.0 - cspell-glob@9.4.0: + cspell-glob@9.8.0: dependencies: - '@cspell/url': 9.4.0 - picomatch: 4.0.3 + '@cspell/url': 9.8.0 + picomatch: 4.0.4 - cspell-grammar@9.4.0: + cspell-grammar@9.8.0: dependencies: - '@cspell/cspell-pipe': 9.4.0 - '@cspell/cspell-types': 9.4.0 + '@cspell/cspell-pipe': 9.8.0 + '@cspell/cspell-types': 9.8.0 - cspell-io@9.4.0: + cspell-io@9.8.0: dependencies: - '@cspell/cspell-service-bus': 9.4.0 - '@cspell/url': 9.4.0 + '@cspell/cspell-service-bus': 9.8.0 + '@cspell/url': 9.8.0 - cspell-lib@9.4.0: + cspell-lib@9.8.0: dependencies: - '@cspell/cspell-bundled-dicts': 9.4.0 - '@cspell/cspell-pipe': 9.4.0 - '@cspell/cspell-resolver': 9.4.0 - '@cspell/cspell-types': 9.4.0 - '@cspell/dynamic-import': 9.4.0 - '@cspell/filetypes': 9.4.0 - '@cspell/strong-weak-map': 9.4.0 - '@cspell/url': 9.4.0 + '@cspell/cspell-bundled-dicts': 9.8.0 + '@cspell/cspell-performance-monitor': 9.8.0 + '@cspell/cspell-pipe': 9.8.0 + '@cspell/cspell-resolver': 9.8.0 + '@cspell/cspell-types': 9.8.0 + '@cspell/dynamic-import': 9.8.0 + '@cspell/filetypes': 9.8.0 + '@cspell/rpc': 9.8.0 + '@cspell/strong-weak-map': 9.8.0 + '@cspell/url': 9.8.0 clear-module: 4.1.2 - cspell-config-lib: 9.4.0 - cspell-dictionary: 9.4.0 - cspell-glob: 9.4.0 - cspell-grammar: 9.4.0 - cspell-io: 9.4.0 - cspell-trie-lib: 9.4.0 - env-paths: 3.0.0 + cspell-config-lib: 9.8.0 + cspell-dictionary: 9.8.0 + cspell-glob: 9.8.0 + cspell-grammar: 9.8.0 + cspell-io: 9.8.0 + cspell-trie-lib: 9.8.0(@cspell/cspell-types@9.8.0) + env-paths: 4.0.0 gensequence: 8.0.8 import-fresh: 3.3.1 resolve-from: 5.0.0 @@ -8067,33 +8053,33 @@ snapshots: vscode-uri: 3.1.0 xdg-basedir: 5.1.0 - cspell-trie-lib@9.4.0: + cspell-trie-lib@9.8.0(@cspell/cspell-types@9.8.0): dependencies: - '@cspell/cspell-pipe': 9.4.0 - '@cspell/cspell-types': 9.4.0 - gensequence: 8.0.8 + '@cspell/cspell-types': 9.8.0 - cspell@9.4.0: + cspell@9.8.0: dependencies: - '@cspell/cspell-json-reporter': 9.4.0 - '@cspell/cspell-pipe': 9.4.0 - '@cspell/cspell-types': 9.4.0 - '@cspell/dynamic-import': 9.4.0 - '@cspell/url': 9.4.0 + '@cspell/cspell-json-reporter': 9.8.0 + '@cspell/cspell-performance-monitor': 9.8.0 + '@cspell/cspell-pipe': 9.8.0 + '@cspell/cspell-types': 9.8.0 + '@cspell/cspell-worker': 9.8.0 + '@cspell/dynamic-import': 9.8.0 + '@cspell/url': 9.8.0 ansi-regex: 6.2.2 chalk: 5.6.2 chalk-template: 1.1.2 - commander: 14.0.2 - cspell-config-lib: 9.4.0 - cspell-dictionary: 9.4.0 - cspell-gitignore: 9.4.0 - cspell-glob: 9.4.0 - cspell-io: 9.4.0 - cspell-lib: 9.4.0 + commander: 14.0.3 + cspell-config-lib: 9.8.0 + cspell-dictionary: 9.8.0 + cspell-gitignore: 9.8.0 + cspell-glob: 9.8.0 + cspell-io: 9.8.0 + cspell-lib: 9.8.0 fast-json-stable-stringify: 2.1.0 - flatted: 3.3.3 - semver: 7.7.3 - tinyglobby: 0.2.15 + flatted: 3.4.2 + semver: 7.7.4 + tinyglobby: 0.2.16 css-selector-parser@3.2.0: {} @@ -8335,7 +8321,7 @@ snapshots: direction@2.0.1: {} - dompurify@3.3.0: + dompurify@3.3.3: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -8374,7 +8360,9 @@ snapshots: entities@6.0.1: {} - env-paths@3.0.0: {} + env-paths@4.0.0: + dependencies: + is-safe-filename: 0.1.1 es-define-property@1.0.1: {} @@ -8407,35 +8395,6 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 - esbuild@0.25.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.12 - '@esbuild/android-arm': 0.25.12 - '@esbuild/android-arm64': 0.25.12 - '@esbuild/android-x64': 0.25.12 - '@esbuild/darwin-arm64': 0.25.12 - '@esbuild/darwin-x64': 0.25.12 - '@esbuild/freebsd-arm64': 0.25.12 - '@esbuild/freebsd-x64': 0.25.12 - '@esbuild/linux-arm': 0.25.12 - '@esbuild/linux-arm64': 0.25.12 - '@esbuild/linux-ia32': 0.25.12 - '@esbuild/linux-loong64': 0.25.12 - '@esbuild/linux-mips64el': 0.25.12 - '@esbuild/linux-ppc64': 0.25.12 - '@esbuild/linux-riscv64': 0.25.12 - '@esbuild/linux-s390x': 0.25.12 - '@esbuild/linux-x64': 0.25.12 - '@esbuild/netbsd-arm64': 0.25.12 - '@esbuild/netbsd-x64': 0.25.12 - '@esbuild/openbsd-arm64': 0.25.12 - '@esbuild/openbsd-x64': 0.25.12 - '@esbuild/openharmony-arm64': 0.25.12 - '@esbuild/sunos-x64': 0.25.12 - '@esbuild/win32-arm64': 0.25.12 - '@esbuild/win32-ia32': 0.25.12 - '@esbuild/win32-x64': 0.25.12 - esbuild@0.27.0: optionalDependencies: '@esbuild/aix-ppc64': 0.27.0 @@ -8465,6 +8424,35 @@ snapshots: '@esbuild/win32-ia32': 0.27.0 '@esbuild/win32-x64': 0.27.0 + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -8566,13 +8554,19 @@ snapshots: '@fast-csv/format': 4.3.5 '@fast-csv/parse': 4.3.6 - fast-equals@5.3.3: {} + fast-equals@6.0.0: {} fast-json-stable-stringify@2.1.0: {} - fast-xml-parser@5.2.5: + fast-xml-builder@1.1.4: + dependencies: + path-expression-matcher: 1.4.0 + + fast-xml-parser@5.5.8: dependencies: - strnum: 2.1.2 + fast-xml-builder: 1.1.4 + path-expression-matcher: 1.4.0 + strnum: 2.2.3 fault@2.0.1: dependencies: @@ -8582,9 +8576,9 @@ snapshots: dependencies: pend: 1.2.0 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.4 fetch-blob@3.2.0: dependencies: @@ -8596,7 +8590,7 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - flatted@3.3.3: {} + flatted@3.4.2: {} follow-redirects@1.15.11: {} @@ -8684,9 +8678,9 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 - global-directory@4.0.1: + global-directory@5.0.0: dependencies: - ini: 4.1.1 + ini: 6.0.0 gopd@1.2.0: {} @@ -8869,7 +8863,7 @@ snapshots: property-information: 7.1.0 space-separated-tokens: 2.0.2 - hono@4.12.3: {} + hono@4.12.12: {} html-void-elements@3.0.0: {} @@ -8905,7 +8899,7 @@ snapshots: inherits@2.0.4: {} - ini@4.1.1: {} + ini@6.0.0: {} inline-style-parser@0.2.7: {} @@ -8930,6 +8924,8 @@ snapshots: is-plain-obj@4.1.0: {} + is-safe-filename@0.1.1: {} + is-stream@2.0.1: {} is-unicode-supported@1.3.0: {} @@ -9325,7 +9321,7 @@ snapshots: d3-sankey: 0.12.3 dagre-d3-es: 7.0.13 dayjs: 1.11.19 - dompurify: 3.3.0 + dompurify: 3.3.3 katex: 0.16.25 khroma: 2.1.0 lodash-es: 4.17.21 @@ -9632,11 +9628,11 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 1.1.13 minimatch@5.1.9: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.0.3 minimist@1.2.8: {} @@ -9771,6 +9767,8 @@ snapshots: path-exists@4.0.0: {} + path-expression-matcher@1.4.0: {} + path-is-absolute@1.0.1: {} path-key@3.1.1: {} @@ -9781,7 +9779,7 @@ snapshots: picocolors@1.1.1: {} - picomatch@4.0.3: {} + picomatch@4.0.4: {} pkg-types@1.3.1: dependencies: @@ -9818,7 +9816,7 @@ snapshots: property-information@7.1.0: {} - proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} pump@3.0.3: dependencies: @@ -10198,6 +10196,8 @@ snapshots: semver@7.7.3: {} + semver@7.7.4: {} + send@0.19.0: dependencies: debug: 2.6.9 @@ -10283,7 +10283,7 @@ snapshots: sisteransi@1.0.5: {} - smol-toml@1.5.2: {} + smol-toml@1.6.1: {} source-map-js@1.2.1: {} @@ -10331,7 +10331,7 @@ snapshots: strip-final-newline@2.0.0: {} - strnum@2.1.2: {} + strnum@2.2.3: {} style-to-js@1.1.21: dependencies: @@ -10363,8 +10363,13 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tmp@0.2.5: {} @@ -10543,7 +10548,7 @@ snapshots: debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -10558,11 +10563,11 @@ snapshots: - tsx - yaml - vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2): + vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2): dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + esbuild: 0.27.7 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 postcss: 8.5.6 rollup: 4.59.0 tinyglobby: 0.2.15 @@ -10576,7 +10581,7 @@ snapshots: vocs@1.2.2(@types/node@24.10.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.1(react@19.2.1))(react-router-dom@7.12.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(rollup@4.59.0)(typescript@5.9.3): dependencies: '@floating-ui/react': 0.27.16(react-dom@19.2.1(react@19.2.1))(react@19.2.1) - '@hono/node-server': 1.19.6(hono@4.12.3) + '@hono/node-server': 1.19.13(hono@4.12.12) '@mdx-js/mdx': 3.1.1 '@mdx-js/react': 3.1.1(@types/react@19.2.7)(react@19.2.1) '@mdx-js/rollup': 3.1.1(rollup@4.59.0) @@ -10592,11 +10597,11 @@ snapshots: '@shikijs/rehype': 1.29.2 '@shikijs/transformers': 1.29.2 '@shikijs/twoslash': 1.29.2(typescript@5.9.3) - '@tailwindcss/vite': 4.1.15(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@tailwindcss/vite': 4.1.15(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) '@vanilla-extract/css': 1.17.5 '@vanilla-extract/dynamic': 2.1.5 - '@vanilla-extract/vite-plugin': 5.1.3(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(yaml@2.8.2) - '@vitejs/plugin-react': 5.1.1(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@vanilla-extract/vite-plugin': 5.1.3(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))(yaml@2.8.2) + '@vitejs/plugin-react': 5.1.1(vite@7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) autoprefixer: 10.4.22(postcss@8.5.6) cac: 6.7.14 chroma-js: 3.2.0 @@ -10606,7 +10611,7 @@ snapshots: cross-spawn: 7.0.6 fs-extra: 11.3.2 hastscript: 8.0.0 - hono: 4.12.3 + hono: 4.12.12 mark.js: 8.11.1 mdast-util-directive: 3.1.0 mdast-util-from-markdown: 2.0.2 @@ -10620,7 +10625,7 @@ snapshots: nuqs: 2.8.2(react-router-dom@7.12.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react-router@7.12.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1) ora: 7.0.1 p-limit: 5.0.0 - picomatch: 4.0.3 + picomatch: 4.0.4 playwright: 1.57.0 postcss: 8.5.6 radix-ui: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) @@ -10646,7 +10651,7 @@ snapshots: unified: 11.0.5 unist-util-visit: 5.0.0 vfile-matter: 5.0.1 - vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + vite: 7.3.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) yaml: 2.8.2 transitivePeerDependencies: - '@remix-run/react' @@ -10707,6 +10712,8 @@ snapshots: yaml@2.8.2: {} + yaml@2.8.3: {} + yauzl@2.10.0: dependencies: buffer-crc32: 0.2.13 From 9f2158aec3f6ab432a32859c5518e53e93291afa Mon Sep 17 00:00:00 2001 From: txscope-sol Date: Tue, 21 Apr 2026 18:45:42 +0200 Subject: [PATCH 16/92] =?UTF-8?q?Add=20TxScope=20=E2=80=94=20Solana=20mult?= =?UTF-8?q?isig=20pre-signing=20threat=20scanner=20(#444)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds TxScope to monitoring tools and wallet security tools pages. TxScope is a Solana-native pre-signing transaction threat scanner for Squads Protocol multisigs, filling the non-EVM tooling gap noted in the current tools page. Co-authored-by: black Co-authored-by: Claude Opus 4.6 (1M context) --- docs/pages/monitoring/tools.mdx | 12 ++++++++++++ docs/pages/wallet-security/tools-and-resources.mdx | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/docs/pages/monitoring/tools.mdx b/docs/pages/monitoring/tools.mdx index eb2f62a0e..f360a5cdc 100644 --- a/docs/pages/monitoring/tools.mdx +++ b/docs/pages/monitoring/tools.mdx @@ -105,6 +105,18 @@ serverless functions that run on Tenderly's infrastructure). The CLI and SDKs ar - **Website:** [tenderly.co](https://tenderly.co) - **GitHub:** [Tenderly](https://github.com/Tenderly) (CLI, SDKs, framework plugins) +### TxScope + +Pre-signing transaction threat scanner for Solana multisigs. Monitors Squads Protocol vaults for pending proposals, +simulates each transaction against mainnet via Helius RPC, and generates plain-language threat reports before any +signer approves. Detection modules cover durable nonces, authority and admin transfers, withdrawal guard +manipulation, known attack pattern matching, instruction-level trace and decode, proposer history and anomaly +detection, and risk scoring (0–100). Free for on-demand scans; paid tiers for continuous monitoring with Telegram +and Slack alerts. + +- **Chains:** Solana +- **Website:** [txscope.com](https://txscope.com) + ## Reliability Considerations Your monitoring system is only effective if it is itself reliable. Before committing to a tooling setup, evaluate diff --git a/docs/pages/wallet-security/tools-and-resources.mdx b/docs/pages/wallet-security/tools-and-resources.mdx index e856745be..32624c251 100644 --- a/docs/pages/wallet-security/tools-and-resources.mdx +++ b/docs/pages/wallet-security/tools-and-resources.mdx @@ -84,6 +84,10 @@ Implement continuous monitoring to detect unauthorized or suspicious activity on - Connect to Telegram for notifications - Monitor for suspicious delegateCall transactions +### Solana Multisig Monitoring + +- **[TxScope](https://txscope.com)**: Pre-signing threat scanner for Solana Squads multisigs. Simulates pending proposals against mainnet state and generates plain-language threat reports with risk scoring, durable nonce detection, authority transfer alerts, and known attack pattern matching. + ### Network Security For dedicated signing machines, implement network-level protections: From a1637dcab1c2d25d0e29aa6ea64103deb8e7e511 Mon Sep 17 00:00:00 2001 From: odin free <58338510+welttowelt@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:46:17 +0200 Subject: [PATCH 17/92] [codex] Clarify hot vs cold wallet taxonomy (#449) * Clarify hot vs cold wallet taxonomy * Narrow hot vs cold wallet edits --------- Co-authored-by: welttowelt --- docs/pages/wallet-security/cold-vs-hot-wallet.mdx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/pages/wallet-security/cold-vs-hot-wallet.mdx b/docs/pages/wallet-security/cold-vs-hot-wallet.mdx index b68097f24..cf4aec64f 100644 --- a/docs/pages/wallet-security/cold-vs-hot-wallet.mdx +++ b/docs/pages/wallet-security/cold-vs-hot-wallet.mdx @@ -38,9 +38,9 @@ are stored on device with minimal connectivity. > ❓ Did you know? > -> Most cold wallets come with some way to connect to the internet, such as via a USB connection. This technically makes -> them "hot" when connected. However, the key distinction is that they are not continuously online and are designed to -> minimize exposure to online threats. +> Most cold wallets still need a way to exchange transaction data with a connected device, such as via USB, Bluetooth, +> QR codes, or SD cards. This does not make them hot by itself. The key distinction is whether the private key remains +> isolated from the internet-connected system during signing. ### Types of Cold Wallets @@ -50,8 +50,9 @@ exposing the keys to a connected internet device. - **Software Wallets on Air-Gapped Devices**: Standard wallet software installed on a device that is permanently disconnected from the internet, used for offline transaction signing. - **Brain Wallets**: Private keys that are memorized. -- **Account Abstraction Wallets**: Using smart contracts to manage keys and transactions without exposing private keys. -- **Multisig Wallets**: Require multiple signatures to authorize a transaction, enhancing security. + +Account abstraction wallets and multisig wallets are separate account and authorization models, not types of cold +wallets. Either approach can use hot signers, cold signers, or a mix of both. ### Use Cases From 7558510663d624ddac57f7ea9a2a6e9074592caf Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Tue, 21 Apr 2026 18:47:46 +0200 Subject: [PATCH 18/92] Frontend Web app FW: Add third party script security page (#454) * Add third party script security page in the frontend web app fw * fix Further Reading link * Fix links in web3-supply-chain-threats.mdx --- docs/pages/front-end-web-app/index.mdx | 1 + docs/pages/front-end-web-app/overview.mdx | 5 +- .../third-party-script-security.mdx | 388 ++++++++++++++++++ .../supply-chain/dependency-awareness.mdx | 2 + .../web3-supply-chain-threats.mdx | 14 +- utils/fetched-tags.json | 4 + vocs.config.tsx | 1 + 7 files changed, 411 insertions(+), 4 deletions(-) create mode 100644 docs/pages/front-end-web-app/third-party-script-security.mdx diff --git a/docs/pages/front-end-web-app/index.mdx b/docs/pages/front-end-web-app/index.mdx index 7a2fe11eb..4ab594cce 100644 --- a/docs/pages/front-end-web-app/index.mdx +++ b/docs/pages/front-end-web-app/index.mdx @@ -13,6 +13,7 @@ title: "Front End Web App" - [Front-End Web Application Security](/front-end-web-app/overview) - [Web Application Security](/front-end-web-app/web-application-security) +- [Third-Party Script Security](/front-end-web-app/third-party-script-security) - [Mobile Application Security](/front-end-web-app/mobile-application-security) - [Common Web Vulnerabilities](/front-end-web-app/common-vulnerabilities) - [Security Tools & Resources](/front-end-web-app/security-tools-resources) diff --git a/docs/pages/front-end-web-app/overview.mdx b/docs/pages/front-end-web-app/overview.mdx index d3d2f1307..c7770fdf1 100644 --- a/docs/pages/front-end-web-app/overview.mdx +++ b/docs/pages/front-end-web-app/overview.mdx @@ -25,8 +25,9 @@ as they could, for example, start interacting with a malicious contract instead 1. [Web Application Security](/front-end-web-app/web-application-security) 2. [Mobile Application Security](/front-end-web-app/mobile-application-security) -3. [Common Vulnerabilities](/front-end-web-app/common-vulnerabilities) -4. [Security Tools and Resources](/front-end-web-app/security-tools-resources) +3. [Third-Party Script Security](/front-end-web-app/third-party-script-security) +4. [Common Vulnerabilities](/front-end-web-app/common-vulnerabilities) +5. [Security Tools and Resources](/front-end-web-app/security-tools-resources) --- diff --git a/docs/pages/front-end-web-app/third-party-script-security.mdx b/docs/pages/front-end-web-app/third-party-script-security.mdx new file mode 100644 index 000000000..c1eb0c8a2 --- /dev/null +++ b/docs/pages/front-end-web-app/third-party-script-security.mdx @@ -0,0 +1,388 @@ +--- +title: "Third-Party Script Security | SEAL" +description: "Safely load third-party scripts in Web3 frontends using Content Security Policy (CSP) and Subresource Integrity (SRI). Prevent supply chain attacks on Web3 users." +tags: + - Engineer/Developer + - Security Specialist +contributors: + - role: wrote + users: [scode2277] +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' + + + + +# Third-Party Script Security + + + + +> 🔑 **Key Takeaway:** Every third-party script running on your frontend shares your page's origin, meaning it +> can access cookies, localStorage, session tokens, and call `window.ethereum` methods directly. Use Content +> Security Policy, Subresource Integrity, and Trusted Types to control what code executes, verify it has not +> been tampered with, and lock down dangerous DOM APIs. + +Web3 frontends are high-value targets because they sit between users and their wallets. Every script that runs +on your page executes with the same permissions as your own code. When a user visits your application and signs +a transaction, any script on the page can observe, modify, or replace that interaction. If an attacker +compromises a third-party script your frontend loads (a CDN-hosted library, an analytics snippet, a wallet +connector), they can silently redirect transactions, drain wallets, or harvest credentials without touching your +own code. + +This is not a theoretical concern. Multiple attacks have exploited third-party scripts to compromise +Web3 frontends at scale (see [Past Incidents](#past-incidents) below). Three browser-native +mechanisms, **Content Security Policy (CSP)**, **Subresource Integrity (SRI)**, and **Trusted Types**, provide +strong defenses against these attacks. Neither requires external tooling or dependencies because they are built +into every modern browser. + +## Content Security Policy (CSP) + +A Content Security Policy is an HTTP response header that tells the browser which sources of content are +permitted to load on your page. If a script, style, image, or connection does not match your policy, the browser +blocks it. This means that even if an attacker injects a ` +``` + +### How It Works + +1. You generate a SHA-256, SHA-384, or SHA-512 hash of the exact file you expect to load. +2. You include this hash in the `integrity` attribute of the ` +``` + +With this import map in place, any code that runs `import('ethers')` will resolve to your self-hosted copy, not +to an external CDN. Combined with a CSP that restricts `script-src` to your own origin (or uses +`strict-dynamic`), this gives you centralized control over module resolution even for dynamic imports. + +Import maps do not replace SRI. They address a different part of the problem: SRI verifies file integrity; +import maps control where files are loaded from. Use them together: import maps to pin resolution to known +locations, and SRI on any statically loaded scripts from those locations. + +## Trusted Types + +Trusted Types is a browser API (enforced via CSP) that prevents DOM-based cross-site scripting by locking down +dangerous sink APIs. Functions like `innerHTML`, `outerHTML`, `document.write`, `eval`, and `setTimeout(string)` +are the most common vectors for DOM XSS. Trusted Types makes it impossible to pass raw strings to these sinks. +You must instead pass a "trusted" value created through a policy you define. + +For Web3 frontends, this is directly relevant. Wallet connection flows, transaction confirmation UIs, and token +approval dialogs often manipulate the DOM. If an attacker can inject markup through a compromised dependency +that uses `innerHTML`, they can render a fake approval dialog or redirect a transaction. Trusted Types block +this class of attack at the API level. + +### Enabling Trusted Types + +Add `require-trusted-types-for 'script'` to your CSP header: + +``` +Content-Security-Policy: + require-trusted-types-for 'script'; + trusted-types myapp-policy; +``` + +Then define a named policy in your application code. At minimum the policy should sanitize HTML input before +it reaches the DOM (using a library like DOMPurify), block dynamic script creation, and restrict script URLs +to your own origin. The [MDN Trusted Types documentation](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) and +the [Google adoption guide](https://web.dev/articles/trusted-types) cover policy implementation in detail. + +### Adoption Strategy + +Trusted Types can be disruptive to adopt because many libraries (including some wallet connectors) use +`innerHTML` internally. Roll it out gradually: + +1. **Start in report-only mode** using `Content-Security-Policy-Report-Only` with + `require-trusted-types-for 'script'`. This logs violations without breaking anything. +2. **Identify violating code** from the reports. Common culprits are UI libraries, rich text rendering, and + third-party widgets. +3. **Create a `default` policy** as a transitional measure. The browser calls it for any assignment that does + not already use a trusted type, letting you handle violations centrally before enforcing per-policy. +4. **Enforce** once violations are resolved. + +Trusted Types are supported across all modern browsers as of early 2026. The CSP directive degrades gracefully +on older browsers: it is simply ignored, so it will not break your application. + +## Self-Hosting Critical Dependencies + +The strongest defense against third-party script compromise is to eliminate the third party entirely by +bundling dependencies into your own build output. CSP and SRI reduce the risk of loading external scripts; +self-hosting removes it. For guidance on vendoring, version pinning, and managing the operational trade-offs, +see [Dependency Awareness](/supply-chain/dependency-awareness). + +## Past Incidents + +These attacks demonstrate why third-party script controls are not optional for Web3 frontends: + +- **npm registry attack (September 2025).** A maintainer's credentials were phished and malicious versions of + 18 widely-used packages, including `chalk`, `debug`, and `ansi-styles`, were published. The payload hooked + `window.ethereum` to intercept wallet calls and rewrote `fetch`/`XMLHttpRequest` to reroute transactions to + attacker-controlled addresses. SRI would have blocked the tampered scripts; a strict `connect-src` policy + would have prevented exfiltration. +- **Lottie Player (October 2024).** Malicious versions of `@lottiefiles/lottie-player` were published, + injecting a crypto drainer into any site displaying Lottie animations. The payload prompted users to connect + their wallets and drained funds. Applications loading the library via CDN without SRI were immediately + vulnerable. +- **Polyfill.io (June 2024).** After a change in ownership, the `polyfill.io` CDN began injecting malicious + redirects into a script served to over 100,000 websites, targeting mobile users. Sites using SRI would have + blocked the modified script entirely. +- **Ledger Connect Kit (December 2023).** A former employee's npm credentials were used to publish a malicious + version of `@ledgerhq/connect-kit`. The injected code rendered a fake wallet connection modal that redirected + funds. Every application loading the library without integrity checks was affected simultaneously. + +For a broader catalog of supply chain attack vectors, see [Web3 Supply Chain Threats](/supply-chain/web3-supply-chain-threats). + +## Runtime Monitoring + +Build-time defenses (CSP, SRI, self-hosting) establish the baseline, but you also need visibility into what +happens at runtime in production. Attacks do not always come through the vectors you anticipated, and +misconfigurations can silently weaken your defenses. + +### CSP Violation Reporting + +Start every new policy in report-only mode using `Content-Security-Policy-Report-Only`. This logs what would +be blocked without breaking anything. Review the reports, adjust the policy, and only switch to the enforcing +`Content-Security-Policy` header once you are confident it will not break legitimate functionality. After +switching to enforcement, keep `report-to` configured so any new violations surface immediately. + +Set `report-to` in your CSP header and pair it with a `Report-To` header that points to a collection endpoint +you control. Every blocked resource generates a report containing the violated directive, the blocked URI, and +the page where it happened. Aggregate these to detect injection attempts, misconfigurations after deployments, +and third-party scripts loading unexpected resources. + +### DOM Mutation Monitoring + +CSP cannot detect all forms of script injection, particularly when a trusted script's behavior changes (for +example, a compromised dependency that starts injecting iframes or modifying wallet-related DOM elements). +MutationObservers let you watch for suspicious changes at runtime: flag any script node added without a nonce, +and remove unexpected iframes that were not part of your known page structure. + +This is not a replacement for CSP. It is a detection layer. Use it to catch things that slip through policy +gaps or to monitor for unexpected behavior from trusted scripts. + +### External Script Change Detection + +For any script you load from a third-party origin (even with SRI), run a periodic job that fetches the resource +and compares its hash to the expected value. A hash mismatch on a resource that should not have changed is an +early indicator of a supply chain compromise. Integrate this into your CI pipeline and your production monitoring +separately: CI catches changes at build time, production monitoring catches changes at serve time. + +## Additional Defenses + +Beyond the core mechanisms above, consider these complementary measures: + +- **Iframe sandboxing for third-party widgets.** If you must embed third-party content, load it in sandboxed + iframes with restrictive `sandbox` attributes. This isolates it from your main page context and prevents it + from interacting with wallet-related DOM or JavaScript. +- **Permissions Policy.** The `Permissions-Policy` HTTP header restricts which browser features third-party + scripts and frames can access: payment APIs, clipboard, camera, and others. Even if a compromised script + runs, it cannot invoke features your application never needed. + +## Further Reading + +- [MDN: Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) -> API reference and browser compatibility +- [MDN: Securing CDN resources with SRI](https://developer.mozilla.org/en-US/blog/securing-cdn-using-sri-why-how/) -> walkthrough on applying SRI to CDN-hosted assets +- [MDN: Import Maps](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) -> specification and usage guide +- [MDN: Trusted Types](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) -> API reference and browser compatibility +- [Google: Trusted Types Adoption Guide](https://web.dev/articles/trusted-types) -> step-by-step policy implementation guide + +## Related Frameworks + +- [Supply Chain Security](/supply-chain/overview): Dependency management, vendor risk, and incident response + for compromised components +- [Web3 Supply Chain Threats](/supply-chain/web3-supply-chain-threats): Catalog of frontend, smart contract, + and infrastructure attack vectors +- [Dependency Awareness](/supply-chain/dependency-awareness): Version pinning, lockfile integrity, and + vulnerability scanning for the packages your frontend loads +- [DevSecOps](/devsecops/overview): Integrating security checks into your build and deployment pipelines +- [Domain & DNS Security](/infrastructure/domain-and-dns-security/overview): Preventing DNS hijacking and CDN + cache poisoning + +--- + + + diff --git a/docs/pages/supply-chain/dependency-awareness.mdx b/docs/pages/supply-chain/dependency-awareness.mdx index 1780714b8..2026934cb 100644 --- a/docs/pages/supply-chain/dependency-awareness.mdx +++ b/docs/pages/supply-chain/dependency-awareness.mdx @@ -381,6 +381,8 @@ contracts and their imported libraries, see - [Snyk Vulnerability Database](https://security.snyk.io/): Searchable database of known vulnerabilities - [Cargo Security Advisories](https://rustsec.org/): Rust security advisory database and `cargo-audit` - [Go Vulnerability Database](https://vuln.go.dev/): Official Go vulnerability tracking and `govulncheck` +- [Third-Party Script Security](/front-end-web-app/third-party-script-security): Runtime integrity controls (Content + Security Policy, Subresource Integrity, self-hosting) that complement build-time dependency practices --- diff --git a/docs/pages/supply-chain/web3-supply-chain-threats.mdx b/docs/pages/supply-chain/web3-supply-chain-threats.mdx index 032959adf..7c0105d7f 100644 --- a/docs/pages/supply-chain/web3-supply-chain-threats.mdx +++ b/docs/pages/supply-chain/web3-supply-chain-threats.mdx @@ -56,6 +56,9 @@ the user's browser. For practices to defend against these attacks, see [Dependency Awareness](/supply-chain/dependency-awareness). +For runtime integrity verification using Subresource Integrity and Content Security Policy, see +[Third-Party Script Security](/front-end-web-app/third-party-script-security). + ### Wallet Connector Library Hijacking Wallet connector libraries are a particularly high-value target because they sit at the exact point where user intent @@ -69,6 +72,9 @@ meets transaction construction. For wallet-specific security practices, see the [Wallet Security](/wallet-security/overview) framework. +For browser-level controls that would have blocked execution of the tampered library, see +[Third-Party Script Security](/front-end-web-app/third-party-script-security). + ### CDN and Hosting Compromise An attacker who compromises your hosting provider or poisons a CDN cache can serve tampered JavaScript to all users @@ -78,8 +84,10 @@ without touching your repository. signing with fund redirection. - **CDN cache poisoning** can serve malicious scripts even if your origin server is clean. -For DNS hardening, registrar locks, and monitoring, see the -[Domain & DNS Security](/infrastructure/domain-and-dns-security/overview) framework. +For DNS hardening, registrar locks, and monitoring, see the [Domain & DNS Security](/infrastructure/domain-and-dns-security/overview) framework. + +For browser-level defenses against CDN and hosting compromise, including Content Security Policy, Subresource Integrity, and self-hosting +strategies, see [Third-Party Script Security](/front-end-web-app/third-party-script-security). ## Smart Contract Dependency Risks @@ -252,6 +260,8 @@ For hardware wallet security guidance, see the [Wallet Security](/wallet-securit [Vendor Risk Management](/supply-chain/vendor-risk-management) guide - Track real-time exploit data and historical incidents on the [DeFiLlama Hacks Dashboard](https://defillama.com/hacks) +- [Third-Party Script Security](/front-end-web-app/third-party-script-security): Runtime integrity controls (Content + Security Policy, Subresource Integrity, self-hosting) that complement build-time dependency practices --- diff --git a/utils/fetched-tags.json b/utils/fetched-tags.json index ed3ecbb4e..eebc4e555 100644 --- a/utils/fetched-tags.json +++ b/utils/fetched-tags.json @@ -418,6 +418,10 @@ "Engineer/Developer", "Security Specialist" ], + "/front-end-web-app/third-party-script-security": [ + "Engineer/Developer", + "Security Specialist" + ], "/front-end-web-app/web-application-security": [ "Engineer/Developer", "Security Specialist" diff --git a/vocs.config.tsx b/vocs.config.tsx index 8b9b94ba4..0cc10f7ce 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -176,6 +176,7 @@ const config = { items: [ { text: 'Overview', link: '/front-end-web-app/overview', dev: true }, { text: 'Web Application Security', link: '/front-end-web-app/web-application-security', dev: true }, + { text: 'Third-Party Script Security', link: '/front-end-web-app/third-party-script-security', dev: true }, { text: 'Mobile Application Security', link: '/front-end-web-app/mobile-application-security', dev: true }, { text: 'Common Vulnerabilities', link: '/front-end-web-app/common-vulnerabilities', dev: true }, { text: 'Security Tools and Resources', link: '/front-end-web-app/security-tools-resources', dev: true }, From 7e19a307410c2a935531d558d66d77097e3c49f6 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Tue, 21 Apr 2026 18:48:13 +0200 Subject: [PATCH 19/92] Chore: weekly repo cleanup (#455) * weekly cleanup: - sync badges - fix outputs spellchecker - fix outputs linter - sync contributing files - sync tags * Cleanup of the latest PR merged * sync contributor badges with latest PRs * Fix indentation --- CONTRIBUTING.md | 28 +++--- docs/pages/config/contributors.json | 93 +++++++++++++------ docs/pages/contribute/contributing.mdx | 9 +- .../techniques-tactics-and-procedures.mdx | 8 +- docs/pages/opsec/endpoint/overview.mdx | 25 +++-- docs/pages/opsec/mfa/overview.mdx | 34 +++++-- wordlist.txt | 2 + 7 files changed, 137 insertions(+), 62 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c0a82b63..422c43852 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -69,7 +69,7 @@ in MDX files. 5. **Notify reviewers** by tagging a steward or maintainer, requesting reviews directly in your PR. 6. Additionally, you can paste your PR and/or potential associated issues to the `frameworks-contribs` Discord channel. 7. Once reviewed and approved, your changes will be merged into `develop`. -8. Don't forget to add yourself to the YAML header of the file you're modifying, since that is how we provide +8. Don't forget to add yourself to the YAML header of the file you're modifying, given that is the way we provide attribution. You should also create your profile inside the contributors list, at `docs/pages/config/contributors.json`. 9. Periodically, reviewed content from `develop` is merged into `main` for the stable site. @@ -85,7 +85,7 @@ Choose the development approach that works best for you: ### Option A: DevContainer with VSCode -The easiest way to get started is using our pre-configured devcontainer with VSCode: +The easiest way to get started is to use our pre-configured devcontainer with VSCode: 1. **Prerequisites**: VSCode with [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) @@ -96,14 +96,14 @@ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote. ### Option B: DevContainer CLI Only (No VSCode Required) Since you won't require extensions for the initiative to work, you can just create a devcontainer using the CLI and -access it through whatever means you think suits you best. +access it through whatever means suit you best. **Using DevContainer CLI (Recommended):** - Install [DevContainer CLI](https://github.com/devcontainers/cli) ```bash -git clone +git clone https://github.com/security-alliance/frameworks.git cd frameworks && git checkout develop devcontainer up --workspace-folder . devcontainer exec --workspace-folder . bash @@ -129,7 +129,7 @@ If you prefer to install dependencies locally on your machine: 2. Clone the repository: ```bash - git clone + git clone https://github.com/security-alliance/frameworks.git cd frameworks && git checkout develop ``` @@ -145,7 +145,7 @@ If you prefer to install dependencies locally on your machine: pnpm exec just serve ``` -5. Once the server is running, access the site at port ```5173``` +5. Once the server is running, access the site on port `5173`. **(Optional) Authenticate with GitHub CLI**: The GitHub CLI (`gh`) is already preinstalled in the devcontainer. You can authenticate by running `gh auth login` in the terminal, making it easy to interact with GitHub directly from your @@ -298,7 +298,7 @@ Example of a category with multiple pages: This ensures that new content appears correctly in the site’s navigation for readers on the `.dev` site while staying hidden from the stable `.org` site until ready. -### 4. Error Checking +### 3. Error Checking Before pushing changes, always make sure your build works without errors: @@ -312,7 +312,7 @@ This helps catch build or formatting issues early so reviewers see clean contrib Wiki pages follow standard MDX. The audience of this wiki is technical, and the content should reflect that. There are many guides on technical and -documentation writing you can learn from, for example, you can check [this +documentation writing you can learn from; for example, you can check [this lecture](https://www.youtube.com/watch?v=vtIzMaLkCaM) to get started. ### Writing guidelines @@ -321,13 +321,13 @@ lecture](https://www.youtube.com/watch?v=vtIzMaLkCaM) to get started. - Use concise sentences and break down complex ideas with bullet points, tables, images, or block-quotes. - Always link your resources and verify them - Introduce acronyms and technical jargon before using them. -- Web3 changes fast, write the content to be as future-proof as possible +- Web3 changes fast; write the content to be as future-proof as possible. - Do **not** submit content entirely generated by AI; however, we recommend using it to fix grammar or phrasing - Consider tutorials or hands-on guides for practical steps. - Use visualizations (mermaid, diagrams, tables) to clarify concepts. - Add recommended reading or dependencies at the top of a page if relevant. - Focus on delivering credible, formal, technical content without unnecessary high-level introductions; use examples, -comparisons, or anecdotes to clarify complex topics. + comparisons, or anecdotes to clarify complex topics. - You can use mermaid diagrams for visualizations ### Content standardization @@ -351,15 +351,17 @@ fits, for example in block-quotes. where you can jump straight to draw! ```mermaid - pie title What Voldemort doesn't have? +pie title What Voldemort doesn't have? "FRIENDS" : 2 "FAMILY" : 3 "NOSE" : 45 - ``` +``` + - Adding images is welcome and encouraged. Please follow the steps below to include them correctly: - 1. After making your changes and opening a PR, add the images you want to include in the PR's comments (by uploading them directly) + 1. After making your changes and opening a PR, add the images you want to include in the PR's comments + (by uploading them directly) 2. During the review, a maintainer will upload your images to our S3 bucket and reply with the links you should use. 3. Once you receive the new links, update your PR to add the images' links. diff --git a/docs/pages/config/contributors.json b/docs/pages/config/contributors.json index ae43731a9..b39892752 100644 --- a/docs/pages/config/contributors.json +++ b/docs/pages/config/contributors.json @@ -23,7 +23,7 @@ { "name": "Issue-Opener-5", "assigned": "2024-08-22" }, { "name": "Issue-Opener-10", "assigned": "2024-08-24" }, { "name": "Issue-Opener-25", "assigned": "2024-09-25" }, - { "name": "Active-Last-7d", "lastActive": "2026-03-24" } + { "name": "Active-Last-7d", "lastActive": "2026-04-08" } ] }, "fredriksvantes": { @@ -191,7 +191,7 @@ { "name": "Framework-Steward", "assigned": "2025-07-10", "framework": "Wallet Security" }, { "name": "First-Contribution", "assigned": "2025-07-10" }, { "name": "First-Review", "assigned": "2025-09-25" }, - { "name": "Dormant-90d+", "lastActive": "2025-10-27" } + { "name": "Active-Last-7d", "lastActive": "2026-04-07" } ] }, "njelich": { @@ -231,7 +231,7 @@ { "name": "First-Review", "assigned": "2025-08-11" }, { "name": "Reviewer-10", "assigned": "2026-02-24" }, { "name": "Reviewer-25", "assigned": "2024-03-01" }, - { "name": "Active-Last-7d", "lastActive": "2026-03-23" } + { "name": "Active-Last-7d", "lastActive": "2026-04-06" } ] }, "blackbigswan": { @@ -445,7 +445,7 @@ { "name": "First-Review", "assigned": "2025-08-12" }, { "name": "Reviewer-10", "assigned": "2025-09-12" }, { "name": "Reviewer-25", "assigned": "2026-03-20" }, - { "name": "Active-Last-7d", "lastActive": "2026-03-25" } + { "name": "Active-Last-7d", "lastActive": "2026-04-09" } ] }, "gunnim": { @@ -461,7 +461,7 @@ "description": "Cloud architecture enthusiast with a passion for IT Security", "badges": [ { "name": "First-Contribution", "assigned": "2026-01-21" }, - { "name": "Active-Last-7d", "lastActive": "2026-03-21" } + { "name": "Active-Last-30d", "lastActive": "2026-03-21" } ] }, "madjin": { @@ -552,8 +552,11 @@ "company": "QuillAudits", "job_title": "Smart Contract Audit Firm", "role": "contributor", - "description": "Leading smart contract audit firm specializing in Web3 security solutions, DeFi auditing, and DApp penetration testing.", - "badges": [] + "description": "Frameworks Contributor", + "badges": [ + { "name": "First-Contribution", "assigned": "2025-12-30" }, + { "name": "Dormant-90d+", "lastActive": "2025-12-30" } + ] }, "hexnickk4997": { "slug": "hexnickk4997", @@ -634,21 +637,21 @@ { "name": "First-Contribution", "assigned": "2025-09-18" }, { "name": "Dormant-90d+", "lastActive": "2025-09-18" } ] -}, -"andrew-chang-gu": { - "slug": "andrew-chang-gu", - "name": "Andrew Chang-Gu", - "avatar": "", - "github": "", - "twitter": "", - "website": "https://www.linkedin.com/in/achanggu", - "company": "Google Cloud Security", - "job_title": "Google Cloud Security", - "role": "contributor", - "description": "Google Cloud Security", - "badges": [] -}, -"JosepBove": { + }, + "andrew-chang-gu": { + "slug": "andrew-chang-gu", + "name": "Andrew Chang-Gu", + "avatar": null, + "github": null, + "twitter": null, + "website": "https://www.linkedin.com/in/achanggu", + "company": "Google Cloud Security", + "job_title": "Google Cloud Security", + "role": "contributor", + "description": "Google Cloud Security", + "badges": [] + }, + "JosepBove": { "slug": "JosepBove", "name": "Josep Bove", "avatar": "https://avatars.githubusercontent.com/JosepBove", @@ -662,14 +665,47 @@ "badges": [ { "name": "Framework-Steward", "assigned": "2026-03-17", "framework": "Monitoring" }, { "name": "First-Contribution", "assigned": "2026-03-16" }, - { "name": "New-Joiner", "lastActive": "2026-03-17" }, - { "name": "Active-Last-7d", "lastActive": "2026-03-23" } + { "name": "Active-Last-30d", "lastActive": "2026-03-23" } ] -}, -"shallem": { + }, + "tim-sha256": { + "slug": "tim-sha256", + "name": "tim-sha256", + "avatar": "https://avatars.githubusercontent.com/tim-sha256", + "github": "https://github.com/tim-sha256", + "twitter": null, + "website": null, + "company": null, + "job_title": null, + "role": "contributor", + "description": "Frameworks Contributor", + "badges": [ + { "name": "First-Contribution", "assigned": "2026-04-05" }, + { "name": "New-Joiner", "assigned": "2026-04-05" }, + { "name": "Active-Last-7d", "lastActive": "2026-04-05" } + ] + }, + "fvelazquez-x": { + "slug": "fvelazquez-x", + "name": "fvelazquez-x", + "avatar": "https://avatars.githubusercontent.com/fvelazquez-x", + "github": "https://github.com/fvelazquez-x", + "twitter": null, + "website": null, + "company": null, + "job_title": null, + "role": "contributor", + "description": "Frameworks Contributor", + "badges": [ + { "name": "First-Contribution", "assigned": "2026-04-07" }, + { "name": "New-Joiner", "assigned": "2026-04-07" }, + { "name": "Active-Last-7d", "lastActive": "2026-04-07" } + ] + }, + "shallem": { "slug": "shallem", "name": "Seth Hallem", - "avatar": "", + "avatar": null, "github": "https://github.com/shallem", "twitter": "https://x.com/seth_certora", "website": "https://www.certora.com/", @@ -678,6 +714,9 @@ "job_title": null, "description": "Steward of Opsec framework", "badges": [ + { "name": "Framework-Steward", "assigned": "2026-04-09", "framework": "Operational Security" }, + { "name": "First-Contribution", "assigned": "2025-09-10" }, + { "name": "Active-Last-7d", "lastActive": "2026-04-09" } ] } } diff --git a/docs/pages/contribute/contributing.mdx b/docs/pages/contribute/contributing.mdx index 6ba348a05..a2612bf4a 100644 --- a/docs/pages/contribute/contributing.mdx +++ b/docs/pages/contribute/contributing.mdx @@ -285,7 +285,7 @@ This helps track contributions and ensures proper attribution. For a complete frontmatter example, see the [template file](https://github.com/security-alliance/frameworks/blob/develop/docs/pages/config/template.mdx?plain=1). -### 3. Sidebar / Navigation +### 2. Sidebar / Navigation Because of how we handle the `.org` and `.dev` domains in different branches, when contributing **new pages** you must also **update `vocs.config.tsx`** so that the page appears in the site’s sidebar. For content still in review, remember @@ -310,7 +310,7 @@ Example of a category with multiple pages: This ensures that new content appears correctly in the site’s navigation for readers on the `.dev` site while staying hidden from the stable `.org` site until ready. -### 4. Error Checking +### 3. Error Checking Before pushing changes, always make sure your build works without errors: @@ -372,7 +372,8 @@ fits, for example in block-quotes. - Adding images is welcome and encouraged. Please follow the steps below to include them correctly: - 1. After making your changes and opening a PR, add the images you want to include in the PR's comments (by uploading them directly) + 1. After making your changes and opening a PR, add the images you want to include + in the PR's comments (by uploading them directly) 2. During the review, a maintainer will upload your images to our S3 bucket and reply with the links you should use. 3. Once you receive the new links, update your PR to add the images' links. @@ -404,7 +405,7 @@ This page is also open for contributions! Suggest improvements to our style and ## About this page -Originally inspired by the [Ethereum Protocol Fellows](https://github.com/eth-protocol-fellows/protocol-studies) +Originally inspired by the [Ethereum Protocol Fellows](https://github.com/eth-protocol-fellows/protocol-studies). --- diff --git a/docs/pages/dprk-it-workers/techniques-tactics-and-procedures.mdx b/docs/pages/dprk-it-workers/techniques-tactics-and-procedures.mdx index 3c8f0f9dd..fbe514083 100644 --- a/docs/pages/dprk-it-workers/techniques-tactics-and-procedures.mdx +++ b/docs/pages/dprk-it-workers/techniques-tactics-and-procedures.mdx @@ -210,18 +210,18 @@ hiring a DPRK IT Worker. profile, which can uncover further identity mismatches. 1. On LinkedIn, examine the strength of the actor's connection network. - - ### Defeating Deepfakes: Liveness Verification -Pre-recorded deepfake video can fool a casual interviewer, particularly when audio "technical difficulties" are used as cover. Incorporate unpredictable, interactive requests that a pre-rendered deepfake cannot handle: +Pre-recorded deepfake video can fool a casual interviewer, particularly when audio "technical difficulties" +are used as cover. Incorporate unpredictable, interactive requests that a pre-rendered deepfake cannot handle: - Ask the candidate to turn their head sideways and hold the position - Have them read a randomly generated phrase displayed on screen for the first time during the call - Request a hand movement across the face mid-stream - Ask them to screen-share and perform a live technical task requiring real-time interaction with their environment -> Any candidate who persistently avoids in-person interaction — even for high-value roles — warrants a security review. This is a documented indicator of DPRK IT worker activity. +> Any candidate who persistently avoids in-person interaction — even for high-value roles — warrants a security +> review. This is a documented indicator of DPRK IT worker activity. ## Did I hire a DPRK IT Worker? diff --git a/docs/pages/opsec/endpoint/overview.mdx b/docs/pages/opsec/endpoint/overview.mdx index d960d0ea5..8608e2c04 100644 --- a/docs/pages/opsec/endpoint/overview.mdx +++ b/docs/pages/opsec/endpoint/overview.mdx @@ -22,9 +22,12 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } fr -> **Key Takeaway:** Match device security investment to role risk. Managed hardware for privileged operators, VDI for global contractors, enterprise browsers as minimum viable security for everyone else. +> **Key Takeaway:** Match device security investment to role risk. Managed hardware for privileged operators, +> VDI for global contractors, enterprise browsers as minimum viable security for everyone else. -Unmanaged personal devices are a primary vector for credential theft and lateral movement in Web3 organizations. Infostealers, malicious browser extensions, and compromised development environments all start at the endpoint. Organizations need a device provisioning strategy that scales security with role sensitivity. +Unmanaged personal devices are a primary vector for credential theft and lateral movement in Web3 organizations. +Infostealers, malicious browser extensions, and compromised development environments all start at the endpoint. +Organizations need a device provisioning strategy that scales security with role sensitivity. ## Device Security Tiers @@ -42,7 +45,9 @@ Issue organization-managed hardware to your highest-risk roles. This provides fu ### Tier 2: Virtual Desktop Infrastructure (Privacy-First Scale) -For global contractors where issuing hardware is impractical, VDI provides a secure cloud-hosted environment accessible from any device. The employee's personal machine becomes a thin client — all sensitive work happens inside the managed virtual desktop. +For global contractors where issuing hardware is impractical, VDI provides a secure cloud-hosted environment +accessible from any device. The employee's personal machine becomes a thin client — all sensitive work happens +inside the managed virtual desktop. - Complete visibility and control inside the virtual environment - Corporate web proxying and traffic inspection @@ -51,7 +56,8 @@ For global contractors where issuing hardware is impractical, VDI provides a sec - **Limitation:** Performance and latency overhead - **Limitation:** Hardware authentication dongle (YubiKey) compatibility issues in virtualized environments -**Target roles:** Global operations, customer support, regional teams, contractors with defined scopes. Providers: AWS WorkSpaces, Azure Virtual Desktop, Google Cloud Workstations. +**Target roles:** Global operations, customer support, regional teams, contractors with defined scopes. +Providers: AWS WorkSpaces, Azure Virtual Desktop, Google Cloud Workstations. ### Tier 3: Enterprise Browser (Minimum Viable Security) @@ -65,23 +71,26 @@ For general staff and short-term contractors, an enterprise browser provides a m **Target roles:** General staff, community managers, short-term contractors. -> If you use Google Workspace, you already have **Chrome Enterprise Core** at no additional cost. Enabling extension allowlisting alone eliminates one of the most common attack vectors against Discord and web-based platforms. +> If you use Google Workspace, you already have **Chrome Enterprise Core** at no additional cost. Enabling +> extension allowlisting alone eliminates one of the most common attack vectors against Discord and web-based platforms. ## Choosing the Right Tier | Factor | Managed Device | VDI | Enterprise Browser | -|--------|---------------|-----|-------------------| +| ------ | -------------- | --- | ------------------ | | **Visibility** | Full (OS + apps) | Inside VDI only | Browser only | | **Host compromise protection** | Yes — EDR on host | Partial — Host keyloggers | No — None | | **Hardware cost** | High (org buys devices) | Low (any device) | None | | **Privacy** | Low (org owns device) | Medium (host is private) | High (only browser managed) | | **Best for** | Core team, signers | Global contractors | General staff | -Most Web3 organizations will use all three tiers simultaneously — the goal is to match investment to actual risk, not to force a single approach across all roles. +Most Web3 organizations will use all three tiers simultaneously — the goal is to match investment to actual risk, +not to force a single approach across all roles. ## Further Reading -- [Hardening your organization](/dprk-it-workers/mitigating-dprk-it-workers#hardening-your-organization) — Access control policies for remote workers +- [Hardening your organization](/dprk-it-workers/mitigating-dprk-it-workers#hardening-your-organization) + — Access control policies for remote workers - [Browser Security](/opsec/browser/overview) — Browser-specific hardening diff --git a/docs/pages/opsec/mfa/overview.mdx b/docs/pages/opsec/mfa/overview.mdx index b80776da2..a87b67aeb 100644 --- a/docs/pages/opsec/mfa/overview.mdx +++ b/docs/pages/opsec/mfa/overview.mdx @@ -18,7 +18,8 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } fr -MFA is necessary but not sufficient as an OpSec strategy. If you have not yet implemented MFA, we suggest making it your first priority as soon as you finish reading this page. +MFA is necessary but not sufficient as an OpSec strategy. If you have not yet implemented MFA, we suggest making it +your first priority as soon as you finish reading this page. Not all MFAs are created equally. A few recommendations: @@ -26,23 +27,44 @@ Not all MFAs are created equally. A few recommendations: is a good idea. Suffice it to say, best practices have long since outlawed these MFA methods. 2. **TOTP (e.g., Google Authenticator) is good but not great.** Why? It is easy enough to trick users into - entering TOTP codes into a phishing site. The methods cited below are more difficult to exploit. Also, any manual typing is susceptible to keyloggers. + entering TOTP codes into a phishing site. The methods cited below are more difficult to exploit. Also, any + manual typing is susceptible to keyloggers. 3. **Push-based MFA is better.** Why? Because initiating a push notification on iOS/Android requires that the device itself be enrolled with the identity provider. Phishing sites cannot initiate a push notification to the Gmail app, for example, without a major compromise of Google's infrastructure. 4. **Passkeys are the best.** Biometrics are hard to fake, and in a world where attackers are looking for low - hanging fruit, passkeys protected by biometric factors are typically too hard for them to reach. However, passkey storage is critical to ensuring that this choice is secure - please read the note below. + hanging fruit, passkeys protected by biometric factors are typically too hard for them to reach. However, passkey + storage is critical to ensuring that this choice is secure - please read the note below. 5. **Key admins (e.g., your G Suite admin) should be using Yubikeys.** They are inexpensive and easy. There is no excuse here for not protecting the keys to the castle with the industry gold standard for MFA. -Once you have MFA in place, you are ready to move on to the next step in your Opsec framework. However, before you declare your MFA journey a success, make sure you haven't forgotten any of your communication tools along the way. In this industry, we often use a combination of X, Signal, and Telegram, and each of them can and should be protected with an additional authentication factor. Also, note that the more you allow one-off sign-ins for each tool you use, the more you have to be concerned about the MFA features of each tool. Implementing single sign-on wherever possible is the best way to enforce MFA across all the tools you use. +Once you have MFA in place, you are ready to move on to the next step in your Opsec framework. However, before you +declare your MFA journey a success, make sure you haven't forgotten any of your communication tools along the way. +In this industry, we often use a combination of X, Signal, and Telegram, and each of them can and should be protected +with an additional authentication factor. Also, note that the more you allow one-off sign-ins for each tool you use, +the more you have to be concerned about the MFA features of each tool. Implementing single sign-on wherever possible +is the best way to enforce MFA across all the tools you use. -Take into consideration that, while using passkeys seems the most appropriate course of action, using them irresponsibly could lower your security posture. Passkeys only improve security when they remain tied to a strong device and recovery model; used carelessly, they can weaken it by shifting trust from a hard-to-phish login flow to softer cloud sync and account recovery paths. A common failure case is storing high-value passkeys in consumer sync ecosystems protected by weak recovery, reused credentials, SMS reset, or unmanaged devices, so an attacker who compromises the sync account can restore those passkeys on their own device and log in cleanly with what appears to be strong authentication. In that setup, the passkey itself is not broken, but the overall security posture is worse because the real attack surface becomes account recovery, device enrollment, and endpoint compromise rather than direct credential theft. A typical mistake would be to store a passkey in the Google password manager for your personal Google account, which may not be adequately protected with a strong password, MFA, and a secure account recovery method. +Take into consideration that, while using passkeys seems the most appropriate course of action, using them +irresponsibly could lower your security posture. Passkeys only improve security when they remain tied to a +strong device and recovery model; used carelessly, they can weaken it by shifting trust from a hard-to-phish login +flow to softer cloud sync and account recovery paths. A common failure case is storing high-value passkeys in consumer +sync ecosystems protected by weak recovery, reused credentials, SMS reset, or unmanaged devices, so an attacker who +compromises the sync account can restore those passkeys on their own device and log in cleanly with what appears to +be strong authentication. In that setup, the passkey itself is not broken, but the overall security posture is worse +because the real attack surface becomes account recovery, device enrollment, and endpoint compromise rather than direct +credential theft. A typical mistake would be to store a passkey in the Google password manager for your personal Google +account, which may not be adequately protected with a strong password, MFA, and a secure account recovery method. -To address the passkey storage issue, this section should be read in conjunction with the section (coming soon) about password handling and password management. Passkeys are the strongest form of MFA when stored in a secure password manager with biometric authentication (e.g., Bitwarden, 1Password). Passkeys stored in a personal account that uses phone or SMS as a recovery mechanism make overall security worse, not better. Before finalizing any decision to migrate to passkeys, please read the password management section and ensure that you are ready to store your passkeys securely. +To address the passkey storage issue, this section should be read in conjunction with the section (coming soon) about +password handling and password management. Passkeys are the strongest form of MFA when stored in a secure +password manager with biometric authentication (e.g., Bitwarden, 1Password). Passkeys stored in a personal account +that uses phone or SMS as a recovery mechanism make overall security worse, not better. Before finalizing any decision +to migrate to passkeys, please read the password management section and ensure that you are ready to store your +passkeys securely. diff --git a/wordlist.txt b/wordlist.txt index ac6954ca0..55f292f62 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -360,3 +360,5 @@ Upbit Valimail viem wagmi +NCSC +Intune From 9cb87deeb7e56f255a2f3acba07e33adf8229dc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zealot=20=E2=80=94=20Framework=27s=20intern?= Date: Tue, 21 Apr 2026 13:48:58 -0300 Subject: [PATCH 20/92] feat: add forensic readiness page (#457) * feat: add forensic readiness page (closes #433) * fix: add forensic readiness to sidebar in vocs.config.tsx (refs #457) --- .../forensic-readiness.mdx | 149 ++++++++++++++++++ .../incident-detection-and-response.mdx | 2 + docs/pages/incident-management/overview.mdx | 9 +- docs/pages/monitoring/guidelines.mdx | 8 + vocs.config.tsx | 1 + 5 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 docs/pages/incident-management/forensic-readiness.mdx diff --git a/docs/pages/incident-management/forensic-readiness.mdx b/docs/pages/incident-management/forensic-readiness.mdx new file mode 100644 index 000000000..5b67e68e6 --- /dev/null +++ b/docs/pages/incident-management/forensic-readiness.mdx @@ -0,0 +1,149 @@ +--- +title: "Forensic Readiness | Security Alliance" +description: "Forensic readiness: prepare your organization to preserve, collect, and present trustworthy evidence before an incident occurs. Design evidence collection into architecture, maintain chain of custody, and meet regulatory disclosure requirements." +tags: + - Security Specialist + - Operations & Strategy + - DevOps + - SRE +contributors: + - role: wrote + users: [frameworks-volunteer] + - role: reviewed + users: [] + - role: fact-checked + users: [] +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' + + + + +# Forensic Readiness + + + + +> 🔑 **Key Takeaway**: Forensic readiness means designing your systems and processes so that trustworthy evidence survives an incident -- attributable, time-coherent, tamper-resistant, and preservable. It is not the same as incident response or monitoring; it is the capability layer that lets you reconstruct, prove, and defend what happened. + +Many organizations can detect that something suspicious happened, yet still struggle to reconstruct the event in a way that is operationally reliable, legally defensible, and regulator-ready. Forensic readiness closes that gap. It is an organization's ability to preserve, collect, correlate, and present trustworthy evidence when an incident occurs. In practice, it sits between security operations, governance, legal exposure, and resilience. + +The question is no longer only whether you can identify malicious activity. The question is whether you can rapidly produce a defensible account of what happened, what was affected, what evidence supports that conclusion, and how much confidence decision-makers should have in that account. + +## Why forensic readiness is becoming strategic + +Three converging pressures are pushing forensic readiness from a niche DFIR specialty to a strategic concern. + +### 1. Institutional framing has changed + +NIST CSF 2.0 broadened the conversation beyond narrow control checklists and emphasized governance and organizational preparedness. The updated NIST incident response guidance puts preparation back at the center, rather than treating incident handling as something that begins only after detection. NIST's cloud forensics work makes clear that evidence handling in modern distributed environments requires deliberate design, not improvised collection after the fact. + +### 2. Infrastructure changed faster than evidence practices + +Traditional forensic assumptions came from environments where systems were relatively stable, hosts were long-lived, and data locations were predictable. That model does not map cleanly to cloud-native and distributed systems, including blockchain infrastructure. Containers are ephemeral. Workloads shift quickly. Application logic is fragmented across services. Critical context may exist briefly in memory, transient queues, reverse proxies, or short-retention telemetry layers. In these environments, evidence must be intentionally captured, preserved, normalized, and linked before it disappears. + +### 3. The cost of ambiguity increased + +Regulatory and disclosure regimes are compressing response timelines. Organizations increasingly have to make externally visible statements under time pressure, sometimes before internal certainty is complete. If an organization has weak evidence hygiene -- fragmented logs, inconsistent timestamps, poor chain-of-custody discipline, or no reliable way to reconstruct incident scope -- the risk is not only technical. It becomes legal, financial, and reputational. CIRCIA, SEC disclosure expectations, and related regulatory pressure increase the premium on being able to substantiate what happened, quickly and credibly. + +## Forensic readiness vs. monitoring vs. incident response + +These are related but distinct capabilities. + +| Property | Monitoring | Incident Response | Forensic Readiness | +|----------|-----------|-------------------|-------------------| +| Primary goal | Detect anomalies in real time | Contain and recover from incidents | Preserve and present defensible evidence | +| Timing | During the event | After detection | Designed before, used after | +| Evidence standard | Sufficient for alerting | Sufficient for operational decisions | Attributable, time-coherent, tamper-resistant, legally defensible | +| Chain of custody | Not required | Helpful | Required | +| Typical output | Alerts and dashboards | Incident timeline, containment actions | Forensic report, evidence package, scope analysis | + +Having SIEM, EDR, cloud logs, and retention policies is useful. None of it automatically creates forensic readiness. Forensic readiness requires properties that ordinary monitoring programs do not consistently provide: evidence must be attributable, time-coherent, preservable, and resistant to accidental or intentional tampering. Cross-source relationships have to survive collection boundaries. Critical context has to remain understandable after the incident, not only during real-time alert review. In some cases, the organization also needs proof that an artifact was collected in a controlled way, preserved without silent mutation, and associated with a defensible chain of custody. That is a different standard than "the log existed in the platform at some point." + +## Practical guidance + +### Step-by-step actions + +1. **Inventory your evidence sources** -- Identify all systems, services, and infrastructure components that could generate relevant evidence during an incident (on-chain events, off-chain logs, API request/response traces, access logs, deployment pipelines, communication channels). +2. **Assess evidence volatility** -- Classify each source by how long data persists before it is overwritten or lost. Ephemeral containers, in-memory caches, and short-retention telemetry are high-volatility. Persistent databases and on-chain data are low-volatility. +3. **Define retention and preservation requirements** -- For each evidence source, determine how long data must be retained and in what format. Regulatory requirements, contractual obligations, and realistic investigation timelines all factor in. +4. **Implement evidence collection pipelines** -- Build automated collection that captures high-volatility evidence before it disappears. This may include sidecar log shippers, memory dump triggers, or event stream archivers. +5. **Establish chain-of-custody procedures** -- Document who collected each artifact, when, how, and from where. Use hash verification to prove integrity. Store artifacts in write-once or append-only storage where feasible. +6. **Normalize and correlate evidence** -- Ensure timestamps are synchronized (NTP/PTP) across all sources. Use consistent formats and identifiers so that evidence from different systems can be correlated reliably. +7. **Test forensic readiness regularly** -- Run tabletop exercises and simulated incidents to verify that evidence collection works end-to-end, artifacts are retrievable, and chain-of-custody documentation holds up. +8. **Integrate with incident response** -- Ensure the [incident response plan](/incident-management/incident-detection-and-response) references forensic readiness procedures. First responders must know what to preserve and how, not just how to contain. + +### Best-practice checklist + +- [ ] All critical evidence sources are inventoried and classified by volatility +- [ ] Retention periods are defined for each evidence source and meet or exceed regulatory requirements +- [ ] High-volatility evidence (ephemeral containers, in-memory data) has automated capture before loss +- [ ] Time synchronization (NTP) is configured on all systems generating evidence +- [ ] Evidence artifacts are stored in tamper-evident or append-only storage +- [ ] Hash verification (SHA-256 or stronger) is performed on collected artifacts +- [ ] Chain-of-custody documentation is defined and tested +- [ ] Forensic readiness procedures are referenced in the incident response plan +- [ ] Tabletop exercises include forensic readiness validation at least annually +- [ ] Access to forensic artifacts is logged and restricted to authorized personnel + +### Role-based tips + +- **Operations/SRE**: Focus on evidence volatility -- ensure ephemeral workloads, container logs, and short-retention telemetry are captured before they disappear. Automate preservation workflows. +- **Security specialists**: Focus on evidence integrity and chain of custody. Ensure artifacts are hash-verified, stored securely, and accessible only to authorized investigators. +- **Legal/compliance**: Focus on retention requirements, disclosure timelines, and whether your evidence practices meet the standard for regulatory submissions. Work with security to understand what can be substantiated under pressure. +- **Engineers/developers**: Focus on application-layer evidence. Ensure your services emit structured, traceable runtime context (request IDs, transaction hashes, decision logs) that survives collection boundaries. + +## Application-layer forensic readiness + +Much of the security industry reasons from the network, endpoint, or identity perimeter inward. But many high-value incidents involve application behavior, API misuse, privilege boundary confusion, or complex multi-step sequences only partially visible to conventional telemetry. The missing evidence is often not another generic alert -- it is structured, trustworthy runtime context tied to what the application actually received, processed, rejected, transformed, or emitted. + +For Web3 projects, this is especially relevant. On-chain transactions are immutable and publicly verifiable, which is an advantage for forensic readiness. However, the off-chain infrastructure surrounding those transactions -- RPC endpoints, indexers, relayers, frontends, API gateways, key management systems -- generates ephemeral evidence that must be deliberately preserved. + +### Application-layer evidence to capture + +- API request and response traces with correlation IDs +- Authentication and authorization decision logs +- Transaction submission and signing events +- Smart contract interaction sequences (including reverted transactions) +- Admin and privileged operation audit trails +- Configuration change histories +- Deployment pipeline artifacts and approvals + +## Common pitfalls + +- **Assuming existing monitoring is sufficient**: Monitoring alerts tell you something happened. Forensic readiness ensures you can prove what happened, to whom, and with what evidence. These are different standards. +- **Ignoring high-volatility evidence**: Ephemeral containers, in-memory queues, and short-retention logs are often the most valuable evidence sources and the first to disappear. If you do not capture them before an incident, they are gone. +- **No chain-of-custody discipline**: Without documentation of who collected what, when, and how, even authentic evidence becomes hard to defend. Courts, regulators, and insurers may question whether evidence was tampered with. +- **Inconsistent timestamps**: If systems are not time-synchronized, correlating evidence across sources becomes unreliable. A few seconds of clock drift can make timelines inconsistent. +- **Forensic readiness as an afterthought**: By the time an organization decides it "now needs forensic evidence," the highest-value evidence may already be degraded, overwritten, scattered, or contextless. Readiness must be designed into the system before the incident. + +## Quick-reference + +| Category | Action | Priority | +|----------|--------|----------| +| Evidence inventory | Catalog all evidence sources and classify by volatility | High | +| Preservation | Automate capture of high-volatility sources | High | +| Retention | Define and enforce minimum retention per source | High | +| Time sync | Deploy NTP across all evidence-generating systems | High | +| Integrity | Hash-verify artifacts on collection (SHA-256+) | Medium | +| Chain of custody | Document collection, transfer, and access for every artifact | Medium | +| Correlation | Normalize formats and identifiers across sources | Medium | +| Testing | Run annual tabletop exercises that validate forensic procedures | Medium | +| Access control | Restrict and log access to forensic artifacts | Medium | +| Application layer | Emit structured trace context from services (request IDs, decision logs) | Low-Medium | + +## Further reading + +- [NIST CSF 2.0](https://www.nist.gov/cyberframework) -- Governance and organizational preparedness framework +- [NIST SP 800-86](https://csrc.nist.gov/publications/detail/sp/800-86/rev-1/final) -- Guide to integrating forensic techniques into incident response +- [NIST Cloud Forensics Reference Architecture](https://csrc.nist.gov/projects/cloud-forensics) -- Evidence handling in distributed cloud environments +- [Tracehound: Forensic Readiness Is Becoming a Strategic Security Discipline](https://tracehoundlabs.com/blog/forensic-readiness-is-becoming-a-strategic-security-discipline) -- Analysis of why forensic readiness is shifting from niche practice to strategic discipline +- [Incident Detection and Response](/incident-management/incident-detection-and-response) -- Detection and response procedures +- [Monitoring Guidelines](/monitoring/guidelines) -- On-chain monitoring best practices +- [Lessons Learned](/incident-management/lessons-learned) -- Post-incident review and improvement + +--- + + + diff --git a/docs/pages/incident-management/incident-detection-and-response.mdx b/docs/pages/incident-management/incident-detection-and-response.mdx index d700c3950..014edb275 100644 --- a/docs/pages/incident-management/incident-detection-and-response.mdx +++ b/docs/pages/incident-management/incident-detection-and-response.mdx @@ -36,6 +36,8 @@ to, and recovering from security incidents. incidents. - **Post-Incident Review**: Conduct a thorough review of the incident to identify lessons learned and improve future response efforts. +- **Forensic Readiness**: Ensure your systems can preserve and present defensible evidence before an incident occurs. +See [Forensic Readiness](/incident-management/forensic-readiness). For a complete incident response policy template covering roles, severity, documentation, and response flow, see [Incident Response Template: Incident Response Policy](/incident-management/incident-response-template/incident-response-policy) diff --git a/docs/pages/incident-management/overview.mdx b/docs/pages/incident-management/overview.mdx index d07ccbe45..7e24ba6fa 100644 --- a/docs/pages/incident-management/overview.mdx +++ b/docs/pages/incident-management/overview.mdx @@ -26,10 +26,11 @@ timely recovery. 1. [Communication Strategies](/incident-management/communication-strategies) 2. [Incident Detection and Response](/incident-management/incident-detection-and-response) -3. [Lessons Learned](/incident-management/lessons-learned) -4. [Playbooks](/incident-management/playbooks/overview) -5. [SEAL 911 War Room Guidelines](/incident-management/playbooks/seal-911-war-room-guidelines) -6. [Incident Response Template](/incident-management/incident-response-template/overview) +3. [Forensic Readiness](/incident-management/forensic-readiness) +4. [Lessons Learned](/incident-management/lessons-learned) +5. [Playbooks](/incident-management/playbooks/overview) +6. [SEAL 911 War Room Guidelines](/incident-management/playbooks/seal-911-war-room-guidelines) +7. [Incident Response Template](/incident-management/incident-response-template/overview) --- diff --git a/docs/pages/monitoring/guidelines.mdx b/docs/pages/monitoring/guidelines.mdx index f90e47072..8f3ac0918 100644 --- a/docs/pages/monitoring/guidelines.mdx +++ b/docs/pages/monitoring/guidelines.mdx @@ -109,6 +109,14 @@ Structure monitoring coverage across these tracks: 2. Document who gets paged for each alert category and what the first response steps are. This should be decided before an incident, not during one. +### Forensic readiness + +Monitoring detects anomalies. Forensic readiness ensures you can reconstruct and prove what happened with defensible +evidence. These are complementary capabilities: monitoring without forensic readiness means you can notice an incident +but may struggle to investigate, substantiate, or disclose it reliably. See +[Forensic Readiness](/incident-management/forensic-readiness) for how to design evidence collection into your +architecture. + --- diff --git a/vocs.config.tsx b/vocs.config.tsx index 0cc10f7ce..14c9824a6 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -212,6 +212,7 @@ const config = { { text: 'Overview', link: '/incident-management/overview' }, { text: 'Communication Strategies', link: '/incident-management/communication-strategies' }, { text: 'Incident Detection and Response', link: '/incident-management/incident-detection-and-response' }, + { text: 'Forensic Readiness', link: '/incident-management/forensic-readiness', dev: true }, { text: 'Lessons Learned', link: '/incident-management/lessons-learned' }, { text: 'Playbooks', From 78a49abb593467e509e14bbffff9a7374b981bac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 13:50:09 -0300 Subject: [PATCH 21/92] chore(deps): bump the npm_and_yarn group across 1 directory with 2 updates (#425) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the npm_and_yarn group with 2 updates in the / directory: [dompurify](https://github.com/cure53/DOMPurify) and [flatted](https://github.com/WebReflection/flatted). Updates `dompurify` from 3.3.0 to 3.3.3 - [Release notes](https://github.com/cure53/DOMPurify/releases) - [Commits](https://github.com/cure53/DOMPurify/compare/3.3.0...3.3.3) Updates `flatted` from 3.3.3 to 3.4.2 - [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.2) --- updated-dependencies: - dependency-name: dompurify dependency-version: 3.3.3 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: flatted dependency-version: 3.4.2 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Matías Aereal Aeón <388605+mattaereal@users.noreply.github.com> From c0b22ea9c8e1cf25a557af2e9932e6c3a5d771bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zealot=20=E2=80=94=20Framework=27s=20intern?= Date: Tue, 21 Apr 2026 14:05:38 -0300 Subject: [PATCH 22/92] fix: expand DevSecOps incomplete categories (closes #439) (#461) - code-signing.mdx: Add GPG key generation, subkeys, YubiKey setup, passphrase management, key backup/recovery - continuous-integration-continuous-deployment.mdx: Add SLSA provenance, SBOM generation, OIDC federation for cloud access - repository-hardening.mdx: Add CODEOWNERS patterns, GitHub Advanced Security (CodeQL, secret scanning, dependency review), security policy template - security-testing.mdx: Add severity thresholds table, Semgrep custom rules, false positive management, coverage and mutation testing --- docs/pages/devsecops/code-signing.mdx | 272 +++++++++++++- ...uous-integration-continuous-deployment.mdx | 335 +++++++++++++++++- docs/pages/devsecops/repository-hardening.mdx | 313 +++++++++++++++- docs/pages/devsecops/security-testing.mdx | 272 +++++++++++++- 4 files changed, 1149 insertions(+), 43 deletions(-) diff --git a/docs/pages/devsecops/code-signing.mdx b/docs/pages/devsecops/code-signing.mdx index 538964642..2d544d425 100644 --- a/docs/pages/devsecops/code-signing.mdx +++ b/docs/pages/devsecops/code-signing.mdx @@ -1,10 +1,15 @@ --- title: "Implementing Code Signing | Security Alliance" -description: "Verify code integrity with GPG-signed Pull Requests. Best practices for Multi-Factor Authentication (MFA) with Yubikeys, mandatory code reviews, and regular GPG key rotation." +description: "Verify code integrity with GPG-signed commits and pull requests. Best practices for key management, MFA with Yubikeys, mandatory code reviews, key rotation, and establishing a chain of trust from commit to deployment." tags: - Engineer/Developer - Security Specialist - DevOps +contributors: + - role: wrote + users: [frameworks-volunteer] + - role: reviewed + users: [] --- import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' @@ -17,16 +22,263 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } fr -Code signing ensures that the code has not been tampered with, and verifies the identity of the developer. Here are some -best practices to follow: +> 🔑 **Key Takeaway**: Sign every commit and tag with GPG (or SSH/SMIME), +> enforce signature verification in CI and branch protection, rotate keys +> regularly, and bind developer identity to hardware-backed MFA so that +> every change in the repository is traceable to a verified human. -1. Ensure all Pull Requests (PRs) are signed with the user’s GPG key. -2. Every PR must be reviewed by another core team member before being merged into the stable/main/master branch, with - github settings set to reflect this. -3. Require Multi-Factor Authentication (MFA) for all users where applicable and available. Encourage the use of hardware - MFA such as Yubikeys. -4. Rotate GPG keys regularly to mitigate the risk of key compromise. -5. Maintain clear documentation on the code signing procedures for your team members. +Code signing guarantees **integrity** (the code has not been tampered with) and +**authenticity** (the change came from the claimed author). Without signing, an +attacker who obtains push access can inject malicious code that is +indistinguishable from legitimate commits. In Web3 projects, where a single +unverified commit could introduce a backdoor into smart contract deployment +tooling or steal signing keys, the stakes are especially high. + +## Practical guidance + +### 1. Require signed commits on protected branches + +Enable GitHub's **Require signed commits** branch protection rule on `main`, +`develop`, and any release branches. This rejects any push that does not carry +a verifiable GPG, SSH, or S/MIME signature. + +- In GitHub: Settings > Branches > Branch protection rules > Require signed + commits. +- Verify in CI: add `git log --verify-signatures` or `git merge --verify-signatures` + as a pipeline check so that unsigned merge commits also fail. + +### 2. Require signed pull requests + +Every PR must be reviewed by another core team member before merging. Configure +GitHub to require at least one approving review and enforce that the PR +author's commits are signed. + +- Branch protection: require "Signed commits" and "Pull request reviews" (at + least 1 approving review, dismiss stale reviews on push). +- Consider requiring reviews from specific teams (CODEOWNERS file) for sensitive + paths such as deployment scripts, contract artifacts, or CI workflow files. + +### 3. Enforce MFA for all repository members + +Require Multi-Factor Authentication for every contributor with push access. + +- Organization-level: enable "Require two-factor authentication for members" in + the GitHub organization settings. +- Encourage hardware MFA (Yubikey, Titan) over SMS or TOTP. Hardware keys resist + phishing via FIDO2/WebAuthn. +- For Yubikey GPG signing: generate the GPG subkey directly on the Yubikey's + OpenPGP applet so the private key never leaves the device. + +### 4. Generate and manage GPG keys properly + +Good key management is the foundation of code signing. A poorly managed key undermines the entire trust chain. + +#### Generating a strong GPG key + +Use RSA 4096 or Ed25519 (the latter is modern, fast, and secure): + +```bash +# Ed25519 (recommended for modern setups) +gpg --full-generate-key +# Choose Ed25519 when prompted, or: gpg --quick-gen-key your@email.com ed25519 sign,auth cert never + +# RSA 4096 (legacy compatibility) +gpg --full-generate-key +# Choose RSA 4096 +``` + +#### Using subkeys for separation of duties + +Create separate subkeys for signing and encryption. This lets you keep the master +key completely offline while using subkeys daily: + +```bash +# Add a signing subkey +gpg --edit-key YOUR_KEYID +gpg> addkey +# Choose: RSA 4096, Sign only, expiry 1-2 years + +# Add an encryption subkey (separate from any encryption subkey you already have) +gpg> addkey +# Choose: RSA 4096, Encrypt only, expiry 1-2 years + +gpg> save +``` + +Your master key stays on an encrypted USB or paper backup. The subkeys go on your +regular machine. If a subkey is compromised, you revoke only the subkey — the +identity stays intact. + +#### YubiKey: move subkeys to hardware + +Generate GPG keys directly on the YubiKey so the private key material never +touches the host system: + +```bash +# Initialize the YubiKey OpenPGP applet +gpg --card-edit +gpg> admin +gpg> generate +# Choose a touch policy (require touch for signing operations) + +# Or move existing subkeys to the YubiKey: +gpg --edit-key YOUR_KEYID +gpg> key 1 # Select the signing subkey +gpg> keytocard # Move it to YubiKey +gpg> key # Deselect +gpg> key 2 # Select the encryption subkey +gpg> keytocard +gpg> save +``` + +The YubiKey now holds your private keys. The host machine can use them only when +the YubiKey is physically present and unlocked. + +#### Passphrase management + +Protect GPG keys with a strong passphrase (20+ characters, random). Use `gpg-agent` +caching to avoid re-entering it constantly: + +```bash +# In ~/.gnupg/gpg-agent.conf: +pinentry-program /usr/bin/pinentry-tty +default-cache-ttl 86400 # Cache for 24 hours +max-cache-ttl 604800 # Expire after 1 week +``` + +### 5. Rotate GPG keys regularly + +Key rotation limits the damage window if a key is compromised. + +- Define a rotation schedule: every 12 months for standard keys, every 6 months + for high-privilege accounts (release managers, deployers). +- When rotating: create a new key pair, publish the new public key to GitHub and + your keyserver, add a signing subkey, update keyserver records, then revoke the + old key with a reason of "superseded." +- Maintain a key rotation log: key ID, creation date, expiry date, revocation + date, reason. +- Protect GPG private keys with a strong passphrase and store the revocation + certificate in a secure, offline location (encrypted USB, password manager). + +### 5b. Backup and recover keys safely + +Without proper backup, a lost key means lost identity. Without secure storage, +a stolen key means forged commits. + +**Backup the master key:** +```bash +# Export to an encrypted file +gpg --export-secret-keys --armor YOUR_KEYID | \ + gpg --symmetric --cipher-algo AES256 \ + --output master-key-backup.gpg + +# Store on: encrypted USB (LUKS), paper (print the ASCII armor and seal in a safe), +# or a password manager as an encrypted attachment +``` + +**Generate and store revocation certificates immediately:** +```bash +gpg --gen-revoke YOUR_KEYID > revocation-certificate.asc +# Store this certificate alongside the backup. If you lose access to the key, +# publishing the revocation certificate invalidates the compromised key. +``` + +**Recovery test:** Periodically verify you can decrypt using your backup +without the original key. Store the backup passphrase separately from the backup +medium. + +### 6. Publish and verify public keys + +A signature is meaningless if the verifying party cannot obtain the correct +public key. + +- Upload your public key to GitHub (Settings > SSH and GPG keys) and to a + public keyserver (keys.openpgp.org, keys.mailvelope.com). +- Use the same key across all platforms so that the identity is consistent. +- In CI, pin trusted public key fingerprints in the pipeline configuration. + Reject signatures from unknown keys. + +### 6b. Document code signing procedures + +Maintain clear, accessible documentation so that every team member can set up and +maintain signing correctly. + +- Onboarding guide: how to generate a GPG key, configure git to sign commits, + upload to GitHub, and set up a Yubikey for signing. +- Troubleshooting: common issues (expired keys, wrong key selected, gpg-agent + not running) with solutions. +- Policy: rotation schedule, revocation procedures, acceptable signing methods + (GPG, SSH, S/MIME), and enforcement mechanism. + +## Why is it important + +Unsigned commits allow impersonation. If an attacker obtains credentials or an +active session, they can push commits that appear to come from any author. +Without signature verification, there is no cryptographic proof of authorship. + +Real-world implications: + +- The Linux kernel community experienced a breach where an attacker attempted to + inject a backdoor via a seemingly legitimate commit. Signed commits and review + processes are a primary defense against this class of attack. +- NIST SP 800-53 Rev. 5 control **AU-10 (Non-Repudiation)** requires that the + identity of individuals who perform specific actions be determined and + verified. +- CISA's Secure Software Development Self-Attestation form requires attestors to + confirm that they verify the integrity of software releases, which includes + code signing. + +## Implementation details + +| Sub-topic | Related page | +| --- | --- | +| Branch protection & signed commits | [Repository Hardening](/devsecops/repository-hardening) | +| CI pipeline enforcement of signatures | [Securing CI/CD Pipelines](/devsecops/continuous-integration-continuous-deployment) | +| Artifact signing and provenance | [Sandboxing & Isolation](/devsecops/isolation/sandboxing-and-isolation) | + +## Common pitfalls + +- **Lost or expired GPG key**: If you lose your private key or it expires and + you cannot revoke it, GitHub cannot verify your past or future commits. Always + set an expiry date, generate a revocation certificate immediately, and store + it securely offline. +- **gpg-agent caching causes signing with the wrong key**: When you have + multiple keys, git may sign with the wrong one. Explicitly set + `user.signingkey` per repository: `git config user.signingkey + `. +- **Signing tags but not commits**: Annotated tags are signed, but if the + commits they point to are unsigned, an attacker could rebase onto unsigned + history. Sign both commits and tags. +- **Using SSH signing without understanding trust model**: GitHub supports SSH + signing keys, but verification depends on the SSH `allowed_signers` file. If + this file is not maintained, signatures verify against any key in the file. + Keep `allowed_signers` pinned to current team members. +- **CI merges bypassing signature checks**: Some CI workflows auto-merge PRs + (e.g., Dependabot). Ensure that bot accounts also sign commits or that the + merge commit itself is verified by the CI system. + +## Quick-reference cheat sheet + +| Action | Command | +| --- | --- | +| Generate GPG key | `gpg --full-generate-key` (choose RSA 4096 or ed25519) | +| List secret keys | `gpg --list-secret-keys --keyid-format long` | +| Set git signing key | `git config user.signingkey ` | +| Sign a commit | `git commit -S -m "message"` | +| Sign a tag | `git tag -s v1.0.0 -m "release 1.0.0"` | +| Verify a commit | `git log --verify-signatures -1` | +| Verify a tag | `git tag -v v1.0.0` | +| Export public key | `gpg --armor --export > pubkey.asc` | +| Generate revocation cert | `gpg --gen-revoke > revoke.asc` | +| Upload to keyserver | `gpg --keyserver keys.openpgp.org --send-keys ` | + +## References + +- [GitHub Docs: Managing commit signature verification](https://docs.github.com/en/authentication/managing-commit-signature-verification) +- [GitHub Docs: About signature verification for commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification) +- [NIST SP 800-53 Rev. 5, AU-10 Non-Repudiation](https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final) +- [Yubico PGP guide: Generate GPG keys on YubiKey](https://developers.yubico.com/PGP/Card_edit.html) +- [GnuPG documentation](https://gnupg.org/documentation/) --- diff --git a/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx b/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx index 45395f7bc..3ca00be74 100644 --- a/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx +++ b/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx @@ -1,11 +1,16 @@ --- -title: "Securing CI/CD Pipelines | SEAL" -description: "Build secure CI/CD pipelines with GitHub Actions: unit tests, integration tests, vulnerability scanning, deterministic builds, and strict access controls for pipeline configurations." +title: "Securing CI/CD Pipelines | Security Alliance" +description: "Build secure CI/CD pipelines with GitHub Actions: unit tests, integration tests, vulnerability scanning, deterministic builds, secret management, and strict access controls for pipeline configurations." tags: - Engineer/Developer - Security Specialist - DevOps - SRE +contributors: + - role: wrote + users: [frameworks-volunteer] + - role: reviewed + users: [] --- import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' @@ -18,17 +23,321 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } fr -Continuous Integration and Continuous Deployment are there to ensure good code quality and create rapid and secure -deployments. Some best practices are: - -1. Ensure every PR undergoes CI testing (e.g., GitHub Actions) that must pass before merging. CI tests should at least - include unit tests, integration tests, and checks for known vulnerabilities in dependencies. -2. The CI/CD pipeline should check for misconfigurations and leaked credentials. -3. Produce deterministic builds with a strict set of dependencies and/or a build container that can reliably produce the - same results on different machines. -4. Integrate security scanning tools to detect vulnerabilities in code and dependencies during the CI process. -5. Use isolated environments for building and testing to prevent contamination between different stages of the pipeline. -6. Implement strict access controls for CI/CD pipelines to limit who can modify the pipeline configurations. +> 🔑 **Key Takeaway**: Treat CI/CD as a production system: every pipeline +> must run tests, scans, and signature checks before allowing a merge; secrets +> must never reach untrusted jobs; and build environments must be ephemeral and +> deterministic so that no single compromise can tamper with what ships. + +CI/CD pipelines are the backbone of modern software delivery, but they are also +high-value targets. An attacker who controls a pipeline can inject backdoors +into every artifact it produces, steal secrets, or deploy malicious code to +production. In Web3, where CI often has access to deployment keys and signing +wallets, a compromised pipeline can directly lead to on-chain exploits. + +## Practical guidance + +### 1. Require CI checks before merging + +Every PR must pass CI before it can be merged. At minimum, the pipeline should +include: + +- **Unit tests** — fast, isolated tests covering core logic. +- **Integration tests** — end-to-end flows (e.g., deploy to testnet, verify + contract state, run fork tests). +- **Dependency vulnerability scan** — detect known CVEs in packages using + Dependabot, Snyk, or npm audit. +- **Static analysis** — lint code and detect common bugs (Slither for Solidity, + Ruff/Pylint for Python, ESLint for JS/TS). +- **Secret detection** — scan for accidentally committed keys and tokens + (git-secrets, TruffleHog, GitHub secret scanning). + +Configure branch protection to require all status checks to pass before merging. + +### 2. Scan for misconfigurations and leaked credentials + +Pipelines themselves can introduce vulnerabilities if misconfigured. + +- Use tools like **tfsec** (Terraform), **Checkov**, or **Falco** to detect IaC + misconfigurations. +- Enable **GitHub secret scanning** and **push protection** at the organization + level to prevent credentials from entering the repository. +- Run **zizmor** or **actionlint** to lint GitHub Actions workflows for common + security anti-patterns (e.g., `pull_request_target` with untrusted checkout, + unchecked `GITHUB_TOKEN` permissions). + +### 3. Produce deterministic, reproducible builds + +A build that varies between runs makes it impossible to verify that the deployed +artifact matches the reviewed source code. + +- Pin all dependency versions: use lockfiles (`package-lock.json`, + `poetry.lock`, `Pipfile.lock`) and pin Docker base images by digest + (`image@sha256:...`), not by tag. +- Use a fixed build container or Nix derivation so that the same source always + produces the same output. +- For Web3: produce deterministic bytecode by pinning the compiler version + (`solc`), optimizer settings, and build environment. Use + `--standard-json` input and verify the resulting bytecode matches across + independent builds. +- Adopt SLSA (Supply-chain Levels for Software Artifacts) levels to track build + provenance: generate and sign provenance attestations, verify them before + deployment. + +### 4. Integrate security scanning into CI + +Security scanning must be automated and run on every PR, not just periodically. + +| Scan type | Tools | When to run | +| --- | --- | --- | +| SAST (static analysis) | Slither, Semgrep, CodeQL, Aderyn | Every PR | +| SCA (dependency scan) | Dependabot, Snyk, npm audit | Every PR + daily cron | +| Secret scanning | TruffleHog, git-secrets, GitHub push protection | Pre-commit + every PR | +| DAST (dynamic analysis) | OWASP ZAP, Nikto | Nightly + pre-release | +| IaC scanning | tfsec, Checkov, KICS | Every PR touching infra code | +| Container scanning | Trivy, Grype, Snyk Container | Every image build | + +- Fail the pipeline on Critical and High severity findings. +- Track Medium/Low findings as issues for triage. +- Configure tools to suppress false positives carefully and document the + reason. + +### 5. Isolate build and test environments + +Pipeline stages must not share state, secrets, or filesystem access. + +- Use **ephemeral runners**: a fresh environment per job, no persistent state. + GitHub-hosted runners are ephemeral by default; self-hosted runners must be + re-provisioned per job. +- Separate **untrusted PR runners** from **trusted build/deploy runners**. + Fork PRs should never have access to deployment secrets. +- Deny outbound network by default for build jobs; allow only required hosts + (package registries, API endpoints) via allowlist. +- Use **rootless containers** and **seccomp profiles** for build jobs. Never + run CI with `--privileged` or `sudo`. +- See [Sandboxing & Isolation](/devsecops/isolation/sandboxing-and-isolation) + for deeper containment patterns. + +### 6. Restrict access to pipeline configurations + +Who can modify CI workflows determines who can alter what runs in the pipeline. + +- Limit write access to `.github/workflows/` and equivalent CI config + directories to a small, trusted group. +- Require PR reviews for any change to CI workflow files. Use CODEOWNERS to + enforce this: `/.github/workflows/ @security-team @devops-lead`. +- Pin third-party GitHub Actions by commit SHA, not by tag. Tags are mutable: + `v1` can be retagged to point to malicious code. + ```yaml + # Unsafe: tag can be changed + - uses: actions/checkout@v4 + # Safe: pinned by SHA + - uses: actions/checkout@b4ffde65f46336ab88eb53be80866792576f8620 + ``` +- Restrict `GITHUB_TOKEN` permissions to least privilege: set + `permissions: {}` at the workflow level and grant only what each job needs. + +### 7. Manage secrets securely + +CI secrets (API keys, deployment keys, signing keys) are the highest-value +targets in any pipeline. + +- Store secrets in a dedicated vault (GitHub Secrets, HashiCorp Vault, AWS + Secrets Manager), not in environment files or code. +- Never pass secrets to untrusted jobs. In GitHub Actions, fork PRs cannot + access repository secrets by default; do not override this with + `pull_request_target` unless you fully understand the risk. +- Rotate secrets regularly and after any suspected exposure. +- Use OIDC federation where possible (GitHub Actions to AWS/GCP/Azure) to + eliminate long-lived credentials entirely. +- Audit secret access: enable GitHub secret access logs and alert on + unexpected reads. + +### 8. Sign and verify artifacts + +Every artifact produced by CI must be signed and verifiable. + +- Sign Docker images with Cosign or Notary. +- Sign smart contract deployment artifacts and verify their hash matches + audit-reviewed source. +- Generate SLSA provenance attestations during the build. +- Verify signatures and provenance in the deployment stage before releasing. + +### 8b. Enforce SLSA provenance + +SLSA (Supply-chain Levels for Software Artifacts) provides a graded framework +for build integrity. GitHub Actions can generate provenance using the +`slsa-framework/slsa-github-generator` action: + +```yaml +# In your release workflow +- name: Generate SLSA provenance + uses: slsa-framework/slsa-github-generator@v2.0.0 + with: + image-tag: ${{ github.sha }} + attestation-name: attestation.intoto.jsonl + # Requires OIDC identity federation setup +``` + +Provenance attestation links the artifact to the exact source commit, builder +identity, and build process. Verify provenance before deployment: + +```bash +# Verify with Cosign (if using Cosign for image signing) +cosign verify-attestation \ + --certificate-identity=https://github.com//.github/workflows/@refs/tags/ \ + --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ + \ + --type=slsaprovenance \ + < /dev/stdin < attestation.intoto.jsonl +``` + +Adopt SLSA levels progressively: + +| Level | Requirement | Realistic target | +| --- | --- | --- | +| L1 | Build process documented, provenance generated | Most teams start here | +| L2 | Hosted build service, provenance signed | GitHub Actions with OIDC | +| L3 | Hardened build service, no human influence on Provenance | High-security deployments | +| L4 | Two-party review, hermetic builds | Critical Web3 infrastructure | + +### 8c. Generate and verify SBOMs + +A Software Bill of Materials (SBOM) enumerates all dependencies and their versions, +enabling rapid vulnerability response when a CVE is disclosed. + +```yaml +# Generate SBOM with Syft in CI +- name: Generate SBOM + uses: anchore/sbom-action@v0 + with: + image: ${{ env.IMAGE_TAG }} + format: spdx-json + output-file: sbom.spdx.json + +# Upload as artifact +- name: Upload SBOM + uses: actions/upload-artifact@v4 + with: + name: sbom + path: sbom.spdx.json + retention-days: 90 +``` + +When a vulnerability affects a dependency, the SBOM lets you determine exactly +which artifacts are affected and whether a rebuild is needed. Store SBOMs alongside +artifacts and retain them for the artifact's lifetime. + +### 8d. Set up OIDC federation for cloud access + +Long-lived cloud credentials in CI are a high-value target. OpenID Connect (OIDC) +eliminates them entirely by granting short-lived, scoped tokens on demand. + +**GitHub Actions → AWS:** +```yaml +# In your workflow +- name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole + aws-region: us-east-1 + # No long-lived secrets needed — GitHub's OIDC token is exchanged + # for temporary AWS credentials +``` + +**AWS side (trust policy for the IAM role):** +```json +{ + "Version": "2012-10-17", + "Statement": [{ + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "token.actions.githubusercontent.com:sub": "repo:/:ref:refs/heads/main" + }, + "StringLike": { + "token.actions.githubusercontent.com:aud": "https://github.com" + } + } + }] +} +``` + +The role grants access only for pushes to `main`, only for your specific repo, +and the token expires within minutes. If the CI system is breached, the attacker +gets a short-lived token — not a permanent credential. + +## Why is it important + +CI/CD compromises have led to real-world breaches: + +- **SolarWinds (2020)**: Attackers compromised the build system and injected a + backdoor into Orion software updates, affecting 18,000+ organizations. This + demonstrated that a build-system compromise can bypass all downstream code + review. +- **Codecov (2021)**: Attackers modified a CI script to exfiltrate secrets and + environment variables, affecting thousands of customers. +- **3Commas (2022)**: Leaked API keys from a compromised CI environment were + used to drain user funds from trading bots. + +NIST SP 800-218 (Secure Software Development Framework) and the SLSA framework +both define requirements for build integrity and provenance that directly apply +to CI/CD pipeline security. + +## Implementation details + +| Sub-topic | Related page | +| --- | --- | +| Isolation for untrusted CI jobs | [Sandboxing & Isolation](/devsecops/isolation/sandboxing-and-isolation) | +| Network and resource controls | [Network & Resource Isolation](/devsecops/isolation/network-and-resource-isolation) | +| Code signing and verification | [Implementing Code Signing](/devsecops/code-signing) | +| Repository branch protection | [Repository Hardening](/devsecops/repository-hardening) | +| Security testing tools | [Security Testing](/devsecops/security-testing) | + +## Common pitfalls + +- **Using `pull_request_target` with untrusted checkout**: This event gives + fork PRs access to repository secrets. If the workflow checks out the PR + code with those secrets available, an attacker can exfiltrate them. Use + `pull_request` for untrusted code, or use `pull_request_target` only with a + trusted checkout (e.g., the base branch). +- **Pinning actions by tag instead of SHA**: Tags are mutable. `v2` can be + retagged at any time. Always pin by commit SHA and verify with a tool like + `zizmor` or `frizbee`. +- **Over-permissioned `GITHUB_TOKEN`**: The default token has write access to + the repository. Restrict it: set `permissions: read-all` or `permissions: {}` + at the workflow level, then add only the permissions each job needs. +- **Self-hosted runners without cleanup**: Self-hosted runners persist state + between jobs. Secrets, environment variables, and build artifacts from one + job may be readable by the next. Use ephemeral runners or implement strict + cleanup scripts. +- **Skipping CI for "small changes"**: Any bypass of CI checks creates a gap. + Even documentation changes can introduce malicious JavaScript in MDX files. + Require CI for all branches. + +## Quick-reference cheat sheet + +| Check | How | +| --- | --- | +| Require CI on all branches | Branch protection > Require status checks | +| Pin actions by SHA | `uses: action@` | +| Restrict GITHUB_TOKEN | `permissions: {}` + per-job grants | +| Isolate fork PR secrets | Use `pull_request`, not `pull_request_target` | +| Scan for secrets | Enable GitHub push protection + TruffleHog in CI | +| Deterministic builds | Pin deps by hash, pin solc version, lock Docker digests | +| Sign artifacts | Cosign for containers, GPG for tags, SLSA provenance | +| Audit workflow changes | CODEOWNERS on `.github/workflows/` | + +## References + +- [SLSA Specification v1.0](https://slsa.dev/spec/v1.0/) +- [NIST SP 800-218, Secure Software Development Framework](https://csrc.nist.gov/pubs/sp/800/218/final) +- [GitHub Docs: Security hardening for GitHub Actions](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) +- [CISA: Securing the Software Supply Chain for Developers](https://www.cisa.gov/sbom) +- [OWASP CI/CD Security Guide](https://owasp.org/www-project-devsecops-guideline/latest/03-CI-CD/) --- diff --git a/docs/pages/devsecops/repository-hardening.mdx b/docs/pages/devsecops/repository-hardening.mdx index 39e25e1ab..6883df6db 100644 --- a/docs/pages/devsecops/repository-hardening.mdx +++ b/docs/pages/devsecops/repository-hardening.mdx @@ -1,10 +1,15 @@ --- title: "Repository Hardening | Security Alliance" -description: "Harden your GitHub repository with MFA, protected branches, signed commits, and security hardening for GitHub Actions. Prevent token stealing and unauthorized access to critical branches." +description: "Harden your GitHub repository with MFA, protected branches, signed commits, CODEOWNERS, and security hardening for GitHub Actions. Prevent token theft, unauthorized access, and supply chain attacks on critical branches." tags: - Engineer/Developer - Security Specialist - DevOps +contributors: + - role: wrote + users: [frameworks-volunteer] + - role: reviewed + users: [] --- import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' @@ -17,19 +22,234 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } fr -If a threat actor obtains access to your repository, it could have very severe consequences. In order to help avoid -this, you could consider implementing the following best practices: +> 🔑 **Key Takeaway**: A hardened repository enforces MFA, signed commits, +> protected branches, and least-privilege access by default; monitors for +> leaked secrets and suspicious activity; and makes every change traceable +> to a verified human through code review and signature verification. -1. Require Multi-Factor Authentication (MFA) for all repository members. -2. Enable protected branches to prevent unauthorized changes to critical branches. [Learn more about protected - branches](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches). -3. Follow the [Security hardening for GitHub - Actions](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) to avoid token - stealing and other vulnerabilities. -4. Implement strict access controls to limit who can push to critical branches and repositories. -5. Conduct regular security audits of the repository to identify and mitigate potential vulnerabilities. -6. Require all commits to be signed to verify the identity of contributors and ensure the integrity of the code. -7. Regularly update dependencies and use tools to check for and manage vulnerabilities in dependencies. +If a threat actor obtains access to your repository, the consequences can be +catastrophic: injected backdoors, stolen secrets, compromised releases, and +eroded trust. Repository hardening is the first line of defense, ensuring that +even if individual credentials are compromised, layered controls limit the +damage. + +## Practical guidance + +### 1. Require MFA for all repository members + +Multi-Factor Authentication is the single most effective control against +credential theft. + +- Enable "Require two-factor authentication" at the **organization level** in + GitHub. This forces all members, including outside collaborators, to enable + MFA before they can access any org repository. +- Encourage hardware MFA (Yubikey, Titan) over SMS or TOTP. FIDO2/WebAuthn + keys resist phishing and credential replay. +- Audit MFA enrollment periodically: review org member settings and remove + members who have not enabled MFA. + +### 2. Enable protected branches + +Protected branches prevent unauthorized or unreviewed changes to critical +branches. + +- Protect `main`, `develop`, and any release branches. +- Enable these rules at minimum: + - **Require a pull request before merging** — at least 1 approving review, + dismiss stale reviews on push. + - **Require signed commits** — reject unsigned pushes. + - **Require status checks to pass before merging** — link to CI checks. + - **Require linear history** — prevent merge commits that obscure the + commit graph. + - **Do not allow force pushes** — prevent history rewriting. + - **Do not allow deletion** — prevent branch removal. +- For high-sensitivity branches, require **2+ approving reviews** and + restrict who can push to specific teams. + +[Learn more about protected branches](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches). + +### 3. Harden GitHub Actions + +GitHub Actions workflows have access to repository secrets and can execute +arbitrary code, making them a high-value target. + +- Follow [Security hardening for GitHub Actions](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions). +- Pin third-party actions by **commit SHA**, not by tag. Tags are mutable and + can be retagged to point to malicious code. +- Restrict `GITHUB_TOKEN` permissions: set `permissions: {}` at the workflow + level and grant only what each job needs. +- Never use `pull_request_target` with an untrusted checkout. This gives fork + PRs access to repository secrets. +- Use **CODEOWNERS** to require review from the security or DevOps team for any + change to `.github/workflows/`. + +### 4. Enforce strict access controls + +Limit who can push to critical branches and repositories. + +- Use the **principle of least privilege**: grant Admin access only to + maintainers who need it. Most contributors need Write access at most, and + many need only Read or Triage. +- Use **teams** to manage access: assign repository permissions to teams, not + individuals. This makes onboarding/offboarding easier and provides a clear + audit trail. +- For organization repositories, set the **default permission** to Read and + explicitly grant Write/Admin to teams that need it. +- Review repository collaborator lists quarterly. Remove stale accounts and + reduce over-privileged access. + +### 5. Require signed commits + +Signed commits verify the identity of contributors and the integrity of the +code. + +- Enable "Require signed commits" in branch protection rules. +- Configure `git config commit.gpgsign true` to sign by default. +- Verify signatures in CI: `git log --verify-signatures`. +- For automated commits (Dependabot, bots), use a dedicated bot account with + its own signing key, or accept unsigned bot commits via a separate, + lower-privilege branch. +- See [Implementing Code Signing](/devsecops/code-signing) for full guidance. + +### 6. Conduct regular security audits + +Audit the repository for configuration drift, abandoned access, and new +vulnerabilities. + +**Quarterly access review:** list all collaborators and teams, verify each +still needs access, reduce permissions where possible. + +**Workflow audit:** review all GitHub Actions workflows for security issues +(over-permissioned tokens, unpinned actions, exposed secrets). + +**Dependency audit:** run `npm audit`, `pip audit`, or equivalent. Enable +Dependabot security updates. + +**Secret scan:** run TruffleHog or git-secrets on the repository history to +find accidentally committed credentials. + +Use tools like **Scorecard** (OpenSSF) to automate security posture +assessment of your repository. + +### 6b. Use CODEOWNERS to enforce security reviews + +CODEOWNERS files define who must review changes to specific paths. Use them +to require security or ops team sign-off on sensitive directories: + +```bash +# .github/CODEOWNERS + +# Security-sensitive paths — require security team review +/.github/workflows/ @myorg/security-team +/contracts/ @myorg/security-team @myorg/lead-dev +/deploy/ @myorg/security-team @myorg/devops +/secrets.env.example @myorg/security-team + +# Dependencies — require security review for any dependency changes +package.json @myorg/security-team +package-lock.json @myorg/security-team +Gemfile @myorg/security-team +requirements.txt @myorg/security-team + +# Default — all other changes need at least 1 approval from anyone +* @myorg/dev-team +``` + +CODEOWNERS alone does not block merges — pair it with branch protection +rules requiring reviews from the specified teams. + +### 6c. Enable GitHub Advanced Security features + +GitHub provides additional security features under "Code security and analysis": + +**Code scanning (CodeQL):** Automated SAST integrated directly into GitHub. + +```yaml +# .github/workflows/codeql.yml +name: CodeQL +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + +jobs: + codeql: + runs-on: ubuntu-latest + permissions: + security-events: write + actions: read + steps: + - uses: github/codeql-action/init@v3 + with: + languages: javascript, python, solidity + - uses: github/codeql-action/analyze@v3 +``` + +**Secret scanning:** Detects committed secrets across all branches. Enable +push protection to block secrets before they enter history: + +Settings → Code security and analysis → Secret scanning → ✅ Scanning + +✅ Push protection. + +**Dependency review:** Shows the security impact of dependency changes in +PRs before they merge. Requires GitHub Advanced Security: + +Settings → Code security and analysis → Dependency review → ✅ Enable. + +**Unified security dashboard:** At the organization level, get a unified +view of security alerts across all repos. + +### 6d. Publish a security policy + +A `SECURITY.md` file in the repository root tells security researchers how +to report vulnerabilities: + +```markdown +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 1.x | :white_check_mark: | +| 0.x | :x: | + +## Reporting a Vulnerability + +Please report vulnerabilities by: +1. **Do not** open a public GitHub issue. +2. Email security@example.com with details. +3. Include a proof of concept if possible. + +We aim to respond within 48 hours and will keep you informed +throughout the disclosure process. + +## Bug Bounty + +We participate in the Immunefi bug bounty program. Rewards up to +$50,000 for critical vulnerabilities. See: https://immunefi.com/bounty +``` + +Link `SECURITY.md` in: +- The repository's sidebar (via vocs config) +- The PR template +- Any public communication about the project + +### 7. Manage dependencies and vulnerabilities + +Dependencies are a major attack surface for supply chain compromises. + +- Enable **Dependabot** for version updates and security updates. +- Review Dependabot PRs promptly; security updates are time-sensitive. +- Pin dependencies by hash or exact version in production. Avoid floating + ranges (`^1.0.0`) in deployed artifacts. +- Use lockfiles (`package-lock.json`, `poetry.lock`) and commit them to the + repository. +- For Web3: audit Solidity dependencies (OpenZeppelin, forge-std) for known + vulnerabilities. Use `npm audit` or `pip audit` for off-chain dependencies. +- Consider using a private registry (Artifactory, npm enterprise) to proxy + and cache dependencies, preventing dependency confusion attacks. ## Configuration Checklists @@ -38,6 +258,73 @@ For detailed GitHub configuration checklists adapted from [Auditware's W3OSC](ht - [Individual GitHub Account Settings](/guides/account-management/github#for-individuals) - [Organization GitHub Repository & Org Settings](/guides/account-management/github#for-admins) +## Why is it important + +Repository compromises have led to significant incidents: + +- **PHP/Composer supply chain attack (2021)**: Attacker obtained maintainer + credentials and pushed malicious commits to the PHP repository, injecting a + backdoor that would have allowed remote code execution. Signed commits and + review would have caught this. +- **ua-parser-js, event-stream incidents**: Maintainer accounts were + compromised to publish malicious npm packages. These could have been + prevented by MFA, signed commits, and dependency pinning. +- **Action-scrubber attack pattern**: Malicious GitHub Actions can exfiltrate + `GITHUB_TOKEN` and repository secrets if workflows are not properly + sandboxed and permissioned. + +NIST SP 800-53 Rev. 5 controls **AC-3 (Access Enforcement)**, **AU-10 +(Non-Repudiation)**, and **SC-28 (Protection of Information at Rest)** all +apply to repository hardening. + +## Implementation details + +| Sub-topic | Related page | +| --- | --- | +| Code signing and key management | [Implementing Code Signing](/devsecops/code-signing) | +| CI/CD pipeline security | [Securing CI/CD Pipelines](/devsecops/continuous-integration-continuous-deployment) | +| Security testing tools | [Security Testing](/devsecops/security-testing) | +| Sandbox isolation for CI | [Sandboxing & Isolation](/devsecops/isolation/sandboxing-and-isolation) | + +## Common pitfalls + +- **Admin access granted too broadly**: Many repositories grant Admin to all + core contributors. Admins can change branch protection rules, remove + reviewers, and modify secrets. Grant Admin only to maintainers who + explicitly need it. +- **Stale collaborator accounts**: Former team members or contractors who + still have access are a common attack vector. Remove access immediately + when someone leaves the team. +- **Unpinned GitHub Actions**: `uses: action@v3` can be retagged. Always pin + by SHA: `uses: action@abc123def456...`. +- **Ignoring Dependabot PRs**: Security update PRs are time-sensitive. + Establish an SLA for reviewing them (e.g., Critical within 24 hours). +- **Disabling branch protection for convenience**: Temporary exceptions + become permanent. If a workflow requires an exception, automate it through + a documented process, not by disabling the rule. + +## Quick-reference cheat sheet + +| Hardening measure | How to enable | +| --- | --- | +| Org-wide MFA | GitHub Org Settings > Authentication > Require 2FA | +| Protected branches | Repo Settings > Branches > Add rule > Enable all checks | +| Require signed commits | Branch protection > Require signed commits | +| CODEOWNERS review | Create `.github/CODEOWNERS` with path-to-team mappings | +| Pin actions by SHA | `uses: action@<40-char-sha>` | +| Restrict GITHUB_TOKEN | `permissions: {}` at workflow level | +| Dependabot security updates | Repo Settings > Code security > Enable Dependabot | +| Secret scanning + push protection | Org Settings > Code security > Enable both | +| Scorecard audit | `npx @ossf/scorecard --repo=/` | + +## References + +- [GitHub Docs: About protected branches](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches) +- [GitHub Docs: Security hardening for GitHub Actions](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) +- [OpenSSF Scorecard](https://github.com/ossf/scorecard) +- [NIST SP 800-53 Rev. 5, AC-3 Access Enforcement](https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final) +- [Auditware W3OSC](https://github.com/AuditWare-io/W3OSC) + --- diff --git a/docs/pages/devsecops/security-testing.mdx b/docs/pages/devsecops/security-testing.mdx index 889c8e4bc..aca4818c4 100644 --- a/docs/pages/devsecops/security-testing.mdx +++ b/docs/pages/devsecops/security-testing.mdx @@ -1,11 +1,16 @@ --- title: "Security Testing | Security Alliance" -description: "Identify vulnerabilities early with SAST, DAST, and IAST tools in your CI/CD pipeline. Implement fuzz testing to discover security issues before they reach production." +description: "Identify vulnerabilities early with SAST, DAST, IAST, and fuzz testing in your CI/CD pipeline. Shift-left security testing, integrate automated scanning, and implement comprehensive testing strategies for Web3 projects." tags: - Engineer/Developer - Security Specialist - DevOps - SRE +contributors: + - role: wrote + users: [frameworks-volunteer] + - role: reviewed + users: [] --- import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' @@ -18,13 +23,266 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } fr -Security testing is a crucial part of the DevSecOps process, as it helps identify vulnerabilities early on so that they -can be taken care of before they become an issue in production. +> 🔑 **Key Takeaway**: Shift security testing left by running SAST, DAST, +> IAST, and fuzzing in CI on every PR; treat findings as actionable pipeline +> gates (not just reports); and complement automation with periodic manual +> penetration testing for high-value targets. -1. Integrate SAST tools into the CI/CD pipeline to analyze source code for vulnerabilities. -2. Use DAST tools to test running applications for security issues. -3. Combine SAST and DAST approaches with IAST tools for comprehensive security testing. -4. Implement fuzz testing to discover security vulnerabilities by inputting random data. +Security testing identifies vulnerabilities before they reach production. The +"shift-left" principle means integrating these tests as early as possible in the +development lifecycle: in the IDE, in pre-commit hooks, and in CI. The later a +vulnerability is found, the more expensive it is to fix. In Web3, where +deployed smart contracts are often immutable, finding bugs before deployment is +critical. + +## Practical guidance + +### 1. Static Application Security Testing (SAST) + +SAST analyzes source code without executing it. It catches common +vulnerabilities early and cheaply. + +- Integrate SAST into the CI pipeline so that every PR is scanned before + merge. +- For Solidity: use **Slither** (Trail of Bits), **Aderyn** (Cyfrin), and + **Solhint**. These detect reentrancy, access control issues, and unsafe + arithmetic patterns. +- For JavaScript/TypeScript: use **Semgrep**, **CodeQL**, or **SonarQube**. +- For Python: use **Semgrep**, **Bandit**, or **Ruff** with security rules. +- Configure SAST tools to fail CI on Critical and High findings. Track + Medium/Low as issues for triage. +- Suppress false positives carefully: document the reason and set an expiry + date for re-evaluation. +- Run SAST locally in pre-commit hooks or IDE plugins so developers get + immediate feedback before pushing. + +### 2. Dynamic Application Security Testing (DAST) + +DAST tests running applications by sending crafted inputs and observing +responses. It catches issues that SAST misses: misconfigured servers, +authentication bypasses, and runtime-specific vulnerabilities. + +- Run DAST against deployed test environments (not production). +- Tools: **OWASP ZAP** (free, extensible), **Burp Suite** (commercial, + professional-grade), **Nuclei** (template-based, fast scanning). +- For Web3 APIs: test RPC endpoints, REST APIs, and WebSocket interfaces for + injection, authentication bypass, and rate limiting issues. +- Schedule DAST scans nightly and before every release. +- Test authentication flows: login, session management, privilege escalation, + and API key validation. + +### 3. Interactive Application Security Testing (IAST) + +IAST combines SAST and DAST by instrumenting the application at runtime and +observing data flow during functional testing. + +- IAST agents run inside the application process and monitor method calls, data + flow, and control flow in real time. +- Tools: **Contrast Security**, **Seeker** (Synopsys), **Hdiv Security**. +- IAST excels at finding injection vulnerabilities (SQL, command, LDAP, XSS) + that SAST may miss due to complex data flow and DAST may miss because it + does not cover all code paths. +- Integrate IAST into your automated test suite: functional tests drive the + IAST agent, which reports vulnerabilities in real time. +- Limitations: IAST requires language-specific agents and may impact + application performance. Use it in staging environments, not production. + +### 4. Fuzz testing + +Fuzz testing feeds random or semi-random data to application interfaces to +discover crashes, assertion failures, and security vulnerabilities. + +- For Solidity smart contracts: use **Echidna** (Trail of Bits) for property- + based testing, **Medusa** (Cryptic Labs) and **Halmos** for symbolic fuzzing. + Define invariants (e.g., "total supply equals sum of balances") and let the + fuzzer try to break them. +- For general software: use **AFL++**, **libFuzzer**, or **Jazzer** (Java). + Write fuzz harnesses that target parsing logic, input validation, and + protocol handling. +- For APIs: use **Hypothesis** (Python) or **fast-check** (JS/TS) for + property-based testing in unit tests. +- Run fuzzing campaigns as part of CI: fast fuzz tests in PR pipelines (short + duration, focused invariants), extended fuzzing overnight (longer duration, + broader coverage). +- Monitor for new coverage: a good fuzzer should continuously discover new + code paths. If coverage plateaus, add new seeds or adjust mutation + strategies. + +### 5. Software Composition Analysis (SCA) + +SCA scans dependencies for known vulnerabilities. + +- Enable **Dependabot** alerts and security updates in GitHub. +- Use **Snyk**, **npm audit**, or **pip audit** in CI to scan on every PR. +- For Web3: audit Solidity dependencies (OpenZeppelin, Solmate, Forge-std) and + off-chain dependencies (ethers.js, viem, web3.js). +- Track dependency risk: flag packages with no recent updates, single + maintainers, or known vulnerabilities. +- Consider using a private package registry (Artifactory, Verdaccio) to proxy + and cache dependencies, reducing supply chain risk. + +### 6. Manual penetration testing + +Automated tools cannot replace human creativity in finding complex logic flaws. + +- Schedule external penetration tests for high-value targets (smart contracts, + bridges, oracles, authentication systems) before major releases. +- Use bug bounty platforms (Immunefi, HackerOne, Code4rena) for continuous + community-driven testing. +- After internal testing and automated scans, engage an independent security + firm (Trail of Bits, OpenZeppelin, Spearbit) for a formal audit. +- Publish audit reports for transparency. Track and remediate all findings. + +### 6b. Set realistic severity thresholds + +Context matters. A "High" finding in a low-impact context might not warrant +blocking a merge. Establish explicit triage criteria: + +| Severity | Definition | Action | +| --- | --- | --- | +| Critical | Remote code execution, full wallet drain, signature forgery | Block PR immediately | +| High | Data exfiltration, unauthorized state change, privilege escalation | Block PR, fix within 24h | +| Medium | Denial of service, partial bypass, information disclosure | Block PR, fix within 1 week | +| Low | Code quality, best practice violations, minor misconfigurations | Track as issue, fix within 1 sprint | + +Document severity triage decisions. A finding dismissed as "Low" should have +a written rationale and an expiry date for re-evaluation. + +### 6c. Integrate Semgrep for custom security rules + +Semgrep supports custom rules that encode your project's security policies: + +```yaml +# .semgrep/security-high.yaml +rules: + - id: hardcoded-private-key + pattern: $KEY = /0x[a-fA-F0-9]{64}/ + message: Hardcoded private key detected. Use environment variables. + severity: ERROR + languages: [javascript, python, solidity] + + - id: sensitive-data-logging + pattern: console.log(...$SENSITIVE) + message: Do not log sensitive data in production. + severity: WARNING + languages: [javascript, typescript] + + - id: prevent-eval + pattern: eval($INPUT) + message: eval() with user input is a code injection risk. + severity: ERROR + languages: [javascript, python] +``` + +Semgrep Rule Registry contains thousands of pre-built rules. Use +`semgrep --config=auto` to run all applicable rules, then supplement with +project-specific rules. + +### 6d. Manage false positives systematically + +False positives erode trust in the scanning pipeline. If developers learn to +ignore alerts, real findings get missed. + +**Suppress with documentation, not silence:** +```yaml +# nosemgrep: hardcoded-private-key +# Reason: Test fixtures only — not real keys. +# Ticket: SEC-1234, suppress until test fixture refactored. +# Expiry: 2025-06-01 +``` + +Every suppression should include: the finding ID, why it is a false positive, +a linked ticket, and an expiry date for re-evaluation. + +### 6e. Track coverage and mutation testing + +Code coverage metrics alone do not measure security, but they indicate +whether critical paths are tested: + +```bash +# Solidity with Foundry +forge coverage --report lcov +# Target: >90% line coverage for contract code + +# Python with pytest-cov +pytest --cov=src --cov-report=html +# Target: >85% line coverage + +# JavaScript with c8 +npx c8 --reporter=lcov mocha +# Target: >80% line coverage +``` + +For smart contracts, supplement coverage with **mutation testing** using +Foundry's `forge snapshot` or Echidna invariants. Mutation testing modifies +code slightly (changes `>` to `>=`) and verifies that tests catch the change. +If tests don't catch mutated code, they aren't testing the right thing. + +## Why is it important + +Undetected vulnerabilities in production lead to exploits. In Web3, the +consequences are often irreversible due to smart contract immutability. + +- **Ronin Bridge ($625M, 2022)**: The bridge's validator set was compromised + because monitoring and access controls were insufficient. Penetration testing + and security monitoring could have identified the weak access control earlier. +- **Wormhole ($325M, 2022)**: A signature verification bug in upgraded guardian + logic went undetected. Formal verification and fuzz testing of invariants + would have caught this. +- **Parity Wallet ($150M, 2017)**: An unprotected `initWallet` function allowed + anyone to take ownership. SAST and manual review would have identified the + missing access control. + +NIST SP 800-53 Rev. 5 control **RA-5 (Vulnerability Scanning)** and **SI-2 +(Flaw Remediation)** mandate regular vulnerability scanning and timely +remediation. + +## Implementation details + +| Sub-topic | Related page | +| --- | --- | +| CI/CD pipeline integration of scans | [Securing CI/CD Pipelines](/devsecops/continuous-integration-continuous-deployment) | +| Isolated test environments | [Sandboxing & Isolation](/devsecops/isolation/sandboxing-and-isolation) | +| Code signing to verify test provenance | [Implementing Code Signing](/devsecops/code-signing) | + +## Common pitfalls + +- **Running SAST but ignoring results**: Scanning is only valuable if findings + are triaged and remediated. Establish an SLA: Critical findings block the PR, + High within 48 hours, Medium tracked as issues. +- **DAST only on production**: Running DAST against production can cause + disruptions and misses the opportunity to catch issues before deploy. Run DAST + against staging or dedicated test environments. +- **Fuzzing without invariants**: A fuzzer without clear properties to test + will find crashes but miss logic errors. Define meaningful invariants (e.g., + "no user can withdraw more than their balance") that capture business logic. +- **Skipping IAST due to performance overhead**: IAST has a performance cost, + but running it in staging during automated functional tests catches injection + vulnerabilities that SAST and DAST miss. The trade-off is worthwhile for + high-value applications. +- **Treating security testing as a one-time event**: Security is continuous. + New code, new dependencies, and new attack techniques require ongoing testing. + Integrate scans into CI, not just pre-release checklists. + +## Quick-reference cheat sheet + +| Test type | When to run | Tools (Web3/Solidity) | Tools (General) | +| --- | --- | --- | --- | +| SAST | Every PR | Slither, Aderyn, Solhint | Semgrep, CodeQL, SonarQube | +| DAST | Nightly + pre-release | Custom RPC fuzzers | OWASP ZAP, Nuclei, Burp Suite | +| IAST | Staging functional tests | — | Contrast Security, Seeker | +| Fuzzing | PR (short) + nightly (long) | Echidna, Medusa, Halmos | AFL++, libFuzzer, Hypothesis | +| SCA | Every PR + daily cron | npm audit, pip audit | Dependabot, Snyk | +| Pen test | Pre-release + ongoing | Immunefi, Code4rena | HackerOne, Bugcrowd | + +## References + +- [OWASP Testing Guide v4](https://owasp.org/www-project-web-security-testing-guide/) +- [NIST SP 800-53 Rev. 5, RA-5 Vulnerability Scanning](https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final) +- [Slither: Static Analyzer for Solidity](https://github.com/crytic/slither) +- [Echidna: Fuzzer for Ethereum Smart Contracts](https://github.com/crytic/echidna) +- [OWASP Fuzzing Guide](https://owasp.org/www-community/Fuzzing) +- [Immunefi Bug Bounty Platform](https://immunefi.com/) --- From 2a2c3a42d4cc509dc3b2a07e57c65050e7873b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zealot=20=E2=80=94=20Framework=27s=20intern?= Date: Tue, 21 Apr 2026 15:16:22 -0300 Subject: [PATCH 23/92] fix: remediate PR #461 attribution and review findings (#462) - Update contributors frontmatter: mattaereal as author, scode2277 as reviewer - Replace discontinued keys.mailvelope.com with keyserver.ubuntu.com - Fix CISA link text to match URL (Software Bill of Materials) - Update expired nosemgrep example date to 2027-06-01 - Add note about simplified Semgrep rule example --- docs/pages/devsecops/code-signing.mdx | 6 +++--- .../continuous-integration-continuous-deployment.mdx | 6 +++--- docs/pages/devsecops/repository-hardening.mdx | 4 ++-- docs/pages/devsecops/security-testing.mdx | 8 +++++--- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/docs/pages/devsecops/code-signing.mdx b/docs/pages/devsecops/code-signing.mdx index 2d544d425..ea18599e6 100644 --- a/docs/pages/devsecops/code-signing.mdx +++ b/docs/pages/devsecops/code-signing.mdx @@ -7,9 +7,9 @@ tags: - DevOps contributors: - role: wrote - users: [frameworks-volunteer] + users: [mattaereal] - role: reviewed - users: [] + users: [scode2277] --- import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' @@ -193,7 +193,7 @@ A signature is meaningless if the verifying party cannot obtain the correct public key. - Upload your public key to GitHub (Settings > SSH and GPG keys) and to a - public keyserver (keys.openpgp.org, keys.mailvelope.com). + public keyserver (keys.openpgp.org, keyserver.ubuntu.com). - Use the same key across all platforms so that the identity is consistent. - In CI, pin trusted public key fingerprints in the pipeline configuration. Reject signatures from unknown keys. diff --git a/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx b/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx index 3ca00be74..1e86fc218 100644 --- a/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx +++ b/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx @@ -8,9 +8,9 @@ tags: - SRE contributors: - role: wrote - users: [frameworks-volunteer] + users: [mattaereal] - role: reviewed - users: [] + users: [scode2277] --- import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' @@ -336,7 +336,7 @@ to CI/CD pipeline security. - [SLSA Specification v1.0](https://slsa.dev/spec/v1.0/) - [NIST SP 800-218, Secure Software Development Framework](https://csrc.nist.gov/pubs/sp/800/218/final) - [GitHub Docs: Security hardening for GitHub Actions](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) -- [CISA: Securing the Software Supply Chain for Developers](https://www.cisa.gov/sbom) +- [CISA: Software Bill of Materials](https://www.cisa.gov/sbom) - [OWASP CI/CD Security Guide](https://owasp.org/www-project-devsecops-guideline/latest/03-CI-CD/) --- diff --git a/docs/pages/devsecops/repository-hardening.mdx b/docs/pages/devsecops/repository-hardening.mdx index 6883df6db..7a328a505 100644 --- a/docs/pages/devsecops/repository-hardening.mdx +++ b/docs/pages/devsecops/repository-hardening.mdx @@ -7,9 +7,9 @@ tags: - DevOps contributors: - role: wrote - users: [frameworks-volunteer] + users: [mattaereal] - role: reviewed - users: [] + users: [scode2277] --- import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' diff --git a/docs/pages/devsecops/security-testing.mdx b/docs/pages/devsecops/security-testing.mdx index aca4818c4..f0a4f8bed 100644 --- a/docs/pages/devsecops/security-testing.mdx +++ b/docs/pages/devsecops/security-testing.mdx @@ -8,9 +8,9 @@ tags: - SRE contributors: - role: wrote - users: [frameworks-volunteer] + users: [mattaereal] - role: reviewed - users: [] + users: [scode2277] --- import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' @@ -161,6 +161,8 @@ rules: severity: ERROR languages: [javascript, python, solidity] + # NOTE: This is a simplified example. In production, use a more specific + # pattern that matches actual sensitive variables, not any spread argument. - id: sensitive-data-logging pattern: console.log(...$SENSITIVE) message: Do not log sensitive data in production. @@ -188,7 +190,7 @@ ignore alerts, real findings get missed. # nosemgrep: hardcoded-private-key # Reason: Test fixtures only — not real keys. # Ticket: SEC-1234, suppress until test fixture refactored. -# Expiry: 2025-06-01 +# Expiry: 2027-06-01 ``` Every suppression should include: the finding ID, why it is a false positive, From 9827f36272deab275d9a186b27ab672375801436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zealot=20=E2=80=94=20Framework=27s=20intern?= Date: Mon, 4 May 2026 20:34:51 -0300 Subject: [PATCH 24/92] fix: include hidden files in preview build artifact (closes #469) (#471) The search index lives in docs/dist/.vocs/ (a hidden directory). actions/upload-artifact@v4 excludes hidden files by default, so the .vocs directory was missing from the uploaded artifact. This caused the search index to be absent from preview deployments, breaking the native search (/) on all preview URLs. Adding include-hidden-files: true ensures the .vocs directory (and any other hidden assets) are included in the artifact. --- .github/workflows/preview-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml index e92af7868..eb8dd9719 100644 --- a/.github/workflows/preview-build.yml +++ b/.github/workflows/preview-build.yml @@ -41,5 +41,6 @@ jobs: with: name: preview-build path: docs/dist + include-hidden-files: true if-no-files-found: error retention-days: 1 From d2045be09f5550324de6836049a015c3f5038816 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Tue, 5 May 2026 19:04:41 +0200 Subject: [PATCH 25/92] Chore: LLMs generator (#463) * feat: add llms.txt generator + llms-{framework}.txt for all the frameworks we have + changed titles to 2 pages as previously confusing * Add branch-aware links + better hadle of contributing folder * fix: making the generator inherit dev:true from parent sidebar blocks * refactor: restructure llms output with per-page files, folder layout, and tighter routing index * fix: make branch check stronger --- .../dprk-it-workers/general-information.mdx | 2 +- docs/pages/dprk-it-workers/index.mdx | 4 +- docs/pages/dprk-it-workers/overview.mdx | 2 +- docs/pages/intro/index.mdx | 1 + docs/pages/intro/introduction.mdx | 4 + docs/pages/intro/llms.mdx | 40 ++ package.json | 2 +- utils/generate-llms.js | 367 ++++++++++++++++++ vocs.config.tsx | 3 +- 9 files changed, 419 insertions(+), 6 deletions(-) create mode 100644 docs/pages/intro/llms.mdx create mode 100644 utils/generate-llms.js diff --git a/docs/pages/dprk-it-workers/general-information.mdx b/docs/pages/dprk-it-workers/general-information.mdx index eec93b43e..41871e9a3 100644 --- a/docs/pages/dprk-it-workers/general-information.mdx +++ b/docs/pages/dprk-it-workers/general-information.mdx @@ -1,5 +1,5 @@ --- -title: "DPRK IT Workers Overview | Security Alliance" +title: "DPRK IT Workers: General Information | Security Alliance" description: "What are DPRK IT Workers? Learn their operational structure, goals, and the average profile indicators to watch for." tags: - Security Specialist diff --git a/docs/pages/dprk-it-workers/index.mdx b/docs/pages/dprk-it-workers/index.mdx index 721b616a8..2dccb5542 100644 --- a/docs/pages/dprk-it-workers/index.mdx +++ b/docs/pages/dprk-it-workers/index.mdx @@ -11,8 +11,8 @@ title: "Dprk It Workers" ## Pages -- [Insider Threats (DPRK)](/dprk-it-workers/overview) -- [DPRK IT Workers Overview](/dprk-it-workers/general-information) +- [DPRK IT Workers](/dprk-it-workers/overview) +- [DPRK IT Workers: General Information](/dprk-it-workers/general-information) - [DPRK IT Worker TTPs](/dprk-it-workers/techniques-tactics-and-procedures) - [Mitigating DPRK IT Worker Threats](/dprk-it-workers/mitigating-dprk-it-workers) - [DPRK IT Worker Case Studies](/dprk-it-workers/case-studies) diff --git a/docs/pages/dprk-it-workers/overview.mdx b/docs/pages/dprk-it-workers/overview.mdx index 75701bffe..a56b6e092 100644 --- a/docs/pages/dprk-it-workers/overview.mdx +++ b/docs/pages/dprk-it-workers/overview.mdx @@ -1,5 +1,5 @@ --- -title: "Insider Threats (DPRK) | Security Alliance" +title: "DPRK IT Workers | Security Alliance" description: "DPRK IT Workers Framework: Protect your organization from North Korean insider threats. Recognize hacker-freelancers, harden hiring processes, and mitigate supply-chain compromise risks." tags: - Security Specialist diff --git a/docs/pages/intro/index.mdx b/docs/pages/intro/index.mdx index 8ca00ab8d..b7480feae 100644 --- a/docs/pages/intro/index.mdx +++ b/docs/pages/intro/index.mdx @@ -16,3 +16,4 @@ title: "Intro" - [Overview Of Each Framework](/intro/overview-of-each-framework) - [What It Is](/intro/what-is-it) - [What It Isn't](/intro/what-it-isnt) +- [LLMs | Security Alliance Frameworks](/intro/llms) diff --git a/docs/pages/intro/introduction.mdx b/docs/pages/intro/introduction.mdx index 932cfd897..02bfffc69 100644 --- a/docs/pages/intro/introduction.mdx +++ b/docs/pages/intro/introduction.mdx @@ -47,6 +47,10 @@ public good for the Web3 ecosystem, its supporting technologies, and communities desire to foster a safer, more informed digital landscape. We do this by designing innovative projects, engaging elite technologists, and coordinating on the social layer to ensure meaningful adoption. +:::info[AI-friendly documentation] +All framework content is available in LLM-friendly format following the [llms.txt](https://llmstxt.org/) standard - one file per framework, plus a routing index. See the [LLMs page](/intro/llms) for the full list. +::: + --- diff --git a/docs/pages/intro/llms.mdx b/docs/pages/intro/llms.mdx new file mode 100644 index 000000000..df45e832e --- /dev/null +++ b/docs/pages/intro/llms.mdx @@ -0,0 +1,40 @@ +--- +title: LLMs | Security Alliance Frameworks +description: LLM-friendly documentation for the Security Alliance Frameworks, following the llms.txt standard. +--- + +# LLMs + +The Security Alliance Frameworks documentation is available in LLM-friendly format following the [llms.txt](https://llmstxt.org/) standard. These files are generated at build time and designed to be fetched by AI assistants and coding tools to provide accurate, up-to-date framework content as context. + +## How to use + +AI assistants should start with [`/llms.txt`](https://frameworks.securityalliance.org/llms.txt), the routing index. It lists every framework with a description and topic summary, so the AI can identify the best match and fetch the framework index file. From there, per-page files can be fetched for detailed content on a specific topic. + +## File structure + +Three file types are available: + +``` +/llms.txt routing index —> lists all framework index files +/llms/{framework-name}.txt framework index —> overview + links to per-page files +/llms/{framework-name}/{page}.txt per-page file —> full content of a single page +``` + +The `{framework-name}` maps to the framework's folder name in the repository and `{page}` to the page's slug. For example, the Wallet Security seed phrase page lives at `docs/pages/wallet-security/seed-phrase-management.mdx` and its per-page file is at `/llms/wallet-security/seed-phrase-management.txt`. + +## What each file contains + +**`/llms.txt`** - routing index: +- One entry per framework with its index file URL, description, and topic list + +**`/llms/{framework-name}.txt`** - framework index: +- Header, description, and AI instructions +- Full content of the overview page for immediate context +- Links to all per-page files with one-line descriptions + +**`/llms/{framework-name}/{page}.txt`** - per-page file: +- Full markdown content of that page +- Source URL and framework attribution + +New frameworks and pages are picked up automatically on the next build. diff --git a/package.json b/package.json index 6dfec677e..ebbb550b4 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "test": "echo \"Error: no test specified\" && exit 1", "docs:dev": "pnpm run generate-tags && pnpm run generate-indexes && pnpm run mermaid-wrapper && vocs dev --host 0.0.0.0 --port 5173", "docs:build": "pnpm run generate-tags && pnpm run generate-indexes && pnpm run mermaid-wrapper && pnpm run generate-printables && pnpm run generate-cert-data && vocs build", - "postdocs:build": "node utils/searchbar-indexing.js && node utils/sitemap-generator.js", + "postdocs:build": "node utils/searchbar-indexing.js && node utils/sitemap-generator.js && node utils/generate-llms.js", "docs:preview": "vocs preview", "generate-tags": "node utils/tags-fetcher.js", "mermaid-wrapper": "node utils/mermaid-block-wrapper.js", diff --git a/utils/generate-llms.js b/utils/generate-llms.js new file mode 100644 index 000000000..dc08583ac --- /dev/null +++ b/utils/generate-llms.js @@ -0,0 +1,367 @@ +/* + Generates llms.txt files following the llms.txt standard (https://llmstxt.org/) + - llms.txt: thin routing index listing all frameworks with descriptions and page topics + - llms/{framework}.txt: framework index — overview content + links to all per-page files + - llms/{framework}/{page}.txt: one file per sidebar page with full stripped markdown content + - Page order follows the sidebar order defined in vocs.config.tsx + - Runs post-build and writes to the dist directory +*/ + +const fs = require('fs'); +const path = require('path'); +const matter = require('gray-matter'); + +const PROD_URL = 'https://frameworks.securityalliance.org'; +const BASE_URL = process.env.CF_PAGES_BRANCH === 'main' + ? PROD_URL + : (process.env.CF_PAGES_URL || PROD_URL); +const workspaceRoot = process.cwd(); +const PAGES_DIR = path.join(workspaceRoot, 'docs', 'pages'); +const isMainBranch = process.env.CF_PAGES_BRANCH === 'main'; + +function findDistDir() { + const candidates = [ + path.join(workspaceRoot, 'docs', 'dist'), + path.join(workspaceRoot, 'dist'), + ]; + return candidates.find((dir) => fs.existsSync(dir)); +} + +// Returns all sidebar links in document order, filtered to a specific folder prefix. +// Tracks brace depth so a dev: true flag on a parent block is inherited by all child links. +function getSidebarLinksForFolder(folderName) { + const configPath = path.join(workspaceRoot, 'vocs.config.tsx'); + if (!fs.existsSync(configPath)) return []; + + const lines = fs.readFileSync(configPath, 'utf8').split('\n'); + const links = []; + let depth = 0; + const devDepths = new Set(); // brace depths at which a parent block carries dev: true + + for (const line of lines) { + if (isMainBranch) { + const opens = (line.match(/\{/g) || []).length; + const closes = (line.match(/\}/g) || []).length; + const newDepth = depth + opens - closes; + + // Pop dev markers for blocks we're closing + if (newDepth < depth) { + for (const d of devDepths) { + if (d > newDepth) devDepths.delete(d); + } + } + depth = newDepth; + + // If this line carries dev: true but is not a link item, it's a container flag - inherit downward + if (line.includes('dev:') && line.includes('true') && !line.match(/link:/)) { + devDepths.add(depth); + } + } + + const match = line.match(/link:\s*(['"])(\/[^'"]+)\1/); + if (!match) continue; + const link = match[2]; + + // Skip if the item itself has dev: true, or if any ancestor block does + if (isMainBranch && ((line.includes('dev:') && line.includes('true')) || devDepths.size > 0)) continue; + + if (link.startsWith(`/${folderName}/`)) { + links.push(link); + } + } + + return links; +} + +function linkToFilePath(link) { + const base = path.join(PAGES_DIR, link); + for (const candidate of [`${base}.mdx`, `${base}.md`, path.join(base, 'index.mdx')]) { + if (fs.existsSync(candidate)) return candidate; + } + return null; +} + +function getPageUrl(filePath) { + return filePath + .replace(PAGES_DIR, '') + .replace(/\.(mdx|md)$/, '') + .replace(/\/index$/, ''); +} + +function stripSiteSuffix(title) { + return title ? title.replace(/\s*\|.*$/, '').trim() : ''; +} + +const ACRONYMS = new Set(['ai', 'iam', 'ens', 'dprk', 'dns', 'it']); +const SPECIAL_CASES = { opsec: 'OpSec', devsecops: 'DevSecOps' }; + +function toTitleCase(slug) { + return slug + .split('-') + .map((w) => { + if (SPECIAL_CASES[w]) return SPECIAL_CASES[w]; + if (ACRONYMS.has(w)) return w.toUpperCase(); + return w.charAt(0).toUpperCase() + w.slice(1); + }) + .join(' '); +} + +function extractHeadings(raw) { + const stripped = raw + .replace(/^---[\s\S]*?---\n?/, '') + .replace(/^(import|export)\s+.*$/gm, '') + .replace(/```[\s\S]*?```/gm, ''); + + const headings = []; + for (const line of stripped.split('\n')) { + const match = line.match(/^(#{1,2}) (.+)/); + if (match) headings.push({ level: match[1].length, text: match[2].trim() }); + } + return headings; +} + +function stripMdxSyntax(raw) { + return raw + .replace(/^---[\s\S]*?---\n?/, '') // YAML frontmatter + .replace(/\{\/\*[\s\S]*?\*\/\}/g, '') // JSX comment blocks: {/* ... */} + .replace(/^(import|export)\s+.*$/gm, '') // import/export lines + .replace(/<[A-Z][^/\s>][^>]*\/>/g, '') // self-closing JSX: , + .replace(/<\/?[A-Z][^\s>]*[^>]*>/g, '') // JSX open/close: , + .replace(/^\s*# .+\n+/, '') // strip leading h1 (redundant with our page header) + .replace(/^(#{1,5}) /gm, '#$1 ') // shift all headings down one level (## → ###, etc.) + .replace(/\n{3,}/g, '\n\n') // collapse excess blank lines + .replace(/\n*---\s*$/, '') // strip trailing hr + .trim(); +} + +const FOLDERS_FIRST = ['intro']; +const FOLDERS_LAST = ['certs']; +const FOLDERS_EXCLUDE = ['config']; + +const FOLDER_DESCRIPTION_OVERRIDES = { + contribute: 'How to contribute to the Security Frameworks - either through direct contributions (fixes, new content, enhancements) or by becoming a Framework Steward.', +}; +const PAGE_DESCRIPTION_OVERRIDES = { + '/contribute/spotlight-zone': 'The Spotlight Zone is where all contributor activity across the Security Frameworks is tracked and recognized.', +}; + +function getFrameworkFolders() { + const folders = fs + .readdirSync(PAGES_DIR) + .filter((entry) => !FOLDERS_EXCLUDE.includes(entry) && fs.statSync(path.join(PAGES_DIR, entry)).isDirectory()) + .sort(); + + const first = folders.filter((f) => FOLDERS_FIRST.includes(f)); + const last = folders.filter((f) => FOLDERS_LAST.includes(f)); + const rest = folders.filter((f) => !FOLDERS_FIRST.includes(f) && !FOLDERS_LAST.includes(f)); + return [...first, ...rest, ...last]; +} + +function getFrameworkDescription(folderName) { + if (FOLDER_DESCRIPTION_OVERRIDES[folderName]) return FOLDER_DESCRIPTION_OVERRIDES[folderName]; + const folderPath = path.join(PAGES_DIR, folderName); + for (const candidate of ['overview.mdx', 'introduction.mdx', 'index.mdx']) { + const candidatePath = path.join(folderPath, candidate); + if (fs.existsSync(candidatePath)) { + try { + const { data } = matter(fs.readFileSync(candidatePath, 'utf-8')); + if (data.description) return data.description; + } catch (_) {} + } + } + return ''; +} + +// Builds the framework index file: header + AI instructions + overview content + per-page link list +function buildFrameworkIndex(folderName, title, overviewUrl, frameworkDescription, pages) { + const lines = [ + `# ${title}`, + '', + frameworkDescription ? `> ${frameworkDescription}` : `> Security framework covering ${title.toLowerCase()}.`, + '', + `Full documentation: ${overviewUrl}`, + '', + '---', + '', + '## Instructions for AI Assistants', + '', + ...(folderName === 'intro' + ? [ + 'This file provides orientation about the Security Alliance (SEAL) and describes all available security frameworks. Read it when the user wants a general overview, does not know which framework to look at, or asks about SEAL itself.', + '', + '**When responding:**', + `- Make clear this information comes from the [Security Alliance Frameworks](${BASE_URL}) documentation`, + `- To answer questions about a specific framework, fetch the relevant file listed at ${BASE_URL}/llms.txt`, + `- All individual framework files are listed with descriptions at ${BASE_URL}/llms.txt`, + ] + : [ + `This is the index for the ${title} framework. The overview is included below for immediate context. For detailed content on a specific topic, fetch the relevant per-page file from the Pages list.`, + '', + '**When responding:**', + `- Reference the [${title} framework](${overviewUrl}) in your answer`, + '- Fetch a per-page file for detailed content on a specific topic — do not fetch multiple unless explicitly asked', + `- If the question spans multiple frameworks, check ${BASE_URL}/llms.txt`, + ]), + '', + '---', + '', + ]; + + // Embed the overview (first page) content + const overview = pages[0]; + if (overview) { + const sectionHeader = overview.pageTitle !== title ? overview.pageTitle : 'Overview'; + lines.push(`## ${sectionHeader}`); + lines.push(''); + lines.push(`Source: ${BASE_URL}${overview.urlPath}`); + lines.push(''); + if (overview.descriptionOverride) lines.push(overview.descriptionOverride + '\n'); + lines.push(overview.strippedContent); + lines.push(''); + lines.push('---'); + lines.push(''); + } + + // List remaining pages with links to their per-page files (first page is already embedded above) + lines.push('## Pages'); + lines.push(''); + for (const page of pages.slice(1)) { + const pageFileUrl = `${BASE_URL}/llms/${folderName}/${page.slug}.txt`; + lines.push(`- [${page.pageTitle}](${pageFileUrl})${page.description ? ` — ${page.description}` : ''}`); + } + + return lines.join('\n'); +} + +// Builds a single per-page llms file +function buildPageFile(folderName, title, overviewUrl, page) { + const lines = [ + `# ${page.pageTitle}`, + '', + ...(page.description ? [`> ${page.description}`, ''] : []), + `Source: ${BASE_URL}${page.urlPath}`, + `Framework: [${title}](${overviewUrl})`, + '', + '---', + '', + ...(page.descriptionOverride ? [page.descriptionOverride, ''] : []), + page.strippedContent, + ]; + + return lines.join('\n'); +} + +// Builds the thin routing index (llms.txt) +function buildRoutingIndex(frameworks) { + const lines = [ + '# Security Frameworks by SEAL', + '', + '> A collection of technology-agnostic security best practices to secure Web3 projects and build resilience against potential threats. Maintained by the Security Alliance (SEAL).', + '', + `Full documentation: ${BASE_URL}`, + '', + '---', + '', + '## Instructions for AI Assistants', + '', + 'To help users with a specific topic:', + '', + '1. Find the framework that best matches the question in the list below', + '2. Fetch the framework index file — it includes an overview and links to all per-page files', + '3. If you need detailed content on a specific topic, fetch the relevant per-page file', + '4. In your response, name the framework and link to its documentation', + '', + 'Do not fetch multiple framework files at once. Each framework index is self-contained.', + '', + '---', + '', + '## Frameworks', + '', + ]; + + for (const { folderName, title, description, pages } of frameworks) { + lines.push(`### ${title}`); + lines.push(`File: ${BASE_URL}/llms/${folderName}.txt`); + if (description) lines.push(`Description: ${description}`); + if (pages.length > 0) { + lines.push(`Topics: ${pages.map((p) => p.pageTitle).join(', ')}`); + } + lines.push(''); + } + + return lines.join('\n'); +} + +const distDir = findDistDir(); +if (!distDir) { + console.error('Dist directory not found - run docs:build first'); + process.exit(1); +} + +const llmsDir = path.join(distDir, 'llms'); +if (!fs.existsSync(llmsDir)) fs.mkdirSync(llmsDir); + +const frameworkFolders = getFrameworkFolders(); +const frameworkMeta = []; +let totalPageFiles = 0; + +for (const folderName of frameworkFolders) { + const title = toTitleCase(folderName); + const frameworkDescription = getFrameworkDescription(folderName); + const sidebarLinks = getSidebarLinksForFolder(folderName); + const firstLink = sidebarLinks[0]; + const overviewUrl = firstLink ? `${BASE_URL}${firstLink}` : `${BASE_URL}/${folderName}`; + + // Collect page data in sidebar order + const pages = []; + const seen = new Set(); + + for (const link of sidebarLinks) { + const filePath = linkToFilePath(link); + if (!filePath || seen.has(filePath)) continue; + seen.add(filePath); + + try { + const raw = fs.readFileSync(filePath, 'utf-8'); + const { data } = matter(raw); + if (isMainBranch && data.dev === true) continue; + + const urlPath = getPageUrl(filePath); + const pageTitle = stripSiteSuffix(data.title) || path.basename(filePath, path.extname(filePath)); + const slug = link.replace(`/${folderName}/`, '').replace(/\//g, '-'); + const h2headings = extractHeadings(raw).filter((h) => h.level === 2); + const description = data.description || (h2headings.length > 0 ? h2headings.map((h) => h.text).join(', ') : ''); + const descriptionOverride = PAGE_DESCRIPTION_OVERRIDES[urlPath] || ''; + const strippedContent = stripMdxSyntax(raw); + + pages.push({ slug, urlPath, pageTitle, description, descriptionOverride, strippedContent }); + } catch (e) { + console.error(`Error processing ${link}:`, e.message); + } + } + + // On main, skip frameworks with no publishable pages (all pages were dev-only) + if (isMainBranch && pages.length === 0) continue; + + // Write per-page files under llms/{folderName}/ — skip the first page (already embedded in the framework index) + const frameworkLlmsDir = path.join(llmsDir, folderName); + if (!fs.existsSync(frameworkLlmsDir)) fs.mkdirSync(frameworkLlmsDir); + + for (const page of pages.slice(1)) { + const content = buildPageFile(folderName, title, overviewUrl, page); + fs.writeFileSync(path.join(frameworkLlmsDir, `${page.slug}.txt`), content); + totalPageFiles++; + } + + // Write framework index under llms/{folderName}.txt + const frameworkContent = buildFrameworkIndex(folderName, title, overviewUrl, frameworkDescription, pages); + fs.writeFileSync(path.join(llmsDir, `${folderName}.txt`), frameworkContent); + + frameworkMeta.push({ folderName, title, description: frameworkDescription, pages }); +} + +// Write routing index at root +const routingIndex = buildRoutingIndex(frameworkMeta); +fs.writeFileSync(path.join(distDir, 'llms.txt'), routingIndex); + +console.log(`Done. ${frameworkFolders.length} framework index files + ${totalPageFiles} per-page files + routing index written to ${distDir}`); diff --git a/vocs.config.tsx b/vocs.config.tsx index 14c9824a6..720a21361 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -199,7 +199,7 @@ const config = { collapsed: true, dev: true, items: [ - { text: 'Overview', link: '/iam/overview' }, + { text: 'Overview', link: '/iam/overview', dev: true }, { text: 'Role-Based Access Control', link: '/iam/role-based-access-control', dev: true }, { text: 'Secure Authentication', link: '/iam/secure-authentication', dev: true }, { text: 'Access Management Best Practices', link: '/iam/access-management', dev: true }, @@ -594,6 +594,7 @@ const config = { ] }, + { text: 'LLMs', link: '/intro/llms' }, ] } ], From ce2b4fed8e6c3a9a151f7b873d0123117f499561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zealot=20=E2=80=94=20Framework=27s=20intern?= Date: Tue, 5 May 2026 14:49:29 -0300 Subject: [PATCH 26/92] fix: add Web3-specific standards (AADAPT, SCWE, EthTrust, SC Top 10) to Integration (closes #176) (#475) --- docs/pages/opsec/integration/overview.mdx | 21 +++++++++++++++------ wordlist.txt | 3 +++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/pages/opsec/integration/overview.mdx b/docs/pages/opsec/integration/overview.mdx index 5fce2a767..f69ed0604 100644 --- a/docs/pages/opsec/integration/overview.mdx +++ b/docs/pages/opsec/integration/overview.mdx @@ -1,11 +1,16 @@ --- title: "OpSec Integration | Security Alliance" -description: "Integrate OpSec with DevSecOps, privacy frameworks, and governance. Map controls to ISO 27001, NIST, CIS Controls, OWASP, and SOC 2 standards for unified Web3 security approach." +description: "Integrate OpSec with DevSecOps, privacy frameworks, and governance. Map controls to ISO 27001, NIST, CIS Controls, OWASP, SOC 2, MITRE AADAPT, OWASP SCWE, EEA EthTrust, and OWASP Smart Contract Top 10 standards for unified Web3 security approach." tags: - Security Specialist - Operations & Strategy - DevOps - SRE +contributors: + - role: wrote + users: [mattaereal] + - role: reviewed + users: [scode2277] --- import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' @@ -138,11 +143,15 @@ Aligning operational security practices with established security standards and ### Web3-Specific Standards -1. **Blockchain Security Framework**: Emerging standards for blockchain security -2. **Smart Contract Security Standard**: Best practices for contract security -3. **OWASP for Smart Contracts**: Adapting web security principles to contracts -4. **Token Security Standards**: Security standards for token implementations -5. **Cross-Chain Security Standards**: Emerging standards for cross-chain security +1. **MITRE AADAPT** — [Adversarial Tactics, Techniques, and Procedures for Digital Asset Systems](https://aadapt.mitre.org/). Modeled after MITRE ATT&CK, AADAPT catalogs real-world adversary behavior targeting blockchain and digital-asset infrastructure. It provides a structured taxonomy of tactics, techniques, and sub-techniques that helps security teams understand attacker workflows, map detections, and prioritize defenses. Use AADAPT to align OpSec threat modeling with the latest Web3 TTPs. + +2. **OWASP Smart Contract Weakness Enumeration (SCWE)** — [OWASP SCWE](https://scs.owasp.org/SCWE/). A smart-contract-specific weakness enumeration inspired by CWE. SCWE supersedes the now-outdated SWC Registry, covering all 36 SWC entries plus additional weakness classes identified since. Reference SCWE when classifying vulnerabilities found during audits, penetration tests, or formal verification to maintain a consistent, community-maintained taxonomy. + +3. **OWASP Smart Contract Top 10** — [Smart Contract Top 10](https://owasp.org/www-project-smart-contract-top-10/). An awareness standard under the OWASP Top 10 initiative that highlights the ten most critical smart contract vulnerability categories, ranked and updated yearly. Use it as a risk-prioritization checklist during design reviews and audit scoping to ensure the most impactful weaknesses receive attention first. + +4. **EEA EthTrust Security Levels v3** — [EthTrust SL v3](https://entethalliance.org/specs/ethtrust-sl/). Defines certification requirements and three assurance levels (S, M, Q) for audited smart contracts. Level S requires formal verification and comprehensive testing; Level M requires thorough manual review; Level Q covers quick-scan assessments. Map OpSec audit procedures to the appropriate EthTrust level to communicate assurance rigor to stakeholders and regulators. + +5. **Cross-Chain Security Standards**: Emerging standards for cross-chain bridge and messaging protocol security, where bridge exploits remain a dominant attack vector. ## Creating a Unified Security Approach diff --git a/wordlist.txt b/wordlist.txt index 55f292f62..92c026a8a 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -362,3 +362,6 @@ viem wagmi NCSC Intune +AADAPT +SCWE +EthTrust From d52180bdaa9e1be3369b3223a05f4d58c292d06d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zealot=20=E2=80=94=20Framework=27s=20intern?= Date: Tue, 5 May 2026 15:06:09 -0300 Subject: [PATCH 27/92] fix: expand VPN services page with HTTPS vs VPN, metadata, threat models (#473) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: expand VPN services page with HTTPS vs VPN, metadata, threat models (closes #406) * Update docs/pages/privacy/vpn-services.mdx Co-authored-by: Sara Russo * refactor: split VPN services into subcategory pages per review feedback Restructure vpn-services.mdx into a vpns/ directory with sub-pages: - overview.mdx: entrance page with intro and links to subsections - https-vs-vpn.mdx: HTTPS vs VPN comparison and metadata gap - attack-surfaces-public-networks.mdx: public Wi-Fi risks - when-to-use-vpn.mdx: threat model decision framework + choosing a VPN - vpn-limitations.mdx: VPN limitations and DNS leaks - vpn-providers-and-tools.mdx: recommended providers, tools, and resources Update vocs.config.tsx with nested sidebar structure for VPN Services. All new pages include outline: deep for subsection navigation. --------- Co-authored-by: Matías Aereal Aeón <388605+mattaereal@users.noreply.github.com> Co-authored-by: Sara Russo --- docs/pages/privacy/vpn-services.mdx | 90 ----------- .../vpns/attack-surfaces-public-networks.mdx | 60 ++++++++ docs/pages/privacy/vpns/https-vs-vpn.mdx | 73 +++++++++ docs/pages/privacy/vpns/overview.mdx | 53 +++++++ docs/pages/privacy/vpns/vpn-limitations.mdx | 54 +++++++ .../privacy/vpns/vpn-providers-and-tools.mdx | 97 ++++++++++++ docs/pages/privacy/vpns/when-to-use-vpn.mdx | 63 ++++++++ vocs.config.tsx | 13 +- wordlist.txt | 145 +++++++++--------- 9 files changed, 485 insertions(+), 163 deletions(-) delete mode 100644 docs/pages/privacy/vpn-services.mdx create mode 100644 docs/pages/privacy/vpns/attack-surfaces-public-networks.mdx create mode 100644 docs/pages/privacy/vpns/https-vs-vpn.mdx create mode 100644 docs/pages/privacy/vpns/overview.mdx create mode 100644 docs/pages/privacy/vpns/vpn-limitations.mdx create mode 100644 docs/pages/privacy/vpns/vpn-providers-and-tools.mdx create mode 100644 docs/pages/privacy/vpns/when-to-use-vpn.mdx diff --git a/docs/pages/privacy/vpn-services.mdx b/docs/pages/privacy/vpn-services.mdx deleted file mode 100644 index 0f4c3905c..000000000 --- a/docs/pages/privacy/vpn-services.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: "VPN Services | Security Alliance" -description: "Choose the right VPN: no-logs policy, AES-256 encryption, kill switch, DNS leak protection. Recommended services: MullvadVPN, ProtonVPN, NordVPN, Surfshark, and ExpressVPN compared." -tags: - - Engineer/Developer - - Security Specialist ---- - -import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../components' - - - - -# VPN Services - - - - -Virtual Private Networks (VPNs) can help increase online privacy. They encrypt your internet traffic and hide your IP -address, increasing the protection of your data from eavesdroppers and providing additional anonymity online. - -## Choosing a VPN Service - -When selecting a VPN service, consider the following factors: - -1. **Privacy Policy** - - Ensure the VPN provider has a strict no-logs policy, meaning they do not store any information about your online - activities. - -2. **Encryption Standards** - - Look for VPNs that use strong encryption standards, such as AES-256, to protect your data. - -3. **Server Locations** - - A wide range of server locations allows you to access content from different regions and improves connection - speeds. - -4. **Speed and Performance** - - Choose a VPN that offers high-speed connections and minimal impact on your browsing and streaming experience. - -5. **Security Features** - - Look for additional security features such as kill switch, DNS leak protection, and multi-hop connections. - -## Recommended VPN Services - -1. **MullvadVPN** - - **Pros**: Strong privacy policy, fast speeds, wide range of server locations, robust security features. - - **Cons**: More expensive than some competitors. - -2. **ProtonVPN** - - **Pros**: Strong focus on privacy, no-logs policy, free tier available, high security standards. - - **Cons**: Fewer servers compared to competitors, can be slower on free tier. - -3. **NordVPN** - - **Pros**: No-logs policy, strong encryption, numerous servers, additional security features (e.g., Double VPN, - CyberSec). - - **Cons**: Some servers can be slow, interface can be cluttered. - -4. **Surfshark** - - **Pros**: Affordable, no-logs policy, unlimited simultaneous connections, strong security features. - - **Cons**: Relatively new, smaller network of servers. - -5. **ExpressVPN** - - **Pros**: Strong privacy policy, fast speeds, wide range of server locations, robust security features. - - **Cons**: More expensive than some competitors. - -## Best Practices for Using a VPN - -1. **Always Connect to a VPN on Public Wi-Fi** - - Public Wi-Fi networks are often unsecured, making them prime targets for attackers. Always use a VPN when connected - to public Wi-Fi. - -2. **Enable the Kill Switch** - - A kill switch ensures that your internet connection is cut off if the VPN connection drops, preventing your data - from being exposed. - -3. **Regularly Update Your VPN Software** - - Ensure that you are using the latest version of your VPN software to benefit from the latest security updates and - features. - -4. **Use Multi-Hop Connections for Extra Security** - - Some VPNs offer multi-hop connections, which route your traffic through multiple servers for additional security. - -5. **Avoid Free VPNs** - - Free VPNs often come with limitations, such as data caps and slower speeds, and may not offer the same level of - privacy and security as paid services. - ---- - - - diff --git a/docs/pages/privacy/vpns/attack-surfaces-public-networks.mdx b/docs/pages/privacy/vpns/attack-surfaces-public-networks.mdx new file mode 100644 index 000000000..44cd78e9a --- /dev/null +++ b/docs/pages/privacy/vpns/attack-surfaces-public-networks.mdx @@ -0,0 +1,60 @@ +--- +title: "Attack Surfaces on Public Networks | Security Alliance" +description: "Risks on open Wi-Fi networks: rogue hotspots, captive portals, DNS spoofing, SSL stripping, and the dangers of mini-browsers and in-app WebViews." +tags: + - Engineer/Developer + - Security Specialist +contributors: + - role: wrote + users: [mattaereal] + - role: reviewed + users: [scode2277] +outline: deep +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' + + + + +# Attack Surfaces on Public Networks + + + + +Public and open Wi-Fi networks (airports, hotels, coffee shops) introduce specific risks: + +## Common Risks + +- **Rogue hotspot (evil twin)** — Attackers set up networks mimicking legitimate ones. Devices auto-reconnect to the + stronger, attacker-controlled SSID. +- **Captive-portal credential harvesting** — Login pages on captive portals ask for email, phone, or OAuth tokens that + can be sold or used in phishing. +- **Lateral scanning** — Open client-to-client traffic allows anyone to probe SMB, AirDrop, SSH, and other exposed + services. +- **Malicious updates** — Man-in-the-middle positions allow tampering with insecure update channels. +- **Long-term device fingerprinting** — MAC randomization has been defeated by browser and protocol fingerprinting and + stable radio attributes. + +## Less Frequent Risks + +- **DNS spoofing / rogue DHCP** — Attackers respond to DNS or DHCP requests faster than legitimate servers, pushing a + malicious resolver or gateway. +- **SSL stripping / downgrade** — Intercepting HTTP-to-HTTPS redirects. Modern browsers display warnings or block this, + but outdated or weakened browsers remain vulnerable. +- **Session hijack / cookie theft** — Sniffed session cookies might allow attackers to replay your authenticated state. + +## Unsecure Browsers and Captive Portals + +When connecting to a network with a captive portal, your OS fires a plain-HTTP request to a hard-coded URL. If it +receives anything other than the expected response, it launches a minimal browser (mini-browser) to display the portal. +These mini-browsers often lack the security features of full browsers — no extension support, limited certificate +validation, and reduced script blocking. + +Similarly, in-app browsers (WebViews) used by social media and messaging apps can inject JavaScript, share cookies with +the host app, and lack address bars for verifying origins. Both scenarios weaken the protection HTTPS normally provides. + +--- + + + diff --git a/docs/pages/privacy/vpns/https-vs-vpn.mdx b/docs/pages/privacy/vpns/https-vs-vpn.mdx new file mode 100644 index 000000000..e07a5bef8 --- /dev/null +++ b/docs/pages/privacy/vpns/https-vs-vpn.mdx @@ -0,0 +1,73 @@ +--- +title: "HTTPS vs VPN | Security Alliance" +description: "Why HTTPS does not make VPNs unnecessary. How SNI leakage, DNS query exposure, traffic analysis, and IP-as-identity create a metadata gap that only a VPN can address." +tags: + - Engineer/Developer + - Security Specialist +contributors: + - role: wrote + users: [mattaereal] + - role: reviewed + users: [scode2277] +outline: deep +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' + + + + +# HTTPS vs VPN + + + + +A common misconception is that HTTPS makes VPNs unnecessary. They solve different problems: + +- **HTTPS** encrypts the content of a single connection between your browser (or app) and a specific server. It operates + at the application layer — each connection is independently wrapped. +- **VPN** wraps all your device's traffic at the network layer into a single encrypted tunnel to a gateway you trust. + Every app, every protocol, every DNS query goes through it. + +They overlap on encryption but diverge on almost everything else: + +| Aspect | HTTPS | VPN | +|--------|-------|-----| +| What is encrypted | Content of a single connection | All traffic from the device | +| Who sees the destination | Your ISP, local network, anyone on path (via SNI) | Only the VPN provider | +| Who sees your IP | Every server you connect to | Only the VPN provider | +| DNS queries | Typically unprotected unless DoH/DoT is configured | Tunneled through the VPN (if properly configured) | +| Metadata (timing, volume, patterns) | Fully visible to the local network | Hidden from the local network, visible to the VPN provider | + +## The Metadata Gap + +Even with HTTPS everywhere, significant metadata leaks by default: + +### SNI Leakage + +The domain name is sent in plaintext during the TLS handshake. Anyone on your local network can see that you are +connecting to a specific domain, even if they cannot see the page content. Encrypted Client Hello (ECH) aims to fix +this but is not yet widely deployed. + +### DNS Query Exposure + +Unless you have explicitly configured DNS over HTTPS (DoH) or DNS over TLS (DoT), every domain you resolve is +broadcast as a plain UDP packet on port 53. Your local network and ISP see every lookup. + +### Traffic Analysis + +Packet sizes, timing, and burst patterns are often enough to infer what you are doing. Loading a video, sending a +message, or browsing a specific site all have recognizable signatures. + +### IP as Identity + +Every server you connect to over HTTPS sees your real public IP, which is tied to your ISP account, approximate +location, and in many jurisdictions, your legal identity. + +--- + +If you only care about someone reading your messages, HTTPS is enough. If you care about someone knowing what you are +doing — where you go, when, and how often — that is where a VPN earns its place. + + + diff --git a/docs/pages/privacy/vpns/overview.mdx b/docs/pages/privacy/vpns/overview.mdx new file mode 100644 index 000000000..81d342c96 --- /dev/null +++ b/docs/pages/privacy/vpns/overview.mdx @@ -0,0 +1,53 @@ +--- +title: "VPN Services | Security Alliance" +description: "Understand when a VPN is necessary, how it differs from HTTPS, what metadata it hides, DNS leaks, attack surfaces on public networks, threat models, and recommended providers and tools." +tags: + - Engineer/Developer + - Security Specialist +contributors: + - role: wrote + users: [mattaereal] + - role: reviewed + users: [scode2277] +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' + + + + +# VPN Services + + + + +Virtual Private Networks (VPNs) encrypt your internet traffic and route it through a remote server, hiding your IP +address from the websites you visit and shielding your activity from anyone on your local network. But a VPN is not a +magic shield — it solves specific problems, and understanding those problems is essential before choosing to use one. + +This section covers the key aspects of VPN usage, from understanding why HTTPS alone is not enough, to evaluating +providers and configuring tools that match your threat model. + +--- + +## Subtopics + +- **[HTTPS vs VPN](/privacy/vpns/https-vs-vpn)** — Why HTTPS does not make VPNs unnecessary. How metadata leaks even + with encrypted connections, and what a VPN actually hides. + +- **[Attack Surfaces on Public Networks](/privacy/vpns/attack-surfaces-public-networks)** — Specific risks on open + Wi-Fi networks: rogue hotspots, captive portals, DNS spoofing, and insecure mini-browsers. + +- **[When to Use a VPN](/privacy/vpns/when-to-use-vpn)** — A threat-model-based decision framework. Includes how to + choose a VPN service that fits your needs. + +- **[VPN Limitations](/privacy/vpns/vpn-limitations)** — What a VPN cannot protect you from: provider trust, DNS leaks, + TunnelVision, browser fingerprinting, and endpoint compromise. + +- **[VPN Providers and Tools](/privacy/vpns/vpn-providers-and-tools)** — Recommended no-logs providers, plus tools + organized by network, DNS, device, and browser level. + +--- + + + diff --git a/docs/pages/privacy/vpns/vpn-limitations.mdx b/docs/pages/privacy/vpns/vpn-limitations.mdx new file mode 100644 index 000000000..447823ef6 --- /dev/null +++ b/docs/pages/privacy/vpns/vpn-limitations.mdx @@ -0,0 +1,54 @@ +--- +title: "VPN Limitations | Security Alliance" +description: "What a VPN cannot protect you from: provider trust issues, DNS and IP leaks, TunnelVision attacks, browser fingerprinting, free VPN risks, and endpoint compromise." +tags: + - Engineer/Developer + - Security Specialist +contributors: + - role: wrote + users: [mattaereal] + - role: reviewed + users: [scode2277] +outline: deep +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' + + + + +# VPN Limitations + + + + +A VPN is not infallible. Consider these factors in your threat model: + +- **Provider can log or get raided** — If police serve a warrant or the company folds under subpoena, your traffic + metadata can leak. +- **DNS and IP leaks still happen** — Misconfigured clients, IPv6 routes, or split-tunnel mistakes send lookups to your + ISP in the clear. +- **TunnelVision and rogue-route attacks** — Can force traffic outside the tunnel if the local gateway manipulates DHCP + or routes. +- **Browser fingerprinting survives** — Device fonts, canvas/WebGL, and TLS quirks still identify you even with a new + IP. +- **Malicious or free VPN apps** — Many no-cost Android VPNs bundle adware or spyware. +- **Streaming and firewalls detect and block exits** — Popular services blacklist known VPN ranges. +- **Kill-switch failure reveals your real IP** — If the tunnel drops and no kill switch is active, traffic falls back to + the raw internet. +- **Protocol metadata** — WireGuard stores client IPs on the server for handshakes, a privacy trade-off. +- **Government blocking and legality** — Some countries throttle, detect, or criminalize unapproved VPNs. +- **Endpoint still vulnerable** — Malware on your laptop records keystrokes before encryption. A VPN cannot fix a + compromised device. + +## DNS Leaks + +HTTPS encrypts what you are saying, but not who you are talking to, when, how often, or how much data you are +exchanging. A DNS leak occurs when your DNS queries bypass the VPN tunnel and go directly to your ISP's resolver, +revealing the domains you visit. You can test for DNS leaks using tools like +[dnsleaktest.com](https://dnsleaktest.com/). + +--- + + + diff --git a/docs/pages/privacy/vpns/vpn-providers-and-tools.mdx b/docs/pages/privacy/vpns/vpn-providers-and-tools.mdx new file mode 100644 index 000000000..e39da4e02 --- /dev/null +++ b/docs/pages/privacy/vpns/vpn-providers-and-tools.mdx @@ -0,0 +1,97 @@ +--- +title: "VPN Providers and Tools | Security Alliance" +description: "Recommended no-logs VPN providers, plus privacy tools organized by network, DNS, device, and browser level, and verification tools to test your setup." +tags: + - Engineer/Developer + - Security Specialist +contributors: + - role: wrote + users: [mattaereal] + - role: reviewed + users: [scode2277] +outline: deep +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' + + + + +# VPN Providers and Tools + + + + +## Recommended VPN Services + +These providers are commonly recommended by privacy-focused communities. Evaluate each against your own threat model: + +1. **MullvadVPN** — Strong privacy policy, no-logs (audited), fast speeds, WireGuard support, account-number-only + registration (no email required). +2. **ProtonVPN** — Strong focus on privacy, no-logs policy, free tier available, Secure Core architecture routes + traffic through privacy-friendly jurisdictions, open-source apps. +3. **IVPN** — No-logs (audited), WireGuard and OpenVPN support, account-number-only registration, privacy-first + business model. + +> Avoid free VPNs — they often come with data caps, slower speeds, and may monetize your data or bundle malware. + +## Tools and Measures + +Select tools that match your threat model. You do not need all of these. + +### Network Level + +- **Portable travel router** — Devices like the GL.iNet Beryl or Slate run OpenWrt, let you force all traffic through a + VPN before it leaves your pocket, and isolate your devices from untrusted networks. +- **Pi-hole** — A DNS sinkhole that blocks ads and trackers at the network level. Pair it with Unbound for local + recursive DNS resolution. +- **Curated VPN providers** — Check [Privacy Guides](https://www.privacyguides.org/en/vpn/) recommended providers and + their selection criteria. +- **PiVPN or Algo** — If you do not trust any VPN provider, run your own. Algo by Trail of Bits deploys a WireGuard + server on any cloud VM in minutes. + +### DNS Level + +- **DoH or DoT** — Configure encrypted DNS at the OS level so it applies to all apps, not just your browser. +- **Cloudflare WARP (1.1.1.1)** — Free app that encrypts DNS queries and device traffic through Cloudflare's network. + Not a full VPN (does not anonymize or spoof location). +- **Mullvad DNS** — Available at 100.64.0.2 (with ad/tracker blocking) or via their DoH endpoint. No account needed. +- **AdGuard DNS** — Similar to Mullvad's offering with configurable filter lists and DoH/DoT support. Free tier + available. +- **NextDNS** — Granular custom blocklists with query logging (optional). Free up to 300k queries per month. + +### Device Level + +- **Force HTTPS-Only mode** — Safari, Firefox, Chrome, and Brave all support this. Prevents accidental HTTP connections. +- **iCloud Private Relay** — Apple's two-hop proxy for Safari traffic. Not a VPN, not Tor, but separates who you are + from where you are going. Only works in Safari on iCloud+ plans. +- **Disable WPAD** — On Windows, disable "Automatically detect settings" in proxy config. WPAD lets a local network + push a proxy configuration to your machine without asking. +- **Turn off auto-join for open networks** — On iOS and Android, disable the setting that auto-connects to known open + SSIDs. + +### Browser Level + +- **Tor Browser** — The only browser that truly defeats fingerprinting. Use it when anonymity matters. +- **Mullvad Browser** — A fork of Tor Browser with the Tor network removed, designed to be paired with a VPN or used + standalone. Trades network-level anonymity for a general-purpose privacy-hardened browser. +- **Brave** — Fingerprint randomization on by default, built-in ad blocking, and optional Tor windows. +- **Firefox with hardening** — Enable `privacy.resistFingerprinting`, HTTPS-Only mode, and a content blocker like + uBlock Origin. + +### Verification Tools + +- [EFF Cover Your Tracks](https://coveryourtracks.eff.org/) — Shows your browser fingerprint uniqueness. +- [amiunique.org](https://amiunique.org/) — Detailed fingerprint breakdown. +- [IPLeak.net](https://ipleak.net/) — Checks IP, DNS, WebRTC, and torrent IP leaks. +- [BadSSL](https://badssl.com/) — Tests your browser's TLS and certificate handling. +- [DNS Leak Test](https://dnsleaktest.com/) — Checks whether your DNS queries are leaking outside the VPN tunnel. + +## Resources + +- [Are VPNs really necessary? Is HTTPS enough?](https://blog.theredguild.org/are-vpns-really-necessary-is-https-enough/): The Red Guild article on HTTPS vs VPN, metadata leakage, and threat modeling. + +--- + + + diff --git a/docs/pages/privacy/vpns/when-to-use-vpn.mdx b/docs/pages/privacy/vpns/when-to-use-vpn.mdx new file mode 100644 index 000000000..e41c6236c --- /dev/null +++ b/docs/pages/privacy/vpns/when-to-use-vpn.mdx @@ -0,0 +1,63 @@ +--- +title: "When to Use a VPN | Security Alliance" +description: "A threat-model-based decision framework for deciding when a VPN is necessary, plus criteria for choosing a VPN service that fits your needs." +tags: + - Engineer/Developer + - Security Specialist +contributors: + - role: wrote + users: [mattaereal] + - role: reviewed + users: [scode2277] +outline: deep +--- + +import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } from '../../../../components' + + + + +# When to Use a VPN + + + + +Use this mental model to decide: + +1. **Public Wi-Fi, low risk** — HTTPS and modern browsers already block most real attacks. A VPN is optional. +2. **Public Wi-Fi, privacy matters** — HTTPS plus encrypted DNS (DoH or DoT) is the baseline most people overlook. Add + a VPN if you want to hide metadata from the local network. +3. **Hiding metadata from your network** — Add a VPN you trust, with a kill switch and no leaks. This hides + destinations, DNS queries, and traffic patterns from your ISP and local network. +4. **You are a real target** — HTTPS and commercial VPNs are table stakes, not solutions. Focus on endpoint security, + compartmentalization, and protocol-level privacy. + +The mistake is not "not using a VPN." The mistake is not knowing what you are defending against or adopting +technologies without understanding why, assuming one tool solves everything. If you do not define the threat, every tool +looks like protection. + +## Choosing a VPN Service + +When selecting a VPN service, consider the following factors: + +1. **No-logs policy** — Ensure the VPN provider does not store information about your online activities. Look for + providers that have undergone independent audits to verify this claim. + +2. **Encryption standards** — Look for VPNs that use strong encryption (AES-256-GCM or ChaCha20-Poly1305) with modern + protocols like WireGuard, which typically outperforms OpenVPN. + +3. **Kill switch** — A kill switch ensures your internet connection is cut off if the VPN connection drops, preventing + your data from being exposed. + +4. **DNS and IP leak protection** — Verify the VPN tunnels DNS queries and prevents IPv6 or WebRTC leaks. + +5. **Server locations** — A wide range of server locations allows you to access content from different regions and + improves connection speeds. + +6. **Provider trust model** — A VPN shifts trust from your ISP to the VPN provider. Evaluate the jurisdiction, ownership + structure, and track record of the provider. + +--- + + + diff --git a/vocs.config.tsx b/vocs.config.tsx index 720a21361..44221968a 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -393,7 +393,18 @@ const config = { { text: 'Encrypted Communication Tools', link: '/privacy/encrypted-communication-tools', dev: true }, { text: 'Financial Privacy Services', link: '/privacy/financial-privacy-services', dev: true }, { text: 'Privacy-Focused Operating Systems and Tools', link: '/privacy/privacy-focused-operating-systems-tools', dev: true }, - { text: 'VPN Services', link: '/privacy/vpn-services', dev: true }, + { + text: 'VPN Services', + collapsed: false, + items: [ + { text: 'Overview', link: '/privacy/vpns/overview', dev: true }, + { text: 'HTTPS vs VPN', link: '/privacy/vpns/https-vs-vpn', dev: true }, + { text: 'Attack Surfaces on Public Networks', link: '/privacy/vpns/attack-surfaces-public-networks', dev: true }, + { text: 'When to Use a VPN', link: '/privacy/vpns/when-to-use-vpn', dev: true }, + { text: 'VPN Limitations', link: '/privacy/vpns/vpn-limitations', dev: true }, + { text: 'VPN Providers and Tools', link: '/privacy/vpns/vpn-providers-and-tools', dev: true }, + ], + }, ] }, { diff --git a/wordlist.txt b/wordlist.txt index 92c026a8a..a4f247d04 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -1,7 +1,12 @@ +AccuKnox +Acuvity Aderyn adraffy +Adversa +agentic Airbnbs AKAMAI +Alchemix ampering anonymization Aublin @@ -17,9 +22,11 @@ Bitgo Bitwarden Blackcloak Bot's +Bram Bugcrowd Bybit BYOK +CAIP CCIP CCPA Certora @@ -35,6 +42,8 @@ collateralized Comms confusables Consensys +Cowork +Cronos Crypto Cryptomator CSA @@ -43,8 +52,10 @@ CSV CTF curations cvc +cyberattacks cybercrime cybercriminals +Cyberhaven Cybrary Cyfrin Cypherock @@ -54,6 +65,7 @@ DCAP Deauthorize debevoise DeFi +Degen DeIRF Dependabot Deseat @@ -64,6 +76,8 @@ discoverable DIY DLP DMARC +dmarcian +dnsleaktest DNSSEC DocuSign DPRK @@ -73,8 +87,8 @@ Dyno DYOR ECDSAP EE -EF's Efani +EF's EIP emptively engn @@ -88,22 +102,28 @@ Eramba erc Ethlint Excalidraw +exfiltrated Exfiltrating exfiltration -Forta +Fábrega Falco FDE fedcba Fi FIDO -Fábrega +firewalld +Forta Galxe Gapped Ghostery +GitHub +GitLab Gnosis +GoDaddy GOkart Golangci gosec +govulncheck Hackathon hacken Hackenproof @@ -112,6 +132,7 @@ Hetrix hevm HIDS HIPAA +Hoogenkamp HSM IAPP IAST @@ -122,11 +143,14 @@ IMEI Implementers incentivized infesable +Intune IPFS iriusrisk IRP IRT isnt +IVPN +Jamf jammer JEA JIT @@ -134,6 +158,7 @@ Jitsi Jong Juels Kapitza +Kata kdig keccak KeePassXC @@ -141,9 +166,12 @@ Kelkar KMS Kpis kyc +Lakera LDNS +ledgerhq levation lexpunk +liquidatable LLVM LUKS Mahhouk @@ -156,6 +184,7 @@ mev mgmt mimick mitre +MNGO Monero Morena mortem @@ -163,35 +192,47 @@ Mortems MPC MTTD MTTR +Mullvad MullvadVPN multisigs MVNO Mythril +NCSC +NDSS nformation nftdreww nicinfo noout Nord +Notar +nsjail oaicite OAuth Octo ofac OpenSCAP OPSEC +Opsek pagetoc +Palo +pcaversaccio PCI Pectra Pendle Pengu pentesters +permissioned PFP Phalcon +pids PII pinniped Pinnipeds plimpton Polymarket poofing +Posteo +PPPC practinces pre Predefining @@ -201,15 +242,21 @@ Provenanced prover pseudonymity pseudonymization +qube Qubes Qwertycards Rabby rata RBAC RDAP +reauthenticated +reauthentication +Reco +recoverooor redactions reentrancy reimage +Rekt renderer requestor resolvability @@ -218,6 +265,7 @@ responder Retainable Revolut ronin +rootfs saas sambacha satisfiable @@ -231,21 +279,28 @@ shorteners SIEM sig simbolik +SLSA SLYFONE SMS smt +smtpdane SOE Solana Solcurity SonarQube +Sourcify +Sourcify's Spokeo SRE srldf +SSDF +SSHFP SSL SSO Stax ster Storj +tccutil TDE teamers Terpin's @@ -253,36 +308,49 @@ tfsec themacguys Threema TLS +TLSA TOS TOTP TPM triaging +trojanized Tuta TVL +TWAP TXT unaudited +unbond +unbonding uncollateralized uncompromised unproxied Unrekt unstake unstaking +unvalidated +Upbit +UPGRADER USDS UTS +UUPS +Valimail VDI Vercel verifiability verifiably +viem Vishing Vocs VPC vyper WAF +wagmi WebAuthn WHOIS Whonix WIDS WPA +WPAD xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff xkcd XSS @@ -292,76 +360,9 @@ Yubikey Yubikeys Zcash zedt +Zenity Zerouali ZK Zksync ZWJ -cyberattacks -SSHFP -Notar -TLSA -Posteo -smtpdane -reauthenticated -reauthentication -CAIP -unbonding -unbond -Cronos -Zenity -Cowork -nsjail -unvalidated -exfiltrated -permissioned -agentic -Palo -Cyberhaven -Acuvity -Reco -AccuKnox -Adversa -Lakera -PPPC -Jamf -tccutil -pcaversaccio -recoverooor -Degen -Zyfai -SSDF -SLSA -pids -Kata -rootfs -GitHub -GitLab -GoDaddy -Opsek -Alchemix -Bram -dmarcian -firewalld -govulncheck -Hoogenkamp -ledgerhq -liquidatable -MNGO -NDSS -qube -Rekt -Sourcify -Sourcify's -trojanized -TWAP -UPGRADER -UUPS -Upbit -Valimail -viem -wagmi -NCSC -Intune -AADAPT -SCWE -EthTrust +Zyfai \ No newline at end of file From de970dfd961a428b9efcf55cb02f9a276887b534 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 15:52:22 -0300 Subject: [PATCH 28/92] chore(deps): bump the npm_and_yarn group across 1 directory with 4 updates (#464) Bumps the npm_and_yarn group with 3 updates in the / directory: [axios](https://github.com/axios/axios), [dompurify](https://github.com/cure53/DOMPurify) and [hono](https://github.com/honojs/hono). Updates `axios` from 1.14.0 to 1.15.0 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.14.0...v1.15.0) Updates `dompurify` from 3.3.3 to 3.4.1 - [Release notes](https://github.com/cure53/DOMPurify/releases) - [Commits](https://github.com/cure53/DOMPurify/compare/3.3.3...3.4.1) Updates `follow-redirects` from 1.15.11 to 1.16.0 - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.11...v1.16.0) Updates `hono` from 4.12.12 to 4.12.14 - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.12.12...v4.12.14) --- updated-dependencies: - dependency-name: axios dependency-version: 1.15.0 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: dompurify dependency-version: 3.4.1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: follow-redirects dependency-version: 1.16.0 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: hono dependency-version: 4.12.14 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 52 +++++++++++++++++++++++++------------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index ebbb550b4..7cb96e6b4 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "dependencies": { "@aws-sdk/client-s3": "3.1024.0", "@aws-sdk/lib-storage": "3.1024.0", - "axios": "1.14.0", + "axios": "1.15.0", "exceljs": "^4.4.0", "gray-matter": "^4.0.3", "just-install": "^2.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 119295410..4bc8ff5e1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: 3.1024.0 version: 3.1024.0(@aws-sdk/client-s3@3.1024.0) axios: - specifier: 1.14.0 - version: 1.14.0 + specifier: 1.15.0 + version: 1.15.0 exceljs: specifier: ^4.4.0 version: 4.4.0 @@ -2575,8 +2575,8 @@ packages: peerDependencies: postcss: ^8.1.0 - axios@1.14.0: - resolution: {integrity: sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==} + axios@1.15.0: + resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==} bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -3087,8 +3087,8 @@ packages: resolution: {integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==} hasBin: true - dompurify@3.3.3: - resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} + dompurify@3.4.1: + resolution: {integrity: sha512-JahakDAIg1gyOm7dlgWSDjV4n7Ip2PKR55NIT6jrMfIgLFgWo81vdr1/QGqWtFNRqXP9UV71oVePtjqS2ebnPw==} dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} @@ -3289,8 +3289,8 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -3406,8 +3406,8 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} engines: {node: '>= 0.4'} hast-util-classnames@3.0.0: @@ -3464,8 +3464,8 @@ packages: hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} - hono@4.12.12: - resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==} + hono@4.12.14: + resolution: {integrity: sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==} engines: {node: '>=16.9.0'} html-void-elements@3.0.0: @@ -5898,9 +5898,9 @@ snapshots: '@fortawesome/fontawesome-free@6.7.2': {} - '@hono/node-server@1.19.13(hono@4.12.12)': + '@hono/node-server@1.19.13(hono@4.12.14)': dependencies: - hono: 4.12.12 + hono: 4.12.14 '@iconify/types@2.0.0': {} @@ -7766,9 +7766,9 @@ snapshots: postcss: 8.5.6 postcss-value-parser: 4.2.0 - axios@1.14.0: + axios@1.15.0: dependencies: - follow-redirects: 1.15.11 + follow-redirects: 1.16.0 form-data: 4.0.5 proxy-from-env: 2.1.0 transitivePeerDependencies: @@ -8321,7 +8321,7 @@ snapshots: direction@2.0.1: {} - dompurify@3.3.3: + dompurify@3.4.1: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -8379,7 +8379,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.3 esast-util-from-estree@2.0.0: dependencies: @@ -8592,14 +8592,14 @@ snapshots: flatted@3.4.2: {} - follow-redirects@1.15.11: {} + follow-redirects@1.16.0: {} form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 - hasown: 2.0.2 + hasown: 2.0.3 mime-types: 2.1.35 format@0.2.2: {} @@ -8651,7 +8651,7 @@ snapshots: get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.3 math-intrinsics: 1.1.0 get-nonce@1.0.1: {} @@ -8701,7 +8701,7 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.2: + hasown@2.0.3: dependencies: function-bind: 1.1.2 @@ -8863,7 +8863,7 @@ snapshots: property-information: 7.1.0 space-separated-tokens: 2.0.2 - hono@4.12.12: {} + hono@4.12.14: {} html-void-elements@3.0.0: {} @@ -9321,7 +9321,7 @@ snapshots: d3-sankey: 0.12.3 dagre-d3-es: 7.0.13 dayjs: 1.11.19 - dompurify: 3.3.3 + dompurify: 3.4.1 katex: 0.16.25 khroma: 2.1.0 lodash-es: 4.17.21 @@ -10581,7 +10581,7 @@ snapshots: vocs@1.2.2(@types/node@24.10.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.1(react@19.2.1))(react-router-dom@7.12.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(rollup@4.59.0)(typescript@5.9.3): dependencies: '@floating-ui/react': 0.27.16(react-dom@19.2.1(react@19.2.1))(react@19.2.1) - '@hono/node-server': 1.19.13(hono@4.12.12) + '@hono/node-server': 1.19.13(hono@4.12.14) '@mdx-js/mdx': 3.1.1 '@mdx-js/react': 3.1.1(@types/react@19.2.7)(react@19.2.1) '@mdx-js/rollup': 3.1.1(rollup@4.59.0) @@ -10611,7 +10611,7 @@ snapshots: cross-spawn: 7.0.6 fs-extra: 11.3.2 hastscript: 8.0.0 - hono: 4.12.12 + hono: 4.12.14 mark.js: 8.11.1 mdast-util-directive: 3.1.0 mdast-util-from-markdown: 2.0.2 From a02cce037efceae505efda75fc25cb5da624b19e Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Tue, 5 May 2026 21:39:10 +0200 Subject: [PATCH 29/92] Weekly repo cleanup: (#465) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sync badges sync indexes sync tags fix spellchecker outputs fix linter outputs Co-authored-by: Matías Aereal Aeón <388605+mattaereal@users.noreply.github.com> --- .markdownlint.json | 6 +- docs/pages/certs/changelog.mdx | 37 ++++- docs/pages/certs/overview.mdx | 96 ++++++++---- docs/pages/certs/sfc-identity-accounts.mdx | 10 +- docs/pages/certs/sfc-incident-response.mdx | 4 +- docs/pages/certs/sfc-treasury-ops.mdx | 9 +- docs/pages/community-management/overview.mdx | 6 +- docs/pages/config/contributors.json | 98 +++++++++--- docs/pages/contribute/stewards.mdx | 3 +- docs/pages/devsecops/code-signing.mdx | 2 + ...uous-integration-continuous-deployment.mdx | 4 + docs/pages/devsecops/repository-hardening.mdx | 1 + docs/pages/devsecops/security-testing.mdx | 1 + .../third-party-script-security.mdx | 28 ++-- .../forensic-readiness.mdx | 139 +++++++++++++----- docs/pages/incident-management/index.mdx | 1 + docs/pages/intro/attack-surface.mdx | 5 +- docs/pages/intro/index.mdx | 1 + .../web3-supply-chain-threats.mdx | 8 +- .../wallet-security/tools-and-resources.mdx | 4 +- utils/fetched-tags.json | 10 ++ wordlist.txt | 18 ++- 22 files changed, 371 insertions(+), 120 deletions(-) diff --git a/.markdownlint.json b/.markdownlint.json index 048e046e3..004f5603b 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -41,10 +41,12 @@ "MermaidRenderer", "DevOnly", "BadgeLegend", - "ExportAllCerts" + "ExportAllCerts", + "AttackSurfaceDashboard" ] }, "MD037": false, "MD040": false, - "MD041": false + "MD041": false, + "MD051": false } \ No newline at end of file diff --git a/docs/pages/certs/changelog.mdx b/docs/pages/certs/changelog.mdx index e795af272..f1625bbb6 100644 --- a/docs/pages/certs/changelog.mdx +++ b/docs/pages/certs/changelog.mdx @@ -14,7 +14,8 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter } fr # SEAL Certifications Changelog -Revision history across all SFC certifications. Each cert carries a `version` in its page header; this page aggregates what changed, when, and why so protocols re-certifying after a revision can scope the delta quickly. +Revision history across all SFC certifications. Each cert carries a `version` in its page header; this page +aggregates what changed, when, and why so protocols re-certifying after a revision can scope the delta quickly. --- @@ -22,11 +23,20 @@ Revision history across all SFC certifications. Each cert carries a `version` in ### New -- **[SFC - Identity & Accounts](/certs/sfc-identity-accounts)** (v1.0): new horizontal cert covering organizational account management — inventory (social media, email, SSO, registrar, custody, repo admin, cloud root, SaaS), phishing-resistant MFA, password manager with individual accountability, recovery methods restricted to org channels, account lifecycle, takeover monitoring, third-party access. Addresses a gap where org-account takeover (Twitter, status page, registrar) was not covered despite being a top-tier attack vector for crypto protocols. +- **[SFC - Identity & Accounts](/certs/sfc-identity-accounts)** (v1.0): new horizontal cert covering + organizational account management — inventory (social media, email, SSO, registrar, custody, repo + admin, cloud root, SaaS), phishing-resistant MFA, password manager with individual accountability, + recovery methods restricted to org channels, account lifecycle, takeover monitoring, third-party + access. Addresses a gap where org-account takeover (Twitter, status page, registrar) was not covered + despite being a top-tier attack vector for crypto protocols. ### Retired -- **SFC - Workspace Security**: retired. Its crypto-relevant content (accounts, credentials, MFA, takeover monitoring) moved to the new Identity & Accounts cert. Content on device management, EDR/MDM, BYOD, physical/travel security, formal training programs with phishing sims, insider threat assessment as standalone, and data classification was intentionally dropped as out of SEAL SME and better covered by ISO 27001 / SOC 2 / CIS. +- **SFC - Workspace Security**: retired. Its crypto-relevant content (accounts, credentials, MFA, + takeover monitoring) moved to the new Identity & Accounts cert. Content on device management, + EDR/MDM, BYOD, physical/travel security, formal training programs with phishing sims, insider threat + assessment as standalone, and data classification was intentionally dropped as out of SEAL SME and + better covered by ISO 27001 / SOC 2 / CIS. ### SFC - Multisig Operations (v1.0 → v1.1) @@ -54,7 +64,8 @@ Revision history across all SFC certifications. Each cert carries a `version` in ### SFC - DevOps & Infrastructure (v1.0 → v1.1) - `di-1.1.2` supply-chain mention dropped from the baseline (supply chain is already covered substantively in Section 2). -- `di-1.1.4` rewritten to cover both the tool approval process and the maintained approved-tools list in a single control; list review cadence made explicit. +- `di-1.1.4` rewritten to cover both the tool approval process and the maintained approved-tools list + in a single control; list review cadence made explicit. - `di-2.1.1` repo access review cadence tightened; account controls now reference SFC - Identity & Accounts. - `di-2.1.4` dependencies consolidated from 6 bullets to 3. - `di-3.1.1` pipeline runner hardening bullet added; consolidated to 5 bullets. @@ -71,18 +82,28 @@ Revision history across all SFC certifications. Each cert carries a `version` in - Header pointer to SFC - Identity & Accounts added for org account takeover coordination. - `ir-1.1.1` IR team roles consolidated from 7 bullets to 3. - `ir-1.1.2` IR contacts consolidated from 7 bullets to 4. -- `ir-2.1.1` **(NEW)**: threat model for protocol operations, including external dependencies and single points of failure (cross-chain messaging providers, oracle providers, critical infrastructure). Placed before monitoring coverage so monitoring is anchored to a known threat picture. Existing Section 2 controls shifted: previous `ir-2.1.1` → `ir-2.1.2`, `ir-2.1.2` → `ir-2.1.3`, `ir-2.1.3` → `ir-2.1.4`. +- `ir-2.1.1` **(NEW)**: threat model for protocol operations, including external dependencies and + single points of failure (cross-chain messaging providers, oracle providers, critical infrastructure). + Placed before monitoring coverage so monitoring is anchored to a known threat picture. Existing + Section 2 controls shifted: previous `ir-2.1.1` → `ir-2.1.2`, `ir-2.1.2` → `ir-2.1.3`, + `ir-2.1.3` → `ir-2.1.4`. - `ir-2.1.3` alerting and paging (previously `ir-2.1.2`) consolidated from 8 bullets to 4. - `ir-5.1.1` IR drills consolidated from 7 bullets to 4. ### Cross-cutting - Control IDs now rendered next to the title in each control card (UI improvement). -- Account-control pattern (MFA, credential management, access reviews, lifecycle) de-duplicated out of vertical certs; Identity & Accounts is the authoritative source. DNS and DevOps reference it directly; Treasury and Incident Response carry a header pointer and retain domain-specific bullets. +- Account-control pattern (MFA, credential management, access reviews, lifecycle) de-duplicated out of + vertical certs; Identity & Accounts is the authoritative source. DNS and DevOps reference it + directly; Treasury and Incident Response carry a header pointer and retain domain-specific bullets. ### Workbook compatibility -- Control IDs are stable across this revision. No renames. New controls (`tro-2.1.3`, `tro-3.1.5`, `ir-2.1.1`, all `ida-*`) will simply be unpopulated when importing old workbooks, which is expected. Shifted IR Section 2 IDs mean old `ir-2.1.1`/`ir-2.1.2`/`ir-2.1.3` data in workbooks will not align to the new IDs without manual re-mapping. -- Users with saved state for Workspace Security will lose that state (that cert is removed). Other certs retain their localStorage state across the revision. +- Control IDs are stable across this revision. No renames. New controls (`tro-2.1.3`, `tro-3.1.5`, + `ir-2.1.1`, all `ida-*`) will simply be unpopulated when importing old workbooks, which is expected. + Shifted IR Section 2 IDs mean old `ir-2.1.1`/`ir-2.1.2`/`ir-2.1.3` data in workbooks will not + align to the new IDs without manual re-mapping. +- Users with saved state for Workspace Security will lose that state (that cert is removed). Other + certs retain their localStorage state across the revision. diff --git a/docs/pages/certs/overview.mdx b/docs/pages/certs/overview.mdx index 390953000..cbc002836 100644 --- a/docs/pages/certs/overview.mdx +++ b/docs/pages/certs/overview.mdx @@ -16,9 +16,15 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter, Exp -SEAL Certifications is an open-source operational security certification program for crypto protocols. Smart contract vulnerabilities were once the dominant cause of crypto exploits; operational failures now dominate. Compromised signers, poorly managed multisigs, DNS takeovers, leaked credentials, unmonitored infrastructure, and missing incident response playbooks account for the majority of major incidents. Code audits don't catch these, because the code isn't the issue. +SEAL Certifications is an open-source operational security certification program for crypto protocols. +Smart contract vulnerabilities were once the dominant cause of crypto exploits; operational failures now +dominate. Compromised signers, poorly managed multisigs, DNS takeovers, leaked credentials, unmonitored +infrastructure, and missing incident response playbooks account for the majority of major incidents. Code +audits don't catch these, because the code isn't the issue. -SEAL Certifications target exactly this gap. The framework evaluates the operational practices that determine whether a protocol can actually defend itself, detect an incident, and respond when things go wrong. +SEAL Certifications target exactly this gap. The framework evaluates the operational practices that +determine whether a protocol can actually defend itself, detect an incident, and respond when things go +wrong. ## Certification Modules @@ -26,82 +32,114 @@ The framework covers six modules, each independently scopeable: -- **[Multisig Ops](/certs/sfc-multisig-ops)** — Governance, signer security, transaction verification, emergency procedures -- **[Treasury Ops](/certs/sfc-treasury-ops)** — Treasury architecture, transaction security, custody, DeFi risk management -- **[Incident Response](/certs/sfc-incident-response)** — Threat modeling, monitoring, response playbooks, drills -- **[DevOps & Infrastructure](/certs/sfc-devops-infrastructure)** — Development environment, source code, CI/CD, cloud infrastructure, supply chain -- **[DNS & Registrar](/certs/sfc-dns-registrar)** — Domain management, DNS controls, registrar security, email authentication -- **[Identity & Accounts](/certs/sfc-identity-accounts)** — Organizational account inventory, phishing-resistant MFA, credential management, takeover monitoring +- **[Multisig Ops](/certs/sfc-multisig-ops)** — Governance, signer security, transaction verification, + emergency procedures +- **[Treasury Ops](/certs/sfc-treasury-ops)** — Treasury architecture, transaction security, custody, + DeFi risk management +- **[Incident Response](/certs/sfc-incident-response)** — Threat modeling, monitoring, response + playbooks, drills +- **[DevOps & Infrastructure](/certs/sfc-devops-infrastructure)** — Development environment, source + code, CI/CD, cloud infrastructure, supply chain +- **[DNS & Registrar](/certs/sfc-dns-registrar)** — Domain management, DNS controls, registrar security, + email authentication +- **[Identity & Accounts](/certs/sfc-identity-accounts)** — Organizational account inventory, + phishing-resistant MFA, credential management, takeover monitoring See the [changelog](/certs/changelog) for revision history across modules. ## How Certification Works -SEAL maintains the framework and accredits auditing firms. Accredited firms perform the assessments; SEAL issues the on-chain certification. +SEAL maintains the framework and accredits auditing firms. Accredited firms perform the assessments; +SEAL issues the on-chain certification. An engagement runs through five steps: 1. **Scoping.** Align on which controls apply and what infrastructure is in scope. -2. **Evidence collection.** The protocol team gathers documentation and evidence that their practices meet the framework controls. +2. **Evidence collection.** The protocol team gathers documentation and evidence that their practices + meet the framework controls. 3. **Assessment.** The firm reviews evidence against the open-source framework criteria. -4. **Remediation** (if needed). The firm provides recommendations to close any gaps. The protocol team implements fixes. -5. **Certification.** Protocols that meet the standard receive a formal on-chain attestation via the Ethereum Attestation Service (EAS), publicly and cryptographically verifiable. +4. **Remediation** (if needed). The firm provides recommendations to close any gaps. The protocol team + implements fixes. +5. **Certification.** Protocols that meet the standard receive a formal on-chain attestation via the + Ethereum Attestation Service (EAS), publicly and cryptographically verifiable. -A typical engagement runs a few weeks. Protocols can also use the framework independently for self-assessment at any time. +A typical engagement runs a few weeks. Protocols can also use the framework independently for +self-assessment at any time. -See [Certification Guidelines](/certs/certification-guidelines) for the full process and [Certified Auditors](/certs/certified-partners) for accredited firms. +See [Certification Guidelines](/certs/certification-guidelines) for the full process and +[Certified Auditors](/certs/certified-partners) for accredited firms. ## Program Status -The framework has been validated through pilot analyses with protocols across DeFi, staking, treasury management, and infrastructure, plus feedback sessions with auditing firms and independent security researchers. Controls have been refined through that process and are now published. +The framework has been validated through pilot analyses with protocols across DeFi, staking, treasury +management, and infrastructure, plus feedback sessions with auditing firms and independent security +researchers. Controls have been refined through that process and are now published. -The program is moving from pilot validation into active certification, starting with supervised first engagements through accredited firms. +The program is moving from pilot validation into active certification, starting with supervised first +engagements through accredited firms. ## Get Involved -- **Protocols** interested in a certification engagement or a consultation with the SEAL team on the framework: [sign up here](https://securityalliance.typeform.com/CertsWaitlist). -- **Auditing firms and independent security researchers** interested in becoming an accredited third-party certification issuer: [apply here](https://securityalliance.typeform.com/CertsAuditor). +- **Protocols** interested in a certification engagement or a consultation with the SEAL team on the + framework: [sign up here](https://securityalliance.typeform.com/CertsWaitlist). +- **Auditing firms and independent security researchers** interested in becoming an accredited + third-party certification issuer: [apply here](https://securityalliance.typeform.com/CertsAuditor). ## FAQ
What's the difference between self-assessments and certified audits? -Self-assessments are completed by the protocol team using the SEAL Certifications framework as a guide. They help protocols internally evaluate their own security posture and identify gaps. Self-assessments are not verified by a third party. +Self-assessments are completed by the protocol team using the SEAL Certifications framework as a guide. +They help protocols internally evaluate their own security posture and identify gaps. Self-assessments +are not verified by a third party. -Certified audits are completed by a third-party accredited firm through SEAL's [partner program](/certs/certified-partners). The firm independently evaluates the protocol's security controls against the framework. Upon successful completion, protocols receive a formal attestation on-chain. +Certified audits are completed by a third-party accredited firm through SEAL's +[partner program](/certs/certified-partners). The firm independently evaluates the protocol's security +controls against the framework. Upon successful completion, protocols receive a formal attestation +on-chain.
What is an attestation? -Attestations are certificates issued on-chain through the [Ethereum Attestation Service (EAS)](https://ethereum.org/en/developers/docs/standards/tokens/eas/) by SEAL to protocols that successfully complete a certified audit. Attestations serve as verifiable proof that a protocol has met the requirements of a given SEAL certification. +Attestations are certificates issued on-chain through the +[Ethereum Attestation Service (EAS)](https://ethereum.org/en/developers/docs/standards/tokens/eas/) +by SEAL to protocols that successfully complete a certified audit. Attestations serve as verifiable +proof that a protocol has met the requirements of a given SEAL certification. -Attestations do not indicate a protocol is free from security issues. Blockchain security evolves continuously and novel vulnerabilities arise regularly. Attestations demonstrate that a protocol has implemented a set of standardized operational practices to manage and mitigate risk. +Attestations do not indicate a protocol is free from security issues. Blockchain security evolves +continuously and novel vulnerabilities arise regularly. Attestations demonstrate that a protocol has +implemented a set of standardized operational practices to manage and mitigate risk.
What if a protocol doesn't meet all the certification requirements? -Protocols that don't meet all requirements receive a report from the auditor detailing the gaps. If the protocol addresses the gaps, they can work with the auditor to complete a re-assessment. +Protocols that don't meet all requirements receive a report from the auditor detailing the gaps. If the +protocol addresses the gaps, they can work with the auditor to complete a re-assessment.
What kind of evidence is required? -Evidence varies by certification and control. Protocols generally provide documentation, screenshots, or other artifacts demonstrating implementation of each control: a signer registry for multisig, incident response playbooks, DNS configuration records, and so on. +Evidence varies by certification and control. Protocols generally provide documentation, screenshots, or +other artifacts demonstrating implementation of each control: a signer registry for multisig, incident +response playbooks, DNS configuration records, and so on.
Who can see our attestation? -Attestations are publicly accessible on-chain through EAS. Detailed audit reports and evidence shared during the assessment process are confidential between the protocol and the auditor. +Attestations are publicly accessible on-chain through EAS. Detailed audit reports and evidence shared +during the assessment process are confidential between the protocol and the auditor.
Can we use the SEAL badge? -Protocols that successfully complete a certified audit receive a badge from SEAL to display on their website or documentation. +Protocols that successfully complete a certified audit receive a badge from SEAL to display on their +website or documentation.
@@ -113,13 +151,15 @@ SEAL will maintain a public list of certified protocols as the program launches.
How can auditors become accredited? -SEAL accredits third-party auditing firms through a supervised first engagement. See [Certified Auditors](/certs/certified-partners) for the process. +SEAL accredits third-party auditing firms through a supervised first engagement. See +[Certified Auditors](/certs/certified-partners) for the process.
Can a protocol lose its certification? -Yes. Certifications can be revoked if a protocol is found to be non-compliant for an extended period. Certifications are also time-limited and require periodic re-assessment. +Yes. Certifications can be revoked if a protocol is found to be non-compliant for an extended period. +Certifications are also time-limited and require periodic re-assessment.
diff --git a/docs/pages/certs/sfc-identity-accounts.mdx b/docs/pages/certs/sfc-identity-accounts.mdx index fefb439f6..81641f104 100644 --- a/docs/pages/certs/sfc-identity-accounts.mdx +++ b/docs/pages/certs/sfc-identity-accounts.mdx @@ -126,9 +126,15 @@ import { TagList, AttributionList, TagProvider, TagFilter, ContributeFooter, Cer *Revision {frontmatter.version} · Updated {frontmatter.revised} · [Changelog](/certs/changelog)* -The SEAL Framework Checklist (SFC) for Identity & Accounts provides guidelines for managing organizational accounts (social media, email, SSO, registrar, SaaS, privileged platforms) such that account compromise cannot redirect users, funds, or trust. +The SEAL Framework Checklist (SFC) for Identity & Accounts provides guidelines for managing +organizational accounts (social media, email, SSO, registrar, SaaS, privileged platforms) such that +account compromise cannot redirect users, funds, or trust. -**Scope**: This is a horizontal cert covering the cross-cutting account-management pattern that applies to multiple surfaces (domains, code, custody, social media). Other certs reference it for account-control concerns while retaining their domain-specific substance (e.g., [SFC - DNS Registrar](/certs/sfc-dns-registrar) handles DNSSEC and registry locks; this cert handles registrar account hygiene). +**Scope**: This is a horizontal cert covering the cross-cutting account-management pattern that applies +to multiple surfaces (domains, code, custody, social media). Other certs reference it for +account-control concerns while retaining their domain-specific substance (e.g., +[SFC - DNS Registrar](/certs/sfc-dns-registrar) handles DNSSEC and registry locks; this cert handles +registrar account hygiene). For more details on certifications or self-assessments, refer to the [Certification Guidelines](/certs/certification-guidelines). diff --git a/docs/pages/certs/sfc-incident-response.mdx b/docs/pages/certs/sfc-incident-response.mdx index 847d75c56..f5b67bc91 100644 --- a/docs/pages/certs/sfc-incident-response.mdx +++ b/docs/pages/certs/sfc-incident-response.mdx @@ -211,7 +211,9 @@ The SEAL Framework Checklist (SFC) for Incident Response provides structured gui security incidents affecting blockchain protocols. It covers team structure, monitoring, alerting, and response procedures. -**Related**: Organizational account takeover monitoring and response coordinates with [SFC - Identity & Accounts](/certs/sfc-identity-accounts) (ida-4.1.1). IR handles incident response flow; I&A handles account-control baselines. +**Related**: Organizational account takeover monitoring and response coordinates with +[SFC - Identity & Accounts](/certs/sfc-identity-accounts) (ida-4.1.1). IR handles incident response +flow; I&A handles account-control baselines. For more details on certifications or self-assessments, refer to the [Certification Guidelines](/certs/certification-guidelines). diff --git a/docs/pages/certs/sfc-treasury-ops.mdx b/docs/pages/certs/sfc-treasury-ops.mdx index 93c83fda3..2f0769f66 100644 --- a/docs/pages/certs/sfc-treasury-ops.mdx +++ b/docs/pages/certs/sfc-treasury-ops.mdx @@ -325,9 +325,14 @@ The SEAL Framework Checklist (SFC) for Treasury Operations provides structured g operating an organization's treasury covering governance, access control, transaction security, monitoring, and vendor management. -**Scope note**: These baselines target internal treasury operations (DAO treasuries, protocol treasuries, operational wallets). Professional treasury operations such as OTC desks, market-making, or custody-as-a-service may warrant additional or differentiated baselines beyond this scope. +**Scope note**: These baselines target internal treasury operations (DAO treasuries, protocol +treasuries, operational wallets). Professional treasury operations such as OTC desks, market-making, +or custody-as-a-service may warrant additional or differentiated baselines beyond this scope. -**Related**: Custody platform account management (phishing-resistant MFA, credential management, recovery methods, account lifecycle) is governed by [SFC - Identity & Accounts](/certs/sfc-identity-accounts). Treasury-specific controls below focus on transaction security and platform configuration. +**Related**: Custody platform account management (phishing-resistant MFA, credential management, +recovery methods, account lifecycle) is governed by +[SFC - Identity & Accounts](/certs/sfc-identity-accounts). Treasury-specific controls below focus on +transaction security and platform configuration. For more details on certifications or self-assessments, refer to the [Certification Guidelines](/certs/certification-guidelines). diff --git a/docs/pages/community-management/overview.mdx b/docs/pages/community-management/overview.mdx index 2acb2a587..4e2be4082 100644 --- a/docs/pages/community-management/overview.mdx +++ b/docs/pages/community-management/overview.mdx @@ -55,10 +55,12 @@ platform-specific recommendations in more depth. - Clearly communicate to community members that your team will never send the first direct message to them. This is important because attackers often impersonate team members and initiate direct messages to trick users into believing they are legitimate, thereby gaining their trust and potentially compromising their security. - However, statements such as “will never DM first” or labels like “Official,” “Support,” or platform status indicators (e.g., premium badges) must not be treated as proof of legitimacy. + However, statements such as “will never DM first” or labels like “Official,” “Support,” or platform + status indicators (e.g., premium badges) must not be treated as proof of legitimacy. - Publicly define all official communication channels and clearly state which platforms are not used. If a platform is unsupported, declare this alongside official links (e.g., “We do not operate a Telegram community”). -Where possible, reserve common impersonation handles and maintain placeholder accounts that redirect users to official channels. +Where possible, reserve common impersonation handles and maintain placeholder accounts that redirect +users to official channels. Refer to the [**Security Awareness framework**](/awareness/overview) to learn more about social engineering techniques and security training best practices. diff --git a/docs/pages/config/contributors.json b/docs/pages/config/contributors.json index b39892752..3e625c21a 100644 --- a/docs/pages/config/contributors.json +++ b/docs/pages/config/contributors.json @@ -7,8 +7,8 @@ "twitter": "https://twitter.com/mattaereal", "website": "https://theredguild.org", "company": "The Red Guild | SEAL", - "role": "lead", "job_title": "Security Knowmad", + "role": "lead", "description": "Initiative lead and maintainer", "badges": [ { "name": "Lead", "assigned": "2024-06-06" }, @@ -23,7 +23,7 @@ { "name": "Issue-Opener-5", "assigned": "2024-08-22" }, { "name": "Issue-Opener-10", "assigned": "2024-08-24" }, { "name": "Issue-Opener-25", "assigned": "2024-09-25" }, - { "name": "Active-Last-7d", "lastActive": "2026-04-08" } + { "name": "Active-Last-7d", "lastActive": "2026-04-23" } ] }, "fredriksvantes": { @@ -76,8 +76,8 @@ "website": null, "company": "PocketUniverseZ", "job_title": "Security Researcher", - "description": "Steward of Community Management framework", "role": "steward", + "description": "Steward of Community Management framework", "badges": [ { "name": "Framework-Steward", "framework": "Community Management" }, { "name": "First-Contribution", "assigned": "2025-01-29" }, @@ -93,8 +93,8 @@ "website": "https://robertmacwha.ca/", "company": "Skylock | SEAL", "job_title": "Full-stack developer and security enthusiast", - "description": "Web dev & fact checking", "role": "core", + "description": "Web dev & fact checking", "badges": [ { "name": "Core-Contributor", "assigned": "2025-03-20" }, { "name": "Early-Contributor", "assigned": "2024-10-07" }, @@ -191,7 +191,7 @@ { "name": "Framework-Steward", "assigned": "2025-07-10", "framework": "Wallet Security" }, { "name": "First-Contribution", "assigned": "2025-07-10" }, { "name": "First-Review", "assigned": "2025-09-25" }, - { "name": "Active-Last-7d", "lastActive": "2026-04-07" } + { "name": "Active-Last-30d", "lastActive": "2026-04-07" } ] }, "njelich": { @@ -231,7 +231,7 @@ { "name": "First-Review", "assigned": "2025-08-11" }, { "name": "Reviewer-10", "assigned": "2026-02-24" }, { "name": "Reviewer-25", "assigned": "2024-03-01" }, - { "name": "Active-Last-7d", "lastActive": "2026-04-06" } + { "name": "Active-Last-7d", "lastActive": "2026-04-20" } ] }, "blackbigswan": { @@ -337,7 +337,7 @@ "description": "Founder & Engineer", "badges": [ { "name": "First-Contribution", "assigned": "2025-06-02" }, - { "name": "Dormant-90d+", "lastActive": "2025-06-02" } + { "name": "Active-Last-7d", "lastActive": "2026-04-21" } ] }, "isaac": { @@ -353,7 +353,9 @@ "description": "SEAL Certs & SEAL Wargames", "badges": [ { "name": "Framework-Steward", "assigned": "2025-12-17", "framework": "SEAL Certs" }, - { "name": "First-Review", "assigned": "2026-01-26" } + { "name": "First-Contribution", "assigned": "2026-04-21" }, + { "name": "First-Review", "assigned": "2026-01-26" }, + { "name": "Active-Last-7d", "lastActive": "2026-04-21" } ] }, "geoffrey": { @@ -416,14 +418,15 @@ "auditware": { "slug": "auditware", "name": "Auditware", - "role": "contributor", "avatar": "https://avatars.githubusercontent.com/auditware", "github": "https://github.com/Auditware", "twitter": "https://x.com/audit_wizard", "website": "https://www.auditware.io/", "company": "Auditware", "job_title": null, - "description": "Industry leading OpSec audits, security tools, and code reviews performed by true security wizards" + "role": "contributor", + "description": "Industry leading OpSec audits, security tools, and code reviews performed by true security wizards", + "badges": [] }, "scode2277": { "slug": "scode2277", @@ -431,8 +434,8 @@ "avatar": "https://avatars.githubusercontent.com/scode2277", "github": "https://github.com/scode2277", "twitter": "https://x.com/emit_sara", - "company": "SEAL", "website": "https://securityalliance.org", + "company": "SEAL", "job_title": "Security Researcher", "role": "core", "description": "Security Researcher & SEAL Frameworks Core Team", @@ -445,23 +448,22 @@ { "name": "First-Review", "assigned": "2025-08-12" }, { "name": "Reviewer-10", "assigned": "2025-09-12" }, { "name": "Reviewer-25", "assigned": "2026-03-20" }, - { "name": "Active-Last-7d", "lastActive": "2026-04-09" } + { "name": "Active-Last-7d", "lastActive": "2026-04-23" } ] }, "gunnim": { "slug": "gunnim", "name": "Gunnar Óttarsson", - "role": "contributor", "avatar": "https://avatars.githubusercontent.com/gunnim", "github": "https://github.com/gunnim", "twitter": "https://x.com/gunni_m", "website": "https://SolAz.cloud", "company": "SolAz", "job_title": "Founder", + "role": "contributor", "description": "Cloud architecture enthusiast with a passion for IT Security", "badges": [ - { "name": "First-Contribution", "assigned": "2026-01-21" }, - { "name": "Active-Last-30d", "lastActive": "2026-03-21" } + { "name": "First-Contribution", "assigned": "2026-01-21" } ] }, "madjin": { @@ -491,7 +493,8 @@ "role": "contributor", "description": "Frameworks Contributor", "badges": [ - { "name": "First-Contribution", "assigned": "2026-01-21" } + { "name": "First-Contribution", "assigned": "2026-01-21" }, + { "name": "Dormant-90d+", "lastActive": "2026-01-21" } ] }, "munamwasi": { @@ -659,13 +662,13 @@ "twitter": "https://twitter.com/JosepBove", "website": null, "company": "OP Labs", - "role": "steward", "job_title": null, + "role": "steward", "description": "Steward of Monitoring framework", "badges": [ { "name": "Framework-Steward", "assigned": "2026-03-17", "framework": "Monitoring" }, { "name": "First-Contribution", "assigned": "2026-03-16" }, - { "name": "Active-Last-30d", "lastActive": "2026-03-23" } + { "name": "Active-Last-30d", "lastActive": "2026-04-16" } ] }, "tim-sha256": { @@ -681,8 +684,7 @@ "description": "Frameworks Contributor", "badges": [ { "name": "First-Contribution", "assigned": "2026-04-05" }, - { "name": "New-Joiner", "assigned": "2026-04-05" }, - { "name": "Active-Last-7d", "lastActive": "2026-04-05" } + { "name": "Active-Last-7d", "lastActive": "2026-04-21" } ] }, "fvelazquez-x": { @@ -698,8 +700,7 @@ "description": "Frameworks Contributor", "badges": [ { "name": "First-Contribution", "assigned": "2026-04-07" }, - { "name": "New-Joiner", "assigned": "2026-04-07" }, - { "name": "Active-Last-7d", "lastActive": "2026-04-07" } + { "name": "Active-Last-30d", "lastActive": "2026-04-07" } ] }, "shallem": { @@ -710,13 +711,62 @@ "twitter": "https://x.com/seth_certora", "website": "https://www.certora.com/", "company": "Certora", - "role": "steward", "job_title": null, + "role": "steward", "description": "Steward of Opsec framework", "badges": [ { "name": "Framework-Steward", "assigned": "2026-04-09", "framework": "Operational Security" }, { "name": "First-Contribution", "assigned": "2025-09-10" }, - { "name": "Active-Last-7d", "lastActive": "2026-04-09" } + { "name": "Active-Last-30d", "lastActive": "2026-04-09" } + ] + }, + "txscope-sol": { + "slug": "txscope-sol", + "name": "txscope-sol", + "avatar": "https://avatars.githubusercontent.com/txscope-sol", + "github": "https://github.com/txscope-sol", + "twitter": null, + "website": null, + "company": null, + "job_title": null, + "role": "contributor", + "description": "Frameworks Contributor", + "badges": [ + { "name": "First-Contribution", "assigned": "2026-04-21" }, + { "name": "New-Joiner", "lastActive": "2026-04-21" }, + { "name": "Active-Last-7d", "lastActive": "2026-04-21" } + ] + }, + "welttowelt": { + "slug": "welttowelt", + "name": "welttowelt", + "avatar": "https://avatars.githubusercontent.com/welttowelt", + "github": "https://github.com/welttowelt", + "twitter": null, + "website": null, + "company": null, + "job_title": null, + "role": "contributor", + "description": "Frameworks Contributor", + "badges": [ + { "name": "First-Contribution", "assigned": "2026-04-21" }, + { "name": "New-Joiner", "lastActive": "2026-04-21" }, + { "name": "Active-Last-7d", "lastActive": "2026-04-21" } + ] + }, + "iam0ti": { + "slug": "iam0ti", + "name": "iam0ti", + "avatar": "https://avatars.githubusercontent.com/iam0ti", + "github": "https://github.com/iam0ti", + "twitter": null, + "website": null, + "company": null, + "job_title": null, + "role": "contributor", + "description": "Frameworks Contributor", + "badges": [ + { "name": "First-Contribution", "assigned": "2026-03-10" } ] } } diff --git a/docs/pages/contribute/stewards.mdx b/docs/pages/contribute/stewards.mdx index 050927bff..927fc85e7 100644 --- a/docs/pages/contribute/stewards.mdx +++ b/docs/pages/contribute/stewards.mdx @@ -48,7 +48,8 @@ There is no application form. The typical path looks like this: 2. We notice the quality and depth of that work, or the contributor reaches out expressing interest in a more active role. 3. If there is mutual trust and the fit is clear, we designate them as steward: they are - added to the [contributors database](https://github.com/security-alliance/frameworks/blob/develop/docs/pages/config/contributors.json) with the `Framework-Steward` badge, and they get their + added to the [contributors database](https://github.com/security-alliance/frameworks/blob/develop/docs/pages/config/contributors.json) + with the `Framework-Steward` badge, and they get their own entry in the [Spotlight Zone](/contribute/spotlight-zone), the public-facing recognition page where stewards are listed alongside the frameworks they own. diff --git a/docs/pages/devsecops/code-signing.mdx b/docs/pages/devsecops/code-signing.mdx index ea18599e6..3f5eba518 100644 --- a/docs/pages/devsecops/code-signing.mdx +++ b/docs/pages/devsecops/code-signing.mdx @@ -166,6 +166,7 @@ Without proper backup, a lost key means lost identity. Without secure storage, a stolen key means forged commits. **Backup the master key:** + ```bash # Export to an encrypted file gpg --export-secret-keys --armor YOUR_KEYID | \ @@ -177,6 +178,7 @@ gpg --export-secret-keys --armor YOUR_KEYID | \ ``` **Generate and store revocation certificates immediately:** + ```bash gpg --gen-revoke YOUR_KEYID > revocation-certificate.asc # Store this certificate alongside the backup. If you lose access to the key, diff --git a/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx b/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx index 1e86fc218..db832bbc0 100644 --- a/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx +++ b/docs/pages/devsecops/continuous-integration-continuous-deployment.mdx @@ -127,12 +127,14 @@ Who can modify CI workflows determines who can alter what runs in the pipeline. enforce this: `/.github/workflows/ @security-team @devops-lead`. - Pin third-party GitHub Actions by commit SHA, not by tag. Tags are mutable: `v1` can be retagged to point to malicious code. + ```yaml # Unsafe: tag can be changed - uses: actions/checkout@v4 # Safe: pinned by SHA - uses: actions/checkout@b4ffde65f46336ab88eb53be80866792576f8620 ``` + - Restrict `GITHUB_TOKEN` permissions to least privilege: set `permissions: {}` at the workflow level and grant only what each job needs. @@ -233,6 +235,7 @@ Long-lived cloud credentials in CI are a high-value target. OpenID Connect (OIDC eliminates them entirely by granting short-lived, scoped tokens on demand. **GitHub Actions → AWS:** + ```yaml # In your workflow - name: Configure AWS credentials @@ -245,6 +248,7 @@ eliminates them entirely by granting short-lived, scoped tokens on demand. ``` **AWS side (trust policy for the IAM role):** + ```json { "Version": "2012-10-17", diff --git a/docs/pages/devsecops/repository-hardening.mdx b/docs/pages/devsecops/repository-hardening.mdx index 7a328a505..b3ef8f81b 100644 --- a/docs/pages/devsecops/repository-hardening.mdx +++ b/docs/pages/devsecops/repository-hardening.mdx @@ -232,6 +232,7 @@ $50,000 for critical vulnerabilities. See: https://immunefi.com/bounty ``` Link `SECURITY.md` in: + - The repository's sidebar (via vocs config) - The PR template - Any public communication about the project diff --git a/docs/pages/devsecops/security-testing.mdx b/docs/pages/devsecops/security-testing.mdx index f0a4f8bed..b193ec5d6 100644 --- a/docs/pages/devsecops/security-testing.mdx +++ b/docs/pages/devsecops/security-testing.mdx @@ -186,6 +186,7 @@ False positives erode trust in the scanning pipeline. If developers learn to ignore alerts, real findings get missed. **Suppress with documentation, not silence:** + ```yaml # nosemgrep: hardcoded-private-key # Reason: Test fixtures only — not real keys. diff --git a/docs/pages/front-end-web-app/third-party-script-security.mdx b/docs/pages/front-end-web-app/third-party-script-security.mdx index c1eb0c8a2..55af4ba6b 100644 --- a/docs/pages/front-end-web-app/third-party-script-security.mdx +++ b/docs/pages/front-end-web-app/third-party-script-security.mdx @@ -53,7 +53,7 @@ enforces them on every request the page makes. ### Key Directives | Directive | Purpose | -|-----------|---------| +| --------- | ------- | | `script-src` | Controls which scripts can execute. Start restrictive and expand only as needed. | | `connect-src` | Restricts where `fetch`, `XMLHttpRequest`, and WebSocket connections can go. Critical for preventing data exfiltration to attacker-controlled servers. | | `frame-ancestors` | Prevents your application from being embedded in malicious iframes (clickjacking). | @@ -87,7 +87,8 @@ Content-Security-Policy: require-trusted-types-for 'script' ``` -For how to wire nonce generation into your specific framework, see [Framework-Specific Notes](#framework-specific-notes) below. +For how to wire nonce generation into your specific framework, see +[Framework-Specific Notes](#framework-specific-notes) below. ### The `strict-dynamic` Directive @@ -105,6 +106,7 @@ script-src 'nonce-abc123' 'strict-dynamic'; ``` This means: + - Scripts with `nonce="abc123"` will execute. - Scripts those nonced scripts load via `document.createElement('script')` will also execute. - A random `