feat(security): P0 + partial P1 hardening, CI pipeline, multer v2#159
Merged
Conversation
Closes the critical security and dependency gaps surfaced in the codebase audit. All 20 unit tests pass; no behavior change for normal request paths. Backend security - Sanitize regex search inputs across 7 controllers (AI, AdvancedGlobalFilter, Comments, MediaFiles, Project filter, notification-count, trackerDownload) via new utils/escapeRegex.js. Blocks NoSQL regex injection / ReDoS / cross-tenant name enumeration. - Add helmet + global express-rate-limit in index.js. CSP disabled to preserve the Vue inline-script setup; secure / sameSite already env-gated correctly. trust-proxy set to 'loopback' to silence the ERR_ERL_UNEXPECTED_X_FORWARDED_FOR warning behind a same-host reverse proxy. - Harden all 4 multer call sites via new utils/uploadConfig.js (DEFAULT_LIMITS, safeFileFilter blocking executable extensions, safeRelativePath for traversal protection). bucket.helper.js storage destination now validates path before writing. - New requireCompanyAud middleware (Config/jwt.js) enforces the JWT `aud` claim against any companyId in body / params / query / header on the ~50 verifyJWTTokenV2-only routes. Non-ObjectId values (e.g. USER_PROFILES global bucket) pass through. Dependencies - Remove aws-sdk v2 from package.json (EOL Sept 2025); only @aws-sdk v3 was actually imported. - Bump multer 1.4.5-lts.1 -> 2.1.1 to clear known 1.x CVEs. Schema strictness (P1-SEC-11) - utils/mongo-handler/createSchema.js: flipped 7 core schemas to `strict: true` (tasks, comments, timesheet, history, adminDetail, subscriptionPlan, globalCustomFields). Kept `strict: false` on 7 intentionally-dynamic schemas (notification counters, Chargebee webhook mirrors, custom-field definitions, plan-feature maps) with inline comments explaining why. CI / DevOps - .github/workflows/main.yml: added a `validate` job (npm test, npm audit advisory, frontend install + build) that the deploy job now `needs:`. Previously deploys ran with zero validation. Diagnostics - modules/storage/wasabi/controller.js: replaced 6 generic `Error while upload file: ${error}` rejects with a formatS3UploadError helper that logs the AWS error Code, HTTP status, bucket, key, and requestId. Documented for follow-up - Auth/controller.js: TODO comments at both res.cookie sites explaining why httpOnly stays false until the frontend stops reading the cookie via js-cookie (P1-SEC-09 deferred). Pre-existing fixes pulled in - tests/utils/imageGuard.test.js: corrected env var names so the suite goes from 19/20 to 20/20 passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CI pipeline added in the same PR is the first thing to run
tests on Ubuntu. `path.isAbsolute('C:\Windows\...')` returns
false on Linux, so the safeServiceFile absolute-path guard fell
through and the request hit the `.json` extension check instead.
The test in `tests/utils/safeServiceFile.test.js:46` expects the
"relative" error in both shapes and was passing on Windows hosts
by accident.
Use `path.win32.isAbsolute(...)` alongside the platform-bound
`path.isAbsolute(...)` so both POSIX (`/etc/hosts`) and Windows
(`C:\Windows\...`) absolute paths are rejected regardless of
which OS the process runs on. This is also a defensive improvement
to BUG-036: a Linux deployment can no longer be coaxed into
re-resolving a Windows-shaped path inside the project root.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Closes the critical security and dependency gaps surfaced in the recent codebase audit. All 20 unit tests pass; no behavior change for normal request paths.
Backend security (P0)
utils/escapeRegex.js. Blocks ReDoS and cross-tenant name enumeration.helmet+ globalexpress-rate-limitwired inindex.js. CSP disabled to preserve the Vue inline-script setup.trust proxy: 'loopback'to silenceERR_ERL_UNEXPECTED_X_FORWARDED_FORbehind a same-host proxy.utils/uploadConfig.jswith size/file-count limits, an extension blocklist, and path-traversal protection.requireCompanyAudmiddleware checks the JWTaudagainst anycompanyIdin body/params/query/header for the ~50verifyJWTTokenV2-only routes. Non-ObjectId values (e.g.USER_PROFILES) pass through.Dependencies (P0)
aws-sdkv2 (EOL Sept 2025) — only@aws-sdk/*v3 was actually imported.multer1.4.5-lts.1->2.1.1(1.x CVE clearance).Schema strictness (P1-SEC-11)
strict: true(tasks, comments, timesheet, history, adminDetail, subscriptionPlan, globalCustomFields).CI / DevOps (P0)
.github/workflows/main.ymlnow runs avalidatejob (test, audit advisory, frontend build) before the deploy step. Previously deploys ran with zero validation.Diagnostics
modules/storage/wasabi/controller.js: 6 generic upload-error rejects replaced withformatS3UploadError(error, context)— logs AWS errorCode, status, bucket, key,requestId.Deferred / documented
httpOnlycookies): the frontend reads both cookies viajs-cookie(5 files). FlippinghttpOnlyrequires a frontend refactor first.TODO(P1-SEC-09)comments left at bothres.cookiesites.Pre-existing fix pulled in
tests/utils/imageGuard.test.js: env-var names corrected. Suite goes 19/20 -> 20/20.Test plan
npm test— 20/20 passingescapeRegexround-trip test against ReDoS payload(a+)+safeRelativePathsmoke test against traversal payloadsstrict=trueat runtimeRateLimit-*headersRisks to watch
utils/mongo-handler/schema.js.GLOBAL_RATE_LIMIT_PER_MINandTRUST_PROXY.🤖 Generated with Claude Code