[audit] Harden plugin route patterns, i18n keys, and container user - #500
Closed
BillyOutlast wants to merge 256 commits into
Closed
BillyOutlast wants to merge 256 commits into
BillyOutlast wants to merge 256 commits into
Conversation
- fsBackend.ts: open the file handle first and perform all subsequent operations (stat, read stream, write) through that same fd, so there is no window between checking a path and using it - steam.ts: neutralize HTML comments with one regex that consumes both closed and unterminated comment openers; after entity decoding, strip any resurrected tag sequences so output can never contain live markup Verified: typecheck clean, build passes, eslint unchanged vs baseline, ast-grep clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…data CodeQL flagged that a decoded string could still contain '<script' (an unterminated tag survives complete-tag stripping). Since legitimate markup is already converted to Markdown earlier in the pipeline, remove every remaining '<' outright so no tag opener can exist in the output. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CodeQL's model doesn't credit downstream character removal, so restructure the decoder to never emit '<' or '>' in the first place: - named bracket entities (<, >) are excluded from the decode set and dropped outright - numeric/hex references encoding 0x3C/0x3E are dropped as well Adversarial tests confirm no input form (named, double-escaped, decimal, hex, zero-padded, raw tags, comment tricks) can leave a '<' in the output. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Server CI (Lint job): - pages/account/index.vue: remove commented-out template block that tripped vue/no-multiple-template-root under the updated eslint-plugin- vue from lockfile drift; note the drafted layout lives in git history - metadata/igdb.ts: replace dead initializer assignments flagged by no-useless-assignment with definite-assignment declarations - metadata/index.ts: drop redundant '= undefined' initializer Security workflow: - gitleaks: invoke the CLI directly instead of gitleaks-action, which requires a paid license for organization repos - cargo-audit: generate Cargo.lock on the fly for vendored crates that gitignore it (libarchive, native_model) Verified locally: eslint 0 errors (11 pre-existing warnings), typecheck clean, full build passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…hing CodeQL models the /<[^>]*>/g tag-matching regex as incomplete multi-character sanitization (a '<' outside a matched tag survives). Replace it with direct removal of every '<' character — legitimate markup is already Markdown at this point in the pipeline, so removing the character itself is complete by construction and satisfies the sanitizer model. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bumps the cargo group with 2 updates in the /desktop/src-tauri/client directory: [bytes](https://github.com/tokio-rs/bytes) and [rand](https://github.com/rust-random/rand). Updates `bytes` from 1.10.1 to 1.12.1 - [Release notes](https://github.com/tokio-rs/bytes/releases) - [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md) - [Commits](tokio-rs/bytes@v1.10.1...v1.12.1) Updates `rand` from 0.8.5 to 0.8.8 - [Release notes](https://github.com/rust-random/rand/releases) - [Changelog](https://github.com/rust-random/rand/blob/0.8.8/CHANGELOG.md) - [Commits](rust-random/rand@0.8.5...0.8.8) --- updated-dependencies: - dependency-name: bytes dependency-version: 1.12.1 dependency-type: indirect - dependency-name: rand dependency-version: 0.8.8 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* chore: gitignore SonarCloud issue dump and local fix scripts * fix(sast): remediate SonarCloud mechanical issues (S9011, S6822, S7772, S2933, S1128) - add explicit type="button" to bare vue/tsx buttons (S9011) - drop redundant role="list" on ul/ol (Web:S6822) - prefix node: builtin imports (S7772) - mark never-modified class members readonly via flows (S2933) - prune unused named imports (S1128) Covers 89+25+35+32+30 = 211 of 680 open issues. * fix(sast): remediate SonarCloud optional-chain/refactor rules - S6582: nullish-guard -> optional chain (28) - S6759: React props inline types -> Readonly<Props> (35) - S7754: findIndex/find-filter .length -> .some (17) - S3626: remove redundant trailing continue/return (16) - S7752: .map().flat() -> .flatMap (10) - S7723: Array() -> new Array (6) - S7755: [len-1] -> .at(-1) (with non-null assertion in lastItem) - S7750: .filter().at(0) -> .find (1) * fix(sast): shell, Docker, and workflow hardening - shelldre:S7688: if [ .. ] -> [[ .. ]] in clippy-changed.sh + version_update.sh (6) - docker:S7018: alphabetical apt package sort in Dockerfile (2) - docker/githubactions:S6505: add --ignore-scripts to pnpm install runs (5 workflow + 1 Dockerfile) * fix(sast): remediate SonarCloud a11y and small-refactor rules (Phase 3) * fix(sast): Phase 4 code-able security hardening - typescript:S4790: md5 -> sha256 in server/internal/library/import tasks + fsBackend object hash (non-cryptographic use, no value dependency) - swift:S1481: remove unused status local in appletrust/add-certificate.swift - swift:S1066: merge nested if-let in getCertificateFromString - javascript:S7767: | 0 -> Math.trunc in base32 pad calc - javascript:S7751: [].concat(...) -> .flatMap(...) in base32 decoder - shell:S6506: add --max-redirect=5 to wget in optimize-appimage.sh Lockfile policy (text:S8570/S8549), prisma migrations (plsql), TODOs (S1135), SHA-pinning (S7637), regex backtracking (S8786), PATH-taint (S4036), and the OIDC-Spec URI (S5332) are addressed by SonarCloud-side wontfix accepts in the following commit. --------- Co-authored-by: John Smith <you@example.com>
Sync Heretek-AI/drop develop with Drop-OSS/drop@344b89c5. Resolutions: - CI workflows: take upstream wholesale (checkout v7, pnpm v6, setup-node v7, docker action bumps, devcontainer/CI pipeline fixes) - server/package.json: adopt upstream placement + simplewebauthn ^13.3.2; keep Prisma 7.10.0 - server/internal/metadata/steam.ts: keep hardened single-pass entity decoder; adopt full_description undefined fix - .gitignore: union of both sides - pnpm-lock.yaml regenerated (security overrides preserved via pnpm-workspace.yaml) - scripts/clippy-changed.sh: exclude desktop/src-tauri per documented intent
…Dockerfile alignment
- Pin all GitHub Actions in workflow files to immutable commit SHAs with version comments
- Generalize server release workflow REGISTRY_IMAGE to ghcr.io/${{ github.repository }}
- Restore non-blocking knip report job in server-ci.yml
- Align Dockerfile global Prisma version with server/package.json (7.10.0)
- Add .git-blame-ignore-revs for repo-wide Prettier formatting commit
- Add historic commit fingerprint to .gitleaksignore
…nd introduce LaunchInterceptor
…, and desktop multiplayer integration
… capability sandboxing, and Settings UI
- Add dynamic plugin discovery scanning ${dataDir}/plugins/*/drop-plugin.json
- Implement plugin state persistence in _state.json with togglePlugin enabling/disabling
- Implement capability sandboxing (routes, storage, websocket, events, network)
- Add admin API endpoints for toggling plugin state and reloading external plugins
- Create Settings > Plugins page in desktop client with capability badges, toggle switches, and reload controls
- Add unit tests verifying plugin enable/disable lifecycle and capability enforcement
…ease workflow - Fix invalid job dependency in 'winget-publish' from 'build' to 'publish-tauri' - Elevate WINGET_TOKEN to job level to enable step-level conditional evaluation - Fix shellcheck quoting and array expansion in macOS certificate signing steps - Verify workflow cleanly passes actionlint with 0 errors or warnings
- Document monorepo structure across Nuxt/Nitro, Go, Tauri, and Rust crates - Outline core architectural systems: unified High-DPI Tauri window model, Server Plugin SPI, dynamic discovery, capability sandboxing, and LaunchInterceptor pipeline - Document toolchain requirements, nightly Rust caveats, and Prisma conventions - Define local quality gates, Lefthook pre-commit/pre-push hooks, and essential verification commands
…gine - Classify game distributions (Scene RARs/ISOs, GOG multi-bin, FitGirl/KaOs repacks, 7z/zip archives, loose portable) - Score and rank game executables over crash handlers and installers - Generate declarative extraction recipes with standalone setup scripts - Attach pipeline recipes directly to GameVersion dropletManifests - Provide maintenance tool to repair placeholder launch and setup configurations - Strictly maintain zero in-place mutation on seeding storage
- Execute server-generated setup recipes in Rust (7z/innoextract) with streamed progress events. - Add GameSetupModal with stepper, live log, cancellation and archive reclamation. - Register start/cancel/reclaim pipeline IPC commands and fall back to legacy setup when a version has no recipe. - Fix standalone Scene ISO recipes to skip the redundant RAR extraction step.
- Persist discovered unimported games in a new DiscoveredGame model with inferred distribution type. - Add import:discover and import:bulk task groups plus discovery/import APIs. - Auto-create discovered games and import their versions with generated recipes. - Add /admin/import/bulk review table with scan, select, import and ignore actions.
- Cache plaintext depot chunks by SHA-256 checksum with an LRU byte budget (CHUNK_CACHE_DIR, CHUNK_CACHE_MAX_BYTES; disabled by default). - Serve cache hits directly and fill best-effort on misses, falling back to source storage on any failure. - Best-effort single-flight fills, atomic publish and startup orphan cleanup. - Document the optional mount/env and benchmark the hot path.
- Add AGENTS.md as the canonical contributor and AI agent guide covering the monorepo, pipeline runner, bulk auto-import, chunk cache, quality gates and environment quirks. - Reduce CLAUDE.md and GEMINI.md to tool-specific wrappers that point at AGENTS.md. - Correct stale architecture, commands and the hook escape-hatch policy.
Operator::new now returns the operator directly, so drop the removed .finish() call that broke the build. Remove the unused interactive_optional_variable macro/helper, the dead webbrowser dependency, and a discarded v2_manifest binding.
Use write_all to avoid the deny-level unused_io_amount error and drop the no-op "torrential download" scaffolding (empty async fn plus an unused 312 MB temp-file generator).
Delete files with no remaining references (consts, recursivedirs, giantbomb, GameCarousel, RankingList), de-export symbols used only internally, drop unused packages, and correct the prisma client type import. Guard against unresolved companies in the Steam provider that the import fix surfaced.
Narrow useGame's status to Ref<GameStatus> under noUncheckedIndexedAccess and guard optional index access across the library, queue, and settings views. Replace defineNuxtConfig in both configs with satisfies NuxtConfig to work around a broken upstream type when @nuxt/schema is hoisted.
Remove unused droplet dependencies (dyn-clone, serde_json), apply clippy suggestions (Path over PathBuf, derived Default, match ergonomics, field init shorthand, documented unsafe fns, annotated transmute), and sync lockfiles.
… lints Move rust-protobuf output out of the source tree and include it via a hand-written src/proto/mod.rs that applies clippy/allow attributes. This stops generated code from tripping the crate's pedantic lints (previously ~184 warnings) and removes the generate-into-src churn.
- Fall back to source streaming when a cache entry races eviction or has the wrong size, instead of returning 500. - Account for cache-fill readers in the file-descriptor semaphore. - Share DownloadContext behind Arc so no DashMap guard is held across awaits. - Cap RPC frame length, add a response timeout, and recover from bad frames instead of panicking. - Replace panics on peer/manifest data with error propagation. - Keep the chunk cache directory/files private and reject an unbounded (0-byte) budget; keep the index consistent on re-insert. - Clear clippy/pedantic warnings and add regression tests.
…in CI Add a shared deny.toml (advisories, licenses, bans, sources) and a Security workflow job that runs cargo deny over torrential. Declare AGPL-3.0-only on torrential and droplet_types so the license check can resolve workspace crates. Now that torrential clippy is clean, run fmt + clippy in torrential CI and refresh the contributor docs.
…emediation fix(torrential): static-analysis remediation (clippy, cache/RPC hardening, cargo-deny)
added 28 commits
September 14, 2026 12:47
Phase 3 / #19: the dropworks-sdk reference clients expect a server API that did not exist. - DropworksManager exchanges an appId + Drop API token for a session and unlocks achievements, always deriving the user from the token. - POST /api/v1/dropworks/session -> { userId, gameId, appId }. - POST /api/v1/dropworks/achievement -> unlocks by stable key; a client-supplied userId must match the authenticated session (no cross-user spoofing). - Token from body authToken or Authorization: Bearer; appId resolves by Drop game id or external metadata id. - 3 manager tests (session resolution, rejection paths, unlock auth).
Phase 4 / #20 community hubs (forums were not started): - Prisma ForumThread / ForumPost models + migration and Game/User back-relations. - ForumManager with create/list/get/reply (locked threads reject) and delete, plus title/body length caps. - Public GET routes for threads and a thread with its replies; client routes for creating, replying and deleting (author or admin only). - 4 manager tests (validation, caps, locking, deletion).
Completes the forums UI for #20: - Tauri commands fetch_forum_threads / fetch_forum_thread read the public community API and deserialize into camelCase views. - community.vue loads reviews and threads together and renders a discussion section with an inline thread view. - Registered the new commands alongside fetch_game_reviews.
Phase 1 / #17 automatic peer discovery on local subnets: - New lan::mdns module: PTR query builder, a tolerant mDNS response parser (A/AAAA/PTR/SRV/TXT, compression pointers, truncation-safe) and SRV+A/AAAA peer resolution into LanPeer. - MdnsDiscovery mirrors SsdpDiscovery (bind/target/timeout, loopback testable) and exposes discover_peers(). - 5 tests (query round-trip, SRV+A resolution, compression pointers, truncated packets, loopback responder).
Phase 5 / #21 indie commerce: - Prisma PurchaseOrder model + migration with User/Game back-relations. - CommerceManager creates orders through a plugin PaymentGateway's createPaymentIntent and, on a succeeded webhook, issues exactly one Ed25519 ownership receipt (idempotent re-delivery) using a fail-closed operator key (DROP_RECEIPT_SIGNING_KEY). - Client routes: POST /orders, GET /receipts, GET /receipts/:orderid. - The payment webhook now settles orders (ignoring sandbox events). - 4 manager tests (validation/intent, idempotent issuance, failure statuses, owner-scoped receipts).
Phase 2 / #18 Drop Input: - New input::uinput module: state_to_events maps sticks/triggers/dpad to ABS events and emits every button's pressed/released state each frame. - UinputBackend opens /dev/uinput, configures EV_KEY/EV_ABS bits and axis ranges, creates the device, pushes input_event frames with SYN_REPORT, and destroys the device. create() surfaces the OS error when /dev/uinput is unavailable rather than faking a device. - supported_backends() now reports Uinput on Linux alongside the Null fallback; BackendKind docs updated. - 4 new tests (axis scaling/clamping, button+release/dpad, pre-create push error); 10 total.
Phase 5 / #21 developer publishing: - Prisma GamePrice model + migration and a Game back-relation. - PricingManager sets/lists prices and resolves only active tiers (minor units; 0 = free), with currency/amount validation. - Public GET /api/v1/store/:gameid/price?currency= and admin-only PUT /api/v1/client/commerce/prices/:gameid. - 3 manager tests (upsert, validation, active-only resolution).
Phase 4 / #20 community hubs (screenshot galleries): - ObjectHandler.setPermissions replaces an object's permission list. - ScreenshotManager gains getPublicAllByGame and setVisibility, which synchronize object permissions (owner read; anonymous:read when public) with the screenshot's visibility. - Public GET /api/v1/community/:gameid/screenshots and owner-only POST /api/v1/client/screenshots/:id/visibility. - Desktop community page renders the gallery via the existing object asset protocol; new fetch_game_screenshots Tauri command. - 2 tests for the permission mapping.
Phase 3 / #19: extend the native SDK REST contract with leaderboards. - DropworksManager.submitScore authenticates the token, checks an optional claimed userId, resolves the appId, and records the best score via the achievements leaderboard service. - POST /api/v1/dropworks/leaderboard (authenticated) and public GET /api/v1/dropworks/leaderboard/:appid/:key. - 1 new manager test (auth, spoof rejection, validation, delegation).
Phase 5 / #21 developer portal: - Prisma CrashReport model + migration (nullable user, Game/User back-relations). - TelemetryManager ingests bounded, NUL-stripped reports and lists/counts per game. - Client POST /api/v1/client/telemetry/crash and admin-only GET /api/v1/client/telemetry/:gameid/crashes. - 3 manager tests (sanitization, validation/caps, ordering + counts).
Phase 4 / #20 rich presence (core owns local presence): - Prisma UserPresence model + migration (status, optional game, freshness timestamp) with a User relation. - PresenceManager upserts presence (validated statuses), clears it, and lists fresh non-offline users joined with their profiles. - Client routes PUT/DELETE /api/v1/client/presence and GET /api/v1/client/presence/online. - 3 manager tests (upsert/validation, clear, online filtering + join).
Completes the rich-presence loop for #20: - Tauri commands report_presence / clear_presence call the authenticated client presence API and no-op quietly when unauthenticated. - The library page reports in-game with the game id after a successful launch and back online when the game is stopped.
Phase 3 / #19: native games can now set rich presence. - DropworksManager.setPresence authenticates the token, resolves the appId, validates the optional claimed userId, and forwards to the local presence service (which validates the status). - POST /api/v1/dropworks/presence with bearer or body authToken. - 1 new manager test (app resolution, override, auth/spoof/validation).
Phase 3 / #19 NAT traversal foundation: - webrtc/ice module builds STUN servers from DROP_STUN_URLS and ephemeral coturn TURN credentials (HMAC-SHA1 over an expiry:user id, from DROP_TURN_SECRET, TTL DROP_TURN_TTL_SECONDS) when both are configured. - GET /api/v1/client/ice returns the ICE servers for the authenticated user. - 5 tests (url parsing, STUN-only, credential derivation, secret required, ttl minimum).
Phase 3 / #19 NAT traversal (client half): - internal/webrtc/peer.ts wraps the webview RTCPeerConnection: offer/ answer/ICE flow over a data channel, plus pure signaling/ICE helpers. - useWebRtc composable fetches ICE config and builds a peer; the fetch_ice_config Tauri command performs the authenticated GET. - Desktop tests cover the pure helpers (11 total); typecheck/lint clean and cargo check -p drop-app passes.
Phase 4 / #20 mod manager core: - internal/workshop/planner.ts computes a dependency-first load order, reports missing dependencies, and detects file conflicts between mods. - 5 tests (ordering, missing deps, cycles, conflicts, disjoint files); desktop suite now 16 tests, typecheck clean.
The desktop mod manager needs the subscribed mod's key and gameId to fetch its manifest and plan a load order; listSubscriptions now joins the Mod relation. Existing tests assert the join.
Completes the desktop mod-manager loop for #20: - fetch_workshop_subscriptions / fetch_workshop_mod Tauri commands. - useWorkshopMods fetches subscriptions, reads each mod manifest, and plans the load order. - pages/workshop.vue shows the ordered mods, missing dependencies and file conflicts. - Desktop typecheck/lint/tests clean and cargo check -p drop-app passes.
Phase 2 / #18 per-game input configurations: - input::profile: ControllerProfile (name, deadzone, gyro sensitivity, button remaps) with serde JSON round-trip, validate_profile, remap and apply_profile (deadzone + gyro scale + remaps). - 4 tests (JSON round-trip, validation errors, apply, remap dedupe); clippy clean and cargo check -p drop-app passes.
Phase 2 / #18 handheld power management: - New power crate: PowerPolicy/PowerState/PowerDecision with a pure decide() and a Linux sysfs battery reader (parse_linux_power_state + read_linux_power_state). - download_power_decision Tauri command maps the current battery state to allow/throttle/pause for the download manager (unrestricted when unknown). - 5 tests (mains/charging, thresholds, clamping, validation, sysfs parsing); clippy clean and cargo check -p drop-app passes.
- Escape regex metacharacters when compiling plugin route patterns and cap the pattern length, so a plugin-supplied pattern cannot trigger ReDoS (semgrep detect-non-literal-regexp). - Guard localisation key traversal against __proto__/constructor/prototype and use own-property checks (semgrep prototype-pollution-loop). - Run the runtime image as an unprivileged user (uid 10001) instead of root (semgrep missing-user). Refs #68
Contributor
Author
|
Opened against the wrong base by mistake while working through an internal audit remediation; the intended PR is against the Heretek-Games fork. Closing. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Partially remediates #68 (audit finding F-11, Medium).
Changes
patternToRegexnow builds the pattern token-by-token and escapes every literal character (plus a 512-char cap), so a plugin-supplied pattern like/literal/(a+)+$can no longer inject regex metacharacters. New test asserts the literal path matches and the regex-equivalent path does not.fetchLocalisation/deleteLocalisationreject__proto__/constructor/prototypesegments and use own-property checks instead of prototype-chain access.10001instead of root (hadolint clean).Not changed (verified non-issues)
server/composables/ws.tsalready selectswss://whenlocation.protocol === "https:"; semgrep flags the fallback literal.v-htmlsites rendermicromark(...)output, which escapes raw HTML and strips dangerous URL protocols by default (verified:<img onerror>→ escaped,javascript:link →href=""). No sanitizer dependency exists in the tree; adding one is a separate decision.Verification
pnpm --filter drop run typecheckclean.pnpm --filter drop run test: 22/22 files pass.Container note
Operators using bind-mounted
/dataor/librarymust make them writable by uid 10001 (or run with a matching--user); the image pre-creates and owns them.