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.6vs.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
-
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.jsonnpm installreads thepackage.jsonin the current directory and installs every dependency listed underdependenciesanddevDependencies. -
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 theresultsarray 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.
-
Create the
.retireignore.jsonfileFor 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). -
Optional: Download the JSON locally
If you prefer a GUI editor, copy
retire_output.jsonfrom 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 underdependenciesanddevDependencies. Thenpm installcommand reads this file and materialises thenode_modulesfolder. -
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
jqto isolate high‑severity findings and create a clean.retireignore.json. - Understand that
package.jsonandrequirements.txtare 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!