DEV Community

Mustafa ERBAY
Mustafa ERBAY

Posted on • Originally published at mustafaerbay.com.tr

Dependency Vulnerability Pattern: Management Status in Small Projects

When dealing with small and medium-sized projects, dependency vulnerability management is often an overlooked but troublesome issue. At first, everything seems fine; you add your package, your code runs. But over time, dependencies grow, versions become outdated, and suddenly security vulnerabilities start knocking on your door. This situation reveals a significant management pattern, especially for small teams with limited resources.

In my opinion, this pattern is not just a technical problem but also a career issue. Because in such projects, the responsibility for managing security vulnerabilities often falls on the shoulders of a few people like me, who deal with both systems and software. An overlooked vulnerability can damage the reputation of the entire project and even lead to operational disruptions.

Dependency Hell and the First Symptoms

When we start a software project, we rely on hundreds of libraries and frameworks to speed things up. These dependencies form the foundation of the project and are often very useful. However, the other side of this coin is that each dependency brings its own security risks. In small projects, tracking these risks often takes a backseat.

In my experience, the first symptoms of this situation usually begin with warnings in the CI/CD pipeline. Perhaps an npm audit command, or a pip check output, starts spitting out red lines to the console. Although we might initially dismiss them as "warnings, not errors," these alerts are actually the tip of an iceberg. For instance, one of the sub-dependencies of a Python library I used in the backend of one of my side projects was found to have a critical RCE vulnerability. The pip audit output showed something like this:

Found 1 vulnerability affecting 1 package
Name: requests
Version: 2.25.1
ID: PYSEC-2023-42
Advisory: GHSA-w7w5-5mda-26jv
Severity: CRITICAL
Description: requests allows request smuggling with Transfer-Encoding.
        A malicious server could smuggle requests through a vulnerable proxy.
        This affects requests <2.26.0.
Enter fullscreen mode Exit fullscreen mode

Outputs like this are often postponed in small projects with a "not now" attitude. This is because it creates an immediate workload, requiring updates, testing, and perhaps dealing with compatibility issues. However, this postponement invites bigger problems over time.

Resource Constraints and Risk Perception in Small Teams

The biggest challenge for small teams is always working with limited resources. The number of developers is small, the budget is tight, and time is the most valuable asset. In this environment, developing new features, meeting customer demands, and fixing existing bugs become much higher priorities than tracking security vulnerabilities. Risk perception is also affected by this situation.

In my opinion, the thought "nothing will happen to us" is quite common in small projects. No one wants to think their project will be targeted. However, cyber attackers don't discriminate between large or small projects; they try to enter through any door they find open. While working on a manufacturing company's ERP, we once discovered that a critical dependency vulnerability made a part of the system capable of leaking information externally. Although not a direct attack, it indicated a potential breach risk. The 8 hours we spent fixing this vulnerability at that time directly prevented us from developing 3 different small features we had planned. This is a direct trade-off: you either develop a feature with immediate high value, or you prevent a potential future crisis. Most small teams choose the former.

⚠️ Misperception of Risk

In small projects, thoughts like "who cares about us?" or "we're not a big target" can lead to the postponement of security vulnerabilities. However, attacks are often carried out with automated tools, and the size of the project doesn't matter; only the existence of a weakness does.

Lack of Automation and the Traps of Manual Tracking

Automation in dependency vulnerability management has become a standard practice for large projects. However, in small projects, the initial investment cost (time and knowledge) required to set up this automation is often overlooked. This leads to manual tracking, which is a trap in itself.

In my practice, manual tracking initially involves simply reviewing a requirements.txt file. Perhaps once a week, or once a month, a quick search is done for "is there a new vulnerability?" However, this method is doomed to overlook vulnerabilities that go deep into the dependency tree and cover transitive dependencies. Once, during a security audit on a client project, we encountered a critical vulnerability detected in a third-level dependency that even I had failed to notice. Finding this vulnerability manually was almost impossible. That's when I realized that just checking the main dependencies is not enough; the entire dependency tree needs to be scanned.

# Checking only main dependencies can be misleading
# This is a common mistake in manual tracking.

# requirements.txt
# requests==2.25.1
# django==3.2.0

# Many dependencies have their own sub-dependencies.
# requests -> urllib3 -> chardet etc.
# Django also has hundreds of sub-dependencies.
# A vulnerability in any link of this chain can affect the entire system.
Enter fullscreen mode Exit fullscreen mode

