Dependency Management
Application dependencies likely represent our largest attack vector for security vulnerabilities. As a result, it is critical that we keep our project’s dependencies up to date to prevent exploitation. To achieve this, we use a hybrid approach of automated dependency checks via GitHub bots and manual audits.
Manual audits
Section titled “Manual audits”Manual audits should be performed on a quarterly basis. They allow us to verify that our automated dependency checks are working as expected. This is important because automated checks are not perfect and may miss critical vulnerabilities.
Manual Composer Audit
Section titled “Manual Composer Audit”-
Local Setup Install the latest production version of the project locally and ensure it runs as expected. This allows us to verify it is working correctly before attempting to update the dependencies.
-
Branching Create a new branch from
productionthat will be used to update the dependencies. -
Audit Run a composer audit to identify any vulnerabilities.
Terminal window composer audit -
Update Go through the list of vulnerabilities and update each dependency.
Terminal window composer update <package> --with-dependencies -
Verify Fixes Once all vulnerabilities have been fixed, re-run
composer auditto verify they have been resolved. -
Regression Testing Verify the project is still working as expected locally.
-
Deployment Commit the changes and push to the branch. Create a Pull Request (PR) into production for review.
Troubleshooting tips
Section titled “Troubleshooting tips”Updated package requires a higher PHP version
Section titled “Updated package requires a higher PHP version”If running artisan commands fails and complains about the PHP version, you can identify the problematic package by running:
composer prohibits <package> <version>This will show you all the packages that are incompatible with that version.
One solution is to specify the PHP version in the composer.json file and then run composer update <package> to install a compatible version.
"config": { "platform": { "php": "8.3.17" // or whatever version you need }}Manual NPM Audit
Section titled “Manual NPM Audit”-
Local Setup Install the latest production version of the project locally and ensure it runs as expected. This allows us to verify it is working correctly before attempting to update the dependencies.
-
Branching Create a new branch from
productionthat will be used to update the dependencies. -
Audit Run an audit to identify any vulnerabilities.
Terminal window npm auditTerminal window yarn audit -
Update Go through the list of vulnerabilities and update each dependency.
Terminal window npm audit fixTerminal window yarn audit fix# oryarn upgrade-interactive -
Verify Fixes Once all vulnerabilities have been fixed, re-run
npm auditoryarn auditto verify they have been resolved. -
Regression Testing Finally, try and build the project to confirm it is working as expected. Check out troubleshooting for common issues.
NPM Troubleshooting tips
Section titled “NPM Troubleshooting tips”Type errors in node_modules
Section titled “Type errors in node_modules”If you are gettting type errors in node_modules, this is likely because some of your packages are expecting different versions of a dependency. This is likely only an outdated type definition however, so you can set tsc to ignore these errors by adding the following to your tsconfig.json:
{ "compilerOptions": { "skipLibCheck": true }}Dependabot
Section titled “Dependabot”Dependabot is a feature of GitHub used to generate automated pull requests updating dependencies for projects.
- Checks for the latest version of a dependency that’s resolvable given a project’s other dependencies
- Generate updated manifest and lockfiles for a new dependency version
- Generate PR descriptions that include the updated dependency’s changelogs, release notes, and commits
Upgrade Schedule
Section titled “Upgrade Schedule”To get the most out of Dependabot, we must review, fix and merge the changes it has made on a weekly basis. As our configuration examples always run at 4pm on Sundays, we should provision time on Mondays to implement dependency updates across our deployments.
Configuration Examples
Section titled “Configuration Examples”We should have different Dependabot configurations depending on the tech stack, risk profile, dependency structure, workflow, and team expectations within a project.
Low Severity Laravel Dashboard
Section titled “Low Severity Laravel Dashboard”Medium Severity Laravel App / API
Section titled “Medium Severity Laravel App / API”High Severity Laravel SaaS
Section titled “High Severity Laravel SaaS”In this dependabot.yml example, we:
- Split updates between
composerandnpmecosystems. - Use a weekly update schedule to run at 4pm on Sunday.
- Ignore breaking changes, like major framework upgrades (these should be planned).
- Group dependencies updates by usage, to improve the isolation of code changes.
version: 2
updates: # # ===================================== # Composer – backend (highest risk) # ===================================== # - package-ecosystem: "composer" directory: "/" schedule: interval: "weekly" day: "sunday" time: "16:00" timezone: "UTC"
open-pull-requests-limit: 5 rebase-strategy: "auto" versioning-strategy: "increase-if-necessary"
labels: - "dependencies" - "composer" - "backend"
ignore: # Platform upgrades are always planned - dependency-name: "php" update-types: ["version-update:semver-major"]
# Framework upgrades are explicit roadmap items - dependency-name: "laravel/framework" update-types: ["version-update:semver-major"]
# Guaranteed blast-radius packages - dependency-name: "stancl/tenancy" update-types: ["version-update:semver-major"]
groups: # # Core framework – absolutely isolated # laravel-core: patterns: - "laravel/framework" - "illuminate/*"
# # Multitenancy & SaaS-critical infrastructure # tenancy-and-realtime: patterns: - "stancl/*" - "laravel/reverb" - "phrity/*" - "ratchet/*"
# # Observability, error reporting, monitoring # observability: patterns: - "sentry/*" - "laravel/nightwatch"
# # External services & integrations # integrations: patterns: - "laravel/forge-sdk" - "league/*" - "barryvdh/*" - "coconutcraig/*" - "intervention/*"
# # Dev & test tooling – safest to batch # dev-dependencies: dependency-type: "development" update-types: - "patch" - "minor"
# # ===================================== # npm – frontend (high churn, controlled) # ===================================== # - package-ecosystem: "npm" directory: "/" schedule: interval: "weekly" day: "sunday" time: "16:00" timezone: "UTC"
open-pull-requests-limit: 5 rebase-strategy: "auto" versioning-strategy: "increase-if-necessary"
labels: - "dependencies" - "npm" - "frontend"
ignore: # React majors = explicit migration projects - dependency-name: "react" update-types: ["version-update:semver-major"] - dependency-name: "react-dom" update-types: ["version-update:semver-major"]
# Vite majors can break build + SSR - dependency-name: "vite" update-types: ["version-update:semver-major"]
# Tailwind majors affect entire UI - dependency-name: "tailwindcss" update-types: ["version-update:semver-major"]
groups: # # React & core ecosystem # react-core: patterns: - "react" - "react-dom" - "@types/react*"
# # Inertia & Laravel bridge # inertia: patterns: - "@inertiajs/*" - "laravel-vite-plugin" - "@laravel/*"
# # UI primitives (Radix, Headless UI, Tailwind) # ui-primitives: patterns: - "@radix-ui/*" - "@headlessui/*" - "tailwind*" - "class-variance-authority" - "clsx"
# # Charts, motion & UX # ux-and-data-viz: patterns: - "recharts" - "motion" - "react-*" - "sonner"
# # Tooling & build chain # tooling: patterns: - "vite" - "@vitejs/*" - "typescript" - "@biomejs/*" - "dotenv"