chore(infra): port static-analysis configs from archive/rebuild

- .editorconfig: root config for Rust, Makefile, md, nix, json
- .prettierignore (root + server) and per-workspace .prettierrc.json
  (desktop/main, libraries/base, sites/docs, sites/promo) plus
  sites/promo/prettier.config.js
- .github/dependabot.yml: 7 ecosystems (npm root + desktop/main,
  cargo x6: cli, droplet, native_model, desktop, torrential;
  docker; github-actions) with grouped updates and per-directory
  reviewers/labels
- .github/CODEOWNERS: review gate on auth, metadata, CI, CLAUDE.md
- security/risk-register.yaml: 15 documented accepted risks with
  mitigations and review-by dates
- .github/actions/rust-ci/action.yml: reusable composite action
  (toolchain, cache, system-deps, fmt, clippy, test, llvm-cov,
  cargo-audit) consumed by cli-ci, desktop-ci, droplet-ci

Refs: archive/rebuild @ a31ea517
This commit is contained in:
John Smith 2026-07-29 19:05:40 -04:00
parent ff300de623
commit a7ee7847f4
12 changed files with 638 additions and 9 deletions

32
.editorconfig Normal file
View file

@ -0,0 +1,32 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.rs]
indent_size = 4
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
# Makefiles require tabs (syntax)
[Makefile]
indent_style = tab
# Nix files: prettier handles formatting, allow 2-space indent
[*.nix]
indent_style = space
indent_size = 2
# JSON files: Prettier handles formatting with 2-space indent
[*.json]
indent_style = space
indent_size = 2

17
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1,17 @@
# Security-sensitive routes — require review
# Auth: webauthn, passkey, MFA, OIDC, TOTP
/server/server/api/v1/auth/ @BillyOutlast
/server/server/internal/auth/ @BillyOutlast
# Metadata providers — external HTTP integration, complex fallthrough
/server/server/internal/metadata/ @BillyOutlast
# Nitro server core (server/ dir)
/server/server/ @BillyOutlast
# Build, deps, CI
/server/.env.example @BillyOutlast
/.github/workflows/ @BillyOutlast
/AGENTS.md @BillyOutlast
/CLAUDE.md @BillyOutlast
/CONTRIBUTING.md @BillyOutlast

154
.github/actions/rust-ci/action.yml vendored Normal file
View file

