Home Technical Support Integrating Security Scanning Tools into GitLab Pipelines – A Practical Guide

Integrating Security Scanning Tools into GitLab Pipelines – A Practical Guide

Last updated on Jan 06, 2026

Integrating Security Scanning Tools into GitLab Pipelines – A Practical Guide

Security‑focused CI/CD pipelines are a cornerstone of modern DevSecOps. This article walks you through the most common hurdles learners face when adding Safety (Python dependency scanner) and RetireJS (JavaScript component analyzer) to GitLab pipelines. You’ll learn how to interpret version specifiers, structure pipeline jobs, filter high‑severity findings, and correctly use the supporting files (requirements.txt, package.json, .retireignore.json).


1. Why the “unpinned requirement” warning appears in Safety

The warning explained

When you run the Safety step in the Fix issues reported by Safety lab you may see:

Warning: unpinned requirement 'Django' found in requirements.txt, unable to check.

This is not an error; it’s a warning that Safety cannot verify a dependency that isn’t pinned to a specific version.

Understanding Python version specifiers

Specifier Meaning Example outcome
Django==2.2.25 Install exactly version 2.2.25 Only 2.2.25 is used
Django>=2.2.25 Install any version 2.2.25 2.2.25, 2.3.0, 3.0.0 …
Django~=2.2.25 Install the latest minor release within the same major/minor series 2.2.25, 2.2.26, 2.2.27 … but not 2.3.0

The tilde (~) is intentional – it tells pip to fetch the newest patch release while staying within the 2.2.x line. If you prefer a fully pinned version, replace ~= with ==.

Reference:PEP 440 – Version Specifiers


2. Embedding Safety into a GitLab pipeline

Two‑job pattern (recommended)

Safety requires a Docker image that contains the safety binary, while your unit‑test job needs a Python image. Keeping them separate avoids image‑conflict issues.

# .gitlab-ci.yml
stages:
  - test

# 1️⃣ Unit‑test job – runs your Python code
test:
  stage: test
  image: python:3.6
  before_script:
    - pip3 install --upgrade virtualenv
  script:
    - virtualenv env
    - source env/bin/activate
    - pip install -r requirements.txt
    - python manage.py test taskManager

# 2️⃣ Safety scanning job – uses the official Safety image
oast:
  stage: test
  script:
    - docker pull hysnsec/safety               # fetch the scanner
    - docker run --rm -v $(pwd):/src hysnsec/safety check -r requirements.txt --json > oast-results.json
  artifacts:
    paths: [oast-results.json]
  when: always
  allow_failure: true

Why two jobs?

  • Different base images (python:3.6 vs. hysnsec/safety).
  • Clear separation of concerns – unit testing vs. security scanning.
  • Each job can fail independently without breaking the other stage.

If you see docker: command not found, verify that the GitLab Runner is configured with Docker‑in‑Docker (DinD) or that you’re using a shared runner that supports Docker commands.


3. Software Component Analysis with RetireJS

Goal of the challenge

Identify high‑severity JavaScript vulnerabilities, mark them as false positives (FPs), and store those markings in a .retireignore.json file.

Step‑by‑step workflow

  1. Run RetireJS and generate JSON output

    script:
      - npm install            # installs project deps from package.json
      - npm install -g retire  # installs the retire CLI globally
      - retire --outputformat json --outputpath retire_output.json
    

    npm install reads the package.json in the current directory and installs every dependency listed under dependencies and devDependencies.

  2. Filter high‑severity findings

    Use jq (a lightweight JSON processor) to extract only the records you need:

    jq '.data[].results[]' retire_output.json |
      grep -E 'component|version|high'
    

    What it does:

    • jq '.data[].results[]' walks into the results array of each scanned file.
    • grep -E 'component|version|high' prints lines containing the component name, its version, or the word “high”. The result is a list of vulnerable components with high severity.
  3. Create the .retireignore.json file

    For every high‑severity entry you decide is a false positive, add an object to the ignore file:

    [
      {
        "component": "qs",
        "version": "0.6.6",
        "justification": "Vulnerable class is not used"
      },
      {
        "component": "handlebars",
        "version": "4.0.5",
        "justification": "Vulnerable class is not used"
      }
    ]
    

    The structure follows the RetireJS specification: you can ignore by component, component + version, or by path (e.g., node_modules).

  4. Optional: Download the JSON locally

    If you prefer a GUI editor, copy retire_output.json from the runner’s workspace to your machine (e.g., via the GitLab UI “Download artifacts” button) and edit it with Sublime, VS Code, or any text editor.


4. Where do package.json and requirements.txt come from?

  • package.json – Part of the Node.js source code you are scanning. It lists JavaScript libraries under dependencies and devDependencies. The npm install command reads this file and materialises the node_modules folder.

  • requirements.txt – The Python counterpart located in the Python project’s repository. It enumerates third‑party packages (e.g., Django~=2.2.25). Safety reads this file to perform its vulnerability check.

Both files are checked into the lab repository; they are not generated by Docker images.


5. Tips & Common Questions

Issue Quick Fix
docker: command not found in Safety job Ensure the runner has Docker enabled (DinD) or use a shared runner that provides Docker.
“Warning: unpinned requirement” persists Pin the version with == or keep ~= and accept the warning (it does not stop the pipeline).
No high‑severity findings in RetireJS output Verify you are scanning the correct directory (npm install must finish first) and that the target libraries have known CVEs.
.retireignore.json not honoured Confirm the file is placed at the repository root and follows proper JSON syntax (no trailing commas).
Artifacts not appearing after Safety scan Add artifacts: section to the job (as shown above) and make sure when: always is set if you want results even on failure.

6. Conclusion

Integrating Safety and RetireJS into GitLab pipelines enhances your DevSecOps posture by catching vulnerable third‑party components early. Remember to:

  • Use explicit version specifiers for Python dependencies.
  • Separate jobs when they require different Docker images.
  • Leverage jq to isolate high‑severity findings and create a clean .retireignore.json.
  • Understand that package.json and requirements.txt are part of the source code you are scanning.

With these practices in place, your pipelines will reliably test functionality and enforce security standards, keeping your applications safe and compliant. Happy scanning!