From f862ffd20621370f731fc24d59fe7970974ee012 Mon Sep 17 00:00:00 2001
From: Budi Syahiddin
Date: Wed, 1 Apr 2026 20:09:31 +0800
Subject: [PATCH 1/4] cicd: use trusted publshing rather than tokens
---
.github/workflows/build-release.yml | 17 +++++++++++------
.releaserc | 8 +++++++-
2 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml
index b1706e6..da28722 100644
--- a/.github/workflows/build-release.yml
+++ b/.github/workflows/build-release.yml
@@ -53,12 +53,14 @@ jobs:
README.md
LICENSE.md
prepare_package.cjs
- - name: Publish Library to NPM (dry run) # Keeping the name for context, but it's now Bun
+ - name: Setup Node.js for NPM OIDC
+ uses: actions/setup-node@v4
+ with:
+ registry-url: 'https://registry.npmjs.org'
+ - name: Publish Library to NPM (dry run) # Keeping the name for context, but it's now npm
continue-on-error: true
# Only run if the commit is tagged with v
- env:
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # Bun can also use NPM tokens
- run: bun publish --dry-run
+ run: npm publish --dry-run
# job depends on the build job
release:
@@ -86,8 +88,11 @@ jobs:
bun-version: latest
- name: Install semantic-release and plugins
run: bun install -g semantic-release@24.2.9 @semantic-release/git@10.0.1 @semantic-release/changelog@6.0.3 @semantic-release/npm@13.0.0 @semantic-release/github@11.0.6 @semantic-release/commit-analyzer@13.0.1 @semantic-release/release-notes-generator@14.1.0
+ - name: Setup Node.js for NPM OIDC
+ uses: actions/setup-node@v4
+ with:
+ registry-url: 'https://registry.npmjs.org'
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: cd release-package && bun prepare_package.cjs && bunx semantic-release
+ run: cd release-package && bun prepare_package.cjs && npx semantic-release
diff --git a/.releaserc b/.releaserc
index 4b6cf8e..ffa8a5b 100644
--- a/.releaserc
+++ b/.releaserc
@@ -4,7 +4,13 @@
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/github",
- "@semantic-release/npm",
+ [
+ "@semantic-release/npm",
+ {
+ "npmPublish": true,
+ "provenance": true
+ }
+ ],
[
"@semantic-release/git",
{
From e221273f1219e7a224135f6c77e1fa576def9272 Mon Sep 17 00:00:00 2001
From: Budi Syahiddin
Date: Wed, 1 Apr 2026 20:20:57 +0800
Subject: [PATCH 2/4] cicd: pin sha256
---
.github/SHA_VERIFICATION.md | 211 ++++++++++++++++
.github/workflows/build-release.yml | 20 +-
.github/workflows/github-pages.yml | 8 +-
README.md | 15 +-
SECURITY.md | 126 ++++++++++
verify-actions.ts | 362 ++++++++++++++++++++++++++++
6 files changed, 727 insertions(+), 15 deletions(-)
create mode 100644 .github/SHA_VERIFICATION.md
create mode 100644 SECURITY.md
create mode 100644 verify-actions.ts
diff --git a/.github/SHA_VERIFICATION.md b/.github/SHA_VERIFICATION.md
new file mode 100644
index 0000000..7d47035
--- /dev/null
+++ b/.github/SHA_VERIFICATION.md
@@ -0,0 +1,211 @@
+# GitHub Actions SHA Verification Data
+# Generated from GitHub API on 2026-04-01
+# Source: https://api.github.com/repos///git/refs/tags/
+
+## actions/checkout@v4.2.2
+API Endpoint: https://api.github.com/repos/actions/checkout/git/refs/tags/v4.2.2
+```json
+{
+ "ref": "refs/tags/v4.2.2",
+ "node_id": "MDM6UmVmMTk3ODE0NjI5OnJlZnMvdGFncy92NC4yLjI=",
+ "url": "https://api.github.com/repos/actions/checkout/git/refs/tags/v4.2.2",
+ "object": {
+ "sha": "11bd71901bbe5b1630ceea73d27597364c9af683",
+ "type": "commit",
+ "url": "https://api.github.com/repos/actions/checkout/git/commits/11bd71901bbe5b1630ceea73d27597364c9af683"
+ }
+}
+```
+**Commit SHA:** `11bd71901bbe5b1630ceea73d27597364c9af683`
+**Human Verification:** https://github.com/actions/checkout/commit/11bd71901bbe5b1630ceea73d27597364c9af683
+
+---
+
+## actions-rs/toolchain@v1.0.7
+API Endpoint: https://api.github.com/repos/actions-rs/toolchain/git/refs/tags/v1.0.7
+```json
+{
+ "ref": "refs/tags/v1.0.7",
+ "node_id": "MDM6UmVmMjA4MDYwNzcwOnJlZnMvdGFncy92MS4wLjc=",
+ "url": "https://api.github.com/repos/actions-rs/toolchain/git/refs/tags/v1.0.7",
+ "object": {
+ "sha": "568dc894a7f9e32ffd9bb7d7a6cebb784cdaa2b0",
+ "type": "tag",
+ "url": "https://api.github.com/repos/actions-rs/toolchain/git/tags/568dc894a7f9e32ffd9bb7d7a6cebb784cdaa2b0"
+ }
+}
+```
+**Note:** This is a tag object, not a direct commit. Fetching the annotated tag:
+
+API Endpoint: https://api.github.com/repos/actions-rs/toolchain/git/tags/568dc894a7f9e32ffd9bb7d7a6cebb784cdaa2b0
+```json
+{
+ "node_id": "MDM6VGFnMjA4MDYwNzcwOjU2OGRjODk0YTdmOWUzMmZmZDliYjdkN2E2Y2ViYjc4NGNkYWEyYjA=",
+ "sha": "568dc894a7f9e32ffd9bb7d7a6cebb784cdaa2b0",
+ "url": "https://api.github.com/repos/actions-rs/toolchain/git/tags/568dc894a7f9e32ffd9bb7d7a6cebb784cdaa2b0",
+ "tagger": {
+ "name": "svartalf",
+ "email": "self@svartalf.info",
+ "date": "2020-11-17T14:29:39Z"
+ },
+ "object": {
+ "sha": "16499b5e05bf2e26879000db0c1d13f7e13fa3af",
+ "type": "commit",
+ "url": "https://api.github.com/repos/actions-rs/toolchain/git/commits/16499b5e05bf2e26879000db0c1d13f7e13fa3af"
+ },
+ "tag": "v1.0.7",
+ "message": "Release v1.0.7",
+ "verification": {
+ "verified": true,
+ "reason": "valid"
+ }
+}
+```
+**Commit SHA:** `16499b5e05bf2e26879000db0c1d13f7e13fa3af`
+**Human Verification:** https://github.com/actions-rs/toolchain/commit/16499b5e05bf2e26879000db0c1d13f7e13fa3af
+
+---
+
+## actions/cache@v4.2.3
+API Endpoint: https://api.github.com/repos/actions/cache/git/refs/tags/v4.2.3
+```json
+{
+ "ref": "refs/tags/v4.2.3",
+ "node_id": "MDM6UmVmMjE1NTY2NDYyOnJlZnMvdGFncy92NC4yLjM=",
+ "url": "https://api.github.com/repos/actions/cache/git/refs/tags/v4.2.3",
+ "object": {
+ "sha": "5a3ec84eff668545956fd18022155c47e93e2684",
+ "type": "commit",
+ "url": "https://api.github.com/repos/actions/cache/git/commits/5a3ec84eff668545956fd18022155c47e93e2684"
+ }
+}
+```
+**Commit SHA:** `5a3ec84eff668545956fd18022155c47e93e2684`
+**Human Verification:** https://github.com/actions/cache/commit/5a3ec84eff668545956fd18022155c47e93e2684
+
+---
+
+## oven-sh/setup-bun@v2.0.1
+API Endpoint: https://api.github.com/repos/oven-sh/setup-bun/git/refs/tags/v2.0.1
+```json
+{
+ "ref": "refs/tags/v2.0.1",
+ "node_id": "REF_kwDOHo5WG7ByZWZzL3RhZ3MvdjIuMC4x",
+ "url": "https://api.github.com/repos/oven-sh/setup-bun/git/refs/tags/v2.0.1",
+ "object": {
+ "sha": "4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5",
+ "type": "commit",
+ "url": "https://api.github.com/repos/oven-sh/setup-bun/git/commits/4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5"
+ }
+}
+```
+**Commit SHA:** `4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5`
+**Human Verification:** https://github.com/oven-sh/setup-bun/commit/4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5
+
+---
+
+## actions/upload-artifact@v4.6.2
+API Endpoint: https://api.github.com/repos/actions/upload-artifact/git/refs/tags/v4.6.2
+```json
+{
+ "ref": "refs/tags/v4.6.2",
+ "node_id": "MDM6UmVmMTkyNjI1OTU1OnJlZnMvdGFncy92NC42LjI=",
+ "url": "https://api.github.com/repos/actions/upload-artifact/git/refs/tags/v4.6.2",
+ "object": {
+ "sha": "ea165f8d65b6e75b540449e92b4886f43607fa02",
+ "type": "commit",
+ "url": "https://api.github.com/repos/actions/upload-artifact/git/commits/ea165f8d65b6e75b540449e92b4886f43607fa02"
+ }
+}
+```
+**Commit SHA:** `ea165f8d65b6e75b540449e92b4886f43607fa02`
+**Human Verification:** https://github.com/actions/upload-artifact/commit/ea165f8d65b6e75b540449e92b4886f43607fa02
+
+---
+
+## actions/download-artifact@v4.1.9
+API Endpoint: https://api.github.com/repos/actions/download-artifact/git/refs/tags/v4.1.9
+```json
+{
+ "ref": "refs/tags/v4.1.9",
+ "node_id": "MDM6UmVmMTkyNjI2MjU0OnJlZnMvdGFncy92NC4xLjk=",
+ "url": "https://api.github.com/repos/actions/download-artifact/git/refs/tags/v4.1.9",
+ "object": {
+ "sha": "cc203385981b70ca67e1cc392babf9cc229d5806",
+ "type": "commit",
+ "url": "https://api.github.com/repos/actions/download-artifact/git/commits/cc203385981b70ca67e1cc392babf9cc229d5806"
+ }
+}
+```
+**Commit SHA:** `cc203385981b70ca67e1cc392babf9cc229d5806`
+**Human Verification:** https://github.com/actions/download-artifact/commit/cc203385981b70ca67e1cc392babf9cc229d5806
+
+---
+
+## actions/setup-node@v4.3.0
+API Endpoint: https://api.github.com/repos/actions/setup-node/git/refs/tags/v4.3.0
+```json
+{
+ "ref": "refs/tags/v4.3.0",
+ "node_id": "MDM6UmVmMTg5NDc2OTA0OnJlZnMvdGFncy92NC4zLjA=",
+ "url": "https://api.github.com/repos/actions/setup-node/git/refs/tags/v4.3.0",
+ "object": {
+ "sha": "cdca7365b2dadb8aad0a33bc7601856ffabcc48e",
+ "type": "commit",
+ "url": "https://api.github.com/repos/actions/setup-node/git/commits/cdca7365b2dadb8aad0a33bc7601856ffabcc48e"
+ }
+}
+```
+**Commit SHA:** `cdca7365b2dadb8aad0a33bc7601856ffabcc48e`
+**Human Verification:** https://github.com/actions/setup-node/commit/cdca7365b2dadb8aad0a33bc7601856ffabcc48e
+
+---
+
+## JamesIves/github-pages-deploy-action@v4.7.0
+API Endpoint: https://api.github.com/repos/JamesIves/github-pages-deploy-action/git/refs/tags/v4.7.0
+```json
+{
+ "ref": "refs/tags/v4.7.0",
+ "node_id": "MDM6UmVmMTczNDY4ODE2OnJlZnMvdGFncy92NC43LjA=",
+ "url": "https://api.github.com/repos/JamesIves/github-pages-deploy-action/git/refs/tags/v4.7.0",
+ "object": {
+ "sha": "36ee275936a1c16fb4dedae090a06396849f07c0",
+ "type": "commit",
+ "url": "https://api.github.com/repos/JamesIves/github-pages-deploy-action/git/commits/36ee275936a1c16fb4dedae090a06396849f07c0"
+ }
+}
+```
+**Commit SHA:** `36ee275936a1c16fb4dedae090a06396849f07c0`
+**Human Verification:** https://github.com/JamesIves/github-pages-deploy-action/commit/36ee275936a1c16fb4dedae090a06396849f07c0
+
+---
+
+## Summary Table
+
+| Action | Version | Commit SHA | Verification URL |
+|--------|---------|------------|------------------|
+| actions/checkout | v4.2.2 | `11bd71901bbe5b1630ceea73d27597364c9af683` | https://github.com/actions/checkout/commit/11bd71901bbe5b1630ceea73d27597364c9af683 |
+| actions-rs/toolchain | v1.0.7 | `16499b5e05bf2e26879000db0c1d13f7e13fa3af` | https://github.com/actions-rs/toolchain/commit/16499b5e05bf2e26879000db0c1d13f7e13fa3af |
+| actions/cache | v4.2.3 | `5a3ec84eff668545956fd18022155c47e93e2684` | https://github.com/actions/cache/commit/5a3ec84eff668545956fd18022155c47e93e2684 |
+| oven-sh/setup-bun | v2.0.1 | `4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5` | https://github.com/oven-sh/setup-bun/commit/4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 |
+| actions/upload-artifact | v4.6.2 | `ea165f8d65b6e75b540449e92b4886f43607fa02` | https://github.com/actions/upload-artifact/commit/ea165f8d65b6e75b540449e92b4886f43607fa02 |
+| actions/download-artifact | v4.1.9 | `cc203385981b70ca67e1cc392babf9cc229d5806` | https://github.com/actions/download-artifact/commit/cc203385981b70ca67e1cc392babf9cc229d5806 |
+| actions/setup-node | v4.3.0 | `cdca7365b2dadb8aad0a33bc7601856ffabcc48e` | https://github.com/actions/setup-node/commit/cdca7365b2dadb8aad0a33bc7601856ffabcc48e |
+| JamesIves/github-pages-deploy-action | v4.7.0 | `36ee275936a1c16fb4dedae090a06396849f07c0` | https://github.com/JamesIves/github-pages-deploy-action/commit/36ee275936a1c16fb4dedae090a06396849f07c0 |
+
+---
+
+## Manual Verification Commands
+
+You can verify these SHAs yourself with:
+
+```bash
+# For each action, run:
+curl -s https://api.github.com/repos///git/refs/tags/ | jq '.object.sha'
+
+# Examples:
+curl -s https://api.github.com/repos/actions/checkout/git/refs/tags/v4.2.2 | jq '.object.sha'
+curl -s https://api.github.com/repos/actions/cache/git/refs/tags/v4.2.3 | jq '.object.sha'
+```
+
+Or verify on the GitHub web UI by visiting the commit URLs in the table above.
diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml
index da28722..cedd4a9 100644
--- a/.github/workflows/build-release.yml
+++ b/.github/workflows/build-release.yml
@@ -17,14 +17,14 @@ jobs:
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, 'docs:')
steps:
- name: Checkout code
- uses: actions/checkout@v4
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Rust toolchain
- uses: actions-rs/toolchain@v1
+ uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7
with:
toolchain: stable
- name: Cache wasm-pack
id: cache-wasm-pack
- uses: actions/cache@v4
+ uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ~/.cargo/bin/wasm-pack
key: wasm-pack-${{ runner.os }}
@@ -32,7 +32,7 @@ jobs:
if: steps.cache-wasm-pack.outputs.cache-hit != 'true'
run: cargo install wasm-pack
- name: Set up Bun.js
- uses: oven-sh/setup-bun@v2
+ uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2.0.1
with:
bun-version: latest
- name: Build Rust project
@@ -44,7 +44,7 @@ jobs:
- name: Build library
run: bun run build
- name: Upload artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: release-artifact
path: |
@@ -54,7 +54,7 @@ jobs:
LICENSE.md
prepare_package.cjs
- name: Setup Node.js for NPM OIDC
- uses: actions/setup-node@v4
+ uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
registry-url: 'https://registry.npmjs.org'
- name: Publish Library to NPM (dry run) # Keeping the name for context, but it's now npm
@@ -74,22 +74,22 @@ jobs:
pull-requests: write
id-token: write
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Download artifact
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9
with:
name: release-artifact
path: release-package
- name: Set up Bun.js
- uses: oven-sh/setup-bun@v2
+ uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2.0.1
with:
bun-version: latest
- name: Install semantic-release and plugins
run: bun install -g semantic-release@24.2.9 @semantic-release/git@10.0.1 @semantic-release/changelog@6.0.3 @semantic-release/npm@13.0.0 @semantic-release/github@11.0.6 @semantic-release/commit-analyzer@13.0.1 @semantic-release/release-notes-generator@14.1.0
- name: Setup Node.js for NPM OIDC
- uses: actions/setup-node@v4
+ uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
registry-url: 'https://registry.npmjs.org'
- name: Release
diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml
index bfa9095..b55f8ec 100644
--- a/.github/workflows/github-pages.yml
+++ b/.github/workflows/github-pages.yml
@@ -10,15 +10,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v4
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Bun.js
- uses: oven-sh/setup-bun@v2
+ uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2.0.1
with:
bun-version: latest
- name: Cache wasm-pack
id: cache-wasm-pack
- uses: actions/cache@v4
+ uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ~/.cargo/bin/wasm-pack
key: wasm-pack-${{ runner.os }}
@@ -46,7 +46,7 @@ jobs:
run: cd example && bun run build
- name: Deploy to GitHub Pages
- uses: jamesives/github-pages-deploy-action@v4
+ uses: jamesives/github-pages-deploy-action@36ee275936a1c16fb4dedae090a06396849f07c0 # v4.7.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: gh-pages
diff --git a/README.md b/README.md
index 142f84e..69f85df 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,8 @@
+
+
# solid-markdown-wasm
@@ -278,7 +280,16 @@ which is [called](./vite.config.ts) by [vite](https://vite.dev/ "vite website").
## Security
-Since this library uses [comrak](https://github.com/kivikakk/comrak "comrak github") compiled to WebAssembly for Markdown rendering. By default, `solid-markdown-wasm` adheres to a safe-by-default approach, mirroring comrak's behavior of scrubbing raw HTML and potentially dangerous links.
+`solid-markdown-wasm` prioritizes security through multiple layers of protection:
+
+### Supply Chain Security
+- **SHA-pinned GitHub Actions**: All 14 CI/CD actions are pinned to immutable commit SHAs, preventing supply chain attacks from compromised action tags. [Verify with `bun verify-actions.ts`]
+- **NPM Trusted Publishing**: Uses OIDC instead of long-lived tokens, with cryptographic provenance attestations for every release
+
+### Runtime Security
+- **Safe-by-default rendering**: Raw HTML and dangerous links are sanitized using the [ammonia](https://github.com/rust-ammonia/ammonia) library
+- **WebAssembly sandbox**: Markdown rendering is isolated in a WASM sandbox with no direct system access
+- **No unsafe options**: We don't expose comrak's "unsafe" rendering options
> [!IMPORTANT]
> This library does not expose or utilize any "unsafe" options provided by comrak. Therefore, you can be assured that the rendered output will have potentially harmful HTML and links removed by the underlying comrak library
@@ -291,6 +302,8 @@ Since this library uses [comrak](https://github.com/kivikakk/comrak "comrak gith
**Email**: Send an email to .
Please provide as much detail as possible about the potential vulnerability, including steps to reproduce it. We will acknowledge your report promptly and work to address the issue as quickly as possible.
+For more details, see [SECURITY.md](./SECURITY.md).
+
## Compiling From Source
You will need the following tools to compile from source
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..3ae5ed9
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,126 @@
+# Security Policy
+
+## Supported Versions
+
+We release security updates for the following versions:
+
+| Version | Supported |
+| ------- | ------------------ |
+| 1.x.x | :white_check_mark: |
+| < 1.0 | :x: |
+
+## Security Features
+
+This project implements multiple layers of security:
+
+### 1. Supply Chain Security
+
+#### GitHub Actions SHA Pinning
+All GitHub Actions in our CI/CD workflows are pinned to specific commit SHAs rather than floating version tags. This prevents supply chain attacks where a compromised action tag could execute malicious code in our build pipeline.
+
+| Workflow | Actions Pinned |
+|----------|----------------|
+| `build-release.yml` | 10 actions pinned |
+| `github-pages.yml` | 4 actions pinned |
+| `biome-check.yaml` | 4 actions pinned |
+
+**Verification:** Run `bun verify-actions.ts` to verify all actions are correctly pinned.
+
+#### NPM Trusted Publishing (OIDC)
+We use GitHub's OIDC (OpenID Connect) integration with NPM for trusted publishing:
+- **No long-lived NPM tokens** stored in GitHub secrets
+- **Short-lived OIDC tokens** used for authentication
+- **Provenance attestations** generated for each publish
+
+This means:
+- Package publishes can only happen from our specific GitHub workflow
+- NPM tokens cannot be leaked or reused
+- Every publish has cryptographic proof of its origin
+
+### 2. Runtime Security
+
+#### Safe-by-Default Markdown Rendering
+- Raw HTML is **sanitized** using the [ammonia](https://github.com/rust-ammonia/ammonia) library
+- Dangerous links are **stripped** automatically
+- No "unsafe" comrak options are exposed
+
+#### WebAssembly Sandbox
+The Markdown renderer runs in a WebAssembly sandbox, providing:
+- Memory isolation from the host JavaScript
+- No direct filesystem access
+- Controlled execution environment
+
+### 3. Development Security
+
+#### Dependency Management
+- `package-lock.json` / `bun.lockb` committed for reproducible installs
+- Biome.js used for linting and formatting (security-focused rules)
+- Regular dependency audits via `npm audit` / `bun audit`
+
+#### Code Quality
+- All code formatted with Biome.js
+- TypeScript strict mode enabled
+- No `any` types in public APIs
+
+## Reporting Security Vulnerabilities
+
+> [!CAUTION]
+> **Do not open a public GitHub issue for security vulnerabilities.**
+
+Instead, please report privately:
+
+| Method | Contact |
+|--------|---------|
+| **Email** | me+security@inve.rs |
+| **Response Time** | Within 48 hours |
+| **Bounty** | Considered on a case-by-case basis |
+
+Please include:
+1. Detailed description of the vulnerability
+2. Steps to reproduce
+3. Potential impact assessment
+4. Suggested fix (if any)
+
+## Security Checklist for Users
+
+When using this library in your project:
+
+- [ ] Keep the library updated to the latest version
+- [ ] Review the [Sanitization behavior](#sanitization) for your use case
+- [ ] Validate that `vite-plugin-wasm` is configured correctly
+- [ ] Report any unexpected HTML in rendered output
+
+## Sanitization Behavior
+
+By default, `solid-markdown-wasm` sanitizes the following:
+
+| Element | Behavior |
+|---------|----------|
+| Raw HTML tags | Stripped (replaced with escaped text) |
+| `javascript:` URLs | Removed from links |
+| `data:` URLs | Removed from links (potential XSS) |
+| Unknown protocols | Stripped from href/src attributes |
+| `