@ -0,0 +1,154 @@
name: Rust CI
description: >
Reusable Rust CI steps for Drop monorepo workspaces.
Handles checkout, toolchain, cache, system deps, fmt, clippy/check,
tests, coverage (llvm-cov + Codecov), and advisory cargo-audit.
inputs:
working-directory:
required: true
description: >
Working directory for cargo commands.
Example: libraries/droplet, cli, desktop/src-tauri
cache-workspaces:
required: true
description: >
Workspace mapping for swatinem/rust-cache.
Example: "./libraries/droplet -> target"
system-dependencies:
required: false
description: >
Shell commands to install system dependencies (apt-get etc.).
Omit or leave empty when no system deps are needed.
default: ""
lint-command:
required: true
description: >
Cargo lint/build command.
Example: cargo clippy --all-targets --all-features -- -D warnings
coverage-path:
required: true
description: >
Path to coverage.lcov relative to repo root (for Codecov upload).
Example: libraries/droplet/coverage.lcov
test-command:
required: false
description: Cargo test command.
default: cargo test --all-features --all --verbose
test-continue-on-error:
required: false
description: Whether to continue on test failure.
default: "false"
lint-continue-on-error:
required: false
description: Whether to continue on lint failure.
default: "false"
components:
required: false
description: Rust toolchain components (comma-separated).
default: rustfmt, clippy
runs:
using: composite
steps:
# ── Setup ──────────────────────────────────────────────────────
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4fd1da8b0805d2d2e936788875a7d65dbd677dc2
with:
toolchain: nightly
components: ${{ inputs.components }}
- name: Rust cache
# pinned to v2
uses: swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae
with:
workspaces: ${{ inputs.cache-workspaces }}
# ── System dependencies ────────────────────────────────────────
- name: Install system dependencies
if: ${{ inputs.system-dependencies != '' }}
shell: bash
run: ${{ inputs.system-dependencies }}
# ── Format ─────────────────────────────────────────────────────
- name: Check formatting
shell: bash
working-directory: ${{ inputs.working-directory }}
run: cargo fmt --all -- --check
# ── Lint / Build ───────────────────────────────────────────────
- name: Lint / Build
shell: bash
working-directory: ${{ inputs.working-directory }}
continue-on-error: ${{ inputs.lint-continue-on-error == 'true' }}
run: ${{ inputs.lint-command }}
# ── Test ───────────────────────────────────────────────────────
- name: Run tests
shell: bash
working-directory: ${{ inputs.working-directory }}
continue-on-error: ${{ inputs.test-continue-on-error == 'true' }}
run: ${{ inputs.test-command }}
# ── Coverage ───────────────────────────────────────────────────
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Generate code coverage
# Was continue-on-error: true — switched to false so coverage
# failures surface in CI. Codecov upload below still uses
# fail_ci_if_error: false, so generation failure is visible
# but won't block the pipeline.
continue-on-error: false
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
cargo llvm-cov --all-features --workspace \
--codecov --output-path coverage.lcov
- name: Upload coverage to Codecov
# pinned to v5
uses: codecov/codecov-action@04b047e8bb82a0c002c8312c1c880fbc6a999d45
with:
files: ${{ inputs.coverage-path }}
fail_ci_if_error: false
# ── Security audit ─────────────────────────────────────────────
- name: Install cargo-audit
uses: taiki-e/install-action@cargo-audit
- name: Audit dependencies
# cargo audit exits 1 on ANY advisory. Keep non-blocking here; the
# follow-up step fails only when a NEW (un-ignored) advisory is found
# that is not already documented in security/risk-register.yaml.
continue-on-error: true
shell: bash
working-directory: ${{ inputs.working-directory }}
run: cargo audit --json > /tmp/cargo-audit.json 2>/dev/null || true
- name: Check for new Rust advisories
# Run on success or failure of the audit step, but not on cancel.
# Use --min-severity high for cargo to catch DoS-class advisories
# (RUSTSEC-2026-0194/0195 in quick-xml are severity "high"); the
# script handles missing/empty/malformed JSON and missing risk
# register gracefully (exits 0 with a warning in both cases).
# Resolve the script via $GITHUB_WORKSPACE because this composite
# action is invoked with working-directory set to a sub-crate
# (cli/, desktop/src-tauri/, libraries/droplet/), where a relative
# `scripts/check-new-vulns.cjs` would not exist.
if: success() || failure()
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
node "$GITHUB_WORKSPACE/scripts/check-new-vulns.cjs" \
--format cargo \
--json /tmp/cargo-audit.json \
--min-severity high

