Skip to content

Instantly share code, notes, and snippets.

@AmitGupta7580
Created April 17, 2021 09:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save AmitGupta7580/1124c73b9dbc5c6b6cb41a3f5fa1a73a to your computer and use it in GitHub Desktop.
Save AmitGupta7580/1124c73b9dbc5c6b6cb41a3f5fa1a73a to your computer and use it in GitHub Desktop.
bulk_search API Code suggestion for vulnerablecode
from urllib.parse import unquote
from django.db.models import Q
from django.urls import reverse
from django_filters import rest_framework as filters
from drf_spectacular.utils import extend_schema, inline_serializer
from packageurl import PackageURL
from rest_framework import serializers, viewsets, mixins
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.pagination import LimitOffsetPagination
from vulnerabilities.models import Package
from vulnerabilities.models import Vulnerability
from vulnerabilities.models import VulnerabilityReference
from vulnerabilities.models import VulnerabilitySeverity
class VulnerabilitySeveritySerializer(serializers.ModelSerializer):
class Meta:
model = VulnerabilitySeverity
fields = ["value", "scoring_system"]
class VulnerabilityReferenceSerializer(serializers.ModelSerializer):
scores = VulnerabilitySeveritySerializer(many=True)
class Meta:
model = VulnerabilityReference
fields = ["source", "reference_id", "url", "scores"]
class MinimalPackageSerializer(serializers.HyperlinkedModelSerializer):
"""
Used for nesting inside vulnerability focused APIs.
"""
purl = serializers.CharField(source="package_url")
class Meta:
model = Package
fields = ["url", "purl"]
class MinimalVulnerabilitySerializer(serializers.HyperlinkedModelSerializer):
"""
Used for nesting inside package focused APIs.
"""
references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set")
class Meta:
model = Vulnerability
fields = ["url", "vulnerability_id", "references"]
class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer):
resolved_packages = MinimalPackageSerializer(many=True, source="resolved_to", read_only=True)
unresolved_packages = MinimalPackageSerializer(
many=True, source="vulnerable_to", read_only=True
)
references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set")
class Meta:
model = Vulnerability
fields = "__all__"
class PackageSerializer(serializers.HyperlinkedModelSerializer):
unresolved_vulnerabilities = MinimalVulnerabilitySerializer(
many=True, source="vulnerable_to", read_only=True
)
resolved_vulnerabilities = MinimalVulnerabilitySerializer(
many=True, source="resolved_to", read_only=True
)
purl = serializers.CharField(source="package_url")
class Meta:
model = Package
exclude = ["vulnerabilities"]
class PackageFilterSet(filters.FilterSet):
purl = filters.CharFilter(method="filter_purl")
class Meta:
model = Package
fields = ["name", "type", "version", "subpath", "purl"]
def filter_purl(self, queryset, name, value):
purl = unquote(value)
try:
purl = PackageURL.from_string(purl)
except ValueError as ve:
raise serializers.ValidationError(
detail={"error": f'"{purl}" is not a valid Package URL: {ve}'},
)
attrs = {k: v for k, v in purl.to_dict().items() if v}
return self.queryset.filter(**attrs)
class PackageViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Package.objects.all()
serializer_class = PackageSerializer
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = PackageFilterSet
pagination_class = LimitOffsetPagination
class PackageBulkSearchViewSet(viewsets.GenericViewSet):
class PackageBulkRequestSerializer(serializers.Serializer):
purls = serializers.ListField(child=serializers.CharField(max_length=100))
@extend_schema(request=PackageBulkRequestSerializer, responses=PackageSerializer(many=True))
def create(self, request, *args, **kwargs):
response = []
purls = request.data.get("purls", []) or []
if not purls or not isinstance(purls, list):
return Response(
status=400,
data={"Error": "A non-empty 'purls' list of package URLs is required."},
)
for purl in request.data["purls"]:
try:
purl = PackageURL.from_string(purl).to_dict()
except ValueError as ve:
return Response(status=400, data={"Error": f"Invalid Package URL: {purl}"})
purl_data = Package.objects.filter(
**{key: value for key, value in purl.items() if value}
)
purl_response = {}
if purl_data:
purl_response = PackageSerializer(purl_data[0], context={"request": request}).data
else:
purl_response = purl
purl_response["unresolved_vulnerabilities"] = []
purl_response["resolved_vulnerabilities"] = []
response.append(purl_response)
return Response(response)
class VulnerabilityFilterSet(filters.FilterSet):
class Meta:
model = Vulnerability
fields = ["vulnerability_id"]
class VulnerabilityViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Vulnerability.objects.all()
serializer_class = VulnerabilitySerializer
paginate_by = 50
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = VulnerabilityFilterSet
pagination_class = LimitOffsetPagination
"""
Django settings for app project.
Generated by 'django-admin startproject' using Django 1.11.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""
import os
DEV_MODE = os.environ.get("DJANGO_DEV", False)
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
if not DEV_MODE:
SECRET_KEY = os.environ["SECRET_KEY"]
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = [
".herokuapp.com",
]
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
# Disable Django's own staticfiles handling in favour of WhiteNoise, for
# greater consistency between gunicorn and `./manage.py runserver`. See:
# http://whitenoise.evans.io/en/stable/django.html#using-whitenoise-in-development
"whitenoise.runserver_nostatic",
"django.contrib.staticfiles",
"vulnerabilities",
"rest_framework",
"django_filters",
"widget_tweaks",
"drf_spectacular",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "vulnerablecode.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, "templates")],
"APP_DIRS": True,
"OPTIONS": {
"debug": False,
"context_processors": [
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.static",
"django.template.context_processors.request",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "vulnerablecode.wsgi.application"
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ.get("VC_DB_NAME", "vulnerablecode"),
"USER": os.environ.get("VC_DB_USER", "vulnerablecode"),
"PASSWORD": os.environ.get("VC_DB_PASSWORD", "vulnerablecode"),
"HOST": os.environ.get("VC_DB_HOST", "localhost"),
"PORT": "5432",
}
}
if "TRAVIS" in os.environ:
DATABASES["default"]["USER"] = "postgres"
DATABASES["default"]["PASSWORD"] = ""
# Update database configuration with $DATABASE_URL.
import dj_database_url
db_from_env = dj_database_url.config(conn_max_age=500)
DATABASES["default"].update(db_from_env)
# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
STATIC_URL = "/static/"
# Simplified static file serving.
# https://warehouse.python.org/project/whitenoise/
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
# REST API
REST_FRAMEWORK = {
"DEFAULT_FILTER_BACKENDS": ("django_filters.rest_framework.DjangoFilterBackend",),
"PAGE_SIZE": 100,
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}
SPECTACULAR_SETTINGS = {"SERVE_INCLUDE_SCHEMA": False, "TITLE": "VulnerableCode API"}
# TODO: Specify the license for the API here.
# Set this to true to enable community curation, ie users will be able to edit data
ENABLE_CURATION = False
# Set `DJANGO_DEV=1` in env to enable dev mode
if DEV_MODE:
from vulnerablecode.dev import *
#
# Copyright (c) nexB Inc. and others. All rights reserved.
# http://nexb.com and https://github.com/nexB/vulnerablecode/
# The VulnerableCode software is licensed under the Apache License version 2.0.
# Data generated with VulnerableCode require an acknowledgment.
#
# You may not use this software except in compliance with the License.
# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# When you publish or redistribute any data created with VulnerableCode or any VulnerableCode
# derivative work, you must accompany this data with the following acknowledgment:
#
# Generated with VulnerableCode and provided on an 'AS IS' BASIS, WITHOUT WARRANTIES
# OR CONDITIONS OF ANY KIND, either express or implied. No content created from
# VulnerableCode should be considered or used as legal advice. Consult an Attorney
# for any legal advice.
# VulnerableCode is a free software from nexB Inc. and others.
# Visit https://github.com/nexB/vulnerablecode/ for support and download.
from django.contrib import admin
from django.urls import include, path
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
from rest_framework import permissions
from rest_framework.routers import DefaultRouter
from vulnerabilities.api import PackageViewSet
from vulnerabilities.api import VulnerabilityViewSet
from vulnerabilities.api import PackageBulkSearchViewSet
from vulnerabilities.views import HomePage
from vulnerabilities.views import PackageSearchView
from vulnerabilities.views import PackageUpdate
from vulnerabilities.views import PackageCreate
from vulnerabilities.views import PackageRelatedVulnerablityDelete
from vulnerabilities.views import PackageRelatedVulnerablityCreate
from vulnerabilities.views import VulnerabilityDetails
from vulnerabilities.views import VulnerabilitySearchView
from vulnerabilities.views import VulnerabilityCreate
from vulnerabilities.views import VulnerabilityReferenceCreate
from vulnerablecode.settings import ENABLE_CURATION
# See the comment at https://stackoverflow.com/a/46163870.
class OptionalSlashRouter(DefaultRouter):
def __init__(self, *args, **kwargs):
super(DefaultRouter, self).__init__(*args, **kwargs)
self.trailing_slash = "/?"
api_router = OptionalSlashRouter()
api_router.register(r"packages", PackageViewSet)
# `DefaultRouter` requires `basename` when registering viewsets which don't
# define a queryset.
api_router.register(r"vulnerabilities", VulnerabilityViewSet, basename="vulnerability")
api_router.register(r"bulk_search", PackageBulkSearchViewSet, basename="bulk_search")
curation_views = [
path("vulnerabilities/create", VulnerabilityCreate.as_view(), name="vulnerability_create"),
path("packages/create", PackageCreate.as_view(), name="package_create"),
path(
"relations/resolved/<int:pid>/<int:vid>",
PackageRelatedVulnerablityDelete.as_view(),
name="resolved_package_delete",
),
path(
"relations/impacted/<int:pid>/<int:vid>",
PackageRelatedVulnerablityDelete.as_view(),
name="impacted_package_delete",
),
path(
"relations/impacted/<int:pid>/create",
PackageRelatedVulnerablityCreate.as_view(),
name="impacted_package_create",
),
path(
"relations/resolved/<int:pid>/create",
PackageRelatedVulnerablityCreate.as_view(),
name="resolved_package_create",
),
path(
"relations/reference/<int:vid>/create",
VulnerabilityReferenceCreate.as_view(),
name="vulnerability_reference_create",
),
]
urlpatterns = [
path("admin/", admin.site.urls),
path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
path("api/docs", SpectacularSwaggerView.as_view(), name="swagger-ui"),
path("packages/search", PackageSearchView.as_view(), name="package_search"),
path("packages/<int:pk>", PackageUpdate.as_view(), name="package_view"),
path("vulnerabilities/<int:pk>", VulnerabilityDetails.as_view(), name="vulnerability_view"),
path("vulnerabilities/search", VulnerabilitySearchView.as_view(), name="vulnerability_search"),
path("", HomePage.as_view(), name="home"),
path(r"api/", include(api_router.urls)),
]
if ENABLE_CURATION:
urlpatterns.extend(curation_views)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment