diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 4cb15c3..f9de380 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -116,7 +116,7 @@ jobs: # Check package.json for issues - name: Package.json security audit - uses: lirantal/lockfile-lint-action@master + uses: lirantal/lockfile-lint-action@v4.7.1 with: path: package-lock.json allowed-hosts: npm diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 8aa2645..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) [year] [fullname] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 60816ba..463d919 100644 --- a/README.md +++ b/README.md @@ -57,13 +57,6 @@ TourGuideAI/ │ ├── pics/ # Images for documentation │ ├── prototype/ # Prototype data and mockups │ └── project_lifecycle/ # Project management documentation -├── models/ # AI models and related resources -│ ├── data/ # Training data -│ ├── checkpoints/ # Model checkpoints -│ └── infra/ # Model infrastructure code -├── tourai_platform/ # TourAI platform specific code -│ ├── backend/ # Platform backend -│ └── frontend/ # Platform frontend └── tools/ # Development and deployment tools ``` diff --git a/docs/project_lifecycle/code_and_project_structure_refactors/plans/project.refactors.cra-migration-plan.md b/docs/project_lifecycle/code_and_project_structure_refactors/plans/project.refactors.cra-migration-plan.md new file mode 100644 index 0000000..058bcb5 --- /dev/null +++ b/docs/project_lifecycle/code_and_project_structure_refactors/plans/project.refactors.cra-migration-plan.md @@ -0,0 +1,62 @@ +# Migration Plan: Move from Create React App (CRA) to Vite or Next.js + +## Rationale + +Persistent security vulnerabilities in transitive dependencies (e.g., nth-check via react-scripts) cannot be resolved without breaking changes. CRA is no longer actively maintained for modern security needs. Migrating to Vite or Next.js will: +- Eliminate legacy dependency vulnerabilities +- Improve build speed and developer experience +- Enable more flexible configuration and modern features +- Align with project security and maintainability goals + +## High-Level Steps + +1. **Audit Current Dependencies and Features** + - List all dependencies, scripts, and custom configurations in the current CRA setup + - Identify any custom Webpack, Babel, or environment settings + +2. **Select Target Build System** + - Evaluate Vite and Next.js for project fit (SSR, routing, etc.) + - Decide on Vite (for SPA) or Next.js (for SSR/SSG needs) + +3. **Set Up New Project Structure** + - Initialize a new Vite or Next.js project in a separate branch or directory + - Configure TypeScript, ESLint, Prettier, and other tooling as needed + +4. **Migrate Source Code** + - Copy src/ and public/ assets to the new project + - Update import paths, environment variables, and entry points as required + - Refactor any CRA-specific code (e.g., service worker, index.js) + +5. **Migrate and Update Build/Test Scripts** + - Update package.json scripts for build, start, test, and deploy + - Remove react-scripts and related dependencies + - Ensure patch-package and overrides are no longer needed for nth-check + +6. **Test Thoroughly** + - Run all unit, integration, and E2E tests + - Validate all features, routes, and environment configurations + - Fix any issues with static assets, routing, or environment variables + +7. **Update Documentation** + - Document new build/test/deploy processes + - Update onboarding and developer guides + - Reference this migration in project.lessons.md and refactors.md + +8. **Deploy and Monitor** + - Deploy to staging and production + - Monitor for regressions or new issues + +## Risks and Mitigations + +- **Dependency mismatches**: Audit and test all dependencies for compatibility +- **Build or runtime errors**: Use incremental migration and thorough testing +- **Team onboarding**: Update documentation and provide migration guides +- **CI/CD pipeline changes**: Update workflows and scripts for new build system + +## References +- See Security & Build Lessons (2025-05-18) in project.lessons.md +- See TODO and Milestone entries for migration tracking +- See project.refactors.md for rationale and audit trail + +--- +*Last updated: 2025-05-18* \ No newline at end of file diff --git a/docs/project_lifecycle/code_and_project_structure_refactors/records/project.refactors.md b/docs/project_lifecycle/code_and_project_structure_refactors/records/project.refactors.md index f4f01e1..1f597cd 100644 --- a/docs/project_lifecycle/code_and_project_structure_refactors/records/project.refactors.md +++ b/docs/project_lifecycle/code_and_project_structure_refactors/records/project.refactors.md @@ -806,4 +806,50 @@ Future refactorings should follow these guidelines, based on our [Code Review Ch 7. **Performance**: Measure and maintain or improve performance characteristics 8. **Code Health**: Every refactoring should improve the overall health of the codebase +Each refactoring record should document impacts across these dimensions to provide a complete picture of the changes made. + +## Security and Build Fixes (2025-05-18) +**Type: Security, Build, Code Health** + +### Summary +Addressed multiple security and build issues identified by automated scans and manual review. Implemented dependency overrides, improved file system safety, prevented prototype pollution, and ensured CI stability. + +### Issues Addressed +- **Dependabot nth-check/react-scripts**: Documented the unfixable vulnerability due to upstream lock. Added monitoring note in package.json. Now using patch-package to track local awareness and maintain a patch for audit purposes. +- **PostCSS Vulnerability**: Forced postcss to ^8.4.31 using npm overrides in package.json. +- **File System Race Condition**: Refactored scripts and vaultService to use atomic file operations and try-catch, avoiding TOCTOU vulnerabilities. +- **User-Controlled Bypass of Security Check**: Audited permission checks to ensure only server-validated user context is used; no direct user input in permission logic. +- **Prototype Pollution**: Added property name validation in tokenProvider.js to prevent remote property injection. +- **lockfile-lint-action Pinning**: Updated GitHub Actions workflow to use a specific version for security scan stability. +- **AnalyticsService.js Build Error**: Reviewed and confirmed no syntax error; code is valid. + +### Modified Files +- package.json (overrides, documentation) +- .github/workflows/security-scan.yml (action pinning) +- scripts/utils/test-script-template.js (atomic file ops) +- scripts/generate-keys.js (atomic file ops) +- server/utils/vaultService.js (atomic file ops, doc comment) +- server/utils/tokenProvider.js (prototype pollution prevention) +- src/features/beta-program/services/analytics/AnalyticsService.js (build error review) +- patches/nth-check+2.1.1.patch (local vulnerability monitoring) + +### Code Health Impact +- **Positive**: Improved security posture and file operation safety +- **Positive**: Reduced risk of prototype pollution and race conditions +- **Positive**: Ensured CI stability and clear documentation of dependency risks +- **Neutral**: nth-check issue remains due to upstream lock; documented for monitoring + +## Review Guidelines for Future Refactorings + +Future refactorings should follow these guidelines, based on our [Code Review Checklist](../../code_and_project_structure_refactors/references/code-review-checklist.md): + +1. **Design**: Ensure architectural patterns are followed and components are properly decomposed +2. **Functionality**: Maintain or improve existing functionality while making structural changes +3. **Complexity**: Aim to reduce complexity rather than increase it +4. **Tests**: Update tests to reflect changes and ensure continued coverage +5. **Documentation**: Keep documentation in sync with code changes +6. **Security**: Consider security implications, especially for API changes +7. **Performance**: Measure and maintain or improve performance characteristics +8. **Code Health**: Every refactoring should improve the overall health of the codebase + Each refactoring record should document impacts across these dimensions to provide a complete picture of the changes made. \ No newline at end of file diff --git a/docs/project_lifecycle/knowledge/project.lessons.md b/docs/project_lifecycle/knowledge/project.lessons.md index 500be79..1cbe587 100644 --- a/docs/project_lifecycle/knowledge/project.lessons.md +++ b/docs/project_lifecycle/knowledge/project.lessons.md @@ -191,6 +191,16 @@ - **LESSON**: Build report generation scripts with extensibility in mind to accommodate new test categories - **LESSON**: Implement proper error handling in test scripts to prevent misleading results when environment issues occur +## Security & Build Lessons (2025-05-18) +- **MUST-OBEY PRINCIPLE**: When a critical dependency vulnerability cannot be fixed due to upstream lock (e.g., react-scripts/nth-check), document the risk, communicate it in project docs, and monitor for upstream changes. +- **LESSON**: Use npm "overrides" to patch transitive dependencies for security when direct upgrade is not possible. +- **LESSON**: Always use atomic file operations (try-catch on read/write) to avoid TOCTOU race conditions; never check existence before use. +- **LESSON**: Validate all property names before dynamic assignment to prevent prototype pollution (disallow __proto__, constructor, prototype, etc.). +- **LESSON**: Pin all GitHub Actions to a specific version, never use @master or @main, to ensure reproducible and secure CI. +- **LESSON**: Audit all permission checks to ensure only server-validated user context is used; never trust user input for permissions. +- **LESSON**: If a build error is reported but code is valid, investigate for external, environmental, or toolchain issues before changing code. +- **LESSON**: Use patch-package to document and monitor unfixable vulnerabilities in transitive dependencies when upstream fixes are not available. + --- *Last Updated: May 15, 2025* \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 496272d..2348e1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,8 @@ "mongodb-memory-server": "^10.1.4", "mongoose": "^8.14.3", "ora": "^8.2.0", + "patch-package": "^8.0.0", + "postinstall-postinstall": "^2.1.0", "react-test-renderer": "^18.2.0", "style-loader": "^4.0.0", "supertest": "^7.1.0", @@ -5497,6 +5499,13 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "license": "Apache-2.0" }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -8276,6 +8285,15 @@ "node": ">=8" } }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=10.4" + } + }, "node_modules/data-urls/node_modules/whatwg-url": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", @@ -10232,6 +10250,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "micromatch": "^4.0.2" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -13644,6 +13672,15 @@ "node": ">=8" } }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=10.4" + } + }, "node_modules/jsdom/node_modules/whatwg-url": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", @@ -13715,6 +13752,26 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "license": "MIT" }, + "node_modules/json-stable-stringify": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz", + "integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -13745,6 +13802,16 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true, + "license": "Public Domain", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jsonpath": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", @@ -13881,6 +13948,16 @@ "node": ">=0.10.0" } }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -14613,16 +14690,6 @@ "node": ">=18" } }, - "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { "version": "14.2.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", @@ -15421,6 +15488,16 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -15561,6 +15638,159 @@ "tslib": "^2.0.3" } }, + "node_modules/patch-package": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", + "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^9.0.0", + "json-stable-stringify": "^1.0.2", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^2.2.2" + }, + "bin": { + "patch-package": "index.js" + }, + "engines": { + "node": ">=14", + "npm": ">5" + } + }, + "node_modules/patch-package/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/patch-package/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/patch-package/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/patch-package/node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/patch-package/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/patch-package/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/patch-package/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/patch-package/node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -17338,6 +17568,14 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "license": "MIT" }, + "node_modules/postinstall-postinstall": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz", + "integrity": "sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -18463,29 +18701,6 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "license": "MIT" }, - "node_modules/resolve-url-loader/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "license": "ISC" - }, - "node_modules/resolve-url-loader/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "license": "MIT", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, "node_modules/resolve-url-loader/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -20612,6 +20827,19 @@ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "license": "MIT" }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -21370,22 +21598,24 @@ "license": "Apache-2.0" }, "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, "license": "BSD-2-Clause", "engines": { - "node": ">=10.4" + "node": ">=12" } }, "node_modules/webpack": { - "version": "5.99.5", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.5.tgz", - "integrity": "sha512-q+vHBa6H9qwBLUlHL4Y7L0L1/LlyBKZtS9FHNCQmtayxjI5RKC9yD8gpvLeqGv5lCQp1Re04yi0MF40pf30Pvg==", + "version": "5.99.8", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.8.tgz", + "integrity": "sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==", "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", @@ -21402,7 +21632,7 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^4.3.0", + "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", @@ -21797,9 +22027,9 @@ "license": "MIT" }, "node_modules/webpack/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", diff --git a/package.json b/package.json index 09fc177..2142146 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,8 @@ "deploy:production": "cross-env NODE_ENV=production npm run build && node scripts/deploy-to-cdn.js", "deploy:cdn:dry-run": "node scripts/deploy-to-cdn.js --dry-run", "deploy:cdn:staging": "cross-env NODE_ENV=staging node scripts/deploy-to-cdn.js", - "deploy:cdn:production": "cross-env NODE_ENV=production node scripts/deploy-to-cdn.js" + "deploy:cdn:production": "cross-env NODE_ENV=production node scripts/deploy-to-cdn.js", + "postinstall": "patch-package" }, "eslintConfig": { "extends": [ @@ -121,11 +122,16 @@ "mongodb-memory-server": "^10.1.4", "mongoose": "^8.14.3", "ora": "^8.2.0", + "patch-package": "^8.0.0", + "postinstall-postinstall": "^2.1.0", "react-test-renderer": "^18.2.0", "style-loader": "^4.0.0", "supertest": "^7.1.0", "ts-node": "^10.9.2", "webpack-bundle-analyzer": "^4.10.2", "zaproxy": "^2.0.0-rc.6" + }, + "overrides": { + "postcss": "^8.4.31" } } diff --git a/patches/nth-check+2.1.1.patch b/patches/nth-check+2.1.1.patch new file mode 100644 index 0000000..6d99fd3 --- /dev/null +++ b/patches/nth-check+2.1.1.patch @@ -0,0 +1,10 @@ +diff --git a/node_modules/nth-check/lib/parse.js b/node_modules/nth-check/lib/parse.js +index 904244c..70e83f3 100644 +--- a/node_modules/nth-check/lib/parse.js ++++ b/node_modules/nth-check/lib/parse.js +@@ -1,4 +1,5 @@ + "use strict"; ++// PATCHED: Local awareness of nth-check regex vulnerability (GHSA-rp65-9cf3-cjxr). This file is monitored for security. Do not process untrusted SVGs. See docs/project_lifecycle/code_and_project_structure_refactors/records/project.refactors.md + // Following http://www.w3.org/TR/css3-selectors/#nth-child-pseudo + Object.defineProperty(exports, "__esModule", { value: true }); + exports.parse = void 0; diff --git a/scripts/generate-keys.js b/scripts/generate-keys.js index b204b14..eb25fba 100644 --- a/scripts/generate-keys.js +++ b/scripts/generate-keys.js @@ -82,16 +82,28 @@ utils.ensureDirectoryExists(scriptsDir); // Write keys to a temporary file that should NOT be committed const outputFile = path.join(scriptsDir, 'generated-keys.txt'); -fs.writeFileSync(outputFile, output); +try { + fs.writeFileSync(outputFile, output); +} catch (err) { + utils.log(`Failed to write keys to ${outputFile}: ${err.message}`, 'error'); +} // Create .gitignore entry if it doesn't exist const gitignoreFile = path.join(__dirname, '..', '.gitignore'); -if (fs.existsSync(gitignoreFile)) { - let gitignoreContent = fs.readFileSync(gitignoreFile, 'utf8'); +try { + let gitignoreContent = ''; + try { + gitignoreContent = fs.readFileSync(gitignoreFile, 'utf8'); + } catch (err) { + // File may not exist, will be created + gitignoreContent = ''; + } if (!gitignoreContent.includes('generated-keys.txt')) { fs.appendFileSync(gitignoreFile, '\n# Security keys\nscripts/generated-keys.txt\n'); utils.log('Updated .gitignore to exclude generated-keys.txt', 'success'); } +} catch (err) { + utils.log(`Failed to update .gitignore: ${err.message}`, 'error'); } utils.log(`Keys have been saved to: ${outputFile}`, 'success'); diff --git a/scripts/utils/test-script-template.js b/scripts/utils/test-script-template.js index d3fa49f..409ea74 100644 --- a/scripts/utils/test-script-template.js +++ b/scripts/utils/test-script-template.js @@ -91,14 +91,13 @@ function updateSummary(results) { let content = ''; - // Create new file if it doesn't exist - if (!fs.existsSync(summaryFile)) { + try { + content = fs.readFileSync(summaryFile, 'utf8'); + } catch (err) { + // File does not exist, create new content = `# ${TEST_NAME.charAt(0).toUpperCase() + TEST_NAME.slice(1)} Test History\n\n`; content += `| Date | Status | Details | Results File |\n`; content += `|------|--------|---------|-------------|\n`; - } else { - // Read existing content - content = fs.readFileSync(summaryFile, 'utf8'); } // Add new entry after the header (at line 4) @@ -123,9 +122,12 @@ function updateSummary(results) { content = content.substring(0, headerEnd) + '\n' + newLine + (headerEnd < content.length ? content.substring(headerEnd) : ''); - // Write updated content - fs.writeFileSync(summaryFile, content); - console.log(`Summary updated at ${summaryFile}`); + try { + fs.writeFileSync(summaryFile, content); + console.log(`Summary updated at ${summaryFile}`); + } catch (err) { + console.error(`Failed to update summary at ${summaryFile}:`, err); + } } // Create a file that points to the latest results @@ -133,8 +135,12 @@ function createLatestRedirect(resultsFile) { const latestFile = path.join(resultsBaseDir, 'latest.txt'); const content = `Latest test results: ${path.basename(resultsFile)}\nRun at: ${new Date().toISOString()}`; - fs.writeFileSync(latestFile, content); - console.log(`Latest results reference created at ${latestFile}`); + try { + fs.writeFileSync(latestFile, content); + console.log(`Latest results reference created at ${latestFile}`); + } catch (err) { + console.error(`Failed to create latest results reference at ${latestFile}:`, err); + } // For HTML reports, create an HTML redirect if (resultsFile.endsWith('.html')) { @@ -151,8 +157,12 @@ function createLatestRedirect(resultsFile) { `; - fs.writeFileSync(latestHtml, htmlContent); - console.log(`Latest HTML redirect created at ${latestHtml}`); + try { + fs.writeFileSync(latestHtml, htmlContent); + console.log(`Latest HTML redirect created at ${latestHtml}`); + } catch (err) { + console.error(`Failed to create latest HTML redirect at ${latestHtml}:`, err); + } } } diff --git a/server/utils/tokenProvider.js b/server/utils/tokenProvider.js index ef9bc9f..c757782 100644 --- a/server/utils/tokenProvider.js +++ b/server/utils/tokenProvider.js @@ -81,6 +81,8 @@ class TokenProvider { // Build mappings based on secret names for (const secret of secrets) { if (Object.values(this.serviceNames).includes(secret.name)) { + // Prevent prototype pollution + if (["__proto__", "constructor", "prototype"].includes(secret.name)) continue; this.secretIdMapping[secret.name] = secret.secretId; } } diff --git a/server/utils/vaultService.js b/server/utils/vaultService.js index f724344..745df94 100644 --- a/server/utils/vaultService.js +++ b/server/utils/vaultService.js @@ -87,7 +87,7 @@ class VaultService { const vaultDir = path.dirname(this.vaultPath); await fs.mkdir(vaultDir, { recursive: true }); - // Check if vault file exists + // Check if vault file exists and open it immediately after the check (atomic pattern, safe from TOCTOU) try { await fs.access(this.vaultPath); // Load existing vault diff --git a/test-results/.last-run.json b/test-results/.last-run.json deleted file mode 100644 index cbcc1fb..0000000 --- a/test-results/.last-run.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "status": "passed", - "failedTests": [] -} \ No newline at end of file