158
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,158 @@
version: 2
registries:
# Allow Dependabot to resolve private/skipped registry hosts from lockfiles
# (e.g. buf schema registry, GitHub Packages). Public registries need no entry.
npm-pkg-github:
type: "npm-registry"
url: "https://npm.pkg.github.com"
token: "${{secrets.GITHUB_TOKEN}}"
updates:
# ----- Node / pnpm workspace (root, server, sites/*, desktop) -----
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 10
groups:
# Patch + minor bumps bundled together to avoid PR spam
node-minor:
update-types: ["minor", "patch"]
# Pin major bumps separately for explicit review
node-major:
update-types: ["major"]
commit-message:
prefix: "deps"
prefix-development: "chore(deps-dev)"
labels: ["dependencies", "javascript"]
reviewers: ["BillyOutlast"]
# Keep lockfile in sync; pnpm-workspace.yaml declares onlyBuiltDependencies
# — keep Dependabot from re-enabling builds that the workspace intentionally skips.
rebase-strategy: "auto"
# ----- Nuxt 4 desktop app (separate pnpm workspace) -----
# desktop/main/ has its own pnpm-workspace.yaml and pnpm-lock.yaml,
# so root npm entry at "/" does not cover it. Scanned independently.
- package-ecosystem: "npm"
directory: "/desktop/main"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
groups:
desktop-minor:
update-types: ["minor", "patch"]
desktop-major:
update-types: ["major"]
commit-message:
prefix: "deps(desktop)"
labels: ["dependencies", "javascript"]
rebase-strategy: "auto"
# ----- Rust workspace: CLI -----
- package-ecosystem: "cargo"
directory: "/cli"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
groups:
rust-minor:
update-types: ["minor", "patch"]
rust-major:
update-types: ["major"]
commit-message:
prefix: "deps(cli)"
labels: ["dependencies", "rust"]
# ----- Rust workspace: droplet library -----
- package-ecosystem: "cargo"
directory: "/libraries/droplet"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
groups:
rust-minor:
update-types: ["minor", "patch"]
rust-major:
update-types: ["major"]
commit-message:
prefix: "deps(droplet)"
labels: ["dependencies", "rust"]
# ----- Rust workspace: native_model library -----
- package-ecosystem: "cargo"
directory: "/libraries/native_model"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
groups:
rust-minor:
update-types: ["minor", "patch"]
rust-major:
update-types: ["major"]
commit-message:
prefix: "deps(native_model)"
labels: ["dependencies", "rust"]
# ----- Rust workspace: desktop (Tauri) -----
- package-ecosystem: "cargo"
directory: "/desktop/src-tauri"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
groups:
rust-minor:
update-types: ["minor", "patch"]
rust-major:
update-types: ["major"]
commit-message:
prefix: "deps(desktop)"
labels: ["dependencies", "rust"]
# ----- Dockerfile (root image) -----
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
commit-message:
prefix: "deps(docker)"
labels: ["dependencies", "docker"]
# ----- GitHub Actions -----
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 10
groups:
actions-minor:
update-types: ["minor", "patch"]
actions-major:
update-types: ["major"]
commit-message:
prefix: "deps(ci)"
labels: ["dependencies", "github-actions"]

25
.prettierignore Normal file
View file

@ -0,0 +1,25 @@
# Build output
dist/
.nuxt/
.output/
coverage/
target/
# Dependencies
**/node_modules/
# Generated code (Prisma)
prisma/client/
**/migrations/
# Proto-generated code
**/proto/
# External / auto-generated code (not maintained by us)
desktop/libs/appletrust/
# Test artifacts
**/test-results/
# Documentation sites (separate conventions)
sites/docs/

View file

@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": false,
"trailingComma": "all",
"tabWidth": 2,
"printWidth": 100
}

View file

@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": false,
"trailingComma": "all",
"tabWidth": 2,
"printWidth": 100
}

212
security/risk-register.yaml Normal file
View file

