├── .circleci └── config.yml ├── .eslintignore ├── .eslintrc.json ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.json ├── .releaserc ├── .snyk ├── Contributor-Agreement.md ├── LICENSE ├── README.md ├── jest.config.js ├── package.json ├── src ├── cmds │ └── snyk:test.ts ├── index.ts ├── lib │ ├── convert-issue-to-spdx.ts │ ├── generate-date.ts │ ├── generate-document-namespace.ts │ ├── get-input-data.ts │ ├── index.ts │ └── snyk-to-spdx.ts └── types.ts ├── test ├── delete-files.ts ├── fixtures │ ├── no-deps.json │ ├── ruby-vulnerabilities.json │ ├── single-license-issue.json │ ├── single-vuln.json │ └── with-license-issues.json ├── load-json.ts ├── system │ └── cmds │ │ ├── __snapshots__ │ │ └── snyk:test.spec.ts.snap │ │ └── snyk:test.spec.ts ├── tsconfig.json └── unit │ └── lib │ ├── __snapshots__ │ ├── convert-issue-to-spdx.spec.ts.snap │ └── index.spec.ts.snap │ ├── convert-issue-to-spdx.spec.ts │ └── index.spec.ts └── tsconfig.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | snyk: snyk/snyk@1.1.2 4 | jobs: 5 | build-test-monitor: 6 | docker: 7 | - image: circleci/node:12 8 | steps: 9 | - checkout 10 | - run: npm install 11 | - run: npm test 12 | - run: npm run test:system 13 | - snyk/scan: 14 | fail-on-issues: true 15 | monitor-on-build: true 16 | token-variable: SNYK_TOKEN 17 | - run: npx semantic-release 18 | build-test: 19 | parameters: 20 | node_version: 21 | type: string 22 | ocker: 23 | docker: 24 | - image: circleci/node:<< parameters.node_version >> 25 | steps: 26 | - checkout 27 | - run: npm install 28 | - run: npm run lint 29 | - run: npm test 30 | - run: npm run test:system 31 | - snyk/scan: 32 | fail-on-issues: true 33 | monitor-on-build: false 34 | token-variable: SNYK_TOKEN 35 | - run: npx tsc 36 | build-test-from-fork: 37 | parameters: 38 | node_version: 39 | type: string 40 | ocker: 41 | docker: 42 | - image: circleci/node:<< parameters.node_version >> 43 | steps: 44 | - checkout 45 | - run: npm install 46 | - run: npm test 47 | - run: npm run test:system 48 | - run: npx tsc 49 | workflows: 50 | version: 2 51 | nightly: 52 | triggers: 53 | - schedule: 54 | cron: "0 0 * * *" 55 | filters: 56 | branches: 57 | only: 58 | - main 59 | jobs: 60 | - build-test-monitor: 61 | context: SNYK 62 | 63 | build-test-monitor: 64 | jobs: 65 | - build-test-monitor: 66 | context: SNYK 67 | filters: 68 | branches: 69 | only: 70 | - main 71 | build-test: 72 | jobs: 73 | - build-test: 74 | context: SNYK 75 | matrix: 76 | parameters: 77 | node_version: [ '10', '12', '14' ] 78 | filters: 79 | branches: 80 | ignore: 81 | - main 82 | - /pull\/[0-9]+/ 83 | - build-test-from-fork: 84 | matrix: 85 | parameters: 86 | node_version: [ '10', '12', '14' ] 87 | filters: 88 | branches: 89 | only: 90 | - /pull\/[0-9]+/ 91 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": ["@typescript-eslint"], 4 | "parserOptions": { 5 | "ecmaVersion": 6 6 | }, 7 | "env": { 8 | "node": true, 9 | "es6": true 10 | }, 11 | "extends": [ 12 | "eslint:recommended", 13 | "plugin:@typescript-eslint/eslint-recommended", 14 | "plugin:@typescript-eslint/recommended", 15 | "prettier" 16 | ], 17 | "rules": { 18 | "@typescript-eslint/explicit-function-return-type": [ 19 | "error", 20 | { 21 | "allowExpressions": true, 22 | "allowTypedFunctionExpressions": true 23 | } 24 | ], 25 | "no-unused-vars": "off", 26 | "@typescript-eslint/no-unused-vars": ["error"], 27 | "no-var": "error", 28 | "prefer-arrow-callback": "error", 29 | "prefer-const": "error" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Snyk Tech Services will be required for a review on every PR 2 | * @snyk-tech-services/snyk-tech-services 3 | 4 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Contributor Agreement 4 | A pull-request will only be considered for merging into the upstream codebase after you have signed our [contributor agreement](Contributor-Agreement.md), assigning us the rights to the contributed code and granting you a license to use it in return. If you submit a pull request, you will be prompted to review and sign the agreement with one click (we use [CLA assistant](https://cla-assistant.io/)). 5 | 6 | ## Commit messages 7 | 8 | Commit messages must follow the [Angular-style](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format) commit format (but excluding the scope). 9 | 10 | i.e: 11 | 12 | ```text 13 | fix: minified scripts being removed 14 | 15 | Also includes tests 16 | ``` 17 | 18 | This will allow for the automatic changelog to generate correctly. 19 | 20 | ### Commit types 21 | 22 | Must be one of the following: 23 | 24 | * **feat**: A new feature 25 | * **fix**: A bug fix 26 | * **docs**: Documentation only changes 27 | * **test**: Adding missing tests 28 | * **chore**: Changes to the build process or auxiliary tools and libraries such as documentation generation 29 | * **refactor**: A code change that neither fixes a bug nor adds a feature 30 | * **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 31 | * **perf**: A code change that improves performance 32 | 33 | To release a major you need to add `BREAKING CHANGE: ` to the start of the body and the detail of the breaking change. 34 | 35 | ## Code standards 36 | 37 | Ensure that your code adheres to the included lint config by running `npm run lint`. 38 | 39 | ## Sending pull requests 40 | 41 | - new command line options are generally discouraged unless there's a *really* good reason 42 | - add tests for newly added code (and try to mirror directory and file structure if possible) 43 | - spell check 44 | - PRs will not be code reviewed unless all tests are passing 45 | 46 | *Important:* when fixing a bug, please commit a **failing test** first so that Travis CI (or I can) can show the code failing. Once that commit is in place, then commit the bug fix, so that we can test *before* and *after*. 47 | 48 | Remember that you're developing for multiple platforms and versions of node, so if the tests pass on your Mac or Linux or Windows machine, it *may* not pass elsewhere. 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: Submit a bug 4 | title: "[\U0001F41B]" 5 | labels: "\U0001F41B bug" 6 | assignees: '' 7 | --- 8 | 9 | - `node -v`: 10 | - `npm -v`: 11 | - OS: (e.g. OSX, Linux, Windows, ...) 12 | - Command run: (e.g. `snyk2spdx ...`, ...) 13 | 14 | ### Expected behaviour 15 | Please share _expected_ behaviour. 16 | 17 | ### Actual behaviour 18 | Please share _problematic_ behaviour you are seeing. 19 | 20 | ### Steps to reproduce 21 | Please share _minimal_ steps needed to reproduce your issue. Ideally 22 | a paired down manifest / project to showcase the problem that can also 23 | be used for testing. 24 | 25 | 26 | ### Debug log 27 | If applicable, please add `DEBUG=*snyk* ` before your command and include the output here **ensuring to remove any sensitive/personal details or tokens. 28 | 29 | 30 | ### Screenshots 31 | If applicable, add screenshots to help explain your problem. 32 | 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F64F Feature Request" 3 | about: Submit a feature request 4 | title: "[\U0001F64F]" 5 | labels: "\U0001F64F feature request" 6 | assignees: '' 7 | --- 8 | 9 | **Describe the user need** 10 | E.g. I want Snyk to ... 11 | 12 | **Describe expected behaviour** 13 | 14 | If you have an idea how you would like this to behave please share in as much detail as possible. 15 | 16 | **Additional context** 17 | 18 | Add any other context or screenshots about the feature request here. 19 | 20 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - [ ] Tests written and linted [ℹ︎](https://github.com/snyk-tech-services/general/wiki/Tests) 2 | - [ ] Documentation written in Wiki/[README](../README.md) 3 | - [ ] Commit history is tidy & follows Contributing guidelines [ℹ︎](./CONTRIBUTING.md#commit-messages) 4 | 5 | 6 | ### What this does 7 | 8 | _Explain why this PR exists_ 9 | 10 | ### Notes for the reviewer 11 | 12 | _Instructions on how to run this locally, background context, what to review, questions…_ 13 | 14 | ### More information 15 | 16 | - [Link to documentation]() 17 | 18 | ### Screenshots 19 | 20 | _Visuals that may help the reviewer_ 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | .npmrc 4 | # output 5 | dist 6 | .DS_Store 7 | *.log 8 | package-lock.json 9 | yarn.lock 10 | .eslintcache 11 | .dccache 12 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 14 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "htmlWhitespaceSensitivity": "ignore", 6 | "tabWidth": 2 7 | } 8 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "prepare": [ 3 | "@semantic-release/npm", 4 | { 5 | "//": "adds a file to identify a build as a standalone binary", 6 | "path": "@semantic-release/exec", 7 | "cmd": "echo '' > dist/STANDALONE" 8 | }, 9 | { 10 | "//": "build the macos", 11 | "path": "@semantic-release/exec", 12 | "cmd": "npx nexe@3.3.7 dist/index.js -r './dist/**/*.js' -t mac-x64-10.21.0 -o snyk2spdx-macos" 13 | }, 14 | { 15 | "//": "build the linux", 16 | "path": "@semantic-release/exec", 17 | "cmd": "npx nexe@3.3.7 dist/index.js -r './dist/**/*.js' -t linux-x64-12.16.2 -o snyk2spdx-linux" 18 | }, 19 | { 20 | "//": "build the windows binaries", 21 | "path": "@semantic-release/exec", 22 | "cmd": "npx nexe@3.3.7 dist/index.js -r './dist/**/*.js' -t windows-x64-10.16.0 -o snyk2spdx-win.exe" 23 | }, 24 | { 25 | "//": "shasum all binaries", 26 | "path": "@semantic-release/exec", 27 | "cmd": "shasum -a 256 snyk2spdx-linux > snyk2spdx-linux.sha256 && shasum -a 256 snyk2spdx-macos > snyk2spdx-macos.sha256 && shasum -a 256 snyk2spdx-win.exe > snyk2spdx-win.exe.sha256" 28 | }, 29 | { 30 | "//": "removes the file we use to identify a build as a standalone binary", 31 | "path": "@semantic-release/exec", 32 | "cmd": "rm dist/STANDALONE" 33 | } 34 | ], 35 | "publish": [ 36 | "@semantic-release/npm", 37 | { 38 | "path": "@semantic-release/github", 39 | "assets": [ 40 | { 41 | "path": "./snyk2spdx-linux", 42 | "name": "snyk2spdx-linux", 43 | "label": "snyk2spdx-linux" 44 | }, 45 | { 46 | "path": "./snyk2spdx-linux.sha256", 47 | "name": "snyk2spdx-linux.sha256", 48 | "label": "snyk2spdx-linux.sha256" 49 | }, 50 | { 51 | "path": "./snyk2spdx-macos", 52 | "name": "snyk2spdx-macos", 53 | "label": "snyk2spdx-macos" 54 | }, 55 | { 56 | "path": "./snyk2spdx-macos.sha256", 57 | "name": "snyk2spdx-macos.sha256", 58 | "label": "snyk2spdx-macos.sha256" 59 | }, 60 | { 61 | "path": "./snyk2spdx-win.exe", 62 | "name": "snyk2spdx-win.exe", 63 | "label": "snyk2spdx-win.exe" 64 | }, 65 | { 66 | "path": "./snyk2spdx-win.exe.sha256", 67 | "name": "snyk2spdx-win.exe.sha256", 68 | "label": "snyk2spdx-win.exe.sha256" 69 | } 70 | ] 71 | } 72 | ], 73 | "branches": [ 74 | "main" 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.13.5 3 | ignore: {} 4 | # patches apply the minimum changes required to fix a vulnerability 5 | patch: 6 | SNYK-JS-LODASH-450202: 7 | - lodash: 8 | patched: '2019-07-05T10:44:37.780Z' 9 | -------------------------------------------------------------------------------- /Contributor-Agreement.md: -------------------------------------------------------------------------------- 1 | # Snyk CLI tool contributor agreement 2 | 3 | This Snyk CLI tool Agreement (this **"Agreement"**) applies to any Contribution you make to any Work. 4 | 5 | This is a binding legal agreement on you and any organization you represent. If you are signing this Agreement on behalf of your employer or other organization, you represent and warrant that you have the authority to agree to this Agreement on behalf of the organization. 6 | 7 | ## 1. Definitions 8 | 9 | **"Contribution"** means any original work, including any modification of or addition to an existing work, that you submit to Snyk CLI tool repo in any manner for inclusion in any Work. 10 | 11 | **"Snyk", "we"** and **"use"** means Snyk Ltd. 12 | 13 | **"Work"** means any project, work or materials owned or managed by Snyk Ltd. 14 | 15 | **"You"** and **"your"** means you and any organization on whose behalf you are entering this Agreement. 16 | 17 | ## 2. Copyright Assignment, License and Waiver 18 | 19 | **(a) Assignment.** By submitting a Contribution, you assign to Snyk all right, title and interest in any copright you have in the Contribution, and you waive any rights, including any moral rights, database rights, etc., that may affect your ownership of the copyright in the Contribution. 20 | 21 | **(b) License to Snyk.** If your assignment in Section 2(a) is ineffective for any reason, you grant to us and to any recipient of any Work distributed by use, a perpetual, worldwide, transferable, non-exclusive, no-charge, royalty-free, irrevocable, and sublicensable licence to use, reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Contributions and any derivative work created based on a Contribution. If your license grant is ineffective for any reason, you irrevocably waive and covenant to not assert any claim you may have against us, our successors in interest, and any of our direct or indirect licensees and customers, arising out of our, our successors in interest's, or any of our direct or indirect licensees' or customers' use, reproduction, preparation of derivative works, public display, public performance, sublicense, and distribution of a Contribution. You also agree that we may publicly use your name and the name of any organization on whose behalf you're entering into this Agreement in connection with publicizing the Work. 22 | 23 | **(c) License to you.** We grant to you a perpetual, worldwide, transferable, non-exclusive, no-charge, royalty-free, irrevocable, and sublicensable license to use, reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute a Contribution and any derivative works you create based on a Contribution. 24 | 25 | ## 3. Patent License 26 | You grant to us and to any recipient of any Work distributed by us, a perpetual, worldwide, transferable, non-exclusive, no-charge, royalty-free, irrevocable, and sublicensable patent license to make, have made, use, sell, offer to sell, import, and otherwise transfer the Contribution in whole or in part, along or included in any Work under any patent you own, or license from a third party, that is necessarily infringed by the Contribution or by combination of the Contribution with any Work. 27 | 28 | ## 4. Your Representation and Warranties. 29 | By submitting a Contribution, you represent and warrant that: (a) each Contribution you submit is an original work and you can legally grant the rights set out in this Agreement; (b) the Contribution does not, and any exercise of the rights granted by you will not, infringe any third party's intellectual property or other right; and (c) you are not aware of any claims, suits, or actions pertaining to the Contribution. You will notify us immediately if you become aware or have reason to believe that any of your representations and warranties is or becomes inaccurate. 30 | 31 | ##5. Intellectual Property 32 | Except for the assignment and licenses set forth in this Agreement, this Agreement does not transfer any right, title or interest in any intellectual property right of either party to the other. If you choose to provide us with suggestions, ideas for improvement, recommendations or other feedback, on any Work we may use your feedback without any restriction or payment. 33 | 34 | ## Miscellaneous 35 | English law governs this Agreement, excluding any applicable conflict of laws rules or principles, and the parties agree to the exclusive jurisdiction of the courts in England, UK. This Agreement does not create a partnership, agency relationship, or joint venture between the parties. We may assign this Agreement without notice or restriction. If any provision of this Agreement is unenforcable, that provision will be modified to render it enforceable to the extent possible to effect the parties' intention and the remaining provisions will not be affected. The parties may amend this Agreement only in a written amendment signed by both parties. This Agreement comprises the parties' entire agreement relating to the subject matter of this Agreement. 36 | 37 | **Agreed and accepted on my behalf and on behalf of my organization** 38 | 39 | Our contributor agreement is based on the [mongoDB contributor agreement] (https://www.mongodb.com/legal/contributor-agreement). 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Snyk Ltd. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Snyk logo](https://snyk.io/style/asset/logo/snyk-print.svg) 2 | 3 | *** 4 | 5 | [![Known Vulnerabilities](https://snyk.io/test/github/snyk-tech-services/snyk2spdx/badge.svg)](https://snyk.io/test/github/snyk-tech-services/snyk2spdx) 6 | 7 | Snyk helps you find, fix, and monitor known vulnerabilities in your dependencies--both on an ad hoc basis and as part of your CI (build) system. 8 | 9 | ## Snyk snyk2spdx 10 | [![Inactively Maintained](https://img.shields.io/badge/Maintenance%20Level-Inactively%20Maintained-yellowgreen.svg)](https://gist.github.com/cheerfulstoic/d107229326a01ff0f333a1d3476e068d) 11 | 12 | 13 | **This repository is in maintenance mode, no new features are being developed. Bug & security fixes will continue to be delivered. Open source contributions are welcome for small features & fixes (no breaking changes)** 14 | 15 | 16 | Convert the Snyk CLI output to SPDX format. 17 | Note: **This repository is not in active developemnt and critical bug fixes only will be considered.** 18 | 19 | ## Notice 20 | **snyk2spdx does not support using the `--all-projects` flag with `snyk test`. Please use only `snyk test`** 21 | 22 | ## Usage 23 | - Basic 24 | `snyk test --json | snyk2spdx` 25 | 26 | - With output file: 27 | `snyk test --json | snyk2spdx --output=spdx.json` 28 | 29 | ``` 30 | Commands: 31 | snyk2spdx snyk:test Convert `snyk test --json` output to SPDX SBOM [default] 32 | 33 | Options: 34 | --version Show version number [boolean] 35 | --help Show help [boolean] 36 | --output Save the output to the specified file name. Defaults to stdout 37 | ``` 38 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | collectCoverageFrom: ['lib/**/*.ts'], 5 | coverageReporters: ['text-summary', 'html'], 6 | }; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk2spdx", 3 | "description": "Convert the Snyk CLI output to SPDX format output", 4 | "main": "dist/index.js", 5 | "scripts": { 6 | "format:check": "prettier --check '{''{src,test}/!(fixtures)/**/*,*}.{js,ts,json,yml}'", 7 | "format": "prettier --write '{''{src,test}/!(fixtures)/**/*,*}.{js,ts,json,yml}'", 8 | "lint": "npm run format:check && npm run lint:eslint", 9 | "lint:eslint": "eslint --cache '{src,test}/**/*.ts'", 10 | "test": "npm run lint && npm run test:unit", 11 | "test:unit": "jest test/unit --runInBand", 12 | "test:system": "jest test/system", 13 | "test:coverage": "npm run test:unit -- --coverage", 14 | "test:watch": "tsc-watch --onSuccess 'npm run test:unit'", 15 | "build": "tsc", 16 | "build-watch": "tsc -w", 17 | "prepare": "npm run build", 18 | "snyk-test": "snyk test", 19 | "pkg-binaries": "npx nexe@3.3.7 dist/index.js -r './dist/**/*.js' -t mac-x64-10.21.0 -o snyk2spdx-macos" 20 | }, 21 | "bin": { 22 | "snyk2spdx": "dist/index.js" 23 | }, 24 | "types": "./dist/index.d.ts", 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/snyk-tech-services/snyk2spdx" 28 | }, 29 | "author": "Snyk Tech Services", 30 | "license": "Apache-2.0", 31 | "engines": { 32 | "node": ">=10" 33 | }, 34 | "files": [ 35 | "bin", 36 | "dist" 37 | ], 38 | "homepage": "https://github.com/snyk-tech-services/snyk2spdx#readme", 39 | "dependencies": { 40 | "@snyk/protect": "1.724.0", 41 | "debug": "4.3.4", 42 | "lodash": "^4.17.15", 43 | "source-map-support": "^0.5.16", 44 | "tslib": "^1.10.0", 45 | "uuid": "8.3.2", 46 | "yargs": "16.2.0" 47 | }, 48 | "devDependencies": { 49 | "@semantic-release/exec": "5.0.0", 50 | "@types/debug": "4.1.7", 51 | "@types/jest": "26.0.23", 52 | "@types/lodash": "^4.14.149", 53 | "@types/node": "14.17.1", 54 | "@types/uuid": "8.3.1", 55 | "@typescript-eslint/eslint-plugin": "^2.18.0", 56 | "@typescript-eslint/parser": "^2.18.0", 57 | "eslint": "7.27.0", 58 | "eslint-config-prettier": "8.3.0", 59 | "i": "0.3.7", 60 | "jest": "^27.0.1", 61 | "node-notifier": "9.0.1", 62 | "npm": "7.24.1", 63 | "prettier": "2.3.0", 64 | "semantic-release": "17.4.3", 65 | "ts-jest": "27.0.0", 66 | "ts-node": "8.6.2", 67 | "tsc-watch": "^4.1.0", 68 | "typescript": "^4.1" 69 | }, 70 | "pkg": { 71 | "scripts": [ 72 | "dist/**/*.js" 73 | ] 74 | }, 75 | "release": { 76 | "branches": [ 77 | "main" 78 | ] 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/cmds/snyk:test.ts: -------------------------------------------------------------------------------- 1 | import * as debugLib from 'debug'; 2 | import * as fs from 'fs'; 3 | import { convertSnykTestOutputToSPDX, getInputData } from '../lib'; 4 | import { SnykTestOutput } from '../types'; 5 | const debug = debugLib('snyk:generate-data-script'); 6 | 7 | export const command = ['snyk:test', '$0']; 8 | export const desc = 'Convert `snyk test --json` output to SPDX SBOM'; 9 | export const builder = { 10 | // input: { 11 | // required: false, 12 | // default: undefined, 13 | // desc: 14 | // 'Path to `snyk test --json` output JSON file to convert to SPDX. Defaults to stdin', 15 | // }, 16 | output: { 17 | required: false, 18 | default: undefined, 19 | desc: 'Save the output to the specified file name. Defaults to stdout', 20 | }, 21 | }; 22 | 23 | export async function handler(argv: { 24 | // input: string; 25 | output: string; 26 | }): Promise { 27 | try { 28 | const { output } = argv; 29 | debug('ℹ️ Options: ' + JSON.stringify(argv)); 30 | const snykTestJson = await getInputData(); 31 | debug('ℹ️ Got input'); 32 | const spdxOutput = convertSnykTestOutputToSPDX(snykTestJson); 33 | debug('ℹ️ Converted to SPDX'); 34 | 35 | const spdxOutputStringified = JSON.stringify(spdxOutput); 36 | if (output) { 37 | fs.writeFileSync(output, spdxOutputStringified); 38 | console.log('SPDX document saved at ' + output); 39 | } else { 40 | console.log(spdxOutputStringified); 41 | } 42 | } catch (e: any) { 43 | debug('Failed to generate data.\n' + e.message); 44 | console.error( 45 | `ERROR! Failed to convert to SPDX. Try running with \`DEBUG=snyk* for more info\`.\nERROR: ${e}`, 46 | ); 47 | process.exit(1); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import * as yargs from 'yargs'; 3 | import 'source-map-support/register'; 4 | 5 | export { convertSnykTestOutputToSPDX } from './lib'; 6 | 7 | yargs 8 | .commandDir('cmds') 9 | .help() 10 | .demandCommand().argv; 11 | -------------------------------------------------------------------------------- /src/lib/convert-issue-to-spdx.ts: -------------------------------------------------------------------------------- 1 | import * as types from '../types'; 2 | 3 | function capitalize(str: string): string { 4 | return str[0].toUpperCase() + str.slice(1); 5 | } 6 | 7 | function getVulnerabilityRating( 8 | issue: types.SnykIssue, 9 | ): types.VulnerabilityRating[] { 10 | const vulnerabilityRatingScore: types.VulnerabilityRatingScore = { 11 | base: issue.cvssScore, 12 | exploitability: null, 13 | impact: null, 14 | }; 15 | 16 | const vulnerabilityRating: types.VulnerabilityRating = { 17 | method: issue.CVSSv3 ? 'CVSS_3' : undefined, // must be CVSS_2, CVSS_3, OWASP_RISK or OTHER 18 | score: [vulnerabilityRatingScore], 19 | severity: capitalize(issue.severity), // exploitability score of the vulnerability either None, Low, Medium, High or Critical 20 | vector: issue.CVSSv3, 21 | }; 22 | 23 | return [vulnerabilityRating]; 24 | } 25 | 26 | function getExternalReferencesRelationships( 27 | references: types.SnykIssueReference[], 28 | ): types.ExternalReferencesRelationship[] { 29 | return references.map((reference) => ({ 30 | category: 'ADVISORY', // not mandatory, but should be either ADVISORY, ARTICLE, FIX, REPORT or OTHER. 31 | locator: reference.url, // url 32 | })); 33 | } 34 | 35 | function getCWES(cwe: string[]): number[] { 36 | return cwe.map((step) => parseInt(step.replace('CWE-', ''))); 37 | } 38 | 39 | function getVulnerabilityRelationship( 40 | issue: types.SnykIssue, 41 | ): types.VulnerabilityRelationship[] { 42 | const vulnerabilityAffect: types.AffectedBy = { 43 | to: issue.from, 44 | type: 'AFFECTS', 45 | }; 46 | 47 | const vulnerabilityFoundBy: types.AffectedBy = { 48 | to: issue.credit, 49 | type: 'FOUND_BY', 50 | }; 51 | 52 | // not mandatory, unclear what should be in here 53 | const vulnerabilitySuppliedBy: types.AffectedBy = { 54 | to: issue.credit, 55 | type: 'SUPPLIED_BY', 56 | }; 57 | 58 | const ratedBy: types.RatedBy = { 59 | cwes: issue.identifiers.CWE ? getCWES(issue.identifiers.CWE) : [], 60 | rating: getVulnerabilityRating(issue), 61 | to: issue.credit, 62 | type: 'RATED_BY', 63 | }; 64 | 65 | const relationship: types.VulnerabilityRelationship[] = [ 66 | { 67 | affect: vulnerabilityAffect, 68 | foundBy: vulnerabilityFoundBy, 69 | suppliedBy: vulnerabilitySuppliedBy, 70 | ratedBy: ratedBy, 71 | }, 72 | ]; 73 | 74 | return relationship; 75 | } 76 | 77 | export function convertSnykIssueToSpdx( 78 | issue: types.SnykIssue, 79 | ): types.Vulnerability { 80 | return { 81 | id: issue.id, 82 | name: issue.id, 83 | summary: issue.title, 84 | details: issue.description, 85 | relationships: getVulnerabilityRelationship(issue), 86 | externalReferences: getExternalReferencesRelationships(issue.references), 87 | modified: issue.modificationTime, // YYYY-MM-DDThh:mm:ssZ 88 | published: issue.publicationTime, 89 | }; 90 | } 91 | -------------------------------------------------------------------------------- /src/lib/generate-date.ts: -------------------------------------------------------------------------------- 1 | import * as debugLib from 'debug'; 2 | const debug = debugLib('snyk:generate-data-script'); 3 | 4 | export function getDate(): string { 5 | let date: string; 6 | const d = new Date(); 7 | debug('creating date'); 8 | date = d.getFullYear().toString() + '-'; 9 | date = date + d.getMonth().toString().padStart(2, '0') + '-'; 10 | date = date + d.getDay().toString().padStart(2, '0') + 'T'; 11 | date = date + d.getHours().toString().padStart(2, '0') + ':'; 12 | date = date + d.getMinutes().toString().padStart(2, '0') + ':'; 13 | date = date + d.getSeconds().toString().padStart(2, '0') + 'Z'; 14 | 15 | debug(date); 16 | 17 | return date; 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/generate-document-namespace.ts: -------------------------------------------------------------------------------- 1 | import * as uuid from 'uuid'; 2 | 3 | export function generateDocumentNameSpace(outputFileName: string): string { 4 | const randomUuid = uuid.v4(); 5 | return `spdx.org/spdxdocs/${outputFileName}-${randomUuid}`; 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/get-input-data.ts: -------------------------------------------------------------------------------- 1 | export async function readInputFromStdin(): Promise { 2 | return new Promise((resolve, reject) => { 3 | let jsonString = ''; 4 | process.stdin.setEncoding('utf8'); 5 | process.stdin.on('readable', () => { 6 | const chunk = process.stdin.read(); 7 | if (chunk !== null) { 8 | jsonString += chunk; 9 | } 10 | }); 11 | process.stdin.on('error', reject); 12 | process.stdin.on('end', () => resolve(jsonString)); 13 | }); 14 | } 15 | 16 | export async function getInputData(): Promise { 17 | try { 18 | const inputData = await readInputFromStdin(); 19 | return JSON.parse(inputData); 20 | } catch (e: any) { 21 | throw new Error(`Failed to parse input. ERROR: ${e.message}`); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | import 'source-map-support/register'; 2 | import { SnykTestOutput, SPDXv3, Profile } from '../types'; 3 | import { convertSnykIssueToSpdx } from './convert-issue-to-spdx'; 4 | import { generateDocumentNameSpace } from './generate-document-namespace'; 5 | export { getInputData } from './get-input-data'; 6 | import { getDate } from './generate-date'; 7 | 8 | export function convertSnykTestOutputToSPDX(data: SnykTestOutput): SPDXv3 { 9 | const outputFileName = data.projectName; 10 | return { 11 | id: `SPDXRef-${data.projectName}`, 12 | name: data.projectName, 13 | specVersion: 'SPDX-3.0', 14 | profile: [Profile.BASE, Profile.VULNERABILITIES], 15 | dataLicense: 'CC0-1.0', 16 | creator: 'Organization: Snyk Ltd', 17 | documentNamespace: generateDocumentNameSpace(outputFileName), 18 | description: `Snyk test result for project ${data.projectName} in SPDX SBOM format`, 19 | created: getDate(), 20 | vulnerabilities: data.vulnerabilities 21 | .filter((i) => i.type !== 'license') 22 | .map((i) => convertSnykIssueToSpdx(i)), 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/snyk-to-spdx.ts: -------------------------------------------------------------------------------- 1 | import { SnykIssue, SnykTestOutput, SPDXv3, Profile } from '../types'; 2 | import { convertSnykIssueToSpdx } from './convert-issue-to-spdx'; 3 | import { generateDocumentNameSpace } from './generate-document-namespace'; 4 | import { getDate } from './generate-date'; 5 | 6 | export function convertSnykTestOutputToSPDX(data: SnykTestOutput): SPDXv3 { 7 | const outputFileName = data.projectName; 8 | return { 9 | id: `SPDXRef-${data.projectName}`, 10 | name: data.projectName, 11 | specVersion: 'SPDX-3.0', 12 | profile: [Profile.BASE, Profile.VULNERABILITIES], 13 | dataLicense: 'CC0-1.0', 14 | creator: 'Organization: Snyk Ltd', 15 | documentNamespace: generateDocumentNameSpace(outputFileName), 16 | description: `Snyk test result for project ${data.projectName} in SPDX SBOM format`, 17 | created: getDate(), // YYYY-MM-DDThh:mm:ssZ 18 | vulnerabilities: data.vulnerabilities.map((i: SnykIssue) => 19 | convertSnykIssueToSpdx(i), 20 | ), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export interface SPDXv3 { 2 | id: string; // e.g SPDXRef-document-name 3 | name: string; 4 | summary?: string; 5 | description?: string; 6 | comment?: string; 7 | specVersion: string; 8 | profile: Profile[]; 9 | // YYYY-MM-DDThh:mm:ssZ 10 | created: string; 11 | documentNamespace: string; // Url to the doc http://[CreatorWebsite]/[pathToSpdx]/[DocumentName]-[UUID] 12 | // License expression for dataLicense. Compliance with the SPDX specification includes populating the SPDX fields therein with data related to such fields (\"SPDX-Metadata\"). The SPDX specification contains numerous fields where an SPDX document creator may provide relevant explanatory text in SPDX-Metadata. Without opining on the lawfulness of \"database rights\" (in jurisdictions where applicable), such explanatory text is copyrightable subject matter in most Berne Convention countries. By using the SPDX specification, or any portion hereof, you hereby agree that any copyright rights (as determined by your jurisdiction) in any SPDX-Metadata, including without limitation explanatory text, shall be subject to the terms of the Creative Commons CC0 1.0 Universal license. For SPDX-Metadata not containing any copyright rights, you hereby agree and acknowledge that the SPDX-Metadata is provided to you \"as-is\" and without any representations or warranties of any kind concerning the SPDX-Metadata, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non-infringement, or the absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. 13 | dataLicense: string; 14 | // Single line of text with the following keywords: 15 | // "Person: person name" and optional "(email)" 16 | // "Organization: organization" and optional "(email)" 17 | // "Tool: toolIdentifier-version" 18 | creator: string; 19 | vulnerabilities: Vulnerability[]; 20 | defectResponses?: DefectResponse[]; 21 | } 22 | 23 | export enum Profile { 24 | 'BASE' = 'base', 25 | 'VULNERABILITIES' = 'vulnerabilities', 26 | } 27 | 28 | export enum method { 29 | CVSS_2, 30 | CVSS_3, 31 | OWASP_RISK, 32 | OTHER, 33 | } 34 | 35 | export interface Vulnerability { 36 | id: string; //string, unique SPDX id for this element within SPDX doc 37 | name: string; //string, id given by org of person(s) who identified it. 38 | summary: string; // string, one-liner summary of max 120 chars. 39 | details: string; //string, multi line may include steps to reproduce, detail impact analysis or remediation guidance 40 | relationships: VulnerabilityRelationship[]; //field provides information about the relationships between the vulnerability and other SPDX elements. 41 | externalReferences?: ExternalReference[]; 42 | modified?: string; // YYYY-MM-DDThh:mm:ssZ 43 | published?: string; // YYYY-MM-DDThh:mm:ssZ 44 | withdrawn?: string; // YYYY-MM-DDThh:mm:ssZ 45 | } 46 | 47 | export interface VulnerabilityRelationship { 48 | affect: AffectedBy; //pointing to the SPDX Package or SPDX File affected by the vulnerability 49 | foundBy?: AffectedBy; //pointing to an SPDX Identity e.g. the organization, tool or person(s) who identified the vulnerability 50 | suppliedBy?: AffectedBy; //pointing to an SPDX Identity e.g. the organization, tool or person(s) who supplied information about the vulnerability 51 | ratedBy: RatedBy; //an instance of SPDX Vulnerability Rating_ relationship 52 | } 53 | 54 | // supplied_by, affects, found_by 55 | export interface AffectedBy { 56 | to: string[]; // org, person or tool 57 | type: string; // either "RATED_BY", "AFFECTS", "FOUND_BY", "SUPPLIED_BY" 58 | } 59 | 60 | export interface RatedBy { 61 | cwes: number[]; // array of Common Weaknesses Enumerations (CWE) integers 62 | rating: VulnerabilityRating[]; 63 | to: string[]; 64 | type: string; // must be "RATED_BY" 65 | } 66 | 67 | export interface VulnerabilityRating { 68 | method: string | undefined; // must be CVSS_2, CVSS_3, OWASP_RISK or OTHER 69 | score: VulnerabilityRatingScore[]; 70 | severity: string; // exploitability score of the vulnerability either None, Low, Medium, High or Critical 71 | vector: string; // textual representation of the metric values used 72 | } 73 | 74 | export interface VulnerabilityRatingScore { 75 | base: number; 76 | exploitability: string | null; 77 | impact: string | null; 78 | } 79 | 80 | export interface ExternalReferencesRelationship { 81 | category: string | undefined; // must be either ADVISORY, ARTICLE, FIX, REPORT or OTHER. 82 | locator: string; // url 83 | } 84 | 85 | export type ExternalReference = ExternalReferencesRelationship; 86 | 87 | export interface DefectResponse { 88 | id: string; 89 | type: string; // CANT_FIX_VULNERABILITY, INEFFECTIVE_VULNERABILITY, INVALID_MATCH_VULNERABILITY, MITIGATED_VULNERABILITY, ROLLBACK, UPDATE, WILL_NOT_FIX_VULNERABILITY, WORKAROUND_FOR_VULNERABILITY 90 | created: string; //// YYYY-MM-DDThh:mm:ssZ 91 | comment?: string; 92 | defectResponseRelationship: AffectedBy[]; 93 | } 94 | 95 | export interface DefectResponseRelationship { 96 | from: string; 97 | to: string; 98 | type: string; // RESPOND or CREATED_BY (a vul must have at least one RESPOND relationship) 99 | } 100 | 101 | export interface Identity { 102 | id: string; 103 | name: string; 104 | version?: string; 105 | organization?: string; 106 | type: string; // ORGANIZATION, PERSON, TOOL 107 | } 108 | 109 | export interface ProfilePackage { 110 | id: string; 111 | name: string; 112 | artifactUrl: string; 113 | type: string; // LIBRARY, SOURCE 114 | } 115 | 116 | interface ProfileVulnerability { 117 | id: string; 118 | name: string; 119 | specVersion: string; 120 | profile: string[]; 121 | //// YYYY-MM-DDThh:mm:ssZ 122 | created: string; 123 | dataLicense: string; 124 | comment?: string; 125 | description: string; 126 | packages: ProfilePackage[]; 127 | identities: Identity[]; 128 | relationships: DefectResponseRelationship[]; // same format as DefectResponseRelationship but type is different (can't infd a proper listing) 129 | vulnerabilities: Vulnerability[]; 130 | defectResponses?: DefectResponse[]; 131 | } 132 | 133 | export interface SnykIssue { 134 | id: string; 135 | title: string; 136 | description: string; 137 | from: string[]; 138 | credit: string[]; 139 | cvssScore: number; 140 | severity: string; 141 | CVSSv3: string; 142 | exploit: string; 143 | type?: string; // only present on License issues 144 | semver: SnykIssueSemver; 145 | modificationTime: string; 146 | publicationTime: string; 147 | references: SnykIssueReference[]; 148 | creationTime: string; 149 | identifiers: SnykIssueIdentifiers; 150 | } 151 | 152 | export interface SnykIssueSemver { 153 | vulnerable: string[]; 154 | } 155 | 156 | export interface SnykIssueReference { 157 | title: string; 158 | url: string; 159 | } 160 | 161 | export interface SnykTestOutput { 162 | projectName: string; 163 | vulnerabilities: SnykIssue[]; 164 | displayTargetFile?: string; 165 | path: string; 166 | remediation?: RemediationChanges; 167 | } 168 | 169 | export interface RemediationChanges { 170 | unresolved: SnykIssue[]; 171 | upgrade: DependencyUpdates; 172 | patch: { 173 | [name: string]: PatchRemediation; 174 | }; 175 | ignore: unknown; 176 | pin: DependencyPins; 177 | } 178 | 179 | export interface Upgrade { 180 | upgradeTo: string; // name@version 181 | } 182 | 183 | export interface UpgradeVulns extends Upgrade { 184 | vulns: string[]; 185 | } 186 | 187 | export interface UpgradeRemediation extends UpgradeVulns { 188 | upgrades: string[]; 189 | } 190 | 191 | export interface PatchRemediation { 192 | paths: PatchObject[]; 193 | } 194 | 195 | export interface PatchObject { 196 | [name: string]: { 197 | patched: string; 198 | }; 199 | } 200 | 201 | export interface DependencyUpdates { 202 | [from: string]: UpgradeRemediation; 203 | } 204 | 205 | export interface DependencyPins { 206 | [name: string]: PinRemediation; 207 | } 208 | 209 | export interface PinRemediation extends UpgradeVulns { 210 | isTransitive: boolean; 211 | } 212 | 213 | interface SnykIssueIdentifiers { 214 | ALTERNATIVE?: string[]; 215 | CWE: string[]; 216 | CVE: string[]; 217 | NSP?: number; 218 | } 219 | -------------------------------------------------------------------------------- /test/delete-files.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | 3 | export function deleteFiles(logs: string[]): void { 4 | logs.forEach((path) => { 5 | try { 6 | fs.unlinkSync(path); 7 | } catch (e) { 8 | // do nothing 9 | } 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/no-deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "vulnerabilities": [], 3 | "ok": true, 4 | "dependencyCount": 0, 5 | "org": "snyk-test", 6 | "policy": "# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.\nversion: v1.19.0\nignore: {}\npatch: {}\n", 7 | "isPrivate": true, 8 | "licensesPolicy": { 9 | "severities": {}, 10 | "orgLicenseRules": { 11 | "AGPL-1.0": { 12 | "licenseType": "AGPL-1.0", 13 | "severity": "high", 14 | "instructions": "" 15 | }, 16 | "AGPL-3.0": { 17 | "licenseType": "AGPL-3.0", 18 | "severity": "high", 19 | "instructions": "" 20 | }, 21 | "Artistic-1.0": { 22 | "licenseType": "Artistic-1.0", 23 | "severity": "medium", 24 | "instructions": "" 25 | }, 26 | "Artistic-2.0": { 27 | "licenseType": "Artistic-2.0", 28 | "severity": "medium", 29 | "instructions": "" 30 | }, 31 | "CDDL-1.0": { 32 | "licenseType": "CDDL-1.0", 33 | "severity": "medium", 34 | "instructions": "" 35 | }, 36 | "CPOL-1.02": { 37 | "licenseType": "CPOL-1.02", 38 | "severity": "high", 39 | "instructions": "" 40 | }, 41 | "EPL-1.0": { 42 | "licenseType": "EPL-1.0", 43 | "severity": "medium", 44 | "instructions": "" 45 | }, 46 | "GPL-2.0": { 47 | "licenseType": "GPL-2.0", 48 | "severity": "high", 49 | "instructions": "" 50 | }, 51 | "GPL-3.0": { 52 | "licenseType": "GPL-3.0", 53 | "severity": "high", 54 | "instructions": "" 55 | }, 56 | "LGPL-2.0": { 57 | "licenseType": "LGPL-2.0", 58 | "severity": "medium", 59 | "instructions": "" 60 | }, 61 | "LGPL-2.1": { 62 | "licenseType": "LGPL-2.1", 63 | "severity": "medium", 64 | "instructions": "" 65 | }, 66 | "LGPL-3.0": { 67 | "licenseType": "LGPL-3.0", 68 | "severity": "medium", 69 | "instructions": "" 70 | }, 71 | "MPL-1.1": { 72 | "licenseType": "MPL-1.1", 73 | "severity": "medium", 74 | "instructions": "" 75 | }, 76 | "MPL-2.0": { 77 | "licenseType": "MPL-2.0", 78 | "severity": "medium", 79 | "instructions": "" 80 | }, 81 | "MS-RL": { 82 | "licenseType": "MS-RL", 83 | "severity": "medium", 84 | "instructions": "" 85 | }, 86 | "SimPL-2.0": { 87 | "licenseType": "SimPL-2.0", 88 | "severity": "high", 89 | "instructions": "" 90 | }, 91 | "MIT": { 92 | "licenseType": "MIT", 93 | "severity": "high", 94 | "instructions": "Not suitable to use, please find a different package." 95 | } 96 | } 97 | }, 98 | "packageManager": "npm", 99 | "ignoreSettings": null, 100 | "summary": "No known vulnerabilities", 101 | "filesystemPolicy": false, 102 | "uniqueCount": 0, 103 | "projectName": "no-prod-deps", 104 | "displayTargetFile": "package-lock.json", 105 | "path": "/Users/snyk/examples/node/no-prod-deps" 106 | } 107 | -------------------------------------------------------------------------------- /test/fixtures/ruby-vulnerabilities.json: -------------------------------------------------------------------------------- 1 | { 2 | "vulnerabilities": [ 3 | { 4 | "CVSSv3": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:H", 5 | "alternativeIds": [], 6 | "creationTime": "2020-03-20T13:31:56.742021Z", 7 | "credit": [ 8 | "Unknown" 9 | ], 10 | "cvssScore": 9.3, 11 | "description": "## Overview\n[json](https://rubygems.org/gems/json) is a JSON implementation as a Ruby extension in C.\n\nAffected versions of this package are vulnerable to Denial of Service (DoS). When parsing certain JSON documents, the json gem (including the one bundled with Ruby) can be coerced into creating arbitrary objects in the target system.\r\n\r\nThis is the same issue as CVE-2013-0269. The previous fix was incomplete, which addressed `JSON.parse(user_input)`, but didn’t address some other styles of JSON\r\nparsing including `JSON(user_input) and JSON.parse(user_input, nil)`.\r\n\r\nSee [CVE-2013-0269](https://snyk.io/vuln/SNYK-RUBY-JSON-20060) in detail.\n\n## Details\n\nDenial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.\n\nThe Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.\n\nLet’s take the following regular expression as an example:\n```js\nregex = /A(B|C+)+D/\n```\n\nThis regular expression accomplishes the following:\n- `A` The string must start with the letter 'A'\n- `(B|C+)+` The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the `+` matches one or more times). The `+` at the end of this section states that we can look for one or more matches of this section.\n- `D` Finally, we ensure this section of the string ends with a 'D'\n\nThe expression would match inputs such as `ABBD`, `ABCCCCD`, `ABCBCCCD` and `ACCCCCD`\n\nIt most cases, it doesn't take very long for a regex engine to find a match:\n\n```bash\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD\")'\n0.04s user 0.01s system 95% cpu 0.052 total\n\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX\")'\n1.79s user 0.02s system 99% cpu 1.812 total\n```\n\nThe entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.\n\nMost Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as _catastrophic backtracking_.\n\nLet's look at how our expression runs into this problem, using a shorter string: \"ACCCX\". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:\n1. CCC\n2. CC+C\n3. C+CC\n4. C+C+C.\n\nThe engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use [RegEx 101 debugger](https://regex101.com/debugger) to see the engine has to take a total of 38 steps before it can determine the string doesn't match.\n\nFrom there, the number of steps the engine must use to validate a string just continues to grow.\n\n| String | Number of C's | Number of steps |\n| -------|-------------:| -----:|\n| ACCCX | 3 | 38\n| ACCCCX | 4 | 71\n| ACCCCCX | 5 | 136\n| ACCCCCCCCCCCCCCX | 14 | 65,553\n\n\nBy the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.\n\n## Remediation\nUpgrade `json` to version 2.3.0 or higher.\n## References\n- [Ruby Advisory](https://www.ruby-lang.org/en/news/2020/03/19/json-dos-cve-2020-10663/)\n", 12 | "disclosureTime": "2020-03-20T16:06:03Z", 13 | "exploit": "Not Defined", 14 | "fixedIn": [ 15 | "2.3.0" 16 | ], 17 | "functions": [], 18 | "functions_new": [], 19 | "id": "SNYK-RUBY-JSON-560838", 20 | "identifiers": { 21 | "CVE": [ 22 | "CVE-2020-10663" 23 | ], 24 | "CWE": [ 25 | "CWE-400" 26 | ] 27 | }, 28 | "language": "ruby", 29 | "modificationTime": "2020-06-12T14:37:02.660300Z", 30 | "moduleName": "json", 31 | "packageManager": "rubygems", 32 | "packageName": "json", 33 | "patches": [], 34 | "proprietary": false, 35 | "publicationTime": "2020-03-19T16:04:21Z", 36 | "references": [ 37 | { 38 | "title": "Ruby Advisory", 39 | "url": "https://www.ruby-lang.org/en/news/2020/03/19/json-dos-cve-2020-10663/" 40 | } 41 | ], 42 | "semver": { 43 | "vulnerable": [ 44 | "<2.3.0" 45 | ] 46 | }, 47 | "severity": "high", 48 | "severityWithCritical": "critical", 49 | "title": "Denial of Service (DoS)", 50 | "from": [ 51 | "ruby-app@*", 52 | "json@2.0.2" 53 | ], 54 | "upgradePath": [ 55 | false, 56 | "json@2.3.0" 57 | ], 58 | "isUpgradable": true, 59 | "isPatchable": false, 60 | "name": "json", 61 | "version": "2.0.2" 62 | }, 63 | { 64 | "CVSSv3": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L", 65 | "alternativeIds": [], 66 | "creationTime": "2016-09-21T12:36:57Z", 67 | "credit": [ 68 | "Unknown" 69 | ], 70 | "cvssScore": 5.6, 71 | "description": "## Overview\n[`lynx`](https://rubygems.org/gems/lynx) is a command line wrapper for MySQL.\nAffected versions of this gem are vulnerable to arbitrary command executions due to a flaw in `lib/lynx/pipe/run.rb`.\n\n## References\n- http://rubysec.com/advisories/OSVDB-108579\n", 72 | "disclosureTime": "2014-06-29T21:00:00Z", 73 | "exploit": "Not Defined", 74 | "fixedIn": [], 75 | "functions": [], 76 | "functions_new": [], 77 | "id": "SNYK-RUBY-LYNX-20160", 78 | "identifiers": { 79 | "CVE": [], 80 | "CWE": [ 81 | "CWE-77" 82 | ], 83 | "OSVDB": [ 84 | "OSVDB-108579" 85 | ] 86 | }, 87 | "language": "ruby", 88 | "modificationTime": "2019-05-30T11:55:49.846131Z", 89 | "moduleName": "lynx", 90 | "packageManager": "rubygems", 91 | "packageName": "lynx", 92 | "patches": [], 93 | "proprietary": false, 94 | "publicationTime": "2014-06-29T21:00:00Z", 95 | "references": [ 96 | { 97 | "title": "RUBYSEC.COM", 98 | "url": "http://rubysec.com/advisories/OSVDB-108579" 99 | } 100 | ], 101 | "semver": { 102 | "vulnerable": [ 103 | ">= 0" 104 | ] 105 | }, 106 | "severity": "medium", 107 | "severityWithCritical": "medium", 108 | "title": "Arbitrary Command Execution", 109 | "from": [ 110 | "ruby-app@*", 111 | "lynx@0.4.0" 112 | ], 113 | "upgradePath": [], 114 | "isUpgradable": false, 115 | "isPatchable": false, 116 | "name": "lynx", 117 | "version": "0.4.0" 118 | }, 119 | { 120 | "CVSSv3": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", 121 | "alternativeIds": [], 122 | "creationTime": "2016-09-21T12:36:57Z", 123 | "credit": [ 124 | "Unknown" 125 | ], 126 | "cvssScore": 7.8, 127 | "description": "## Overview\n[`lynx`](https://rubygems.org/gems/lynx) is a command line wrapper for MySQL.\nAffected versions of this gem are vulnerable due to a flaw in `command/basic.rb` that exposes password information in plaintext in the process table. This may allow a local attacker to gain access to password information.\n\n## References\n- http://rubysec.com/advisories/CVE-2014-5002\n", 128 | "disclosureTime": "2014-06-29T21:00:00Z", 129 | "exploit": "Not Defined", 130 | "fixedIn": [], 131 | "functions": [], 132 | "functions_new": [], 133 | "id": "SNYK-RUBY-LYNX-20161", 134 | "identifiers": { 135 | "CVE": [ 136 | "CVE-2014-5002" 137 | ], 138 | "CWE": [ 139 | "CWE-200" 140 | ], 141 | "OSVDB": [ 142 | "OSVDB-108580" 143 | ] 144 | }, 145 | "language": "ruby", 146 | "modificationTime": "2019-05-30T11:55:50.567117Z", 147 | "moduleName": "lynx", 148 | "packageManager": "rubygems", 149 | "packageName": "lynx", 150 | "patches": [], 151 | "proprietary": false, 152 | "publicationTime": "2014-06-29T21:00:00Z", 153 | "references": [ 154 | { 155 | "title": "RUBYSEC.COM", 156 | "url": "http://rubysec.com/advisories/CVE-2014-5002" 157 | } 158 | ], 159 | "semver": { 160 | "vulnerable": [ 161 | ">= 0" 162 | ] 163 | }, 164 | "severity": "high", 165 | "severityWithCritical": "high", 166 | "title": "Local Plaintext Password Disclosure", 167 | "from": [ 168 | "ruby-app@*", 169 | "lynx@0.4.0" 170 | ], 171 | "upgradePath": [], 172 | "isUpgradable": false, 173 | "isPatchable": false, 174 | "name": "lynx", 175 | "version": "0.4.0" 176 | } 177 | ], 178 | "ok": false, 179 | "dependencyCount": 2, 180 | "org": "snyk-test", 181 | "policy": "# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.\nversion: v1.19.0\nignore: {}\npatch: {}\n", 182 | "isPrivate": true, 183 | "licensesPolicy": { 184 | "severities": {}, 185 | "orgLicenseRules": { 186 | "AGPL-1.0": { 187 | "licenseType": "AGPL-1.0", 188 | "severity": "high", 189 | "instructions": "" 190 | }, 191 | "AGPL-3.0": { 192 | "licenseType": "AGPL-3.0", 193 | "severity": "high", 194 | "instructions": "" 195 | }, 196 | "Artistic-1.0": { 197 | "licenseType": "Artistic-1.0", 198 | "severity": "medium", 199 | "instructions": "" 200 | }, 201 | "Artistic-2.0": { 202 | "licenseType": "Artistic-2.0", 203 | "severity": "medium", 204 | "instructions": "" 205 | }, 206 | "CDDL-1.0": { 207 | "licenseType": "CDDL-1.0", 208 | "severity": "medium", 209 | "instructions": "" 210 | }, 211 | "CPOL-1.02": { 212 | "licenseType": "CPOL-1.02", 213 | "severity": "high", 214 | "instructions": "" 215 | }, 216 | "EPL-1.0": { 217 | "licenseType": "EPL-1.0", 218 | "severity": "medium", 219 | "instructions": "" 220 | }, 221 | "GPL-2.0": { 222 | "licenseType": "GPL-2.0", 223 | "severity": "high", 224 | "instructions": "" 225 | }, 226 | "GPL-3.0": { 227 | "licenseType": "GPL-3.0", 228 | "severity": "high", 229 | "instructions": "" 230 | }, 231 | "LGPL-2.0": { 232 | "licenseType": "LGPL-2.0", 233 | "severity": "medium", 234 | "instructions": "" 235 | }, 236 | "LGPL-2.1": { 237 | "licenseType": "LGPL-2.1", 238 | "severity": "medium", 239 | "instructions": "" 240 | }, 241 | "LGPL-3.0": { 242 | "licenseType": "LGPL-3.0", 243 | "severity": "medium", 244 | "instructions": "" 245 | }, 246 | "MPL-1.1": { 247 | "licenseType": "MPL-1.1", 248 | "severity": "medium", 249 | "instructions": "" 250 | }, 251 | "MPL-2.0": { 252 | "licenseType": "MPL-2.0", 253 | "severity": "medium", 254 | "instructions": "" 255 | }, 256 | "MS-RL": { 257 | "licenseType": "MS-RL", 258 | "severity": "medium", 259 | "instructions": "" 260 | }, 261 | "SimPL-2.0": { 262 | "licenseType": "SimPL-2.0", 263 | "severity": "high", 264 | "instructions": "" 265 | }, 266 | "MIT": { 267 | "licenseType": "MIT", 268 | "severity": "high", 269 | "instructions": "Not suitable to use, please find a different package." 270 | } 271 | } 272 | }, 273 | "packageManager": "rubygems", 274 | "ignoreSettings": null, 275 | "summary": "3 vulnerable dependency paths", 276 | "remediation": { 277 | "unresolved": [ 278 | { 279 | "CVSSv3": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L", 280 | "alternativeIds": [], 281 | "creationTime": "2016-09-21T12:36:57Z", 282 | "credit": [ 283 | "Unknown" 284 | ], 285 | "cvssScore": 5.6, 286 | "description": "## Overview\n[`lynx`](https://rubygems.org/gems/lynx) is a command line wrapper for MySQL.\nAffected versions of this gem are vulnerable to arbitrary command executions due to a flaw in `lib/lynx/pipe/run.rb`.\n\n## References\n- http://rubysec.com/advisories/OSVDB-108579\n", 287 | "disclosureTime": "2014-06-29T21:00:00Z", 288 | "exploit": "Not Defined", 289 | "fixedIn": [], 290 | "functions": [], 291 | "functions_new": [], 292 | "id": "SNYK-RUBY-LYNX-20160", 293 | "identifiers": { 294 | "CVE": [], 295 | "CWE": [ 296 | "CWE-77" 297 | ], 298 | "OSVDB": [ 299 | "OSVDB-108579" 300 | ] 301 | }, 302 | "language": "ruby", 303 | "modificationTime": "2019-05-30T11:55:49.846131Z", 304 | "moduleName": "lynx", 305 | "packageManager": "rubygems", 306 | "packageName": "lynx", 307 | "patches": [], 308 | "proprietary": false, 309 | "publicationTime": "2014-06-29T21:00:00Z", 310 | "references": [ 311 | { 312 | "title": "RUBYSEC.COM", 313 | "url": "http://rubysec.com/advisories/OSVDB-108579" 314 | } 315 | ], 316 | "semver": { 317 | "vulnerable": [ 318 | ">= 0" 319 | ] 320 | }, 321 | "severity": "medium", 322 | "severityWithCritical": "medium", 323 | "title": "Arbitrary Command Execution", 324 | "from": [ 325 | "ruby-app@*", 326 | "lynx@0.4.0" 327 | ], 328 | "upgradePath": [], 329 | "isUpgradable": false, 330 | "isPatchable": false, 331 | "isPinnable": false, 332 | "name": "lynx", 333 | "version": "0.4.0" 334 | }, 335 | { 336 | "CVSSv3": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", 337 | "alternativeIds": [], 338 | "creationTime": "2016-09-21T12:36:57Z", 339 | "credit": [ 340 | "Unknown" 341 | ], 342 | "cvssScore": 7.8, 343 | "description": "## Overview\n[`lynx`](https://rubygems.org/gems/lynx) is a command line wrapper for MySQL.\nAffected versions of this gem are vulnerable due to a flaw in `command/basic.rb` that exposes password information in plaintext in the process table. This may allow a local attacker to gain access to password information.\n\n## References\n- http://rubysec.com/advisories/CVE-2014-5002\n", 344 | "disclosureTime": "2014-06-29T21:00:00Z", 345 | "exploit": "Not Defined", 346 | "fixedIn": [], 347 | "functions": [], 348 | "functions_new": [], 349 | "id": "SNYK-RUBY-LYNX-20161", 350 | "identifiers": { 351 | "CVE": [ 352 | "CVE-2014-5002" 353 | ], 354 | "CWE": [ 355 | "CWE-200" 356 | ], 357 | "OSVDB": [ 358 | "OSVDB-108580" 359 | ] 360 | }, 361 | "language": "ruby", 362 | "modificationTime": "2019-05-30T11:55:50.567117Z", 363 | "moduleName": "lynx", 364 | "packageManager": "rubygems", 365 | "packageName": "lynx", 366 | "patches": [], 367 | "proprietary": false, 368 | "publicationTime": "2014-06-29T21:00:00Z", 369 | "references": [ 370 | { 371 | "title": "RUBYSEC.COM", 372 | "url": "http://rubysec.com/advisories/CVE-2014-5002" 373 | } 374 | ], 375 | "semver": { 376 | "vulnerable": [ 377 | ">= 0" 378 | ] 379 | }, 380 | "severity": "high", 381 | "severityWithCritical": "high", 382 | "title": "Local Plaintext Password Disclosure", 383 | "from": [ 384 | "ruby-app@*", 385 | "lynx@0.4.0" 386 | ], 387 | "upgradePath": [], 388 | "isUpgradable": false, 389 | "isPatchable": false, 390 | "isPinnable": false, 391 | "name": "lynx", 392 | "version": "0.4.0" 393 | } 394 | ], 395 | "upgrade": { 396 | "json@2.0.2": { 397 | "upgradeTo": "json@2.3.0", 398 | "upgrades": [ 399 | "json@2.0.2" 400 | ], 401 | "vulns": [ 402 | "SNYK-RUBY-JSON-560838" 403 | ] 404 | } 405 | }, 406 | "patch": {}, 407 | "ignore": {}, 408 | "pin": {} 409 | }, 410 | "filesystemPolicy": false, 411 | "filtered": { 412 | "ignore": [], 413 | "patch": [] 414 | }, 415 | "uniqueCount": 3, 416 | "projectName": "ruby-app", 417 | "displayTargetFile": "Gemfile", 418 | "path": "/Users/snyk/workspaces/ruby-app" 419 | } 420 | -------------------------------------------------------------------------------- /test/fixtures/single-license-issue.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "MIT", 3 | "semver": { 4 | "vulnerable": [ 5 | "[2005a,)" 6 | ] 7 | }, 8 | "id": "snyk:lic:pip:pytz:MIT", 9 | "type": "license", 10 | "packageManager": "pip", 11 | "language": "python", 12 | "packageName": "pytz", 13 | "title": "MIT license", 14 | "description": "MIT license", 15 | "publicationTime": "2021-05-23T11:15:36.845Z", 16 | "creationTime": "2021-05-23T11:15:36.845Z", 17 | "patches": [], 18 | "licenseTemplateUrl": "https://raw.githubusercontent.com/spdx/license-list/master/MIT.txt", 19 | "severity": "high", 20 | "legalInstructionsArray": [ 21 | { 22 | "licenseName": "MIT", 23 | "legalContent": "Not suitable to use, please find a different package." 24 | } 25 | ], 26 | "from": [ 27 | "app-with-already-fixed@0.0.0", 28 | "django@2.2.18", 29 | "pytz@2021.1" 30 | ], 31 | "upgradePath": [], 32 | "isUpgradable": false, 33 | "isPatchable": false, 34 | "name": "pytz", 35 | "version": "2021.1" 36 | } 37 | -------------------------------------------------------------------------------- /test/fixtures/single-vuln.json: -------------------------------------------------------------------------------- 1 | { 2 | "CVSSv3": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:H", 3 | "alternativeIds": [], 4 | "creationTime": "2020-03-20T13:31:56.742021Z", 5 | "credit": [ 6 | "Unknown" 7 | ], 8 | "cvssScore": 9.3, 9 | "description": "## Overview\n[json](https://rubygems.org/gems/json) is a JSON implementation as a Ruby extension in C.\n\nAffected versions of this package are vulnerable to Denial of Service (DoS). When parsing certain JSON documents, the json gem (including the one bundled with Ruby) can be coerced into creating arbitrary objects in the target system.\r\n\r\nThis is the same issue as CVE-2013-0269. The previous fix was incomplete, which addressed `JSON.parse(user_input)`, but didn’t address some other styles of JSON\r\nparsing including `JSON(user_input) and JSON.parse(user_input, nil)`.\r\n\r\nSee [CVE-2013-0269](https://snyk.io/vuln/SNYK-RUBY-JSON-20060) in detail.\n\n## Details\n\nDenial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.\n\nThe Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.\n\nLet’s take the following regular expression as an example:\n```js\nregex = /A(B|C+)+D/\n```\n\nThis regular expression accomplishes the following:\n- `A` The string must start with the letter 'A'\n- `(B|C+)+` The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the `+` matches one or more times). The `+` at the end of this section states that we can look for one or more matches of this section.\n- `D` Finally, we ensure this section of the string ends with a 'D'\n\nThe expression would match inputs such as `ABBD`, `ABCCCCD`, `ABCBCCCD` and `ACCCCCD`\n\nIt most cases, it doesn't take very long for a regex engine to find a match:\n\n```bash\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD\")'\n0.04s user 0.01s system 95% cpu 0.052 total\n\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX\")'\n1.79s user 0.02s system 99% cpu 1.812 total\n```\n\nThe entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.\n\nMost Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as _catastrophic backtracking_.\n\nLet's look at how our expression runs into this problem, using a shorter string: \"ACCCX\". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:\n1. CCC\n2. CC+C\n3. C+CC\n4. C+C+C.\n\nThe engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use [RegEx 101 debugger](https://regex101.com/debugger) to see the engine has to take a total of 38 steps before it can determine the string doesn't match.\n\nFrom there, the number of steps the engine must use to validate a string just continues to grow.\n\n| String | Number of C's | Number of steps |\n| -------|-------------:| -----:|\n| ACCCX | 3 | 38\n| ACCCCX | 4 | 71\n| ACCCCCX | 5 | 136\n| ACCCCCCCCCCCCCCX | 14 | 65,553\n\n\nBy the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.\n\n## Remediation\nUpgrade `json` to version 2.3.0 or higher.\n## References\n- [Ruby Advisory](https://www.ruby-lang.org/en/news/2020/03/19/json-dos-cve-2020-10663/)\n", 10 | "disclosureTime": "2020-03-20T16:06:03Z", 11 | "exploit": "Not Defined", 12 | "fixedIn": [ 13 | "2.3.0" 14 | ], 15 | "functions": [], 16 | "functions_new": [], 17 | "id": "SNYK-RUBY-JSON-560838", 18 | "identifiers": { 19 | "CVE": [ 20 | "CVE-2020-10663" 21 | ], 22 | "CWE": [ 23 | "CWE-400" 24 | ] 25 | }, 26 | "language": "ruby", 27 | "modificationTime": "2020-06-12T14:37:02.660300Z", 28 | "moduleName": "json", 29 | "packageManager": "rubygems", 30 | "packageName": "json", 31 | "patches": [], 32 | "proprietary": false, 33 | "publicationTime": "2020-03-19T16:04:21Z", 34 | "references": [ 35 | { 36 | "title": "Ruby Advisory", 37 | "url": "https://www.ruby-lang.org/en/news/2020/03/19/json-dos-cve-2020-10663/" 38 | } 39 | ], 40 | "semver": { 41 | "vulnerable": [ 42 | "<2.3.0" 43 | ] 44 | }, 45 | "severity": "high", 46 | "severityWithCritical": "critical", 47 | "title": "Denial of Service (DoS)", 48 | "from": [ 49 | "ruby-app@*", 50 | "json@2.0.2" 51 | ], 52 | "upgradePath": [ 53 | false, 54 | "json@2.3.0" 55 | ], 56 | "isUpgradable": true, 57 | "isPatchable": false, 58 | "name": "json", 59 | "version": "2.0.2" 60 | } 61 | -------------------------------------------------------------------------------- /test/fixtures/with-license-issues.json: -------------------------------------------------------------------------------- 1 | { 2 | "vulnerabilities": [ 3 | { 4 | "CVSSv3": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:H", 5 | "alternativeIds": [], 6 | "creationTime": "2021-02-19T13:39:56.970650Z", 7 | "credit": [ 8 | "Nick Pope" 9 | ], 10 | "cvssScore": 5.9, 11 | "description": "## Overview\n[django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.\n\nAffected versions of this package are vulnerable to Web Cache Poisoning. Django contains a copy of urllib.parse.parse_qsl() which was added to backport some security fixes. A further security fix has been issued recently such that parse_qsl() no longer allows using ; as a query parameter separator by default.\n## Remediation\nUpgrade `django` to version 2.2.19, 3.0.13, 3.1.7 or higher.\n## References\n- [Django Security Releases](https://www.djangoproject.com/weblog/2021/feb/19/security-releases/)\n- [GitHub Commit](https://github.com/django/django/commit/be8237c7cce24b06aabde0b97afce98ddabbe3b6)\n- [Snyk Cache Poisoning Blogpost](https://snyk.io/blog/cache-poisoning-in-popular-open-source-packages/)\n", 12 | "disclosureTime": "2021-02-19T13:28:51Z", 13 | "exploit": "Not Defined", 14 | "fixedIn": [ 15 | "2.2.19", 16 | "3.0.13", 17 | "3.1.7" 18 | ], 19 | "functions": [], 20 | "functions_new": [], 21 | "id": "SNYK-PYTHON-DJANGO-1076802", 22 | "identifiers": { 23 | "CVE": [ 24 | "CVE-2021-23336" 25 | ], 26 | "CWE": [ 27 | "CWE-444" 28 | ] 29 | }, 30 | "language": "python", 31 | "modificationTime": "2021-02-19T15:54:22.876737Z", 32 | "moduleName": "django", 33 | "packageManager": "pip", 34 | "packageName": "django", 35 | "patches": [], 36 | "proprietary": false, 37 | "publicationTime": "2021-02-19T15:54:23.197747Z", 38 | "references": [ 39 | { 40 | "title": "Django Security Releases", 41 | "url": "https://www.djangoproject.com/weblog/2021/feb/19/security-releases/" 42 | }, 43 | { 44 | "title": "GitHub Commit", 45 | "url": "https://github.com/django/django/commit/be8237c7cce24b06aabde0b97afce98ddabbe3b6" 46 | }, 47 | { 48 | "title": "Snyk Cache Poisoning Blogpost", 49 | "url": "https://snyk.io/blog/cache-poisoning-in-popular-open-source-packages/" 50 | } 51 | ], 52 | "semver": { 53 | "vulnerable": [ 54 | "[2.2,2.2.19)", 55 | "[3.0,3.0.13)", 56 | "[3.1,3.1.7)" 57 | ] 58 | }, 59 | "severity": "medium", 60 | "severityWithCritical": "medium", 61 | "title": "Web Cache Poisoning", 62 | "from": [ 63 | "app-with-already-fixed@0.0.0", 64 | "django@2.2.18" 65 | ], 66 | "upgradePath": [], 67 | "isUpgradable": false, 68 | "isPatchable": false, 69 | "name": "django", 70 | "version": "2.2.18" 71 | }, 72 | { 73 | "CVSSv3": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N", 74 | "alternativeIds": [], 75 | "creationTime": "2021-04-06T09:22:54.987556Z", 76 | "credit": [ 77 | "Dennis Brinkrolf" 78 | ], 79 | "cvssScore": 3.7, 80 | "description": "## Overview\n[django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.\n\nAffected versions of this package are vulnerable to Directory Traversal. `MultiPartParser` allowed directory-traversal via uploaded files with suitably crafted file names.\n\n## Details\n\nA Directory Traversal attack (also known as path traversal) aims to access files and directories that are stored outside the intended folder. By manipulating files with \"dot-dot-slash (../)\" sequences and its variations, or by using absolute file paths, it may be possible to access arbitrary files and directories stored on file system, including application source code, configuration, and other critical system files.\n\nDirectory Traversal vulnerabilities can be generally divided into two types:\n\n- **Information Disclosure**: Allows the attacker to gain information about the folder structure or read the contents of sensitive files on the system.\n\n`st` is a module for serving static files on web pages, and contains a [vulnerability of this type](https://snyk.io/vuln/npm:st:20140206). In our example, we will serve files from the `public` route.\n\nIf an attacker requests the following URL from our server, it will in turn leak the sensitive private key of the root user.\n\n```\ncurl http://localhost:8080/public/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/root/.ssh/id_rsa\n```\n**Note** `%2e` is the URL encoded version of `.` (dot).\n\n- **Writing arbitrary files**: Allows the attacker to create or replace existing files. This type of vulnerability is also known as `Zip-Slip`. \n\nOne way to achieve this is by using a malicious `zip` archive that holds path traversal filenames. When each filename in the zip archive gets concatenated to the target extraction folder, without validation, the final path ends up outside of the target folder. If an executable or a configuration file is overwritten with a file containing malicious code, the problem can turn into an arbitrary code execution issue quite easily.\n\nThe following is an example of a `zip` archive with one benign file and one malicious file. Extracting the malicious file will result in traversing out of the target folder, ending up in `/root/.ssh/` overwriting the `authorized_keys` file:\n\n```\n2018-04-15 22:04:29 ..... 19 19 good.txt\n2018-04-15 22:04:42 ..... 20 20 ../../../../../../root/.ssh/authorized_keys\n```\n\n## Remediation\nUpgrade `django` to version 2.2.20, 3.0.14, 3.1.8 or higher.\n## References\n- [GitHub Commit](https://github.com/django/django/commit/2820fd1be5dfccbf1216c3845fad8580502473e1)\n- [GitHub Commit](https://github.com/django/django/commit/4036d62bda0e9e9f6172943794b744a454ca49c2)\n- [GitHub Commit](https://github.com/django/django/commit/cca0d98118cccf9ae0c6dcf2d6c57fc50469fbf0)\n- [GitHub Commit](https://github.com/django/django/commit/d4d800ca1addc4141e03c5440a849bb64d1582cd)\n- [GitHub Commit](https://github.com/django/django/commit/e7fba62248f604c76da4f23dcf1db4a57b0808ea)\n", 81 | "disclosureTime": "2021-04-06T09:18:59Z", 82 | "exploit": "Not Defined", 83 | "fixedIn": [ 84 | "2.2.20", 85 | "3.0.14", 86 | "3.1.8" 87 | ], 88 | "functions": [], 89 | "functions_new": [], 90 | "id": "SNYK-PYTHON-DJANGO-1090612", 91 | "identifiers": { 92 | "CVE": [ 93 | "CVE-2021-28658" 94 | ], 95 | "CWE": [ 96 | "CWE-22" 97 | ] 98 | }, 99 | "language": "python", 100 | "modificationTime": "2021-04-06T13:57:02.213825Z", 101 | "moduleName": "django", 102 | "packageManager": "pip", 103 | "packageName": "django", 104 | "patches": [], 105 | "proprietary": false, 106 | "publicationTime": "2021-04-06T13:57:02.482219Z", 107 | "references": [ 108 | { 109 | "title": "GitHub Commit", 110 | "url": "https://github.com/django/django/commit/2820fd1be5dfccbf1216c3845fad8580502473e1" 111 | }, 112 | { 113 | "title": "GitHub Commit", 114 | "url": "https://github.com/django/django/commit/4036d62bda0e9e9f6172943794b744a454ca49c2" 115 | }, 116 | { 117 | "title": "GitHub Commit", 118 | "url": "https://github.com/django/django/commit/cca0d98118cccf9ae0c6dcf2d6c57fc50469fbf0" 119 | }, 120 | { 121 | "title": "GitHub Commit", 122 | "url": "https://github.com/django/django/commit/d4d800ca1addc4141e03c5440a849bb64d1582cd" 123 | }, 124 | { 125 | "title": "GitHub Commit", 126 | "url": "https://github.com/django/django/commit/e7fba62248f604c76da4f23dcf1db4a57b0808ea" 127 | } 128 | ], 129 | "semver": { 130 | "vulnerable": [ 131 | "[2.2, 2.2.20)", 132 | "[3.0, 3.0.14)", 133 | "[3.1, 3.1.8)" 134 | ] 135 | }, 136 | "severity": "low", 137 | "severityWithCritical": "low", 138 | "title": "Directory Traversal", 139 | "from": [ 140 | "app-with-already-fixed@0.0.0", 141 | "django@2.2.18" 142 | ], 143 | "upgradePath": [], 144 | "isUpgradable": false, 145 | "isPatchable": false, 146 | "name": "django", 147 | "version": "2.2.18" 148 | }, 149 | { 150 | "CVSSv3": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N", 151 | "alternativeIds": [], 152 | "creationTime": "2021-05-04T12:51:50.753891Z", 153 | "credit": [ 154 | "Jasu Viding" 155 | ], 156 | "cvssScore": 3.3, 157 | "description": "## Overview\n[django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.\n\nAffected versions of this package are vulnerable to Directory Traversal. `MultiPartParser`, `UploadedFile`, and `FieldFile` allow directory-traversal via uploaded files with suitably crafted file names.\n\n## Details\n\nA Directory Traversal attack (also known as path traversal) aims to access files and directories that are stored outside the intended folder. By manipulating files with \"dot-dot-slash (../)\" sequences and its variations, or by using absolute file paths, it may be possible to access arbitrary files and directories stored on file system, including application source code, configuration, and other critical system files.\n\nDirectory Traversal vulnerabilities can be generally divided into two types:\n\n- **Information Disclosure**: Allows the attacker to gain information about the folder structure or read the contents of sensitive files on the system.\n\n`st` is a module for serving static files on web pages, and contains a [vulnerability of this type](https://snyk.io/vuln/npm:st:20140206). In our example, we will serve files from the `public` route.\n\nIf an attacker requests the following URL from our server, it will in turn leak the sensitive private key of the root user.\n\n```\ncurl http://localhost:8080/public/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/root/.ssh/id_rsa\n```\n**Note** `%2e` is the URL encoded version of `.` (dot).\n\n- **Writing arbitrary files**: Allows the attacker to create or replace existing files. This type of vulnerability is also known as `Zip-Slip`. \n\nOne way to achieve this is by using a malicious `zip` archive that holds path traversal filenames. When each filename in the zip archive gets concatenated to the target extraction folder, without validation, the final path ends up outside of the target folder. If an executable or a configuration file is overwritten with a file containing malicious code, the problem can turn into an arbitrary code execution issue quite easily.\n\nThe following is an example of a `zip` archive with one benign file and one malicious file. Extracting the malicious file will result in traversing out of the target folder, ending up in `/root/.ssh/` overwriting the `authorized_keys` file:\n\n```\n2018-04-15 22:04:29 ..... 19 19 good.txt\n2018-04-15 22:04:42 ..... 20 20 ../../../../../../root/.ssh/authorized_keys\n```\n\n## Remediation\nUpgrade `django` to version 2.2.21, 3.1.9, 3.2.1 or higher.\n## References\n- [Django Advisory](https://www.djangoproject.com/weblog/2021/may/04/security-releases/)\n- [GitHub Commit](https://github.com/django/django/commit/c98f446c188596d4ba6de71d1b77b4a6c5c2a007)\n", 158 | "disclosureTime": "2021-05-04T12:44:10Z", 159 | "exploit": "Not Defined", 160 | "fixedIn": [ 161 | "2.2.21", 162 | "3.1.9", 163 | "3.2.1" 164 | ], 165 | "functions": [], 166 | "functions_new": [], 167 | "id": "SNYK-PYTHON-DJANGO-1279042", 168 | "identifiers": { 169 | "CVE": [ 170 | "CVE-2021-31542" 171 | ], 172 | "CWE": [ 173 | "CWE-22" 174 | ] 175 | }, 176 | "language": "python", 177 | "modificationTime": "2021-05-04T14:45:09.894750Z", 178 | "moduleName": "django", 179 | "packageManager": "pip", 180 | "packageName": "django", 181 | "patches": [], 182 | "proprietary": false, 183 | "publicationTime": "2021-05-04T14:45:10.137628Z", 184 | "references": [ 185 | { 186 | "title": "Django Advisory", 187 | "url": "https://www.djangoproject.com/weblog/2021/may/04/security-releases/" 188 | }, 189 | { 190 | "title": "GitHub Commit", 191 | "url": "https://github.com/django/django/commit/c98f446c188596d4ba6de71d1b77b4a6c5c2a007" 192 | } 193 | ], 194 | "semver": { 195 | "vulnerable": [ 196 | "[, 2.2.21)", 197 | "[3.0, 3.1.9)", 198 | "[3.2, 3.2.1)" 199 | ] 200 | }, 201 | "severity": "low", 202 | "severityWithCritical": "low", 203 | "title": "Directory Traversal", 204 | "from": [ 205 | "app-with-already-fixed@0.0.0", 206 | "django@2.2.18" 207 | ], 208 | "upgradePath": [], 209 | "isUpgradable": false, 210 | "isPatchable": false, 211 | "name": "django", 212 | "version": "2.2.18" 213 | }, 214 | { 215 | "CVSSv3": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L", 216 | "alternativeIds": [], 217 | "creationTime": "2021-05-06T14:52:58.420987Z", 218 | "credit": [ 219 | "Unknown" 220 | ], 221 | "cvssScore": 7.3, 222 | "description": "## Overview\n[django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.\n\nAffected versions of this package are vulnerable to HTTP Header Injection. In Python 3.9.5+ urllib.parse() automatically removes ASCII newlines and tabs from URLs. Unfortunately it created an issue in the URLValidator. URLValidator uses `urllib.urlsplit()` and `urllib.urlunsplit()` for creating a URL variant with Punycode which no longer contains newlines and tabs in Python 3.9.5+. As a consequence, the regular expression matched the URL (without unsafe characters) and the source value (with unsafe characters) was considered valid.\r\n\r\nThis issue was introduced by the [bpo-43882](https://bugs.python.org/issue43882) fix.\n## Remediation\nUpgrade `django` to version 3.2.2, 3.1.10, 2.2.22 or higher.\n## References\n- [Django Security Releases](https://www.djangoproject.com/weblog/2021/may/06/security-releases/)\n- [GitHub Commit](https://github.com/django/django/commit/e1e81aa1c4427411e3c68facdd761229ffea6f6f)\n- [GitHub PR](https://github.com/django/django/pull/14360)\n- [Mail Archive](https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1804086.html)\n", 223 | "disclosureTime": "2021-05-06T14:44:15Z", 224 | "exploit": "Not Defined", 225 | "fixedIn": [ 226 | "2.2.22", 227 | "3.1.10", 228 | "3.2.2" 229 | ], 230 | "functions": [], 231 | "functions_new": [], 232 | "id": "SNYK-PYTHON-DJANGO-1290072", 233 | "identifiers": { 234 | "CVE": [ 235 | "CVE-2021-32052" 236 | ], 237 | "CWE": [ 238 | "CWE-644" 239 | ] 240 | }, 241 | "language": "python", 242 | "modificationTime": "2021-05-06T15:41:43.922301Z", 243 | "moduleName": "django", 244 | "packageManager": "pip", 245 | "packageName": "django", 246 | "patches": [], 247 | "proprietary": false, 248 | "publicationTime": "2021-05-06T15:41:44.175836Z", 249 | "references": [ 250 | { 251 | "title": "Django Security Releases", 252 | "url": "https://www.djangoproject.com/weblog/2021/may/06/security-releases/" 253 | }, 254 | { 255 | "title": "GitHub Commit", 256 | "url": "https://github.com/django/django/commit/e1e81aa1c4427411e3c68facdd761229ffea6f6f" 257 | }, 258 | { 259 | "title": "GitHub PR", 260 | "url": "https://github.com/django/django/pull/14360" 261 | }, 262 | { 263 | "title": "Mail Archive", 264 | "url": "https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1804086.html" 265 | } 266 | ], 267 | "semver": { 268 | "vulnerable": [ 269 | "[3.2,3.2.2)", 270 | "[3.0,3.1.10)", 271 | "[,2.2.22)" 272 | ] 273 | }, 274 | "severity": "high", 275 | "severityWithCritical": "high", 276 | "title": "HTTP Header Injection", 277 | "from": [ 278 | "app-with-already-fixed@0.0.0", 279 | "django@2.2.18" 280 | ], 281 | "upgradePath": [], 282 | "isUpgradable": false, 283 | "isPatchable": false, 284 | "name": "django", 285 | "version": "2.2.18" 286 | }, 287 | { 288 | "CVSSv3": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L/E:P", 289 | "alternativeIds": [], 290 | "creationTime": "2020-09-25T17:30:26.286074Z", 291 | "credit": [ 292 | "Yeting Li" 293 | ], 294 | "cvssScore": 5.3, 295 | "description": "## Overview\n[jinja2](https://pypi.org/project/Jinja2/) is a template engine written in pure Python. It provides a Django inspired non-XML syntax but supports inline expressions and an optional sandboxed environment.\n\nAffected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS). The ReDoS vulnerability is mainly due to the `_punctuation_re regex` operator and its use of multiple wildcards. The last wildcard is the most exploitable as it searches for trailing punctuation.\r\n\r\nThis issue can be mitigated by using Markdown to format user content instead of the urlize filter, or by implementing request timeouts or limiting process memory.\r\n\r\n### PoC by Yeting Li\r\n```\r\nfrom jinja2.utils import urlize\r\nfrom time import perf_counter\r\n\r\nfor i in range(3):\r\n text = \"abc@\" + \".\" * (i+1)*5000 + \"!\"\r\n LEN = len(text)\r\n BEGIN = perf_counter()\r\n urlize(text)\r\n DURATION = perf_counter() - BEGIN\r\n print(f\"{LEN}: took {DURATION} seconds!\")\r\n```\n\n## Details\n\nDenial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.\n\nThe Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.\n\nLet’s take the following regular expression as an example:\n```js\nregex = /A(B|C+)+D/\n```\n\nThis regular expression accomplishes the following:\n- `A` The string must start with the letter 'A'\n- `(B|C+)+` The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the `+` matches one or more times). The `+` at the end of this section states that we can look for one or more matches of this section.\n- `D` Finally, we ensure this section of the string ends with a 'D'\n\nThe expression would match inputs such as `ABBD`, `ABCCCCD`, `ABCBCCCD` and `ACCCCCD`\n\nIt most cases, it doesn't take very long for a regex engine to find a match:\n\n```bash\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD\")'\n0.04s user 0.01s system 95% cpu 0.052 total\n\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX\")'\n1.79s user 0.02s system 99% cpu 1.812 total\n```\n\nThe entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.\n\nMost Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as _catastrophic backtracking_.\n\nLet's look at how our expression runs into this problem, using a shorter string: \"ACCCX\". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:\n1. CCC\n2. CC+C\n3. C+CC\n4. C+C+C.\n\nThe engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use [RegEx 101 debugger](https://regex101.com/debugger) to see the engine has to take a total of 38 steps before it can determine the string doesn't match.\n\nFrom there, the number of steps the engine must use to validate a string just continues to grow.\n\n| String | Number of C's | Number of steps |\n| -------|-------------:| -----:|\n| ACCCX | 3 | 38\n| ACCCCX | 4 | 71\n| ACCCCCX | 5 | 136\n| ACCCCCCCCCCCCCCX | 14 | 65,553\n\n\nBy the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.\n\n## Remediation\nUpgrade `jinja2` to version 2.11.3 or higher.\n## References\n- [GitHub Additional Information](https://github.com/pallets/jinja/blob/ab81fd9c277900c85da0c322a2ff9d68a235b2e6/src/jinja2/utils.py#L20)\n- [GitHub PR](https://github.com/pallets/jinja/pull/1343)\n", 296 | "disclosureTime": "2020-09-25T17:29:19Z", 297 | "exploit": "Proof of Concept", 298 | "fixedIn": [ 299 | "2.11.3" 300 | ], 301 | "functions": [], 302 | "functions_new": [], 303 | "id": "SNYK-PYTHON-JINJA2-1012994", 304 | "identifiers": { 305 | "CVE": [ 306 | "CVE-2020-28493" 307 | ], 308 | "CWE": [ 309 | "CWE-400" 310 | ], 311 | "GHSA": [ 312 | "GHSA-g3rq-g295-4j3m" 313 | ] 314 | }, 315 | "language": "python", 316 | "modificationTime": "2021-02-01T19:52:16.877030Z", 317 | "moduleName": "jinja2", 318 | "packageManager": "pip", 319 | "packageName": "jinja2", 320 | "patches": [], 321 | "proprietary": true, 322 | "publicationTime": "2021-02-01T19:52:17Z", 323 | "references": [ 324 | { 325 | "title": "GitHub Additional Information", 326 | "url": "https://github.com/pallets/jinja/blob/ab81fd9c277900c85da0c322a2ff9d68a235b2e6/src/jinja2/utils.py%23L20" 327 | }, 328 | { 329 | "title": "GitHub PR", 330 | "url": "https://github.com/pallets/jinja/pull/1343" 331 | } 332 | ], 333 | "semver": { 334 | "vulnerable": [ 335 | "[,2.11.3)" 336 | ] 337 | }, 338 | "severity": "medium", 339 | "severityWithCritical": "medium", 340 | "title": "Regular Expression Denial of Service (ReDoS)", 341 | "from": [ 342 | "app-with-already-fixed@0.0.0", 343 | "jinja2@2.7.2" 344 | ], 345 | "upgradePath": [], 346 | "isUpgradable": false, 347 | "isPatchable": false, 348 | "name": "jinja2", 349 | "version": "2.7.2" 350 | }, 351 | { 352 | "CVSSv3": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:L/I:L/A:L/RL:O", 353 | "alternativeIds": [], 354 | "creationTime": "2019-04-07T10:24:16.310959Z", 355 | "credit": [ 356 | "Unknown" 357 | ], 358 | "cvssScore": 6, 359 | "description": "## Overview\n[jinja2](https://pypi.org/project/Jinja2/) is a template engine written in pure Python. It provides a Django inspired non-XML syntax but supports inline expressions and an optional sandboxed environment.\n\nAffected versions of this package are vulnerable to Sandbox Escape via the `str.format_map`.\n## Remediation\nUpgrade `jinja2` to version 2.10.1 or higher.\n## References\n- [Release Notes](https://palletsprojects.com/blog/jinja-2-10-1-released)\n", 360 | "disclosureTime": "2019-04-07T00:42:43Z", 361 | "exploit": "Not Defined", 362 | "fixedIn": [ 363 | "2.10.1" 364 | ], 365 | "functions": [], 366 | "functions_new": [], 367 | "id": "SNYK-PYTHON-JINJA2-174126", 368 | "identifiers": { 369 | "CVE": [ 370 | "CVE-2019-10906" 371 | ], 372 | "CWE": [ 373 | "CWE-265" 374 | ] 375 | }, 376 | "language": "python", 377 | "modificationTime": "2020-06-12T14:36:55.661596Z", 378 | "moduleName": "jinja2", 379 | "packageManager": "pip", 380 | "packageName": "jinja2", 381 | "patches": [], 382 | "proprietary": false, 383 | "publicationTime": "2019-04-07T00:42:43Z", 384 | "references": [ 385 | { 386 | "title": "Release Notes", 387 | "url": "https://palletsprojects.com/blog/jinja-2-10-1-released" 388 | } 389 | ], 390 | "semver": { 391 | "vulnerable": [ 392 | "[,2.10.1)" 393 | ] 394 | }, 395 | "severity": "medium", 396 | "severityWithCritical": "medium", 397 | "title": "Sandbox Escape", 398 | "from": [ 399 | "app-with-already-fixed@0.0.0", 400 | "jinja2@2.7.2" 401 | ], 402 | "upgradePath": [], 403 | "isUpgradable": false, 404 | "isPatchable": false, 405 | "name": "jinja2", 406 | "version": "2.7.2" 407 | }, 408 | { 409 | "CVSSv3": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L", 410 | "alternativeIds": [], 411 | "creationTime": "2017-05-28T08:29:50.295000Z", 412 | "credit": [ 413 | "Arun Babu Neelicattu" 414 | ], 415 | "cvssScore": 5.3, 416 | "description": "## Overview\r\n[`jinja2`](https://pypi.python.org/pypi/jinja2) is a small but fast and easy to use stand-alone template engine written in pure python.\r\nFileSystemBytecodeCache in Jinja2 2.7.2 does not properly create temporary directories, which allows local users to gain privileges by pre-creating a temporary directory with a user's uid.\r\n\r\n**NOTE:** this vulnerability exists because of an incomplete fix for [CVE-2014-1402](https://snyk.io/vulnSNYK-PYTHON-JINJA2-40028).\r\n\r\n## References\r\n- [NVD](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-0012)\r\n- [Bugzilla redhat](https://bugzilla.redhat.com/show_bug.cgi?id=1051421)\r\n- [GitHub PR #1](https://github.com/mitsuhiko/jinja2/pull/292)\r\n- [GitHub PR #2](https://github.com/mitsuhiko/jinja2/pull/296)\r\n- [GitHub Commit](https://github.com/mitsuhiko/jinja2/commit/acb672b6a179567632e032f547582f30fa2f4aa7)\r\n", 417 | "disclosureTime": "2014-01-18T05:33:40.101000Z", 418 | "exploit": "Not Defined", 419 | "fixedIn": [], 420 | "functions": [], 421 | "functions_new": [], 422 | "id": "SNYK-PYTHON-JINJA2-40250", 423 | "identifiers": { 424 | "CVE": [ 425 | "CVE-2014-0012" 426 | ], 427 | "CWE": [ 428 | "CWE-264" 429 | ] 430 | }, 431 | "language": "python", 432 | "modificationTime": "2019-02-17T08:46:41.648104Z", 433 | "moduleName": "jinja2", 434 | "packageManager": "pip", 435 | "packageName": "jinja2", 436 | "patches": [], 437 | "proprietary": false, 438 | "publicationTime": "2014-01-18T05:33:40.101000Z", 439 | "references": [ 440 | { 441 | "title": "GitHub Commit", 442 | "url": "https://github.com/mitsuhiko/jinja2/commit/acb672b6a179567632e032f547582f30fa2f4aa7" 443 | }, 444 | { 445 | "title": "GitHub PR", 446 | "url": "https://github.com/mitsuhiko/jinja2/pull/292" 447 | }, 448 | { 449 | "title": "GitHub PR", 450 | "url": "https://github.com/mitsuhiko/jinja2/pull/296" 451 | }, 452 | { 453 | "title": "NVD", 454 | "url": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-0012" 455 | }, 456 | { 457 | "title": "RedHat Bugzilla Bug", 458 | "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1051421" 459 | } 460 | ], 461 | "semver": { 462 | "vulnerable": [ 463 | "[2.7.2]" 464 | ] 465 | }, 466 | "severity": "medium", 467 | "severityWithCritical": "medium", 468 | "title": "Privilege Escalation", 469 | "from": [ 470 | "app-with-already-fixed@0.0.0", 471 | "jinja2@2.7.2" 472 | ], 473 | "upgradePath": [], 474 | "isUpgradable": false, 475 | "isPatchable": false, 476 | "name": "jinja2", 477 | "version": "2.7.2" 478 | }, 479 | { 480 | "CVSSv3": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N", 481 | "alternativeIds": [], 482 | "creationTime": "2019-07-29T13:28:48.288799Z", 483 | "credit": [ 484 | "Unknown" 485 | ], 486 | "cvssScore": 8.6, 487 | "description": "## Overview\n[jinja2](https://pypi.org/project/Jinja2/) is a template engine written in pure Python. It provides a Django inspired non-XML syntax but supports inline expressions and an optional sandboxed environment.\n\nAffected versions of this package are vulnerable to Sandbox Bypass. Users were allowed to insert `str.format` through web templates, leading to an escape from sandbox.\n## Remediation\nUpgrade `jinja2` to version 2.8.1 or higher.\n## References\n- [GitHub Commit](https://github.com/pallets/jinja/commit/9b53045c34e61013dc8f09b7e52a555fa16bed16)\n", 488 | "disclosureTime": "2016-12-29T13:27:18Z", 489 | "exploit": "Not Defined", 490 | "fixedIn": [ 491 | "2.8.1" 492 | ], 493 | "functions": [], 494 | "functions_new": [], 495 | "id": "SNYK-PYTHON-JINJA2-455616", 496 | "identifiers": { 497 | "CVE": [ 498 | "CVE-2016-10745" 499 | ], 500 | "CWE": [ 501 | "CWE-234" 502 | ] 503 | }, 504 | "language": "python", 505 | "modificationTime": "2020-06-12T14:36:58.461729Z", 506 | "moduleName": "jinja2", 507 | "packageManager": "pip", 508 | "packageName": "jinja2", 509 | "patches": [], 510 | "proprietary": false, 511 | "publicationTime": "2019-07-30T13:11:16Z", 512 | "references": [ 513 | { 514 | "title": "GitHub Commit", 515 | "url": "https://github.com/pallets/jinja/commit/9b53045c34e61013dc8f09b7e52a555fa16bed16" 516 | } 517 | ], 518 | "semver": { 519 | "vulnerable": [ 520 | "[2.5, 2.8.1)" 521 | ] 522 | }, 523 | "severity": "high", 524 | "severityWithCritical": "high", 525 | "title": "Sandbox Bypass", 526 | "from": [ 527 | "app-with-already-fixed@0.0.0", 528 | "jinja2@2.7.2" 529 | ], 530 | "upgradePath": [], 531 | "isUpgradable": false, 532 | "isPatchable": false, 533 | "name": "jinja2", 534 | "version": "2.7.2" 535 | }, 536 | { 537 | "license": "MIT", 538 | "semver": { 539 | "vulnerable": [ 540 | "[2005a,)" 541 | ] 542 | }, 543 | "id": "snyk:lic:pip:pytz:MIT", 544 | "type": "license", 545 | "packageManager": "pip", 546 | "language": "python", 547 | "packageName": "pytz", 548 | "title": "MIT license", 549 | "description": "MIT license", 550 | "publicationTime": "2021-05-23T11:15:36.845Z", 551 | "creationTime": "2021-05-23T11:15:36.845Z", 552 | "patches": [], 553 | "licenseTemplateUrl": "https://raw.githubusercontent.com/spdx/license-list/master/MIT.txt", 554 | "severity": "high", 555 | "legalInstructionsArray": [ 556 | { 557 | "licenseName": "MIT", 558 | "legalContent": "Not suitable to use, please find a different package." 559 | } 560 | ], 561 | "from": [ 562 | "app-with-already-fixed@0.0.0", 563 | "django@2.2.18", 564 | "pytz@2021.1" 565 | ], 566 | "upgradePath": [], 567 | "isUpgradable": false, 568 | "isPatchable": false, 569 | "name": "pytz", 570 | "version": "2021.1" 571 | } 572 | ], 573 | "ok": false, 574 | "dependencyCount": 6, 575 | "org": "snyk-test", 576 | "policy": "# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.\nversion: v1.19.0\nignore: {}\npatch: {}\n", 577 | "isPrivate": true, 578 | "licensesPolicy": { 579 | "severities": {}, 580 | "orgLicenseRules": { 581 | "AGPL-1.0": { 582 | "licenseType": "AGPL-1.0", 583 | "severity": "high", 584 | "instructions": "" 585 | }, 586 | "AGPL-3.0": { 587 | "licenseType": "AGPL-3.0", 588 | "severity": "high", 589 | "instructions": "" 590 | }, 591 | "Artistic-1.0": { 592 | "licenseType": "Artistic-1.0", 593 | "severity": "medium", 594 | "instructions": "" 595 | }, 596 | "Artistic-2.0": { 597 | "licenseType": "Artistic-2.0", 598 | "severity": "medium", 599 | "instructions": "" 600 | }, 601 | "CDDL-1.0": { 602 | "licenseType": "CDDL-1.0", 603 | "severity": "medium", 604 | "instructions": "" 605 | }, 606 | "CPOL-1.02": { 607 | "licenseType": "CPOL-1.02", 608 | "severity": "high", 609 | "instructions": "" 610 | }, 611 | "EPL-1.0": { 612 | "licenseType": "EPL-1.0", 613 | "severity": "medium", 614 | "instructions": "" 615 | }, 616 | "GPL-2.0": { 617 | "licenseType": "GPL-2.0", 618 | "severity": "high", 619 | "instructions": "" 620 | }, 621 | "GPL-3.0": { 622 | "licenseType": "GPL-3.0", 623 | "severity": "high", 624 | "instructions": "" 625 | }, 626 | "LGPL-2.0": { 627 | "licenseType": "LGPL-2.0", 628 | "severity": "medium", 629 | "instructions": "" 630 | }, 631 | "LGPL-2.1": { 632 | "licenseType": "LGPL-2.1", 633 | "severity": "medium", 634 | "instructions": "" 635 | }, 636 | "LGPL-3.0": { 637 | "licenseType": "LGPL-3.0", 638 | "severity": "medium", 639 | "instructions": "" 640 | }, 641 | "MPL-1.1": { 642 | "licenseType": "MPL-1.1", 643 | "severity": "medium", 644 | "instructions": "" 645 | }, 646 | "MPL-2.0": { 647 | "licenseType": "MPL-2.0", 648 | "severity": "medium", 649 | "instructions": "" 650 | }, 651 | "MS-RL": { 652 | "licenseType": "MS-RL", 653 | "severity": "medium", 654 | "instructions": "" 655 | }, 656 | "SimPL-2.0": { 657 | "licenseType": "SimPL-2.0", 658 | "severity": "high", 659 | "instructions": "" 660 | }, 661 | "MIT": { 662 | "licenseType": "MIT", 663 | "severity": "high", 664 | "instructions": "Not suitable to use, please find a different package." 665 | } 666 | } 667 | }, 668 | "packageManager": "pip", 669 | "ignoreSettings": null, 670 | "summary": "9 vulnerable dependency paths", 671 | "remediation": { 672 | "unresolved": [ 673 | { 674 | "CVSSv3": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:H", 675 | "alternativeIds": [], 676 | "creationTime": "2021-02-19T13:39:56.970650Z", 677 | "credit": [ 678 | "Nick Pope" 679 | ], 680 | "cvssScore": 5.9, 681 | "description": "## Overview\n[django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.\n\nAffected versions of this package are vulnerable to Web Cache Poisoning. Django contains a copy of urllib.parse.parse_qsl() which was added to backport some security fixes. A further security fix has been issued recently such that parse_qsl() no longer allows using ; as a query parameter separator by default.\n## Remediation\nUpgrade `django` to version 2.2.19, 3.0.13, 3.1.7 or higher.\n## References\n- [Django Security Releases](https://www.djangoproject.com/weblog/2021/feb/19/security-releases/)\n- [GitHub Commit](https://github.com/django/django/commit/be8237c7cce24b06aabde0b97afce98ddabbe3b6)\n- [Snyk Cache Poisoning Blogpost](https://snyk.io/blog/cache-poisoning-in-popular-open-source-packages/)\n", 682 | "disclosureTime": "2021-02-19T13:28:51Z", 683 | "exploit": "Not Defined", 684 | "fixedIn": [ 685 | "2.2.19", 686 | "3.0.13", 687 | "3.1.7" 688 | ], 689 | "functions": [], 690 | "functions_new": [], 691 | "id": "SNYK-PYTHON-DJANGO-1076802", 692 | "identifiers": { 693 | "CVE": [ 694 | "CVE-2021-23336" 695 | ], 696 | "CWE": [ 697 | "CWE-444" 698 | ] 699 | }, 700 | "language": "python", 701 | "modificationTime": "2021-02-19T15:54:22.876737Z", 702 | "moduleName": "django", 703 | "packageManager": "pip", 704 | "packageName": "django", 705 | "patches": [], 706 | "proprietary": false, 707 | "publicationTime": "2021-02-19T15:54:23.197747Z", 708 | "references": [ 709 | { 710 | "title": "Django Security Releases", 711 | "url": "https://www.djangoproject.com/weblog/2021/feb/19/security-releases/" 712 | }, 713 | { 714 | "title": "GitHub Commit", 715 | "url": "https://github.com/django/django/commit/be8237c7cce24b06aabde0b97afce98ddabbe3b6" 716 | }, 717 | { 718 | "title": "Snyk Cache Poisoning Blogpost", 719 | "url": "https://snyk.io/blog/cache-poisoning-in-popular-open-source-packages/" 720 | } 721 | ], 722 | "semver": { 723 | "vulnerable": [ 724 | "[2.2,2.2.19)", 725 | "[3.0,3.0.13)", 726 | "[3.1,3.1.7)" 727 | ] 728 | }, 729 | "severity": "medium", 730 | "severityWithCritical": "medium", 731 | "title": "Web Cache Poisoning", 732 | "from": [ 733 | "app-with-already-fixed@0.0.0", 734 | "django@2.2.18" 735 | ], 736 | "upgradePath": [], 737 | "isUpgradable": false, 738 | "isPatchable": false, 739 | "isPinnable": true, 740 | "name": "django", 741 | "version": "2.2.18" 742 | }, 743 | { 744 | "CVSSv3": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N", 745 | "alternativeIds": [], 746 | "creationTime": "2021-04-06T09:22:54.987556Z", 747 | "credit": [ 748 | "Dennis Brinkrolf" 749 | ], 750 | "cvssScore": 3.7, 751 | "description": "## Overview\n[django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.\n\nAffected versions of this package are vulnerable to Directory Traversal. `MultiPartParser` allowed directory-traversal via uploaded files with suitably crafted file names.\n\n## Details\n\nA Directory Traversal attack (also known as path traversal) aims to access files and directories that are stored outside the intended folder. By manipulating files with \"dot-dot-slash (../)\" sequences and its variations, or by using absolute file paths, it may be possible to access arbitrary files and directories stored on file system, including application source code, configuration, and other critical system files.\n\nDirectory Traversal vulnerabilities can be generally divided into two types:\n\n- **Information Disclosure**: Allows the attacker to gain information about the folder structure or read the contents of sensitive files on the system.\n\n`st` is a module for serving static files on web pages, and contains a [vulnerability of this type](https://snyk.io/vuln/npm:st:20140206). In our example, we will serve files from the `public` route.\n\nIf an attacker requests the following URL from our server, it will in turn leak the sensitive private key of the root user.\n\n```\ncurl http://localhost:8080/public/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/root/.ssh/id_rsa\n```\n**Note** `%2e` is the URL encoded version of `.` (dot).\n\n- **Writing arbitrary files**: Allows the attacker to create or replace existing files. This type of vulnerability is also known as `Zip-Slip`. \n\nOne way to achieve this is by using a malicious `zip` archive that holds path traversal filenames. When each filename in the zip archive gets concatenated to the target extraction folder, without validation, the final path ends up outside of the target folder. If an executable or a configuration file is overwritten with a file containing malicious code, the problem can turn into an arbitrary code execution issue quite easily.\n\nThe following is an example of a `zip` archive with one benign file and one malicious file. Extracting the malicious file will result in traversing out of the target folder, ending up in `/root/.ssh/` overwriting the `authorized_keys` file:\n\n```\n2018-04-15 22:04:29 ..... 19 19 good.txt\n2018-04-15 22:04:42 ..... 20 20 ../../../../../../root/.ssh/authorized_keys\n```\n\n## Remediation\nUpgrade `django` to version 2.2.20, 3.0.14, 3.1.8 or higher.\n## References\n- [GitHub Commit](https://github.com/django/django/commit/2820fd1be5dfccbf1216c3845fad8580502473e1)\n- [GitHub Commit](https://github.com/django/django/commit/4036d62bda0e9e9f6172943794b744a454ca49c2)\n- [GitHub Commit](https://github.com/django/django/commit/cca0d98118cccf9ae0c6dcf2d6c57fc50469fbf0)\n- [GitHub Commit](https://github.com/django/django/commit/d4d800ca1addc4141e03c5440a849bb64d1582cd)\n- [GitHub Commit](https://github.com/django/django/commit/e7fba62248f604c76da4f23dcf1db4a57b0808ea)\n", 752 | "disclosureTime": "2021-04-06T09:18:59Z", 753 | "exploit": "Not Defined", 754 | "fixedIn": [ 755 | "2.2.20", 756 | "3.0.14", 757 | "3.1.8" 758 | ], 759 | "functions": [], 760 | "functions_new": [], 761 | "id": "SNYK-PYTHON-DJANGO-1090612", 762 | "identifiers": { 763 | "CVE": [ 764 | "CVE-2021-28658" 765 | ], 766 | "CWE": [ 767 | "CWE-22" 768 | ] 769 | }, 770 | "language": "python", 771 | "modificationTime": "2021-04-06T13:57:02.213825Z", 772 | "moduleName": "django", 773 | "packageManager": "pip", 774 | "packageName": "django", 775 | "patches": [], 776 | "proprietary": false, 777 | "publicationTime": "2021-04-06T13:57:02.482219Z", 778 | "references": [ 779 | { 780 | "title": "GitHub Commit", 781 | "url": "https://github.com/django/django/commit/2820fd1be5dfccbf1216c3845fad8580502473e1" 782 | }, 783 | { 784 | "title": "GitHub Commit", 785 | "url": "https://github.com/django/django/commit/4036d62bda0e9e9f6172943794b744a454ca49c2" 786 | }, 787 | { 788 | "title": "GitHub Commit", 789 | "url": "https://github.com/django/django/commit/cca0d98118cccf9ae0c6dcf2d6c57fc50469fbf0" 790 | }, 791 | { 792 | "title": "GitHub Commit", 793 | "url": "https://github.com/django/django/commit/d4d800ca1addc4141e03c5440a849bb64d1582cd" 794 | }, 795 | { 796 | "title": "GitHub Commit", 797 | "url": "https://github.com/django/django/commit/e7fba62248f604c76da4f23dcf1db4a57b0808ea" 798 | } 799 | ], 800 | "semver": { 801 | "vulnerable": [ 802 | "[2.2, 2.2.20)", 803 | "[3.0, 3.0.14)", 804 | "[3.1, 3.1.8)" 805 | ] 806 | }, 807 | "severity": "low", 808 | "severityWithCritical": "low", 809 | "title": "Directory Traversal", 810 | "from": [ 811 | "app-with-already-fixed@0.0.0", 812 | "django@2.2.18" 813 | ], 814 | "upgradePath": [], 815 | "isUpgradable": false, 816 | "isPatchable": false, 817 | "isPinnable": true, 818 | "name": "django", 819 | "version": "2.2.18" 820 | }, 821 | { 822 | "CVSSv3": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N", 823 | "alternativeIds": [], 824 | "creationTime": "2021-05-04T12:51:50.753891Z", 825 | "credit": [ 826 | "Jasu Viding" 827 | ], 828 | "cvssScore": 3.3, 829 | "description": "## Overview\n[django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.\n\nAffected versions of this package are vulnerable to Directory Traversal. `MultiPartParser`, `UploadedFile`, and `FieldFile` allow directory-traversal via uploaded files with suitably crafted file names.\n\n## Details\n\nA Directory Traversal attack (also known as path traversal) aims to access files and directories that are stored outside the intended folder. By manipulating files with \"dot-dot-slash (../)\" sequences and its variations, or by using absolute file paths, it may be possible to access arbitrary files and directories stored on file system, including application source code, configuration, and other critical system files.\n\nDirectory Traversal vulnerabilities can be generally divided into two types:\n\n- **Information Disclosure**: Allows the attacker to gain information about the folder structure or read the contents of sensitive files on the system.\n\n`st` is a module for serving static files on web pages, and contains a [vulnerability of this type](https://snyk.io/vuln/npm:st:20140206). In our example, we will serve files from the `public` route.\n\nIf an attacker requests the following URL from our server, it will in turn leak the sensitive private key of the root user.\n\n```\ncurl http://localhost:8080/public/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/root/.ssh/id_rsa\n```\n**Note** `%2e` is the URL encoded version of `.` (dot).\n\n- **Writing arbitrary files**: Allows the attacker to create or replace existing files. This type of vulnerability is also known as `Zip-Slip`. \n\nOne way to achieve this is by using a malicious `zip` archive that holds path traversal filenames. When each filename in the zip archive gets concatenated to the target extraction folder, without validation, the final path ends up outside of the target folder. If an executable or a configuration file is overwritten with a file containing malicious code, the problem can turn into an arbitrary code execution issue quite easily.\n\nThe following is an example of a `zip` archive with one benign file and one malicious file. Extracting the malicious file will result in traversing out of the target folder, ending up in `/root/.ssh/` overwriting the `authorized_keys` file:\n\n```\n2018-04-15 22:04:29 ..... 19 19 good.txt\n2018-04-15 22:04:42 ..... 20 20 ../../../../../../root/.ssh/authorized_keys\n```\n\n## Remediation\nUpgrade `django` to version 2.2.21, 3.1.9, 3.2.1 or higher.\n## References\n- [Django Advisory](https://www.djangoproject.com/weblog/2021/may/04/security-releases/)\n- [GitHub Commit](https://github.com/django/django/commit/c98f446c188596d4ba6de71d1b77b4a6c5c2a007)\n", 830 | "disclosureTime": "2021-05-04T12:44:10Z", 831 | "exploit": "Not Defined", 832 | "fixedIn": [ 833 | "2.2.21", 834 | "3.1.9", 835 | "3.2.1" 836 | ], 837 | "functions": [], 838 | "functions_new": [], 839 | "id": "SNYK-PYTHON-DJANGO-1279042", 840 | "identifiers": { 841 | "CVE": [ 842 | "CVE-2021-31542" 843 | ], 844 | "CWE": [ 845 | "CWE-22" 846 | ] 847 | }, 848 | "language": "python", 849 | "modificationTime": "2021-05-04T14:45:09.894750Z", 850 | "moduleName": "django", 851 | "packageManager": "pip", 852 | "packageName": "django", 853 | "patches": [], 854 | "proprietary": false, 855 | "publicationTime": "2021-05-04T14:45:10.137628Z", 856 | "references": [ 857 | { 858 | "title": "Django Advisory", 859 | "url": "https://www.djangoproject.com/weblog/2021/may/04/security-releases/" 860 | }, 861 | { 862 | "title": "GitHub Commit", 863 | "url": "https://github.com/django/django/commit/c98f446c188596d4ba6de71d1b77b4a6c5c2a007" 864 | } 865 | ], 866 | "semver": { 867 | "vulnerable": [ 868 | "[, 2.2.21)", 869 | "[3.0, 3.1.9)", 870 | "[3.2, 3.2.1)" 871 | ] 872 | }, 873 | "severity": "low", 874 | "severityWithCritical": "low", 875 | "title": "Directory Traversal", 876 | "from": [ 877 | "app-with-already-fixed@0.0.0", 878 | "django@2.2.18" 879 | ], 880 | "upgradePath": [], 881 | "isUpgradable": false, 882 | "isPatchable": false, 883 | "isPinnable": true, 884 | "name": "django", 885 | "version": "2.2.18" 886 | }, 887 | { 888 | "CVSSv3": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L", 889 | "alternativeIds": [], 890 | "creationTime": "2021-05-06T14:52:58.420987Z", 891 | "credit": [ 892 | "Unknown" 893 | ], 894 | "cvssScore": 7.3, 895 | "description": "## Overview\n[django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.\n\nAffected versions of this package are vulnerable to HTTP Header Injection. In Python 3.9.5+ urllib.parse() automatically removes ASCII newlines and tabs from URLs. Unfortunately it created an issue in the URLValidator. URLValidator uses `urllib.urlsplit()` and `urllib.urlunsplit()` for creating a URL variant with Punycode which no longer contains newlines and tabs in Python 3.9.5+. As a consequence, the regular expression matched the URL (without unsafe characters) and the source value (with unsafe characters) was considered valid.\r\n\r\nThis issue was introduced by the [bpo-43882](https://bugs.python.org/issue43882) fix.\n## Remediation\nUpgrade `django` to version 3.2.2, 3.1.10, 2.2.22 or higher.\n## References\n- [Django Security Releases](https://www.djangoproject.com/weblog/2021/may/06/security-releases/)\n- [GitHub Commit](https://github.com/django/django/commit/e1e81aa1c4427411e3c68facdd761229ffea6f6f)\n- [GitHub PR](https://github.com/django/django/pull/14360)\n- [Mail Archive](https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1804086.html)\n", 896 | "disclosureTime": "2021-05-06T14:44:15Z", 897 | "exploit": "Not Defined", 898 | "fixedIn": [ 899 | "2.2.22", 900 | "3.1.10", 901 | "3.2.2" 902 | ], 903 | "functions": [], 904 | "functions_new": [], 905 | "id": "SNYK-PYTHON-DJANGO-1290072", 906 | "identifiers": { 907 | "CVE": [ 908 | "CVE-2021-32052" 909 | ], 910 | "CWE": [ 911 | "CWE-644" 912 | ] 913 | }, 914 | "language": "python", 915 | "modificationTime": "2021-05-06T15:41:43.922301Z", 916 | "moduleName": "django", 917 | "packageManager": "pip", 918 | "packageName": "django", 919 | "patches": [], 920 | "proprietary": false, 921 | "publicationTime": "2021-05-06T15:41:44.175836Z", 922 | "references": [ 923 | { 924 | "title": "Django Security Releases", 925 | "url": "https://www.djangoproject.com/weblog/2021/may/06/security-releases/" 926 | }, 927 | { 928 | "title": "GitHub Commit", 929 | "url": "https://github.com/django/django/commit/e1e81aa1c4427411e3c68facdd761229ffea6f6f" 930 | }, 931 | { 932 | "title": "GitHub PR", 933 | "url": "https://github.com/django/django/pull/14360" 934 | }, 935 | { 936 | "title": "Mail Archive", 937 | "url": "https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1804086.html" 938 | } 939 | ], 940 | "semver": { 941 | "vulnerable": [ 942 | "[3.2,3.2.2)", 943 | "[3.0,3.1.10)", 944 | "[,2.2.22)" 945 | ] 946 | }, 947 | "severity": "high", 948 | "severityWithCritical": "high", 949 | "title": "HTTP Header Injection", 950 | "from": [ 951 | "app-with-already-fixed@0.0.0", 952 | "django@2.2.18" 953 | ], 954 | "upgradePath": [], 955 | "isUpgradable": false, 956 | "isPatchable": false, 957 | "isPinnable": true, 958 | "name": "django", 959 | "version": "2.2.18" 960 | }, 961 | { 962 | "CVSSv3": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L/E:P", 963 | "alternativeIds": [], 964 | "creationTime": "2020-09-25T17:30:26.286074Z", 965 | "credit": [ 966 | "Yeting Li" 967 | ], 968 | "cvssScore": 5.3, 969 | "description": "## Overview\n[jinja2](https://pypi.org/project/Jinja2/) is a template engine written in pure Python. It provides a Django inspired non-XML syntax but supports inline expressions and an optional sandboxed environment.\n\nAffected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS). The ReDoS vulnerability is mainly due to the `_punctuation_re regex` operator and its use of multiple wildcards. The last wildcard is the most exploitable as it searches for trailing punctuation.\r\n\r\nThis issue can be mitigated by using Markdown to format user content instead of the urlize filter, or by implementing request timeouts or limiting process memory.\r\n\r\n### PoC by Yeting Li\r\n```\r\nfrom jinja2.utils import urlize\r\nfrom time import perf_counter\r\n\r\nfor i in range(3):\r\n text = \"abc@\" + \".\" * (i+1)*5000 + \"!\"\r\n LEN = len(text)\r\n BEGIN = perf_counter()\r\n urlize(text)\r\n DURATION = perf_counter() - BEGIN\r\n print(f\"{LEN}: took {DURATION} seconds!\")\r\n```\n\n## Details\n\nDenial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.\n\nThe Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.\n\nLet’s take the following regular expression as an example:\n```js\nregex = /A(B|C+)+D/\n```\n\nThis regular expression accomplishes the following:\n- `A` The string must start with the letter 'A'\n- `(B|C+)+` The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the `+` matches one or more times). The `+` at the end of this section states that we can look for one or more matches of this section.\n- `D` Finally, we ensure this section of the string ends with a 'D'\n\nThe expression would match inputs such as `ABBD`, `ABCCCCD`, `ABCBCCCD` and `ACCCCCD`\n\nIt most cases, it doesn't take very long for a regex engine to find a match:\n\n```bash\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD\")'\n0.04s user 0.01s system 95% cpu 0.052 total\n\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX\")'\n1.79s user 0.02s system 99% cpu 1.812 total\n```\n\nThe entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.\n\nMost Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as _catastrophic backtracking_.\n\nLet's look at how our expression runs into this problem, using a shorter string: \"ACCCX\". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:\n1. CCC\n2. CC+C\n3. C+CC\n4. C+C+C.\n\nThe engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use [RegEx 101 debugger](https://regex101.com/debugger) to see the engine has to take a total of 38 steps before it can determine the string doesn't match.\n\nFrom there, the number of steps the engine must use to validate a string just continues to grow.\n\n| String | Number of C's | Number of steps |\n| -------|-------------:| -----:|\n| ACCCX | 3 | 38\n| ACCCCX | 4 | 71\n| ACCCCCX | 5 | 136\n| ACCCCCCCCCCCCCCX | 14 | 65,553\n\n\nBy the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.\n\n## Remediation\nUpgrade `jinja2` to version 2.11.3 or higher.\n## References\n- [GitHub Additional Information](https://github.com/pallets/jinja/blob/ab81fd9c277900c85da0c322a2ff9d68a235b2e6/src/jinja2/utils.py#L20)\n- [GitHub PR](https://github.com/pallets/jinja/pull/1343)\n", 970 | "disclosureTime": "2020-09-25T17:29:19Z", 971 | "exploit": "Proof of Concept", 972 | "fixedIn": [ 973 | "2.11.3" 974 | ], 975 | "functions": [], 976 | "functions_new": [], 977 | "id": "SNYK-PYTHON-JINJA2-1012994", 978 | "identifiers": { 979 | "CVE": [ 980 | "CVE-2020-28493" 981 | ], 982 | "CWE": [ 983 | "CWE-400" 984 | ], 985 | "GHSA": [ 986 | "GHSA-g3rq-g295-4j3m" 987 | ] 988 | }, 989 | "language": "python", 990 | "modificationTime": "2021-02-01T19:52:16.877030Z", 991 | "moduleName": "jinja2", 992 | "packageManager": "pip", 993 | "packageName": "jinja2", 994 | "patches": [], 995 | "proprietary": true, 996 | "publicationTime": "2021-02-01T19:52:17Z", 997 | "references": [ 998 | { 999 | "title": "GitHub Additional Information", 1000 | "url": "https://github.com/pallets/jinja/blob/ab81fd9c277900c85da0c322a2ff9d68a235b2e6/src/jinja2/utils.py%23L20" 1001 | }, 1002 | { 1003 | "title": "GitHub PR", 1004 | "url": "https://github.com/pallets/jinja/pull/1343" 1005 | } 1006 | ], 1007 | "semver": { 1008 | "vulnerable": [ 1009 | "[,2.11.3)" 1010 | ] 1011 | }, 1012 | "severity": "medium", 1013 | "severityWithCritical": "medium", 1014 | "title": "Regular Expression Denial of Service (ReDoS)", 1015 | "from": [ 1016 | "app-with-already-fixed@0.0.0", 1017 | "jinja2@2.7.2" 1018 | ], 1019 | "upgradePath": [], 1020 | "isUpgradable": false, 1021 | "isPatchable": false, 1022 | "isPinnable": true, 1023 | "name": "jinja2", 1024 | "version": "2.7.2" 1025 | }, 1026 | { 1027 | "CVSSv3": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:L/I:L/A:L/RL:O", 1028 | "alternativeIds": [], 1029 | "creationTime": "2019-04-07T10:24:16.310959Z", 1030 | "credit": [ 1031 | "Unknown" 1032 | ], 1033 | "cvssScore": 6, 1034 | "description": "## Overview\n[jinja2](https://pypi.org/project/Jinja2/) is a template engine written in pure Python. It provides a Django inspired non-XML syntax but supports inline expressions and an optional sandboxed environment.\n\nAffected versions of this package are vulnerable to Sandbox Escape via the `str.format_map`.\n## Remediation\nUpgrade `jinja2` to version 2.10.1 or higher.\n## References\n- [Release Notes](https://palletsprojects.com/blog/jinja-2-10-1-released)\n", 1035 | "disclosureTime": "2019-04-07T00:42:43Z", 1036 | "exploit": "Not Defined", 1037 | "fixedIn": [ 1038 | "2.10.1" 1039 | ], 1040 | "functions": [], 1041 | "functions_new": [], 1042 | "id": "SNYK-PYTHON-JINJA2-174126", 1043 | "identifiers": { 1044 | "CVE": [ 1045 | "CVE-2019-10906" 1046 | ], 1047 | "CWE": [ 1048 | "CWE-265" 1049 | ] 1050 | }, 1051 | "language": "python", 1052 | "modificationTime": "2020-06-12T14:36:55.661596Z", 1053 | "moduleName": "jinja2", 1054 | "packageManager": "pip", 1055 | "packageName": "jinja2", 1056 | "patches": [], 1057 | "proprietary": false, 1058 | "publicationTime": "2019-04-07T00:42:43Z", 1059 | "references": [ 1060 | { 1061 | "title": "Release Notes", 1062 | "url": "https://palletsprojects.com/blog/jinja-2-10-1-released" 1063 | } 1064 | ], 1065 | "semver": { 1066 | "vulnerable": [ 1067 | "[,2.10.1)" 1068 | ] 1069 | }, 1070 | "severity": "medium", 1071 | "severityWithCritical": "medium", 1072 | "title": "Sandbox Escape", 1073 | "from": [ 1074 | "app-with-already-fixed@0.0.0", 1075 | "jinja2@2.7.2" 1076 | ], 1077 | "upgradePath": [], 1078 | "isUpgradable": false, 1079 | "isPatchable": false, 1080 | "isPinnable": true, 1081 | "name": "jinja2", 1082 | "version": "2.7.2" 1083 | }, 1084 | { 1085 | "CVSSv3": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L", 1086 | "alternativeIds": [], 1087 | "creationTime": "2017-05-28T08:29:50.295000Z", 1088 | "credit": [ 1089 | "Arun Babu Neelicattu" 1090 | ], 1091 | "cvssScore": 5.3, 1092 | "description": "## Overview\r\n[`jinja2`](https://pypi.python.org/pypi/jinja2) is a small but fast and easy to use stand-alone template engine written in pure python.\r\nFileSystemBytecodeCache in Jinja2 2.7.2 does not properly create temporary directories, which allows local users to gain privileges by pre-creating a temporary directory with a user's uid.\r\n\r\n**NOTE:** this vulnerability exists because of an incomplete fix for [CVE-2014-1402](https://snyk.io/vulnSNYK-PYTHON-JINJA2-40028).\r\n\r\n## References\r\n- [NVD](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-0012)\r\n- [Bugzilla redhat](https://bugzilla.redhat.com/show_bug.cgi?id=1051421)\r\n- [GitHub PR #1](https://github.com/mitsuhiko/jinja2/pull/292)\r\n- [GitHub PR #2](https://github.com/mitsuhiko/jinja2/pull/296)\r\n- [GitHub Commit](https://github.com/mitsuhiko/jinja2/commit/acb672b6a179567632e032f547582f30fa2f4aa7)\r\n", 1093 | "disclosureTime": "2014-01-18T05:33:40.101000Z", 1094 | "exploit": "Not Defined", 1095 | "fixedIn": [], 1096 | "functions": [], 1097 | "functions_new": [], 1098 | "id": "SNYK-PYTHON-JINJA2-40250", 1099 | "identifiers": { 1100 | "CVE": [ 1101 | "CVE-2014-0012" 1102 | ], 1103 | "CWE": [ 1104 | "CWE-264" 1105 | ] 1106 | }, 1107 | "language": "python", 1108 | "modificationTime": "2019-02-17T08:46:41.648104Z", 1109 | "moduleName": "jinja2", 1110 | "packageManager": "pip", 1111 | "packageName": "jinja2", 1112 | "patches": [], 1113 | "proprietary": false, 1114 | "publicationTime": "2014-01-18T05:33:40.101000Z", 1115 | "references": [ 1116 | { 1117 | "title": "GitHub Commit", 1118 | "url": "https://github.com/mitsuhiko/jinja2/commit/acb672b6a179567632e032f547582f30fa2f4aa7" 1119 | }, 1120 | { 1121 | "title": "GitHub PR", 1122 | "url": "https://github.com/mitsuhiko/jinja2/pull/292" 1123 | }, 1124 | { 1125 | "title": "GitHub PR", 1126 | "url": "https://github.com/mitsuhiko/jinja2/pull/296" 1127 | }, 1128 | { 1129 | "title": "NVD", 1130 | "url": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-0012" 1131 | }, 1132 | { 1133 | "title": "RedHat Bugzilla Bug", 1134 | "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1051421" 1135 | } 1136 | ], 1137 | "semver": { 1138 | "vulnerable": [ 1139 | "[2.7.2]" 1140 | ] 1141 | }, 1142 | "severity": "medium", 1143 | "severityWithCritical": "medium", 1144 | "title": "Privilege Escalation", 1145 | "from": [ 1146 | "app-with-already-fixed@0.0.0", 1147 | "jinja2@2.7.2" 1148 | ], 1149 | "upgradePath": [], 1150 | "isUpgradable": false, 1151 | "isPatchable": false, 1152 | "isPinnable": false, 1153 | "name": "jinja2", 1154 | "version": "2.7.2" 1155 | }, 1156 | { 1157 | "CVSSv3": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N", 1158 | "alternativeIds": [], 1159 | "creationTime": "2019-07-29T13:28:48.288799Z", 1160 | "credit": [ 1161 | "Unknown" 1162 | ], 1163 | "cvssScore": 8.6, 1164 | "description": "## Overview\n[jinja2](https://pypi.org/project/Jinja2/) is a template engine written in pure Python. It provides a Django inspired non-XML syntax but supports inline expressions and an optional sandboxed environment.\n\nAffected versions of this package are vulnerable to Sandbox Bypass. Users were allowed to insert `str.format` through web templates, leading to an escape from sandbox.\n## Remediation\nUpgrade `jinja2` to version 2.8.1 or higher.\n## References\n- [GitHub Commit](https://github.com/pallets/jinja/commit/9b53045c34e61013dc8f09b7e52a555fa16bed16)\n", 1165 | "disclosureTime": "2016-12-29T13:27:18Z", 1166 | "exploit": "Not Defined", 1167 | "fixedIn": [ 1168 | "2.8.1" 1169 | ], 1170 | "functions": [], 1171 | "functions_new": [], 1172 | "id": "SNYK-PYTHON-JINJA2-455616", 1173 | "identifiers": { 1174 | "CVE": [ 1175 | "CVE-2016-10745" 1176 | ], 1177 | "CWE": [ 1178 | "CWE-234" 1179 | ] 1180 | }, 1181 | "language": "python", 1182 | "modificationTime": "2020-06-12T14:36:58.461729Z", 1183 | "moduleName": "jinja2", 1184 | "packageManager": "pip", 1185 | "packageName": "jinja2", 1186 | "patches": [], 1187 | "proprietary": false, 1188 | "publicationTime": "2019-07-30T13:11:16Z", 1189 | "references": [ 1190 | { 1191 | "title": "GitHub Commit", 1192 | "url": "https://github.com/pallets/jinja/commit/9b53045c34e61013dc8f09b7e52a555fa16bed16" 1193 | } 1194 | ], 1195 | "semver": { 1196 | "vulnerable": [ 1197 | "[2.5, 2.8.1)" 1198 | ] 1199 | }, 1200 | "severity": "high", 1201 | "severityWithCritical": "high", 1202 | "title": "Sandbox Bypass", 1203 | "from": [ 1204 | "app-with-already-fixed@0.0.0", 1205 | "jinja2@2.7.2" 1206 | ], 1207 | "upgradePath": [], 1208 | "isUpgradable": false, 1209 | "isPatchable": false, 1210 | "isPinnable": true, 1211 | "name": "jinja2", 1212 | "version": "2.7.2" 1213 | }, 1214 | { 1215 | "license": "MIT", 1216 | "semver": { 1217 | "vulnerable": [ 1218 | "[2005a,)" 1219 | ] 1220 | }, 1221 | "id": "snyk:lic:pip:pytz:MIT", 1222 | "type": "license", 1223 | "packageManager": "pip", 1224 | "language": "python", 1225 | "packageName": "pytz", 1226 | "title": "MIT license", 1227 | "description": "MIT license", 1228 | "publicationTime": "2021-05-23T11:15:36.845Z", 1229 | "creationTime": "2021-05-23T11:15:36.845Z", 1230 | "patches": [], 1231 | "licenseTemplateUrl": "https://raw.githubusercontent.com/spdx/license-list/master/MIT.txt", 1232 | "severity": "high", 1233 | "legalInstructionsArray": [ 1234 | { 1235 | "licenseName": "MIT", 1236 | "legalContent": "Not suitable to use, please find a different package." 1237 | } 1238 | ], 1239 | "from": [ 1240 | "app-with-already-fixed@0.0.0", 1241 | "django@2.2.18", 1242 | "pytz@2021.1" 1243 | ], 1244 | "upgradePath": [], 1245 | "isUpgradable": false, 1246 | "isPatchable": false, 1247 | "isPinnable": false, 1248 | "name": "pytz", 1249 | "version": "2021.1" 1250 | } 1251 | ], 1252 | "upgrade": {}, 1253 | "patch": {}, 1254 | "ignore": {}, 1255 | "pin": { 1256 | "django@2.2.18": { 1257 | "upgradeTo": "django@2.2.19", 1258 | "vulns": [ 1259 | "SNYK-PYTHON-DJANGO-1076802", 1260 | "SNYK-PYTHON-DJANGO-1090612", 1261 | "SNYK-PYTHON-DJANGO-1279042", 1262 | "SNYK-PYTHON-DJANGO-1290072" 1263 | ], 1264 | "isTransitive": false 1265 | }, 1266 | "jinja2@2.7.2": { 1267 | "upgradeTo": "jinja2@2.11.3", 1268 | "vulns": [ 1269 | "SNYK-PYTHON-JINJA2-1012994", 1270 | "SNYK-PYTHON-JINJA2-174126", 1271 | "SNYK-PYTHON-JINJA2-455616" 1272 | ], 1273 | "isTransitive": false 1274 | } 1275 | } 1276 | }, 1277 | "filesystemPolicy": false, 1278 | "filtered": { 1279 | "ignore": [], 1280 | "patch": [] 1281 | }, 1282 | "uniqueCount": 9, 1283 | "projectName": "app-with-already-fixed", 1284 | "foundProjectCount": 2, 1285 | "displayTargetFile": "requirements.txt", 1286 | "path": "/Users/snyk/pip-requirements/app-with-already-fixed" 1287 | } 1288 | -------------------------------------------------------------------------------- /test/load-json.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | 3 | export function loadJson(filename: string): JsonFormat { 4 | return JSON.parse(readFileSync(filename, 'utf-8')); 5 | } 6 | -------------------------------------------------------------------------------- /test/system/cmds/__snapshots__/snyk:test.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`\`snyk2spdx snyk:test\` Shows help text as expected 1`] = ` 4 | "index.js 5 | 6 | Convert \`snyk test --json\` output to SPDX SBOM 7 | 8 | Commands: 9 | index.js snyk:test Convert \`snyk test --json\` output to SPDX SBOM [default] 10 | 11 | Options: 12 | --version Show version number [boolean] 13 | --help Show help [boolean] 14 | --output Save the output to the specified file name. Defaults to stdout" 15 | `; 16 | 17 | exports[`\`snyk2spdx snyk:test\` Shows help text as expected when calling command explicitly 1`] = ` 18 | "index.js 19 | 20 | Convert \`snyk test --json\` output to SPDX SBOM 21 | 22 | Commands: 23 | index.js snyk:test Convert \`snyk test --json\` output to SPDX SBOM [default] 24 | 25 | Options: 26 | --version Show version number [boolean] 27 | --help Show help [boolean] 28 | --output Save the output to the specified file name. Defaults to stdout" 29 | `; 30 | -------------------------------------------------------------------------------- /test/system/cmds/snyk:test.spec.ts: -------------------------------------------------------------------------------- 1 | import { exec } from 'child_process'; 2 | import * as path from 'path'; 3 | import { deleteFiles } from '../../delete-files'; 4 | 5 | const main = './dist/index.js'.replace(/\//g, path.sep); 6 | 7 | describe('`snyk2spdx snyk:test`', () => { 8 | const filesToDelete: string[] = []; 9 | afterEach(async () => { 10 | deleteFiles(filesToDelete); 11 | }); 12 | it('Shows help text as expected', (done) => { 13 | exec(`node ${main} help`, (err, stdout) => { 14 | if (err) { 15 | throw err; 16 | } 17 | expect(err).toBeNull(); 18 | expect(stdout.trim()).toMatchSnapshot(); 19 | done(); 20 | }); 21 | }, 70000); 22 | it('`snyk:test` output converted to SPDX for a project with no dependencies', (done) => { 23 | const cliOutput = path.resolve( 24 | __dirname, 25 | '../../', 26 | 'fixtures', 27 | 'no-deps.json', 28 | ); 29 | exec(`cat ${cliOutput} | node ${main}`, (err, stdout, stderr) => { 30 | expect(err).toBeNull(); 31 | expect(stderr).toEqual(''); 32 | expect(JSON.parse(stdout)).toMatchObject({ 33 | id: 'SPDXRef-no-prod-deps', 34 | name: 'no-prod-deps', 35 | specVersion: 'SPDX-3.0', 36 | profile: ['base', 'vulnerabilities'], 37 | dataLicense: 'CC0-1.0', 38 | creator: 'Organization: Snyk Ltd', 39 | vulnerabilities: [], 40 | }); 41 | done(); 42 | }); 43 | }, 70000); 44 | 45 | it('`snyk:test` output converted to SPDX for a projects with vulnerabilities', (done) => { 46 | const cliOutput = path.resolve( 47 | __dirname, 48 | '../../', 49 | 'fixtures', 50 | 'no-deps.json', 51 | ); 52 | exec(`cat ${cliOutput} | node ${main}`, (err, stdout, stderr) => { 53 | expect(err).toBeNull(); 54 | expect(stderr).toEqual(''); 55 | expect(JSON.parse(stdout)).toMatchObject({ 56 | id: 'SPDXRef-no-prod-deps', 57 | name: 'no-prod-deps', 58 | specVersion: 'SPDX-3.0', 59 | profile: ['base', 'vulnerabilities'], 60 | dataLicense: 'CC0-1.0', 61 | creator: 'Organization: Snyk Ltd', 62 | vulnerabilities: [], 63 | }); 64 | done(); 65 | }); 66 | }, 70000); 67 | it('Shows help text as expected when calling command explicitly', (done) => { 68 | exec(`node ${main} snyk:jest help`, (err, stdout) => { 69 | if (err) { 70 | throw err; 71 | } 72 | expect(err).toBeNull(); 73 | expect(stdout.trim()).toMatchSnapshot(); 74 | done(); 75 | }); 76 | }, 70000); 77 | }); 78 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["**/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /test/unit/lib/__snapshots__/convert-issue-to-spdx.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`convertSnykIssueToSpdx Snyk issue is converted to SPDX v3 vulnerability 1`] = ` 4 | Object { 5 | "details": "## Overview 6 | [json](https://rubygems.org/gems/json) is a JSON implementation as a Ruby extension in C. 7 | 8 | Affected versions of this package are vulnerable to Denial of Service (DoS). When parsing certain JSON documents, the json gem (including the one bundled with Ruby) can be coerced into creating arbitrary objects in the target system. 9 | 10 | This is the same issue as CVE-2013-0269. The previous fix was incomplete, which addressed \`JSON.parse(user_input)\`, but didn’t address some other styles of JSON 11 | parsing including \`JSON(user_input) and JSON.parse(user_input, nil)\`. 12 | 13 | See [CVE-2013-0269](https://snyk.io/vuln/SNYK-RUBY-JSON-20060) in detail. 14 | 15 | ## Details 16 | 17 | Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process. 18 | 19 | The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down. 20 | 21 | Let’s take the following regular expression as an example: 22 | \`\`\`js 23 | regex = /A(B|C+)+D/ 24 | \`\`\` 25 | 26 | This regular expression accomplishes the following: 27 | - \`A\` The string must start with the letter 'A' 28 | - \`(B|C+)+\` The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the \`+\` matches one or more times). The \`+\` at the end of this section states that we can look for one or more matches of this section. 29 | - \`D\` Finally, we ensure this section of the string ends with a 'D' 30 | 31 | The expression would match inputs such as \`ABBD\`, \`ABCCCCD\`, \`ABCBCCCD\` and \`ACCCCCD\` 32 | 33 | It most cases, it doesn't take very long for a regex engine to find a match: 34 | 35 | \`\`\`bash 36 | $ time node -e '/A(B|C+)+D/.test(\\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD\\")' 37 | 0.04s user 0.01s system 95% cpu 0.052 total 38 | 39 | $ time node -e '/A(B|C+)+D/.test(\\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX\\")' 40 | 1.79s user 0.02s system 99% cpu 1.812 total 41 | \`\`\` 42 | 43 | The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated. 44 | 45 | Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as _catastrophic backtracking_. 46 | 47 | Let's look at how our expression runs into this problem, using a shorter string: \\"ACCCX\\". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's: 48 | 1. CCC 49 | 2. CC+C 50 | 3. C+CC 51 | 4. C+C+C. 52 | 53 | The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use [RegEx 101 debugger](https://regex101.com/debugger) to see the engine has to take a total of 38 steps before it can determine the string doesn't match. 54 | 55 | From there, the number of steps the engine must use to validate a string just continues to grow. 56 | 57 | | String | Number of C's | Number of steps | 58 | | -------|-------------:| -----:| 59 | | ACCCX | 3 | 38 60 | | ACCCCX | 4 | 71 61 | | ACCCCCX | 5 | 136 62 | | ACCCCCCCCCCCCCCX | 14 | 65,553 63 | 64 | 65 | By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service. 66 | 67 | ## Remediation 68 | Upgrade \`json\` to version 2.3.0 or higher. 69 | ## References 70 | - [Ruby Advisory](https://www.ruby-lang.org/en/news/2020/03/19/json-dos-cve-2020-10663/) 71 | ", 72 | "externalReferences": Array [ 73 | Object { 74 | "category": "ADVISORY", 75 | "locator": "https://www.ruby-lang.org/en/news/2020/03/19/json-dos-cve-2020-10663/", 76 | }, 77 | ], 78 | "id": "SNYK-RUBY-JSON-560838", 79 | "modified": "2020-06-12T14:37:02.660300Z", 80 | "name": "SNYK-RUBY-JSON-560838", 81 | "published": "2020-03-19T16:04:21Z", 82 | "relationships": Array [ 83 | Object { 84 | "affect": Object { 85 | "to": Array [ 86 | "ruby-app@*", 87 | "json@2.0.2", 88 | ], 89 | "type": "AFFECTS", 90 | }, 91 | "foundBy": Object { 92 | "to": Array [ 93 | "Unknown", 94 | ], 95 | "type": "FOUND_BY", 96 | }, 97 | "ratedBy": Object { 98 | "cwes": Array [ 99 | 400, 100 | ], 101 | "rating": Array [ 102 | Object { 103 | "method": "CVSS_3", 104 | "score": Array [ 105 | Object { 106 | "base": 9.3, 107 | "exploitability": null, 108 | "impact": null, 109 | }, 110 | ], 111 | "severity": "High", 112 | "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:H", 113 | }, 114 | ], 115 | "to": Array [ 116 | "Unknown", 117 | ], 118 | "type": "RATED_BY", 119 | }, 120 | "suppliedBy": Object { 121 | "to": Array [ 122 | "Unknown", 123 | ], 124 | "type": "SUPPLIED_BY", 125 | }, 126 | }, 127 | ], 128 | "summary": "Denial of Service (DoS)", 129 | } 130 | `; 131 | -------------------------------------------------------------------------------- /test/unit/lib/__snapshots__/index.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`convertSnykTestOutputToSPDX Snyk issue is converted to SPDX v3 vulnerability 1`] = ` 4 | Array [ 5 | Object { 6 | "details": "## Overview 7 | [json](https://rubygems.org/gems/json) is a JSON implementation as a Ruby extension in C. 8 | 9 | Affected versions of this package are vulnerable to Denial of Service (DoS). When parsing certain JSON documents, the json gem (including the one bundled with Ruby) can be coerced into creating arbitrary objects in the target system. 10 | 11 | This is the same issue as CVE-2013-0269. The previous fix was incomplete, which addressed \`JSON.parse(user_input)\`, but didn’t address some other styles of JSON 12 | parsing including \`JSON(user_input) and JSON.parse(user_input, nil)\`. 13 | 14 | See [CVE-2013-0269](https://snyk.io/vuln/SNYK-RUBY-JSON-20060) in detail. 15 | 16 | ## Details 17 | 18 | Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process. 19 | 20 | The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down. 21 | 22 | Let’s take the following regular expression as an example: 23 | \`\`\`js 24 | regex = /A(B|C+)+D/ 25 | \`\`\` 26 | 27 | This regular expression accomplishes the following: 28 | - \`A\` The string must start with the letter 'A' 29 | - \`(B|C+)+\` The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the \`+\` matches one or more times). The \`+\` at the end of this section states that we can look for one or more matches of this section. 30 | - \`D\` Finally, we ensure this section of the string ends with a 'D' 31 | 32 | The expression would match inputs such as \`ABBD\`, \`ABCCCCD\`, \`ABCBCCCD\` and \`ACCCCCD\` 33 | 34 | It most cases, it doesn't take very long for a regex engine to find a match: 35 | 36 | \`\`\`bash 37 | $ time node -e '/A(B|C+)+D/.test(\\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD\\")' 38 | 0.04s user 0.01s system 95% cpu 0.052 total 39 | 40 | $ time node -e '/A(B|C+)+D/.test(\\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX\\")' 41 | 1.79s user 0.02s system 99% cpu 1.812 total 42 | \`\`\` 43 | 44 | The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated. 45 | 46 | Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as _catastrophic backtracking_. 47 | 48 | Let's look at how our expression runs into this problem, using a shorter string: \\"ACCCX\\". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's: 49 | 1. CCC 50 | 2. CC+C 51 | 3. C+CC 52 | 4. C+C+C. 53 | 54 | The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use [RegEx 101 debugger](https://regex101.com/debugger) to see the engine has to take a total of 38 steps before it can determine the string doesn't match. 55 | 56 | From there, the number of steps the engine must use to validate a string just continues to grow. 57 | 58 | | String | Number of C's | Number of steps | 59 | | -------|-------------:| -----:| 60 | | ACCCX | 3 | 38 61 | | ACCCCX | 4 | 71 62 | | ACCCCCX | 5 | 136 63 | | ACCCCCCCCCCCCCCX | 14 | 65,553 64 | 65 | 66 | By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service. 67 | 68 | ## Remediation 69 | Upgrade \`json\` to version 2.3.0 or higher. 70 | ## References 71 | - [Ruby Advisory](https://www.ruby-lang.org/en/news/2020/03/19/json-dos-cve-2020-10663/) 72 | ", 73 | "externalReferences": Array [ 74 | Object { 75 | "category": "ADVISORY", 76 | "locator": "https://www.ruby-lang.org/en/news/2020/03/19/json-dos-cve-2020-10663/", 77 | }, 78 | ], 79 | "id": "SNYK-RUBY-JSON-560838", 80 | "modified": "2020-06-12T14:37:02.660300Z", 81 | "name": "SNYK-RUBY-JSON-560838", 82 | "published": "2020-03-19T16:04:21Z", 83 | "relationships": Array [ 84 | Object { 85 | "affect": Object { 86 | "to": Array [ 87 | "ruby-app@*", 88 | "json@2.0.2", 89 | ], 90 | "type": "AFFECTS", 91 | }, 92 | "foundBy": Object { 93 | "to": Array [ 94 | "Unknown", 95 | ], 96 | "type": "FOUND_BY", 97 | }, 98 | "ratedBy": Object { 99 | "cwes": Array [ 100 | 400, 101 | ], 102 | "rating": Array [ 103 | Object { 104 | "method": "CVSS_3", 105 | "score": Array [ 106 | Object { 107 | "base": 9.3, 108 | "exploitability": null, 109 | "impact": null, 110 | }, 111 | ], 112 | "severity": "High", 113 | "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:H", 114 | }, 115 | ], 116 | "to": Array [ 117 | "Unknown", 118 | ], 119 | "type": "RATED_BY", 120 | }, 121 | "suppliedBy": Object { 122 | "to": Array [ 123 | "Unknown", 124 | ], 125 | "type": "SUPPLIED_BY", 126 | }, 127 | }, 128 | ], 129 | "summary": "Denial of Service (DoS)", 130 | }, 131 | Object { 132 | "details": "## Overview 133 | [\`lynx\`](https://rubygems.org/gems/lynx) is a command line wrapper for MySQL. 134 | Affected versions of this gem are vulnerable to arbitrary command executions due to a flaw in \`lib/lynx/pipe/run.rb\`. 135 | 136 | ## References 137 | - http://rubysec.com/advisories/OSVDB-108579 138 | ", 139 | "externalReferences": Array [ 140 | Object { 141 | "category": "ADVISORY", 142 | "locator": "http://rubysec.com/advisories/OSVDB-108579", 143 | }, 144 | ], 145 | "id": "SNYK-RUBY-LYNX-20160", 146 | "modified": "2019-05-30T11:55:49.846131Z", 147 | "name": "SNYK-RUBY-LYNX-20160", 148 | "published": "2014-06-29T21:00:00Z", 149 | "relationships": Array [ 150 | Object { 151 | "affect": Object { 152 | "to": Array [ 153 | "ruby-app@*", 154 | "lynx@0.4.0", 155 | ], 156 | "type": "AFFECTS", 157 | }, 158 | "foundBy": Object { 159 | "to": Array [ 160 | "Unknown", 161 | ], 162 | "type": "FOUND_BY", 163 | }, 164 | "ratedBy": Object { 165 | "cwes": Array [ 166 | 77, 167 | ], 168 | "rating": Array [ 169 | Object { 170 | "method": "CVSS_3", 171 | "score": Array [ 172 | Object { 173 | "base": 5.6, 174 | "exploitability": null, 175 | "impact": null, 176 | }, 177 | ], 178 | "severity": "Medium", 179 | "vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L", 180 | }, 181 | ], 182 | "to": Array [ 183 | "Unknown", 184 | ], 185 | "type": "RATED_BY", 186 | }, 187 | "suppliedBy": Object { 188 | "to": Array [ 189 | "Unknown", 190 | ], 191 | "type": "SUPPLIED_BY", 192 | }, 193 | }, 194 | ], 195 | "summary": "Arbitrary Command Execution", 196 | }, 197 | Object { 198 | "details": "## Overview 199 | [\`lynx\`](https://rubygems.org/gems/lynx) is a command line wrapper for MySQL. 200 | Affected versions of this gem are vulnerable due to a flaw in \`command/basic.rb\` that exposes password information in plaintext in the process table. This may allow a local attacker to gain access to password information. 201 | 202 | ## References 203 | - http://rubysec.com/advisories/CVE-2014-5002 204 | ", 205 | "externalReferences": Array [ 206 | Object { 207 | "category": "ADVISORY", 208 | "locator": "http://rubysec.com/advisories/CVE-2014-5002", 209 | }, 210 | ], 211 | "id": "SNYK-RUBY-LYNX-20161", 212 | "modified": "2019-05-30T11:55:50.567117Z", 213 | "name": "SNYK-RUBY-LYNX-20161", 214 | "published": "2014-06-29T21:00:00Z", 215 | "relationships": Array [ 216 | Object { 217 | "affect": Object { 218 | "to": Array [ 219 | "ruby-app@*", 220 | "lynx@0.4.0", 221 | ], 222 | "type": "AFFECTS", 223 | }, 224 | "foundBy": Object { 225 | "to": Array [ 226 | "Unknown", 227 | ], 228 | "type": "FOUND_BY", 229 | }, 230 | "ratedBy": Object { 231 | "cwes": Array [ 232 | 200, 233 | ], 234 | "rating": Array [ 235 | Object { 236 | "method": "CVSS_3", 237 | "score": Array [ 238 | Object { 239 | "base": 7.8, 240 | "exploitability": null, 241 | "impact": null, 242 | }, 243 | ], 244 | "severity": "High", 245 | "vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", 246 | }, 247 | ], 248 | "to": Array [ 249 | "Unknown", 250 | ], 251 | "type": "RATED_BY", 252 | }, 253 | "suppliedBy": Object { 254 | "to": Array [ 255 | "Unknown", 256 | ], 257 | "type": "SUPPLIED_BY", 258 | }, 259 | }, 260 | ], 261 | "summary": "Local Plaintext Password Disclosure", 262 | }, 263 | ] 264 | `; 265 | 266 | exports[`convertSnykTestOutputToSPDX license issues are not converted to vulnerabilities 1`] = ` 267 | Array [ 268 | Object { 269 | "details": "## Overview 270 | [django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. 271 | 272 | Affected versions of this package are vulnerable to Web Cache Poisoning. Django contains a copy of urllib.parse.parse_qsl() which was added to backport some security fixes. A further security fix has been issued recently such that parse_qsl() no longer allows using ; as a query parameter separator by default. 273 | ## Remediation 274 | Upgrade \`django\` to version 2.2.19, 3.0.13, 3.1.7 or higher. 275 | ## References 276 | - [Django Security Releases](https://www.djangoproject.com/weblog/2021/feb/19/security-releases/) 277 | - [GitHub Commit](https://github.com/django/django/commit/be8237c7cce24b06aabde0b97afce98ddabbe3b6) 278 | - [Snyk Cache Poisoning Blogpost](https://snyk.io/blog/cache-poisoning-in-popular-open-source-packages/) 279 | ", 280 | "externalReferences": Array [ 281 | Object { 282 | "category": "ADVISORY", 283 | "locator": "https://www.djangoproject.com/weblog/2021/feb/19/security-releases/", 284 | }, 285 | Object { 286 | "category": "ADVISORY", 287 | "locator": "https://github.com/django/django/commit/be8237c7cce24b06aabde0b97afce98ddabbe3b6", 288 | }, 289 | Object { 290 | "category": "ADVISORY", 291 | "locator": "https://snyk.io/blog/cache-poisoning-in-popular-open-source-packages/", 292 | }, 293 | ], 294 | "id": "SNYK-PYTHON-DJANGO-1076802", 295 | "modified": "2021-02-19T15:54:22.876737Z", 296 | "name": "SNYK-PYTHON-DJANGO-1076802", 297 | "published": "2021-02-19T15:54:23.197747Z", 298 | "relationships": Array [ 299 | Object { 300 | "affect": Object { 301 | "to": Array [ 302 | "app-with-already-fixed@0.0.0", 303 | "django@2.2.18", 304 | ], 305 | "type": "AFFECTS", 306 | }, 307 | "foundBy": Object { 308 | "to": Array [ 309 | "Nick Pope", 310 | ], 311 | "type": "FOUND_BY", 312 | }, 313 | "ratedBy": Object { 314 | "cwes": Array [ 315 | 444, 316 | ], 317 | "rating": Array [ 318 | Object { 319 | "method": "CVSS_3", 320 | "score": Array [ 321 | Object { 322 | "base": 5.9, 323 | "exploitability": null, 324 | "impact": null, 325 | }, 326 | ], 327 | "severity": "Medium", 328 | "vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:H", 329 | }, 330 | ], 331 | "to": Array [ 332 | "Nick Pope", 333 | ], 334 | "type": "RATED_BY", 335 | }, 336 | "suppliedBy": Object { 337 | "to": Array [ 338 | "Nick Pope", 339 | ], 340 | "type": "SUPPLIED_BY", 341 | }, 342 | }, 343 | ], 344 | "summary": "Web Cache Poisoning", 345 | }, 346 | Object { 347 | "details": "## Overview 348 | [django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. 349 | 350 | Affected versions of this package are vulnerable to Directory Traversal. \`MultiPartParser\` allowed directory-traversal via uploaded files with suitably crafted file names. 351 | 352 | ## Details 353 | 354 | A Directory Traversal attack (also known as path traversal) aims to access files and directories that are stored outside the intended folder. By manipulating files with \\"dot-dot-slash (../)\\" sequences and its variations, or by using absolute file paths, it may be possible to access arbitrary files and directories stored on file system, including application source code, configuration, and other critical system files. 355 | 356 | Directory Traversal vulnerabilities can be generally divided into two types: 357 | 358 | - **Information Disclosure**: Allows the attacker to gain information about the folder structure or read the contents of sensitive files on the system. 359 | 360 | \`st\` is a module for serving static files on web pages, and contains a [vulnerability of this type](https://snyk.io/vuln/npm:st:20140206). In our example, we will serve files from the \`public\` route. 361 | 362 | If an attacker requests the following URL from our server, it will in turn leak the sensitive private key of the root user. 363 | 364 | \`\`\` 365 | curl http://localhost:8080/public/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/root/.ssh/id_rsa 366 | \`\`\` 367 | **Note** \`%2e\` is the URL encoded version of \`.\` (dot). 368 | 369 | - **Writing arbitrary files**: Allows the attacker to create or replace existing files. This type of vulnerability is also known as \`Zip-Slip\`. 370 | 371 | One way to achieve this is by using a malicious \`zip\` archive that holds path traversal filenames. When each filename in the zip archive gets concatenated to the target extraction folder, without validation, the final path ends up outside of the target folder. If an executable or a configuration file is overwritten with a file containing malicious code, the problem can turn into an arbitrary code execution issue quite easily. 372 | 373 | The following is an example of a \`zip\` archive with one benign file and one malicious file. Extracting the malicious file will result in traversing out of the target folder, ending up in \`/root/.ssh/\` overwriting the \`authorized_keys\` file: 374 | 375 | \`\`\` 376 | 2018-04-15 22:04:29 ..... 19 19 good.txt 377 | 2018-04-15 22:04:42 ..... 20 20 ../../../../../../root/.ssh/authorized_keys 378 | \`\`\` 379 | 380 | ## Remediation 381 | Upgrade \`django\` to version 2.2.20, 3.0.14, 3.1.8 or higher. 382 | ## References 383 | - [GitHub Commit](https://github.com/django/django/commit/2820fd1be5dfccbf1216c3845fad8580502473e1) 384 | - [GitHub Commit](https://github.com/django/django/commit/4036d62bda0e9e9f6172943794b744a454ca49c2) 385 | - [GitHub Commit](https://github.com/django/django/commit/cca0d98118cccf9ae0c6dcf2d6c57fc50469fbf0) 386 | - [GitHub Commit](https://github.com/django/django/commit/d4d800ca1addc4141e03c5440a849bb64d1582cd) 387 | - [GitHub Commit](https://github.com/django/django/commit/e7fba62248f604c76da4f23dcf1db4a57b0808ea) 388 | ", 389 | "externalReferences": Array [ 390 | Object { 391 | "category": "ADVISORY", 392 | "locator": "https://github.com/django/django/commit/2820fd1be5dfccbf1216c3845fad8580502473e1", 393 | }, 394 | Object { 395 | "category": "ADVISORY", 396 | "locator": "https://github.com/django/django/commit/4036d62bda0e9e9f6172943794b744a454ca49c2", 397 | }, 398 | Object { 399 | "category": "ADVISORY", 400 | "locator": "https://github.com/django/django/commit/cca0d98118cccf9ae0c6dcf2d6c57fc50469fbf0", 401 | }, 402 | Object { 403 | "category": "ADVISORY", 404 | "locator": "https://github.com/django/django/commit/d4d800ca1addc4141e03c5440a849bb64d1582cd", 405 | }, 406 | Object { 407 | "category": "ADVISORY", 408 | "locator": "https://github.com/django/django/commit/e7fba62248f604c76da4f23dcf1db4a57b0808ea", 409 | }, 410 | ], 411 | "id": "SNYK-PYTHON-DJANGO-1090612", 412 | "modified": "2021-04-06T13:57:02.213825Z", 413 | "name": "SNYK-PYTHON-DJANGO-1090612", 414 | "published": "2021-04-06T13:57:02.482219Z", 415 | "relationships": Array [ 416 | Object { 417 | "affect": Object { 418 | "to": Array [ 419 | "app-with-already-fixed@0.0.0", 420 | "django@2.2.18", 421 | ], 422 | "type": "AFFECTS", 423 | }, 424 | "foundBy": Object { 425 | "to": Array [ 426 | "Dennis Brinkrolf", 427 | ], 428 | "type": "FOUND_BY", 429 | }, 430 | "ratedBy": Object { 431 | "cwes": Array [ 432 | 22, 433 | ], 434 | "rating": Array [ 435 | Object { 436 | "method": "CVSS_3", 437 | "score": Array [ 438 | Object { 439 | "base": 3.7, 440 | "exploitability": null, 441 | "impact": null, 442 | }, 443 | ], 444 | "severity": "Low", 445 | "vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N", 446 | }, 447 | ], 448 | "to": Array [ 449 | "Dennis Brinkrolf", 450 | ], 451 | "type": "RATED_BY", 452 | }, 453 | "suppliedBy": Object { 454 | "to": Array [ 455 | "Dennis Brinkrolf", 456 | ], 457 | "type": "SUPPLIED_BY", 458 | }, 459 | }, 460 | ], 461 | "summary": "Directory Traversal", 462 | }, 463 | Object { 464 | "details": "## Overview 465 | [django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. 466 | 467 | Affected versions of this package are vulnerable to Directory Traversal. \`MultiPartParser\`, \`UploadedFile\`, and \`FieldFile\` allow directory-traversal via uploaded files with suitably crafted file names. 468 | 469 | ## Details 470 | 471 | A Directory Traversal attack (also known as path traversal) aims to access files and directories that are stored outside the intended folder. By manipulating files with \\"dot-dot-slash (../)\\" sequences and its variations, or by using absolute file paths, it may be possible to access arbitrary files and directories stored on file system, including application source code, configuration, and other critical system files. 472 | 473 | Directory Traversal vulnerabilities can be generally divided into two types: 474 | 475 | - **Information Disclosure**: Allows the attacker to gain information about the folder structure or read the contents of sensitive files on the system. 476 | 477 | \`st\` is a module for serving static files on web pages, and contains a [vulnerability of this type](https://snyk.io/vuln/npm:st:20140206). In our example, we will serve files from the \`public\` route. 478 | 479 | If an attacker requests the following URL from our server, it will in turn leak the sensitive private key of the root user. 480 | 481 | \`\`\` 482 | curl http://localhost:8080/public/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/root/.ssh/id_rsa 483 | \`\`\` 484 | **Note** \`%2e\` is the URL encoded version of \`.\` (dot). 485 | 486 | - **Writing arbitrary files**: Allows the attacker to create or replace existing files. This type of vulnerability is also known as \`Zip-Slip\`. 487 | 488 | One way to achieve this is by using a malicious \`zip\` archive that holds path traversal filenames. When each filename in the zip archive gets concatenated to the target extraction folder, without validation, the final path ends up outside of the target folder. If an executable or a configuration file is overwritten with a file containing malicious code, the problem can turn into an arbitrary code execution issue quite easily. 489 | 490 | The following is an example of a \`zip\` archive with one benign file and one malicious file. Extracting the malicious file will result in traversing out of the target folder, ending up in \`/root/.ssh/\` overwriting the \`authorized_keys\` file: 491 | 492 | \`\`\` 493 | 2018-04-15 22:04:29 ..... 19 19 good.txt 494 | 2018-04-15 22:04:42 ..... 20 20 ../../../../../../root/.ssh/authorized_keys 495 | \`\`\` 496 | 497 | ## Remediation 498 | Upgrade \`django\` to version 2.2.21, 3.1.9, 3.2.1 or higher. 499 | ## References 500 | - [Django Advisory](https://www.djangoproject.com/weblog/2021/may/04/security-releases/) 501 | - [GitHub Commit](https://github.com/django/django/commit/c98f446c188596d4ba6de71d1b77b4a6c5c2a007) 502 | ", 503 | "externalReferences": Array [ 504 | Object { 505 | "category": "ADVISORY", 506 | "locator": "https://www.djangoproject.com/weblog/2021/may/04/security-releases/", 507 | }, 508 | Object { 509 | "category": "ADVISORY", 510 | "locator": "https://github.com/django/django/commit/c98f446c188596d4ba6de71d1b77b4a6c5c2a007", 511 | }, 512 | ], 513 | "id": "SNYK-PYTHON-DJANGO-1279042", 514 | "modified": "2021-05-04T14:45:09.894750Z", 515 | "name": "SNYK-PYTHON-DJANGO-1279042", 516 | "published": "2021-05-04T14:45:10.137628Z", 517 | "relationships": Array [ 518 | Object { 519 | "affect": Object { 520 | "to": Array [ 521 | "app-with-already-fixed@0.0.0", 522 | "django@2.2.18", 523 | ], 524 | "type": "AFFECTS", 525 | }, 526 | "foundBy": Object { 527 | "to": Array [ 528 | "Jasu Viding", 529 | ], 530 | "type": "FOUND_BY", 531 | }, 532 | "ratedBy": Object { 533 | "cwes": Array [ 534 | 22, 535 | ], 536 | "rating": Array [ 537 | Object { 538 | "method": "CVSS_3", 539 | "score": Array [ 540 | Object { 541 | "base": 3.3, 542 | "exploitability": null, 543 | "impact": null, 544 | }, 545 | ], 546 | "severity": "Low", 547 | "vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N", 548 | }, 549 | ], 550 | "to": Array [ 551 | "Jasu Viding", 552 | ], 553 | "type": "RATED_BY", 554 | }, 555 | "suppliedBy": Object { 556 | "to": Array [ 557 | "Jasu Viding", 558 | ], 559 | "type": "SUPPLIED_BY", 560 | }, 561 | }, 562 | ], 563 | "summary": "Directory Traversal", 564 | }, 565 | Object { 566 | "details": "## Overview 567 | [django](https://pypi.org/project/Django/) is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. 568 | 569 | Affected versions of this package are vulnerable to HTTP Header Injection. In Python 3.9.5+ urllib.parse() automatically removes ASCII newlines and tabs from URLs. Unfortunately it created an issue in the URLValidator. URLValidator uses \`urllib.urlsplit()\` and \`urllib.urlunsplit()\` for creating a URL variant with Punycode which no longer contains newlines and tabs in Python 3.9.5+. As a consequence, the regular expression matched the URL (without unsafe characters) and the source value (with unsafe characters) was considered valid. 570 | 571 | This issue was introduced by the [bpo-43882](https://bugs.python.org/issue43882) fix. 572 | ## Remediation 573 | Upgrade \`django\` to version 3.2.2, 3.1.10, 2.2.22 or higher. 574 | ## References 575 | - [Django Security Releases](https://www.djangoproject.com/weblog/2021/may/06/security-releases/) 576 | - [GitHub Commit](https://github.com/django/django/commit/e1e81aa1c4427411e3c68facdd761229ffea6f6f) 577 | - [GitHub PR](https://github.com/django/django/pull/14360) 578 | - [Mail Archive](https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1804086.html) 579 | ", 580 | "externalReferences": Array [ 581 | Object { 582 | "category": "ADVISORY", 583 | "locator": "https://www.djangoproject.com/weblog/2021/may/06/security-releases/", 584 | }, 585 | Object { 586 | "category": "ADVISORY", 587 | "locator": "https://github.com/django/django/commit/e1e81aa1c4427411e3c68facdd761229ffea6f6f", 588 | }, 589 | Object { 590 | "category": "ADVISORY", 591 | "locator": "https://github.com/django/django/pull/14360", 592 | }, 593 | Object { 594 | "category": "ADVISORY", 595 | "locator": "https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1804086.html", 596 | }, 597 | ], 598 | "id": "SNYK-PYTHON-DJANGO-1290072", 599 | "modified": "2021-05-06T15:41:43.922301Z", 600 | "name": "SNYK-PYTHON-DJANGO-1290072", 601 | "published": "2021-05-06T15:41:44.175836Z", 602 | "relationships": Array [ 603 | Object { 604 | "affect": Object { 605 | "to": Array [ 606 | "app-with-already-fixed@0.0.0", 607 | "django@2.2.18", 608 | ], 609 | "type": "AFFECTS", 610 | }, 611 | "foundBy": Object { 612 | "to": Array [ 613 | "Unknown", 614 | ], 615 | "type": "FOUND_BY", 616 | }, 617 | "ratedBy": Object { 618 | "cwes": Array [ 619 | 644, 620 | ], 621 | "rating": Array [ 622 | Object { 623 | "method": "CVSS_3", 624 | "score": Array [ 625 | Object { 626 | "base": 7.3, 627 | "exploitability": null, 628 | "impact": null, 629 | }, 630 | ], 631 | "severity": "High", 632 | "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L", 633 | }, 634 | ], 635 | "to": Array [ 636 | "Unknown", 637 | ], 638 | "type": "RATED_BY", 639 | }, 640 | "suppliedBy": Object { 641 | "to": Array [ 642 | "Unknown", 643 | ], 644 | "type": "SUPPLIED_BY", 645 | }, 646 | }, 647 | ], 648 | "summary": "HTTP Header Injection", 649 | }, 650 | Object { 651 | "details": "## Overview 652 | [jinja2](https://pypi.org/project/Jinja2/) is a template engine written in pure Python. It provides a Django inspired non-XML syntax but supports inline expressions and an optional sandboxed environment. 653 | 654 | Affected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS). The ReDoS vulnerability is mainly due to the \`_punctuation_re regex\` operator and its use of multiple wildcards. The last wildcard is the most exploitable as it searches for trailing punctuation. 655 | 656 | This issue can be mitigated by using Markdown to format user content instead of the urlize filter, or by implementing request timeouts or limiting process memory. 657 | 658 | ### PoC by Yeting Li 659 | \`\`\` 660 | from jinja2.utils import urlize 661 | from time import perf_counter 662 | 663 | for i in range(3): 664 | text = \\"abc@\\" + \\".\\" * (i+1)*5000 + \\"!\\" 665 | LEN = len(text) 666 | BEGIN = perf_counter() 667 | urlize(text) 668 | DURATION = perf_counter() - BEGIN 669 | print(f\\"{LEN}: took {DURATION} seconds!\\") 670 | \`\`\` 671 | 672 | ## Details 673 | 674 | Denial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process. 675 | 676 | The Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down. 677 | 678 | Let’s take the following regular expression as an example: 679 | \`\`\`js 680 | regex = /A(B|C+)+D/ 681 | \`\`\` 682 | 683 | This regular expression accomplishes the following: 684 | - \`A\` The string must start with the letter 'A' 685 | - \`(B|C+)+\` The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the \`+\` matches one or more times). The \`+\` at the end of this section states that we can look for one or more matches of this section. 686 | - \`D\` Finally, we ensure this section of the string ends with a 'D' 687 | 688 | The expression would match inputs such as \`ABBD\`, \`ABCCCCD\`, \`ABCBCCCD\` and \`ACCCCCD\` 689 | 690 | It most cases, it doesn't take very long for a regex engine to find a match: 691 | 692 | \`\`\`bash 693 | $ time node -e '/A(B|C+)+D/.test(\\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD\\")' 694 | 0.04s user 0.01s system 95% cpu 0.052 total 695 | 696 | $ time node -e '/A(B|C+)+D/.test(\\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX\\")' 697 | 1.79s user 0.02s system 99% cpu 1.812 total 698 | \`\`\` 699 | 700 | The entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated. 701 | 702 | Most Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as _catastrophic backtracking_. 703 | 704 | Let's look at how our expression runs into this problem, using a shorter string: \\"ACCCX\\". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's: 705 | 1. CCC 706 | 2. CC+C 707 | 3. C+CC 708 | 4. C+C+C. 709 | 710 | The engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use [RegEx 101 debugger](https://regex101.com/debugger) to see the engine has to take a total of 38 steps before it can determine the string doesn't match. 711 | 712 | From there, the number of steps the engine must use to validate a string just continues to grow. 713 | 714 | | String | Number of C's | Number of steps | 715 | | -------|-------------:| -----:| 716 | | ACCCX | 3 | 38 717 | | ACCCCX | 4 | 71 718 | | ACCCCCX | 5 | 136 719 | | ACCCCCCCCCCCCCCX | 14 | 65,553 720 | 721 | 722 | By the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service. 723 | 724 | ## Remediation 725 | Upgrade \`jinja2\` to version 2.11.3 or higher. 726 | ## References 727 | - [GitHub Additional Information](https://github.com/pallets/jinja/blob/ab81fd9c277900c85da0c322a2ff9d68a235b2e6/src/jinja2/utils.py#L20) 728 | - [GitHub PR](https://github.com/pallets/jinja/pull/1343) 729 | ", 730 | "externalReferences": Array [ 731 | Object { 732 | "category": "ADVISORY", 733 | "locator": "https://github.com/pallets/jinja/blob/ab81fd9c277900c85da0c322a2ff9d68a235b2e6/src/jinja2/utils.py%23L20", 734 | }, 735 | Object { 736 | "category": "ADVISORY", 737 | "locator": "https://github.com/pallets/jinja/pull/1343", 738 | }, 739 | ], 740 | "id": "SNYK-PYTHON-JINJA2-1012994", 741 | "modified": "2021-02-01T19:52:16.877030Z", 742 | "name": "SNYK-PYTHON-JINJA2-1012994", 743 | "published": "2021-02-01T19:52:17Z", 744 | "relationships": Array [ 745 | Object { 746 | "affect": Object { 747 | "to": Array [ 748 | "app-with-already-fixed@0.0.0", 749 | "jinja2@2.7.2", 750 | ], 751 | "type": "AFFECTS", 752 | }, 753 | "foundBy": Object { 754 | "to": Array [ 755 | "Yeting Li", 756 | ], 757 | "type": "FOUND_BY", 758 | }, 759 | "ratedBy": Object { 760 | "cwes": Array [ 761 | 400, 762 | ], 763 | "rating": Array [ 764 | Object { 765 | "method": "CVSS_3", 766 | "score": Array [ 767 | Object { 768 | "base": 5.3, 769 | "exploitability": null, 770 | "impact": null, 771 | }, 772 | ], 773 | "severity": "Medium", 774 | "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L/E:P", 775 | }, 776 | ], 777 | "to": Array [ 778 | "Yeting Li", 779 | ], 780 | "type": "RATED_BY", 781 | }, 782 | "suppliedBy": Object { 783 | "to": Array [ 784 | "Yeting Li", 785 | ], 786 | "type": "SUPPLIED_BY", 787 | }, 788 | }, 789 | ], 790 | "summary": "Regular Expression Denial of Service (ReDoS)", 791 | }, 792 | Object { 793 | "details": "## Overview 794 | [jinja2](https://pypi.org/project/Jinja2/) is a template engine written in pure Python. It provides a Django inspired non-XML syntax but supports inline expressions and an optional sandboxed environment. 795 | 796 | Affected versions of this package are vulnerable to Sandbox Escape via the \`str.format_map\`. 797 | ## Remediation 798 | Upgrade \`jinja2\` to version 2.10.1 or higher. 799 | ## References 800 | - [Release Notes](https://palletsprojects.com/blog/jinja-2-10-1-released) 801 | ", 802 | "externalReferences": Array [ 803 | Object { 804 | "category": "ADVISORY", 805 | "locator": "https://palletsprojects.com/blog/jinja-2-10-1-released", 806 | }, 807 | ], 808 | "id": "SNYK-PYTHON-JINJA2-174126", 809 | "modified": "2020-06-12T14:36:55.661596Z", 810 | "name": "SNYK-PYTHON-JINJA2-174126", 811 | "published": "2019-04-07T00:42:43Z", 812 | "relationships": Array [ 813 | Object { 814 | "affect": Object { 815 | "to": Array [ 816 | "app-with-already-fixed@0.0.0", 817 | "jinja2@2.7.2", 818 | ], 819 | "type": "AFFECTS", 820 | }, 821 | "foundBy": Object { 822 | "to": Array [ 823 | "Unknown", 824 | ], 825 | "type": "FOUND_BY", 826 | }, 827 | "ratedBy": Object { 828 | "cwes": Array [ 829 | 265, 830 | ], 831 | "rating": Array [ 832 | Object { 833 | "method": "CVSS_3", 834 | "score": Array [ 835 | Object { 836 | "base": 6, 837 | "exploitability": null, 838 | "impact": null, 839 | }, 840 | ], 841 | "severity": "Medium", 842 | "vector": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:L/I:L/A:L/RL:O", 843 | }, 844 | ], 845 | "to": Array [ 846 | "Unknown", 847 | ], 848 | "type": "RATED_BY", 849 | }, 850 | "suppliedBy": Object { 851 | "to": Array [ 852 | "Unknown", 853 | ], 854 | "type": "SUPPLIED_BY", 855 | }, 856 | }, 857 | ], 858 | "summary": "Sandbox Escape", 859 | }, 860 | Object { 861 | "details": "## Overview 862 | [\`jinja2\`](https://pypi.python.org/pypi/jinja2) is a small but fast and easy to use stand-alone template engine written in pure python. 863 | FileSystemBytecodeCache in Jinja2 2.7.2 does not properly create temporary directories, which allows local users to gain privileges by pre-creating a temporary directory with a user's uid. 864 | 865 | **NOTE:** this vulnerability exists because of an incomplete fix for [CVE-2014-1402](https://snyk.io/vulnSNYK-PYTHON-JINJA2-40028). 866 | 867 | ## References 868 | - [NVD](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-0012) 869 | - [Bugzilla redhat](https://bugzilla.redhat.com/show_bug.cgi?id=1051421) 870 | - [GitHub PR #1](https://github.com/mitsuhiko/jinja2/pull/292) 871 | - [GitHub PR #2](https://github.com/mitsuhiko/jinja2/pull/296) 872 | - [GitHub Commit](https://github.com/mitsuhiko/jinja2/commit/acb672b6a179567632e032f547582f30fa2f4aa7) 873 | ", 874 | "externalReferences": Array [ 875 | Object { 876 | "category": "ADVISORY", 877 | "locator": "https://github.com/mitsuhiko/jinja2/commit/acb672b6a179567632e032f547582f30fa2f4aa7", 878 | }, 879 | Object { 880 | "category": "ADVISORY", 881 | "locator": "https://github.com/mitsuhiko/jinja2/pull/292", 882 | }, 883 | Object { 884 | "category": "ADVISORY", 885 | "locator": "https://github.com/mitsuhiko/jinja2/pull/296", 886 | }, 887 | Object { 888 | "category": "ADVISORY", 889 | "locator": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-0012", 890 | }, 891 | Object { 892 | "category": "ADVISORY", 893 | "locator": "https://bugzilla.redhat.com/show_bug.cgi?id=1051421", 894 | }, 895 | ], 896 | "id": "SNYK-PYTHON-JINJA2-40250", 897 | "modified": "2019-02-17T08:46:41.648104Z", 898 | "name": "SNYK-PYTHON-JINJA2-40250", 899 | "published": "2014-01-18T05:33:40.101000Z", 900 | "relationships": Array [ 901 | Object { 902 | "affect": Object { 903 | "to": Array [ 904 | "app-with-already-fixed@0.0.0", 905 | "jinja2@2.7.2", 906 | ], 907 | "type": "AFFECTS", 908 | }, 909 | "foundBy": Object { 910 | "to": Array [ 911 | "Arun Babu Neelicattu", 912 | ], 913 | "type": "FOUND_BY", 914 | }, 915 | "ratedBy": Object { 916 | "cwes": Array [ 917 | 264, 918 | ], 919 | "rating": Array [ 920 | Object { 921 | "method": "CVSS_3", 922 | "score": Array [ 923 | Object { 924 | "base": 5.3, 925 | "exploitability": null, 926 | "impact": null, 927 | }, 928 | ], 929 | "severity": "Medium", 930 | "vector": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L", 931 | }, 932 | ], 933 | "to": Array [ 934 | "Arun Babu Neelicattu", 935 | ], 936 | "type": "RATED_BY", 937 | }, 938 | "suppliedBy": Object { 939 | "to": Array [ 940 | "Arun Babu Neelicattu", 941 | ], 942 | "type": "SUPPLIED_BY", 943 | }, 944 | }, 945 | ], 946 | "summary": "Privilege Escalation", 947 | }, 948 | Object { 949 | "details": "## Overview 950 | [jinja2](https://pypi.org/project/Jinja2/) is a template engine written in pure Python. It provides a Django inspired non-XML syntax but supports inline expressions and an optional sandboxed environment. 951 | 952 | Affected versions of this package are vulnerable to Sandbox Bypass. Users were allowed to insert \`str.format\` through web templates, leading to an escape from sandbox. 953 | ## Remediation 954 | Upgrade \`jinja2\` to version 2.8.1 or higher. 955 | ## References 956 | - [GitHub Commit](https://github.com/pallets/jinja/commit/9b53045c34e61013dc8f09b7e52a555fa16bed16) 957 | ", 958 | "externalReferences": Array [ 959 | Object { 960 | "category": "ADVISORY", 961 | "locator": "https://github.com/pallets/jinja/commit/9b53045c34e61013dc8f09b7e52a555fa16bed16", 962 | }, 963 | ], 964 | "id": "SNYK-PYTHON-JINJA2-455616", 965 | "modified": "2020-06-12T14:36:58.461729Z", 966 | "name": "SNYK-PYTHON-JINJA2-455616", 967 | "published": "2019-07-30T13:11:16Z", 968 | "relationships": Array [ 969 | Object { 970 | "affect": Object { 971 | "to": Array [ 972 | "app-with-already-fixed@0.0.0", 973 | "jinja2@2.7.2", 974 | ], 975 | "type": "AFFECTS", 976 | }, 977 | "foundBy": Object { 978 | "to": Array [ 979 | "Unknown", 980 | ], 981 | "type": "FOUND_BY", 982 | }, 983 | "ratedBy": Object { 984 | "cwes": Array [ 985 | 234, 986 | ], 987 | "rating": Array [ 988 | Object { 989 | "method": "CVSS_3", 990 | "score": Array [ 991 | Object { 992 | "base": 8.6, 993 | "exploitability": null, 994 | "impact": null, 995 | }, 996 | ], 997 | "severity": "High", 998 | "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N", 999 | }, 1000 | ], 1001 | "to": Array [ 1002 | "Unknown", 1003 | ], 1004 | "type": "RATED_BY", 1005 | }, 1006 | "suppliedBy": Object { 1007 | "to": Array [ 1008 | "Unknown", 1009 | ], 1010 | "type": "SUPPLIED_BY", 1011 | }, 1012 | }, 1013 | ], 1014 | "summary": "Sandbox Bypass", 1015 | }, 1016 | ] 1017 | `; 1018 | -------------------------------------------------------------------------------- /test/unit/lib/convert-issue-to-spdx.spec.ts: -------------------------------------------------------------------------------- 1 | import * as pathLib from 'path'; 2 | 3 | import { convertSnykIssueToSpdx } from '../../../src/lib/convert-issue-to-spdx'; 4 | import { SnykIssue } from '../../../src/types'; 5 | import { loadJson } from '../../load-json'; 6 | 7 | describe('convertSnykIssueToSpdx', () => { 8 | it('Snyk issue is converted to SPDX v3 vulnerability', async () => { 9 | const snykIssue = loadJson( 10 | pathLib.resolve(__dirname, '../../', 'fixtures/single-vuln.json'), 11 | ); 12 | const res = convertSnykIssueToSpdx(snykIssue); 13 | expect(res).toMatchSnapshot(); 14 | }); 15 | it.skip('license issue is not converted to a vulnerability', () => { 16 | const licenseIssue = loadJson( 17 | pathLib.resolve( 18 | __dirname, 19 | '../../', 20 | 'fixtures/single-license-issue.json', 21 | ), 22 | ); 23 | const res = convertSnykIssueToSpdx(licenseIssue); 24 | expect(res).toMatchSnapshot(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/unit/lib/index.spec.ts: -------------------------------------------------------------------------------- 1 | import * as pathLib from 'path'; 2 | 3 | import { convertSnykTestOutputToSPDX } from '../../../src'; 4 | import { SnykTestOutput } from '../../../src/types'; 5 | import { loadJson } from '../../load-json'; 6 | describe('convertSnykTestOutputToSPDX', () => { 7 | it('No Snyk vulnerabilities converted to SPDX v3 correctly', async () => { 8 | const snykTestData = loadJson( 9 | pathLib.resolve(__dirname, '../../', 'fixtures/no-deps.json'), 10 | ); 11 | const projectName = 'no-prod-deps'; 12 | const res = convertSnykTestOutputToSPDX(snykTestData); 13 | expect(res).toMatchObject({ 14 | id: `SPDXRef-${projectName}`, 15 | name: projectName, 16 | specVersion: 'SPDX-3.0', 17 | profile: ['base', 'vulnerabilities'], 18 | created: expect.any(String), 19 | documentNamespace: expect.stringMatching( 20 | `spdx.org/spdxdocs/${projectName}`, 21 | ), 22 | dataLicense: 'CC0-1.0', 23 | creator: 'Organization: Snyk Ltd', 24 | description: `Snyk test result for project ${projectName} in SPDX SBOM format`, 25 | vulnerabilities: [], 26 | }); 27 | }); 28 | it('Snyk issue is converted to SPDX v3 vulnerability', () => { 29 | const snykTestData = loadJson( 30 | pathLib.resolve( 31 | __dirname, 32 | '../../', 33 | 'fixtures/ruby-vulnerabilities.json', 34 | ), 35 | ); 36 | const projectName = 'ruby-app'; 37 | const res = convertSnykTestOutputToSPDX(snykTestData); 38 | expect(res).toMatchObject({ 39 | id: `SPDXRef-${projectName}`, 40 | name: projectName, 41 | specVersion: 'SPDX-3.0', 42 | profile: ['base', 'vulnerabilities'], 43 | created: expect.any(String), 44 | documentNamespace: expect.stringMatching( 45 | `spdx.org/spdxdocs/${projectName}`, 46 | ), 47 | dataLicense: 'CC0-1.0', 48 | creator: 'Organization: Snyk Ltd', 49 | description: `Snyk test result for project ${projectName} in SPDX SBOM format`, 50 | }); 51 | expect(res.vulnerabilities.sort()).toMatchSnapshot(); 52 | }); 53 | it('license issues are not converted to vulnerabilities', () => { 54 | const snykTestData = loadJson( 55 | pathLib.resolve(__dirname, '../../', 'fixtures/with-license-issues.json'), 56 | ); 57 | const projectName = 'app-with-already-fixed'; 58 | const res = convertSnykTestOutputToSPDX(snykTestData); 59 | expect(res).toMatchObject({ 60 | id: `SPDXRef-${projectName}`, 61 | name: projectName, 62 | specVersion: 'SPDX-3.0', 63 | profile: ['base', 'vulnerabilities'], 64 | created: expect.any(String), 65 | documentNamespace: expect.stringMatching( 66 | `spdx.org/spdxdocs/${projectName}`, 67 | ), 68 | dataLicense: 'CC0-1.0', 69 | creator: 'Organization: Snyk Ltd', 70 | description: `Snyk test result for project ${projectName} in SPDX SBOM format`, 71 | }); 72 | expect(res.vulnerabilities.sort()).toMatchSnapshot(); 73 | // TODO: comment out once functionality in place 74 | expect( 75 | res.vulnerabilities.find((i) => i.id === 'snyk:lic:pip:pytz:MIT'), 76 | ).toEqual(undefined); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist", 4 | "pretty": true, 5 | "lib": ["ES2020"], 6 | "module": "commonjs", 7 | "target": "ES2019", 8 | "sourceMap": true, 9 | "declaration": true, 10 | "importHelpers": true, 11 | "strict": true, 12 | "types": ["jest", "node"] 13 | }, 14 | "include": ["./src/**/*"] 15 | } 16 | --------------------------------------------------------------------------------