-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from mam-dev/initial-code
Initial code
- Loading branch information
Showing
14 changed files
with
1,542 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[flake8] | ||
max-line-length = 88 | ||
extend-ignore = E203 | ||
extend-exclude = | ||
.tox, | ||
build, | ||
venv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
branches: [ main ] | ||
|
||
jobs: | ||
build: | ||
strategy: | ||
matrix: | ||
python_version: ["3.7", "3.8", "3.9", "3.10", "3.11"] | ||
runs-on: "ubuntu-latest" | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Set up Python ${{ matrix.python_version }} | ||
uses: actions/setup-python@v2 | ||
with: | ||
python-version: ${{ matrix.python_version }} | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install tox | ||
- name: Run tox | ||
run: tox -e py$(sed 's/\.//' ${{ matrix.python_version }}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Thanks to: Sean Hammond | ||
# https://www.seanh.cc/2022/05/21/publishing-python-packages-from-github-actions | ||
name: Publish to PyPI.org | ||
on: | ||
release: | ||
types: [published] | ||
jobs: | ||
pypi: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
- run: python3 -m pip install --upgrade build && python3 -m build | ||
- name: Publish package | ||
uses: pypa/gh-action-pypi-publish@release/v1 | ||
with: | ||
password: ${{ secrets.PYPI_API_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# security-constraints | ||
|
||
Security-constraints is a command-line application used | ||
to fetch security vulnerabilities in Python packages from | ||
external sources and from them generate version constraints | ||
for the packages. | ||
|
||
The constraints can then be given to `pip install` with the `-c` option, | ||
either on the command line or in a requirements file. | ||
|
||
## Installation | ||
|
||
Just install it with `pip`: | ||
```bash | ||
pip install security-constraints | ||
``` | ||
|
||
## Usage | ||
|
||
The environment variable `SC_GITHUB_TOKEN` needs to be set | ||
to a valid GitHub token which provides read access to public | ||
repositories. This is needed in order to access GitHub Security | ||
Advisory. Once this is set, you can simply run the program to | ||
output safe pip constraints to stdout. | ||
|
||
```bash | ||
>security-constraints | ||
# Generated by security-constraints on 2022-11-04T08:33:54.523625 | ||
# Data sources: Github Security Advisory | ||
# Configuration: {'ignore_ids': []} | ||
... | ||
vncauthproxy<0,>=1.2.0 # CVE-2022-36436 (ID: GHSA-237r-mx84-7x8c) | ||
waitress!=1.4.2 # CVE-2020-5236 (ID: GHSA-73m2-3pwg-5fgc) | ||
waitress>=1.4.0 # GHSA-4ppp-gpcr-7qf6 (ID: GHSA-4ppp-gpcr-7qf6) | ||
ymlref>0.1.1 # CVE-2018-20133 (ID: GHSA-8r8j-xvfj-36f9) | ||
> | ||
``` | ||
|
||
You can use `--output` to instead output to a file. | ||
|
||
```bash | ||
>security-constraints --output constraints.txt | ||
>cat constraints.txt | ||
# Generated by security-constraints on 2022-11-04T08:33:54.523625 | ||
# Data sources: Github Security Advisory | ||
# Configuration: {'ignore_ids': []} | ||
... | ||
vncauthproxy<0,>=1.2.0 # CVE-2022-36436 (ID: GHSA-237r-mx84-7x8c) | ||
waitress!=1.4.2 # CVE-2020-5236 (ID: GHSA-73m2-3pwg-5fgc) | ||
waitress>=1.4.0 # GHSA-4ppp-gpcr-7qf6 (ID: GHSA-4ppp-gpcr-7qf6) | ||
ymlref>0.1.1 # CVE-2018-20133 (ID: GHSA-8r8j-xvfj-36f9) | ||
> | ||
``` | ||
|
||
You can provide a space-separated list of IDs of vulnerabilities that | ||
should be ignored. The IDs in question are those that appear in after | ||
`ID:` in the comments in the output. | ||
|
||
```bash | ||
>security-constraints --ignore-ids GHSA-4ppp-gpcr-7qf6 GHSA-8r8j-xvfj-36f9 | ||
# Generated by security-constraints on 2022-11-04T08:33:54.523625 | ||
# Data sources: Github Security Advisory | ||
# Configuration: {'ignore_ids': ['GHSA-4ppp-gpcr-7qf6', 'GHSA-8r8j-xvfj-36f9']} | ||
... | ||
vncauthproxy<0,>=1.2.0 # CVE-2022-36436 (ID: GHSA-237r-mx84-7x8c) | ||
waitress!=1.4.2 # CVE-2020-5236 (ID: GHSA-73m2-3pwg-5fgc) | ||
> | ||
``` | ||
|
||
The IDs to ignore can also be given in a configuration file using `--config`. | ||
To create an initial configuration file, you can use `--dump-config`. This | ||
will dump the current configuration (including any `--ignore-ids` passed) to | ||
stdout and then exit. You can redirect this into a file to create an | ||
initial configuration file. The configuration file is in yaml format. | ||
|
||
```bash | ||
>security-constraints --ignore-ids GHSA-4ppp-gpcr-7qf6 GHSA-8r8j-xvfj-36f9 --dump-config > sc_config.yaml | ||
>cat sc_config.yaml | ||
ignore_ids: | ||
- GHSA-4ppp-gpcr-7qf6 | ||
- GHSA-8r8j-xvfj-36f9 | ||
>security-constraints --config sc_config.yaml | ||
# Generated by security-constraints on 2022-11-04T08:33:54.523625 | ||
# Data sources: Github Security Advisory | ||
# Configuration: {'ignore_ids': ['GHSA-4ppp-gpcr-7qf6', 'GHSA-8r8j-xvfj-36f9']} | ||
... | ||
vncauthproxy<0,>=1.2.0 # CVE-2022-36436 (ID: GHSA-237r-mx84-7x8c) | ||
waitress!=1.4.2 # CVE-2020-5236 (ID: GHSA-73m2-3pwg-5fgc) | ||
> | ||
``` | ||
|
||
## Contributing | ||
Pull requests as well as new issues are welcome. | ||
|
||
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) | ||
![example workflow](https://github.com/mam-dev/security-constraints/actions/workflows/ci.yml/badge.svg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
[project] | ||
name = "security-constraints" | ||
version = "1.0.0" | ||
description = "Fetches security vulnerabilities and creates pip-constraints based on them." | ||
readme = "README.md" | ||
license = {file = "LICENSE"} | ||
requires-python = ">=3.7" | ||
dependencies = [ | ||
"requests", | ||
"pyyaml", | ||
"importlib-metadata >= 1.0 ; python_version < '3.8'" | ||
] | ||
|
||
[project.optional-dependencies] | ||
test = [ | ||
"pytest", | ||
"requests-mock", | ||
"freezegun" | ||
] | ||
lint = [ | ||
"isort", | ||
"black", | ||
"flake8", | ||
"mypy", | ||
"types-requests", | ||
"types-PyYAML" | ||
] | ||
|
||
[project.scripts] | ||
security-constraints = "security_constraints.main:main" | ||
|
||
[build-system] | ||
requires = ["setuptools>=51", "wheel", "setuptools_scm[toml]>=6.2"] | ||
build-backend = "setuptools.build_meta" | ||
|
||
[tool.setuptools_scm] | ||
|
||
[tool.setuptools.packages.find] | ||
where = ["src"] | ||
namespaces = false | ||
|
||
[tool.isort] | ||
profile = "black" | ||
src_paths = ["src", "test"] | ||
|
||
[tool.pytest.ini_options] | ||
minversion = "6.0" | ||
usefixtures = ["requests_mock"] | ||
testpaths = ["test"] | ||
|
||
[tool.mypy] | ||
warn_return_any = true | ||
warn_unused_configs = true | ||
warn_unused_ignores = true | ||
warn_redundant_casts = true | ||
warn_unreachable = true | ||
files = ["src", "test"] | ||
|
||
[[tool.mypy.overrides]] | ||
module = 'py' | ||
ignore_missing_imports = true |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
"""This module contains common definitions for use in any other module.""" | ||
import abc | ||
import dataclasses | ||
from typing import Dict, List | ||
|
||
|
||
class SecurityConstraintsError(Exception): | ||
"""Base class for all exceptions in this application.""" | ||
|
||
|
||
class FailedPrerequisitesError(SecurityConstraintsError): | ||
"""Error raised when something is missing in order to run the application.""" | ||
|
||
|
||
class FetchVulnerabilitiesError(SecurityConstraintsError): | ||
"""Error which occurred when fetching vulnerabilities.""" | ||
|
||
|
||
@dataclasses.dataclass | ||
class Configuration: | ||
"""The application configuration. | ||
Corresponds to the contents of a configuration file. | ||
""" | ||
|
||
ignore_ids: List[str] = dataclasses.field(default_factory=list) | ||
|
||
def to_dict(self) -> Dict: | ||
return dataclasses.asdict(self) | ||
|
||
@classmethod | ||
def from_dict(cls, json: Dict) -> "Configuration": | ||
return cls(**json) | ||
|
||
@classmethod | ||
def supported_keys(cls) -> List[str]: | ||
"""Return a list of keys which are supported in the config file.""" | ||
return list(cls().to_dict().keys()) | ||
|
||
|
||
@dataclasses.dataclass | ||
class PackageConstraints: | ||
"""Version constraints for a single python package. | ||
Attributes: | ||
package: The name of the package. | ||
specifies: A list of version specifiers, e.g. ">3.0". | ||
""" | ||
|
||
package: str | ||
specifiers: List[str] = dataclasses.field(default_factory=list) | ||
|
||
def __str__(self) -> str: | ||
return f"{self.package}{','.join(self.specifiers)}" | ||
|
||
|
||
@dataclasses.dataclass | ||
class SecurityVulnerability: | ||
"""A security vulnerability in a Python package. | ||
Attributes: | ||
name: Human-readable name of the vulnerability. | ||
identifier: Used to uniquely identify this vulnerability, | ||
e.g. when ignoring it. | ||
package: The name of the affected Python package. | ||
vulnerable_range: String specifying which versions are vulnerable. | ||
Syntax: | ||
= 0.2.0 denotes a single vulnerable version. | ||
<= 1.0.8 denotes a version range up to and including the specified version | ||
< 0.1.11 denotes a version range up to, but excluding, the specified version | ||
>= 4.3.0, < 4.3.5 denotes a version range with a known min and max version. | ||
>= 0.0.1 denotes a version range with a known minimum, but no known maximum. | ||
""" | ||
|
||
name: str | ||
identifier: str | ||
package: str | ||
vulnerable_range: str | ||
|
||
def __str__(self) -> str: | ||
return self.name | ||
|
||
|
||
class SecurityVulnerabilityDatabaseAPI(abc.ABC): | ||
"""An API toward a database of security vulnerabilities in Python packages.""" | ||
|
||
@abc.abstractmethod | ||
def get_database_name(self) -> str: | ||
"""Return the name of the vulnerability database in human-readable text.""" | ||
|
||
@abc.abstractmethod | ||
def get_vulnerabilities(self) -> List[SecurityVulnerability]: | ||
"""Fetch and return all relevant security vulnerabilities from the database.""" |
Oops, something went wrong.