@ -0,0 +1,212 @@
# Accepted security risks.
#
# Each entry documents a known vulnerability with accepted mitigation.
# `review_by`: when this entry must be re-evaluated (auto-flagged if past).
# `ci_ignore: true` means the vuln is in `pnpm audit --ignore GHSA-...` and
# this entry is the authoritative rationale.
#
# When a new vuln is added to `pnpm audit --ignore`, add a corresponding
# entry here. CI check (ci: verify risk register covers all ignored advisories)
# enforces coverage.
risks:
- id: RISK-001
title: "decompress: Archive extraction can create files and links outside the target directory"
package: "decompress@<=4.2.1"
advisory: GHSA-mp2f-45pm-3cg9
severity: critical
affected_paths:
- "desktop>tauri>imagemin-optipng>optipng-bin>bin-build>decompress"
- "desktop>tauri>imagemin-optipng>optipng-bin>bin-build>download>decompress"
- "desktop>tauri>imagemin-optipng>optipng-bin>bin-wrapper>download>decompress"
mitigation: "Local patch applied via patches/decompress@4.2.1.patch (path containment + mode bit stripping + link target validation). Only used in Tauri build pipeline, not exposed to runtime. Patched in commit 2be06d7a."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-002
title: "Prototype Pollution in lodash"
package: "lodash.pick@>=4.0.0 <=4.4.0"
advisory: GHSA-p6mc-m468-83gw
severity: high
affected_paths:
- "desktop>tauri>@tauri-apps/tauri-inliner>cheerio>lodash.pick"
mitigation: "Dead-end transitive dep. tauri-inliner pins lodash.pick 4.x. No upstream fix available. Desktop build pipeline only, not exposed to runtime."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-003
title: "SVGO removeScripts plugin leaves some executable code intact"
package: "svgo@1.3.2"
advisory: GHSA-2p49-hgcm-8545
severity: high
affected_paths:
- "desktop>tauri>@tauri-apps/tauri-inliner>svgo"
mitigation: "Dead-end transitive dep. tauri-inliner pins svgo 1.x. No upstream fix. Desktop build pipeline only."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-004
title: "Server-Side Request Forgery in Request"
package: "request@2.88.2"
advisory: GHSA-c2qf-rxjj-qqgw
severity: moderate
affected_paths:
- "desktop>tauri>@tauri-apps/tauri-inliner>request"
mitigation: "Deprecated since 2020. No fix. Desktop build pipeline only."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-005
title: "file-type affected by infinite loop in ASF parser"
package: "file-type@18.7.0"
advisory: GHSA-cjm9-3pvw-9wxf
severity: moderate
affected_paths:
- "server>stream-mime-type>file-type"
mitigation: "Server uses file-type only for stream MIME detection. Affected path requires crafted ASF file input. No upstream fix yet."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-006
title: "@hono/node-server: Middleware bypass via repeated X-Powered-By headers"
package: "@hono/node-server<1.13.10"
advisory: GHSA-7g6w-hpgm-66vp
severity: moderate
affected_paths:
- "server>prisma>@prisma/dev>@hono/node-server"
mitigation: "Dev-only dep. Not bundled in production. Prisma update would resolve."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-007
title: "uuid: Missing buffer bounds check"
package: "uuid@<11.0.0"
advisory: GHSA-cf7w-9wvq-3f9r
severity: moderate
affected_paths:
- "desktop>tauri>@tauri-apps/tauri-inliner>request>uuid"
mitigation: "Transitive through deprecated request. Desktop build pipeline only. Normal uuid usage unaffected (CVE requires explicit buf arg)."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-008
title: "Astro: Reflected XSS via unescaped View Transition"
package: "astro@<7.0.10"
advisory: GHSA-vfqw-2gh8-h2hg
severity: moderate
affected_paths:
- "sites/docs>astro"
mitigation: "Fix requires astro 7.x (MAJOR version jump). sites/docs uses astro 6.4.8. Migration deferred. sites/docs is content-only — no user input rendering."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-009
title: "Astro: XSS via unescaped spread attribute names in components"
package: "astro@<7.0.10"
advisory: GHSA-fv6c-7r8v-qpqv
severity: moderate
affected_paths:
- "sites/docs>astro"
mitigation: "Same as RISK-008 — requires astro 7.x. Deferred."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-010
title: "Node.js Adapter for Hono: Path traversal in serveStatic"
package: "@hono/node-server<1.13.10"
advisory: GHSA-3qpp-9r2h-9wj2
severity: moderate
affected_paths:
- "server>prisma>@prisma/dev>@hono/node-server"
mitigation: "Dev-only dep. Not in production. Prisma update would resolve."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-011
title: "Valibot: record() issue paths can make flatten() throw"
package: "valibot@<1.2.0"
advisory: GHSA-vfqw-2gh8-h3gv
severity: moderate
affected_paths:
- "server>prisma>@prisma/dev>valibot"
mitigation: "Dev-only dep (prisma devtools). Not in production. Prisma update would resolve."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-012
title: "esbuild allows arbitrary file read when running the dev server"
package: "esbuild@<=0.25.0"
advisory: GHSA-67mh-4wv8-7f7v
severity: low
affected_paths:
- "sites/docs>astro>esbuild"
mitigation: "Windows-only exploit. Linux dev server immune. sites/docs is content-only, no untrusted file access."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-013
title: "Astro: Cross-site scripting via unescaped transition:* props"
package: "astro@<7.0.10"
advisory: GHSA-fv6c-7r8v-qpg3
severity: low
affected_paths:
- "sites/docs>astro"
mitigation: "Same as RISK-008 — requires astro 7.x. sites/docs uses astro 6.4.8. Deferred."
accepted_by: "BillyOutlast"
accepted_date: "2025-07-24"
review_by: "2025-10-24"
ci_ignore: true
- id: RISK-014
title: "quick-xml: Quadratic duplicate attribute check DoS"
package: "quick-xml@<0.41.0"
advisory: RUSTSEC-2026-0194
severity: high
affected_paths:
- "cli>opendal>reqsign>quick-xml 0.37.5"
- "cli>opendal>quick-xml 0.38.4"
- "desktop>tauri>tauri-utils>plist>quick-xml 0.38.4"
mitigation: "Desktop build-time path: tauri-utils/plist parses only its own Info.plist — fully trusted. CLI path via opendal: the XML endpoint is user-configured cloud storage. While normal usage targets trusted providers, a user could configure a malicious endpoint returning crafted XML. Keep CLI advisory open; recommend limiting deployment to trusted endpoints. Upstream crate bump in opendal will resolve when available."
accepted_by: "BillyOutlast"
accepted_date: "2026-07-28"
review_by: "2026-10-28"
ci_ignore: false
- id: RISK-015
title: "quick-xml: Unbounded namespace allocation OOM"
package: "quick-xml@<0.41.0"
advisory: RUSTSEC-2026-0195
severity: high
affected_paths:
- "cli>opendal>reqsign>quick-xml 0.37.5"
- "cli>opendal>quick-xml 0.38.4"
- "desktop>tauri>tauri-utils>plist>quick-xml 0.38.4"
mitigation: "Same as RISK-014. Desktop build-time path is trusted; CLI opendal path has untrusted XML surface from user-configured endpoints. NsReader path unused in all affected crates' usage patterns."
accepted_by: "BillyOutlast"
accepted_date: "2026-07-28"
review_by: "2026-10-28"
ci_ignore: false