This kind of manual tracking becomes unsustainable over time and leads to the accumulation of security vulnerabilities. If a project has 50 main dependencies, each of them can have dozens of sub-dependencies. Manually tracking this chain requires a full-time job, which is a luxury for a small team.

Real-World Cases and Unexpected Impacts

Dependency vulnerabilities sometimes go beyond mere warnings and turn into a real operational nightmare. In small projects, the cost of such a situation can be much more devastating than in large projects because recovery resources are more limited.

One of the most striking examples I've seen in my career was related to a vulnerability in a frontend library used in a manufacturing ERP. An old version of this library had an XSS (Cross-Site Scripting) vulnerability. We didn't notice it at first because there was no bug directly in our code. However, a user managed to trigger this vulnerability with specially crafted input, allowing them to run their desired JavaScript code in other operators' browsers. This situation caused data corruption and temporary system unavailability on the screens of more than 20 operators.

This incident cost us about 4 hours of downtime and data recovery efforts. The worst part was that the time and energy spent to detect and fix this vulnerability could have been prevented much earlier with a regular vulnerability scanning process. This case demonstrated how even a small dependency vulnerability can have a significant operational impact, especially in systems working in critical processes like manufacturing.

ℹ️ Downtime Cost

A 4-hour operational outage might seem like a small figure for a large organization, but for a small business with limited resources, it can lead to significant financial and reputational loss. Therefore, every vulnerability represents a potential crisis.

Solution Approaches and Pragmatic Strategies

So, how can we deal with this dependency vulnerability pattern in small projects? While implementing large corporate solutions may not always be feasible, taking pragmatic and effective steps is definitely possible. My favorite approaches involve integrating automation as early as possible and establishing a culture of continuous tracking.

The first step is to use automated scanning tools. Commands like npm audit, pip audit, go mod security are good starting points for scanning project dependencies. By integrating these into your CI/CD pipeline, you can ensure that scans are performed automatically with every push or merge operation. For example, it can even be integrated as a pre-commit hook:

# .github/workflows/security-scan.yml (Example GitHub Actions)
name: Dependency Security Scan

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.x'
      - name: Install dependencies
        run: pip install -r requirements.txt
      - name: Run pip audit
        run: pip audit --strict # --strict will fail on vulnerable dependencies
      # Similarly for Node.js projects:
      # - uses: actions/setup-node@v3
      #   with:
      #     node-version: '18'
      # - run: npm install
      # - run: npm audit --audit-level=high
Enter fullscreen mode Exit fullscreen mode

These types of automations alert you immediately whenever a new dependency is added or existing ones are updated. Secondly, using tools like Dependabot allows you to automatically track dependency updates and create Pull Requests. This significantly reduces the burden of manual tracking. Thirdly, it's important to identify critical dependencies and give them special attention. Not every dependency carries the same risk level. The difference in risk between a database driver and a UI library is obvious.

Continuous Improvement and Cultural Shift

Dependency vulnerability management is not a one-time task; it's a continuous process. It requires a cultural shift. In small projects, initiating and sustaining this change is the responsibility of those in leadership.

My preference is to regularly bring this topic to the team's agenda. Perhaps add a small item called "security vulnerability status" in weekly meetings. Or, before every deployment, ensure that dependency scans have passed. I even automatically fail the CI/CD pipeline for any vulnerability above a certain audit level in one of my side projects. While this might seem like a small push, it ensures that everyone takes this matter seriously in the long run.

We must remember that no system is 100% secure. What's important is to minimize risks and establish a mechanism to respond quickly when a vulnerability is detected. Last month, I received a CVE alert due to an outdated tool I was using in a systemd unit on my own system. I updated it immediately and automated this process. Sometimes we make such mistakes; what's important is to learn from them and make the system more resilient. This means asking, "This happened, but what can I do to prevent it from happening again?" instead of just saying, "It happens."

💡 A Simple Checklist

A simple checklist can be useful for maintaining dependency vulnerability management in small projects:

  • Are weekly/monthly automated dependency scans being run?
  • Are we receiving instant notifications for critical and high-level vulnerabilities?
  • Are dependency updates being tracked with Dependabot or a similar tool?
  • Before adding a new dependency, are its known vulnerabilities being checked?
  • Are old and unused dependencies being regularly cleaned up?

Understanding and managing the dependency vulnerability pattern in small projects is not just a technical task but also a critical step for the sustainability of the project and my career. Embracing automation, improving risk perception, and making continuous improvement a culture are the most pragmatic ways to overcome these challenges.

Top comments (0)