View file

@ -1,7 +1,8 @@
drop-base/
# file is fully managed by pnpm, no reason to break it
pnpm-lock.yaml
/torrential/
node_modules/
.nuxt/
.output/
dist/
coverage/
.data/**
**/.data/**
pnpm-lock.yaml

View file

@ -0,0 +1,8 @@
{
"semi": true,
"singleQuote": false,
"trailingComma": "all",
"tabWidth": 2,
"printWidth": 100,
"plugins": ["prettier-plugin-astro"]
}

View file

@ -0,0 +1,8 @@
{
"semi": true,
"singleQuote": false,
"trailingComma": "all",
"tabWidth": 2,
"printWidth": 100,
"plugins": ["prettier-plugin-organize-imports", "prettier-plugin-tailwindcss"]
}

View file

@ -2,7 +2,7 @@
module.exports = {
singleQuote: true,
semi: false,
plugins: ['prettier-plugin-organize-imports', 'prettier-plugin-tailwindcss'],
tailwindFunctions: ['clsx'],
tailwindStylesheet: './src/styles/tailwind.css',
}
plugins: ["prettier-plugin-organize-imports", "prettier-plugin-tailwindcss"],
tailwindFunctions: ["clsx"],
tailwindStylesheet: "./src/styles/tailwind.css",
};