From 5df8e241822fa80d1066cb135904d69438eb2460 Mon Sep 17 00:00:00 2001 From: bkaiserinfosec <49665796+bkaiserinfosec@users.noreply.github.com> Date: Fri, 29 Dec 2023 20:53:22 -0800 Subject: [PATCH 1/9] Update pipeline-config.yaml (#440) --- pipeline-config.yaml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pipeline-config.yaml b/pipeline-config.yaml index 3c3e8bea..9d98c47f 100644 --- a/pipeline-config.yaml +++ b/pipeline-config.yaml @@ -9,54 +9,54 @@ stages: branches: - release unitTesting: - enabled: false + enabled: true branches: [] secretScanning: - enabled: false + enabled: true branches: - release sca: - enabled: false + enabled: true branches: - release codeLanguages: - Python - Javascript sast: - enabled: false + enabled: true branches: - release codeLanguages: - Python iac: - enabled: false + enabled: true branches: - release buildDocker: - enabled: false + enabled: true branches: - release containerScan: - enabled: false + enabled: true branches: - release containerName: secusphere containerTag: latest releaseToTest: - enabled: false + enabled: true branches: - release serviceName: secusphere containerTag: latest testRelease: - enabled: false + enabled: true branches: - release targetUrl: 'http://192.168.0.68:5010' dastTestType: full apiTargetUrl: 'http://192.168.0.68:5010/api/openapi.yaml' securityQualityGate: - enabled: false + enabled: true branches: - release deploy: @@ -83,7 +83,7 @@ stages: app.smtp.passwordRef: "SENDGRID-SMTP-PW" app.az.keyVaultName: "BkDevSecOpsKeyVault" post: - enabled: false + enabled: true branches: - release recipientEmails: 'brian@jbfinegoods.com' From c3fb035bf7c9b3c54717eb12a205942eef53905e Mon Sep 17 00:00:00 2001 From: bkaiserinfosec <49665796+bkaiserinfosec@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:09:41 -0800 Subject: [PATCH 2/9] Update tox.ini (#441) --- src/tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tox.ini b/src/tox.ini index 5c4f7a01..eedeee12 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -7,7 +7,7 @@ deps = pytest coverage pytest-cov - Flask==2.2.2 + Flask==2.3.3 Flask-SQLAlchemy==3.0.3 Flask-Login==0.6.2 Flask-Moment==1.0.5 @@ -15,11 +15,11 @@ deps = Flask-Markdown==0.3 Flask-Bootstrap==3.3.7.1 pyotp==2.8.0 - PyJWT==2.6.0 + PyJWT==2.7.0 pycryptodome==3.17 PyQRCode==1.2.1 python-dateutil==2.8.2 - requests==2.28.2 + requests==2.31.0 azure-identity==1.12.0 azure-keyvault-secrets==4.6.0 azure-keyvault-certificates==4.6.0 From 57a681aae8c644eb08cd9166fa2fdd2709583866 Mon Sep 17 00:00:00 2001 From: bkaiserinfosec <49665796+bkaiserinfosec@users.noreply.github.com> Date: Sat, 30 Dec 2023 00:04:00 -0800 Subject: [PATCH 3/9] Feature/fix toxi (#443) * Update tox.ini * fix unit test failures --- src/vr/templates/vulns/all_vulnerabilities_filtered.html | 4 ---- src/vr/vulns/web/findings.py | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/vr/templates/vulns/all_vulnerabilities_filtered.html b/src/vr/templates/vulns/all_vulnerabilities_filtered.html index 22bfd116..f67b34cb 100644 --- a/src/vr/templates/vulns/all_vulnerabilities_filtered.html +++ b/src/vr/templates/vulns/all_vulnerabilities_filtered.html @@ -51,11 +51,7 @@

Filter - - {% include "vulns/csv_upload_modal.html" %} - - {% include "vulns/csv_upload_explanation_modal.html" %} diff --git a/src/vr/vulns/web/findings.py b/src/vr/vulns/web/findings.py index 41a34307..8c9938c8 100644 --- a/src/vr/vulns/web/findings.py +++ b/src/vr/vulns/web/findings.py @@ -768,7 +768,7 @@ def finding(appid, id): else: finding_accuracy = 'N/A' referrer = request.referrer - if 'all_app_vulns_filtered/' in referrer: + if referrer and 'all_app_vulns_filtered/' in referrer: nav_bar = 'Application' else: nav_bar = 'Component' From b3d741cb7da844ed83cc6d73e33f44acb550097a Mon Sep 17 00:00:00 2001 From: bkaiserinfosec <49665796+bkaiserinfosec@users.noreply.github.com> Date: Sat, 30 Dec 2023 00:33:34 -0800 Subject: [PATCH 4/9] Feature/fix toxi (#445) * Update tox.ini * fix unit test failures * Update web_testing.py --- ci_cd/unit_tests/web_testing.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci_cd/unit_tests/web_testing.py b/ci_cd/unit_tests/web_testing.py index 126b4d45..fbe6e2f5 100644 --- a/ci_cd/unit_tests/web_testing.py +++ b/ci_cd/unit_tests/web_testing.py @@ -1632,7 +1632,9 @@ def test_edit_application_post(self): 'regulations': 1, } response = self._post_test_handler(route, data_dict) - assert response.status_code == 200 + match = _three_o_two_handler(response.headers, f"/edit_application/{app.ID}") + assert response.status_code == 302 + assert match def test_contacts_get(self): app = BusinessApplications.query.filter_by(ApplicationName=TEST_APP_NAME).first() From f62e0d15427b42649066bc7da6ac2120b17b24f5 Mon Sep 17 00:00:00 2001 From: bkaiserinfosec <49665796+bkaiserinfosec@users.noreply.github.com> Date: Sat, 30 Dec 2023 19:10:15 -0800 Subject: [PATCH 5/9] Feature/fix toxi (#447) * Update tox.ini * fix unit test failures * Update web_testing.py * Update Jenkinsfile --- Jenkinsfile | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 981681a1..b4e59984 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -299,21 +299,7 @@ pipeline { post { always { script { - def reportProcessor = new PipelineReportProcessor(this) - reportProcessor.processReport('pipeline_stage_report.json') - - def reportFile = 'pipeline_stage_report.json' - archiveArtifacts artifacts: reportFile, allowEmptyArchive: true - - def stageConfig = jslReadYamlConfig('post') - def recipientEmails = stageConfig?.recipientEmails - def recipientTeamsChannels = stageConfig?.recipientTeamsChannels - - jslSendMicrosoftTeamsPipelineReportMessage(recipientTeamsChannels) - jslSendMicrosoftTeamsMessage(recipientTeamsChannels) - jslSendPipelineStageReportEmail(recipientEmails) - jslSendSecurityReportEmail(recipientEmails) - jslSendPipelineReport() + jslPipelineReporter() } } } From 5c40b881a418ac78b4df7b8a94b5d22d1c61aea5 Mon Sep 17 00:00:00 2001 From: bkaiserinfosec <49665796+bkaiserinfosec@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:06:57 -0800 Subject: [PATCH 6/9] Feature/fix toxi (#449) * Update tox.ini * fix unit test failures * Update web_testing.py * Update Jenkinsfile * add dockerImg map for Container scans --- flask_endpoints.csv | 262 +++++++++++------------ src/vr/api/vulns/vulnerabilities.py | 24 ++- src/vr/orchestration/web/dockerimages.py | 2 + 3 files changed, 150 insertions(+), 138 deletions(-) diff --git a/flask_endpoints.csv b/flask_endpoints.csv index 7ce7e5bc..8d5bb91e 100644 --- a/flask_endpoints.csv +++ b/flask_endpoints.csv @@ -1,156 +1,156 @@ Endpoint,Methods -/create_client,GET -/api/documentation,GET -/forgotpw,"GET, POST" -/resetpw//,"GET, POST" -/forgotun,"GET, POST" -/displayun//,"GET, POST" /,GET -/login,"GET, POST" -,GET -/check_if_mfa,POST -/logout,GET -/logout,GET -/messages,"GET, POST" -/suppress_msg,POST -/onboarding,GET -/onboarding_suppress,GET -/register,GET -/register_user/,GET -/register_user_submit,POST -/register_submit,POST -/qrcode,GET -/settings,GET -/unauth_403,GET -/users,"GET, POST" -/add_user_role,POST -/remove_user_role,POST -/remove_user_appview_role,POST -/remove_user,POST +/add_app_integration/,GET +/add_application,"GET, POST" +/add_application_environment/,"GET, POST" +/add_benchmark_attachment,POST +/add_benchmark_note,POST +/add_cicd_pipeline/,"GET, POST" +/add_cicd_pipeline_stage/,GET +/add_contact/,POST +/add_integration,"GET, POST" +/add_issue_dispo,POST +/add_issue_note,POST /add_new_user,POST -/edit_profile,"GET, POST" -/update_mfa_status,POST -/display_mfa_qr,GET -/mobile_sync,GET -/profile,GET -/mobile_qrcode,GET -/mfa_qrcode,GET -/api/oauth/authorize,"GET, POST" -/api/oauth/token,POST -/api/oauth/revoke,POST -/api/openapi.yaml,GET -/api/onboard_new_application,POST +/add_service_ticket/,POST +/add_user_role,POST +/all_app_integrations/,GET +/all_app_vulns_filtered///,"GET, POST" +/all_app_vulns_filtered////csv,"GET, POST" +/all_app_vulns_filtered////export,"GET, POST" +/all_application_benchmarks/,"GET, POST" +/all_application_environments/,GET +/all_application_metrics,GET +/all_applications,"GET, POST" +/all_applications_filtered//,"GET, POST" +/all_cheatsheets,GET +/all_cicd_pipelines/,GET +/all_dockerimages,"GET, POST" +/all_git_repos,GET +/all_integrations,GET +/all_pipeline_jobs,"GET, POST" +/all_pipelines,GET +/all_regulations,GET +/all_service_tickets,"GET, POST" +/all_vulnerabilities,"GET, POST" +/all_vulnerabilities/csv,"GET, POST" +/all_vulnerabilities/export,"GET, POST" +/all_vulnerabilities_filtered//,"GET, POST" +/all_vulnerabilities_filtered///csv,"GET, POST" +/all_vulnerabilities_filtered///export,"GET, POST" +/analyze,POST +/api/add_application_profile/,POST /api/add_loc,POST +/api/add_sg_results,POST +/api/add_vulnerabilities,POST +/api/check_security_scan_status/,GET +/api/closeout_security_scan/,POST +/api/delete_vulnerabilities,POST +/api/documentation,GET +/api/edit_vulnerabilities,POST /api/get_application_profile/,GET -/api/add_application_profile/,POST -/api/jenkins_webhook,POST /api/jenkins_pipeline_reporter,POST +/api/jenkins_webhook,POST +/api/oauth/authorize,"GET, POST" +/api/oauth/revoke,POST +/api/oauth/token,POST +/api/onboard_new_application,POST +/api/openapi.yaml,GET /api/parallel_security_scan,POST -/api/check_security_scan_status/,GET -/api/closeout_security_scan/,POST -/api/add_sg_results,POST -/api/vulnerabilities,GET /api/search_vulnerabilities,POST -/api/add_vulnerabilities,POST -/api/edit_vulnerabilities,POST -/api/delete_vulnerabilities,POST -/all_application_benchmarks/,"GET, POST" -/benchmark_assessments/,"GET, POST" -/application_benchmarks//,"GET, POST" -/assessment_results//,GET -/add_benchmark_note,POST -/delete_benchmark_note,POST -/add_benchmark_attachment,POST -/delete_benchmark_attachment,POST -/download_benchmark_attachment/,GET -/submit_risk_profile,POST -/all_applications,"GET, POST" -/all_applications_filtered//,"GET, POST" +/api/vulnerabilities,GET +/applevel_metrics/,GET /application///,GET -/application//export,GET /application//csv,GET -/add_application,"GET, POST" -/application_issues/,"GET, POST" -/delete_application/,GET +/application//export,GET +/application_benchmarks//,"GET, POST" /application_endpoints/,"GET, POST" -/add_integration,"GET, POST" -/all_integrations,GET -/validate_integration,POST -/add_app_integration/,GET -/submit_app_integration/,POST -/all_app_integrations/,GET -/remove_app_integration,POST -/all_regulations,GET -/edit_application/,"GET, POST" -/add_application_environment/,"GET, POST" -/all_application_environments/,GET -/remove_application_environment,POST -/edit_application_environment//,"GET, POST" -/add_cicd_pipeline/,"GET, POST" -/all_cicd_pipelines/,GET -/remove_cicd_pipeline,POST -/contacts/,GET -/add_contact/,POST -/delete_contact,POST -/all_pipelines,GET -/pipeline_generator/,GET -/analyze,POST -/submit,POST -/all_dockerimages,"GET, POST" -/dockerimages/,"GET, POST" -/all_pipeline_jobs,"GET, POST" -/pipeline_jobs/,"GET, POST" -/add_cicd_pipeline_stage/,GET -/get_cicd_pipeline_stage_data,POST -/validate_cicd_pipeline_stage/,POST +/application_issues/,"GET, POST" +/application_KPIs/,"GET, POST" +/application_profile/,GET +/assessment_results//,GET +/benchmark_assessments/,"GET, POST" /branches/,"GET, POST" -/all_cheatsheets,GET /cheatsheets/,GET -/all_git_repos,GET +/check_if_mfa,POST +/component_KPIs/,"GET, POST" /components/,"GET, POST" -/all_service_tickets,"GET, POST" -/issue//,GET -/add_service_ticket/,POST -/sourcecode_files/,"GET, POST" -/threat_modeler/,"GET, POST" -/threat_assessments/,"GET, POST" -/threat_assessment//,GET +/contacts/,GET +/create_client,GET /dashboard,GET +/delete_application/,GET +/delete_benchmark_attachment,POST +/delete_benchmark_note,POST +/delete_contact,POST +/delete_issue_note,POST /devopsscorecard/,"GET, POST" +/display_mfa_qr,GET +/displayun//,"GET, POST" +/dockerimages/,"GET, POST" +/download_benchmark_attachment/,GET +/edit_application/,"GET, POST" +/edit_application_environment//,"GET, POST" +/edit_profile,"GET, POST" +/filtered_findings///,"GET, POST" +/filtered_findings////csv,"GET, POST" +/filtered_findings////export,"GET, POST" +/finding//,GET +/finding///request_review,GET +/forgotpw,"GET, POST" +/forgotun,"GET, POST" +/get_cicd_pipeline_stage_data,POST +/global_KPIs,"GET, POST" +/issue//,GET +/login,"GET, POST" +/logout,GET +/logout,GET +/messages,"GET, POST" +/metrics/,GET +/mfa_qrcode,GET +/mobile_qrcode,GET +/mobile_sync,GET +/on_demand_testing,POST +/onboarding,GET +/onboarding_suppress,GET /open_findings/,"GET, POST" -/open_findings//export,"GET, POST" /open_findings//csv,"GET, POST" +/open_findings//export,"GET, POST" /open_findings_for_scan//,"GET, POST" -/open_findings_for_scan///export,"GET, POST" /open_findings_for_scan///csv,"GET, POST" -/finding///request_review,GET -/finding//,GET -/filtered_findings///,"GET, POST" -/filtered_findings////export,"GET, POST" -/filtered_findings////csv,"GET, POST" -/add_issue_dispo,POST -/add_issue_note,POST -/delete_issue_note,POST -/metrics/,GET -/all_application_metrics,GET -/applevel_metrics/,GET -/application_KPIs/,"GET, POST" -/component_KPIs/,"GET, POST" -/global_KPIs,"GET, POST" +/open_findings_for_scan///export,"GET, POST" +/pipeline_generator/,GET +/pipeline_jobs/,"GET, POST" +/profile,GET +/qrcode,GET +/register,GET +/register_submit,POST +/register_user/,GET +/register_user_submit,POST +/remove_app_integration,POST +/remove_application_environment,POST +/remove_cicd_pipeline,POST +/remove_user,POST +/remove_user_appview_role,POST +/remove_user_role,POST +/resetpw//,"GET, POST" /securitygatescorecard/,GET /securitygatesettings,GET +/settings,GET +/sourcecode_files/,"GET, POST" +/submit,POST +/submit_app_integration/,POST +/submit_risk_profile,POST +/suppress_msg,POST +/threat_assessment//,GET +/threat_assessments/,"GET, POST" +/threat_modeler/,"GET, POST" +/unauth_403,GET +/update_mfa_status,POST /update_securitygatesettings,POST -/vulnerability_scans/,"GET, POST" -/on_demand_testing,POST -/application_profile/,GET +/users,"GET, POST" +/validate_cicd_pipeline_stage/,POST +/validate_integration,POST /visual_pipeline/,GET /visual_vulnerabilities/,GET -/all_vulnerabilities,"GET, POST" -/all_vulnerabilities/export,"GET, POST" -/all_vulnerabilities/csv,"GET, POST" -/all_vulnerabilities_filtered//,"GET, POST" -/all_vulnerabilities_filtered///export,"GET, POST" -/all_vulnerabilities_filtered///csv,"GET, POST" -/all_app_vulns_filtered///,"GET, POST" -/all_app_vulns_filtered////export,"GET, POST" -/all_app_vulns_filtered////csv,"GET, POST" +/vulnerability_scans/,"GET, POST" +,GET diff --git a/src/vr/api/vulns/vulnerabilities.py b/src/vr/api/vulns/vulnerabilities.py index abb71663..7f0c000e 100644 --- a/src/vr/api/vulns/vulnerabilities.py +++ b/src/vr/api/vulns/vulnerabilities.py @@ -22,6 +22,7 @@ ERROR_RESP = "Error: Invalid API Request" + @api.route("/api/vulnerabilities") @require_oauth('read:vulnerabilities') def get_vulnerabilities(): @@ -93,12 +94,21 @@ def update_vulnerabilities_status(app_cmdb_id, scan_id, req_raw): if i.ID not in scans_to_check: scans_to_check.append(i.ID) scans_to_check = sorted(scans_to_check, reverse=True) - - previous_vulns = Vulnerabilities\ - .query\ - .join(VulnerabilityScans, VulnerabilityScans.ID==Vulnerabilities.ScanId)\ - .filter(text(f"(Vulnerabilities.Status NOT LIKE 'Closed-%' OR Vulnerabilities.Status='Closed-Mitigated') AND (Vulnerabilities.ApplicationId='{app_cmdb_id}') AND (Vulnerabilities.SourceType='{scan_type.split('CI/CD-')[1]}') AND (Vulnerabilities.InitialScanId!='{scan_id}')"))\ - .all() + if req_raw['scanType'] == 'Container': + if 'dockerImg' in req_raw: + previous_vulns = Vulnerabilities \ + .query \ + .join(VulnerabilityScans, VulnerabilityScans.ID == Vulnerabilities.ScanId) \ + .join(DockerImages, DockerImages.ID == Vulnerabilities.DockerImageId) \ + .filter(text( + f"(Vulnerabilities.Status NOT LIKE 'Closed-%' OR Vulnerabilities.Status='Closed-Mitigated') AND (Vulnerabilities.ApplicationId='{app_cmdb_id}') AND (Vulnerabilities.SourceType='{scan_type.split('CI/CD-')[1]}') AND (Vulnerabilities.InitialScanId!='{scan_id}') AND (DockerImages.ImageName=='{req_raw['dockerImg']}')")) \ + .all() + else: + previous_vulns = Vulnerabilities\ + .query\ + .join(VulnerabilityScans, VulnerabilityScans.ID==Vulnerabilities.ScanId)\ + .filter(text(f"(Vulnerabilities.Status NOT LIKE 'Closed-%' OR Vulnerabilities.Status='Closed-Mitigated') AND (Vulnerabilities.ApplicationId='{app_cmdb_id}') AND (Vulnerabilities.SourceType='{scan_type.split('CI/CD-')[1]}') AND (Vulnerabilities.InitialScanId!='{scan_id}')"))\ + .all() closed_cnt = 0 new_vulns = req_raw['findings'] @@ -107,7 +117,7 @@ def update_vulnerabilities_status(app_cmdb_id, scan_id, req_raw): prev_id_check = i.VulnerabilityID for j in new_vulns: new_id_check = j['b_VulnerabilityID'] if 'b_VulnerabilityID' in j else None - if prev_id_check == new_id_check: + if (prev_id_check == new_id_check) and (i.SourceType == j['SourceType']): found = True break if not found and i.Status != "Closed-Mitigated": diff --git a/src/vr/orchestration/web/dockerimages.py b/src/vr/orchestration/web/dockerimages.py index c38f76df..4afb87ce 100644 --- a/src/vr/orchestration/web/dockerimages.py +++ b/src/vr/orchestration/web/dockerimages.py @@ -18,6 +18,7 @@ LOGIN_URL = "admin.login" UNAUTH_URL = "403.html" SERVER_ERR_URL = "500.html" +VULN_STATUS_IS_NOT_CLOSED = "Vulnerabilities.Status NOT LIKE 'Closed-%' AND Vulnerabilities.Status NOT LIKE 'Open-RiskAccepted-%'" @orchestration.route("/all_dockerimages", methods=['GET', 'POST']) @@ -128,6 +129,7 @@ def dockerimages(appid): DockerImages.ID, DockerImages.AddDate, DockerImages.ImageName, DockerImages.ImageTag, DockerImages.ImageId, DockerImages.AppIdList, func.count(Vulnerabilities.VulnerabilityID).label('total_vulnerabilities') ).join(Vulnerabilities, Vulnerabilities.DockerImageId == DockerImages.ID) \ + .filter(text(VULN_STATUS_IS_NOT_CLOSED)) \ .filter(text(f"DockerImages.AppIdList LIKE '%{appid},%'")) \ .group_by(DockerImages.ID) \ .order_by(text(orderby)) \ From b8d2775262ae6ac137916bb244a635fa18ef7bb0 Mon Sep 17 00:00:00 2001 From: bkaiserinfosec <49665796+bkaiserinfosec@users.noreply.github.com> Date: Wed, 3 Jan 2024 20:36:17 -0800 Subject: [PATCH 7/9] Update Jenkinsfile (#452) --- Jenkinsfile | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index b4e59984..bb221109 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,8 +3,12 @@ pipeline { - agent any - + agent { + docker { + image 'securityuniversal/jenkins-pipeline-agent:latest' + args '--group-add 999' + } + } stages { stage('Initialize Config') { @@ -45,6 +49,11 @@ pipeline { } stage('Unit Testing') { + agent { + docker { + image 'securityuniversal/jenkins-python-agent:latest' + } + } when { expression { def config = jslReadYamlConfig('unitTesting') @@ -64,6 +73,11 @@ pipeline { } stage('Secret Scanning') { + agent { + docker { + image 'securityuniversal/jenkins-secret-agent:latest' + } + } when { expression { def config = jslReadYamlConfig('secretScanning') @@ -83,6 +97,11 @@ pipeline { } stage('Software Composition Analysis') { + agent { + docker { + image 'securityuniversal/jenkins-codetesting-agent:latest' + } + } when { expression { def config = jslReadYamlConfig('sca') @@ -106,6 +125,11 @@ pipeline { } stage('Static Application Security Testing') { + agent { + docker { + image 'securityuniversal/jenkins-codetesting-agent:latest' + } + } when { expression { def config = jslReadYamlConfig('sast') @@ -129,6 +153,12 @@ pipeline { } stage('Infrastructure-as-Code Security Testing') { + agent { + docker { + image 'securityuniversal/jenkins-iac-agent:latest' + args '--group-add 999' + } + } when { expression { def config = jslReadYamlConfig('iac') @@ -148,6 +178,12 @@ pipeline { } stage('Build Docker Service') { + agent { + docker { + image 'securityuniversal/jenkins-iac-agent:latest' + args '--group-add 999' + } + } when { expression { def config = jslReadYamlConfig('buildDocker') @@ -171,6 +207,12 @@ pipeline { } stage('Docker Container Scanning') { + agent { + docker { + image 'securityuniversal/jenkins-iac-agent:latest' + args '--group-add 999' + } + } when { expression { def config = jslReadYamlConfig('containerScan') @@ -195,6 +237,11 @@ pipeline { } stage('Release to Test') { + agent { + docker { + image 'securityuniversal/jenkins-deploy-agent:latest' + } + } when { expression { def config = jslReadYamlConfig('releaseToTest') @@ -266,6 +313,11 @@ pipeline { ////////// Deploy to Production ////////// stage('Deploy') { + agent { + docker { + image 'securityuniversal/jenkins-deploy-agent:latest' + } + } when { anyOf { // Condition for the PROD branch From 9e4030de31d47164737f216f7578d45fe04d7eb9 Mon Sep 17 00:00:00 2001 From: bkaiserinfosec <49665796+bkaiserinfosec@users.noreply.github.com> Date: Mon, 8 Jan 2024 07:53:01 -0800 Subject: [PATCH 8/9] Feature/jenkinsfile updates (#453) * Update Jenkinsfile * added scores and grades api endpoint --- src/vr/admin/routes/settings.py | 73 +++++- src/vr/api/__init__.py | 1 + src/vr/api/vulns/dora_extended.py | 378 +++++++++++++++++++++++++++ src/vr/db_models/setup.py | 6 +- src/vr/db_models/setup_2.py | 2 +- src/vr/templates/admin/settings.html | 97 ++++++- 6 files changed, 553 insertions(+), 4 deletions(-) create mode 100644 src/vr/api/vulns/dora_extended.py diff --git a/src/vr/admin/routes/settings.py b/src/vr/admin/routes/settings.py index ea761033..c366ce43 100644 --- a/src/vr/admin/routes/settings.py +++ b/src/vr/admin/routes/settings.py @@ -1,5 +1,7 @@ from flask import session, redirect, url_for, render_template from flask_login import login_required +from vr import db, app +import os # Start of Entity-specific Imports from vr.admin import admin from vr.admin.functions import _auth_user, check_menu_tour_init @@ -9,7 +11,7 @@ JENKINS_HOST, JENKINS_KEY, JENKINS_PROJECT, JENKINS_STAGING_PROJECT, JENKINS_TOKEN, SMTP_ADMIN_EMAIL, \ SMTP_HOST, SMTP_PASSWORD, SMTP_USER, SNOW_CLIENT_ID, SNOW_CLIENT_SECRET, SNOW_INSTANCE_NAME, SNOW_PASSWORD, \ SNOW_USERNAME, VERSION - +from flask_sqlalchemy import SQLAlchemy NAV = { 'CAT': { "name": "Settings", "url": "admin.admin_dashboard"} @@ -63,3 +65,72 @@ def settings(): } return render_template('admin/settings.html', user_roles=user_roles, NAV=NAV, user=user, settings=current_settings) + +@admin.route('/dangerous/delete_all', methods=['POST']) +def delete_all_data(): + NAV['curpage'] = {"name": "Settings"} + user, status, user_roles = _auth_user(session, 'No Role') + if status == 401: + return redirect(url_for('admin.login')) + elif status == 403: + return render_template('403.html', user=user, nav_cat={}, nav_subcat='', \ + nav_subsubcat='', nav_curpage={"name": "Unauthorized"}) + + try: + if ENV == 'test': + # Ensure all connections to the database are closed + db.session.close() + db.engine.dispose() + + # Path to the SQLite database file + db_path = "instance/" + app.config['SQLALCHEMY_DATABASE_URI'].replace('sqlite:///', '') + + # Delete the database file + if os.path.exists(db_path): + os.remove(db_path) + else: + print("The file does not exist") + else: + # Reflect the current database schema + db.reflect() + + # Retrieve all table names + table_names = db.engine.table_names() + + # Drop each table + for table_name in reversed(table_names): + db.engine.execute(f'DROP TABLE IF EXISTS {table_name} CASCADE') + + # Recreate all tables based on the current models + from vr.functions.mysql_db import connect_to_db + cur, db_obj = connect_to_db() + sql = '''CREATE TABLE "User" (id INTEGER NOT NULL, is_active BOOLEAN DEFAULT '1' NOT NULL, is_admin BOOLEAN DEFAULT '0' NOT NULL, is_security BOOLEAN DEFAULT '0' NOT NULL, username VARCHAR(100), password VARCHAR(255), auth_type VARCHAR(20), mfa_enabled BOOLEAN DEFAULT '0' NOT NULL, otp_secret VARCHAR(16), email VARCHAR(255) NOT NULL, email_confirmed_at DATETIME, first_name VARCHAR(100) DEFAULT '' NOT NULL, last_name VARCHAR(100) DEFAULT '' NOT NULL, jobtitle VARCHAR(100), dept VARCHAR(100), user_type VARCHAR(100), avatar_path VARCHAR(100), email_updates VARCHAR(1), app_updates VARCHAR(1), text_updates VARCHAR(1), registration_date DATETIME, loc_zipcode VARCHAR(20), loc_city VARCHAR(100), loc_state VARCHAR(50), about_me VARCHAR(2000), web_tz VARCHAR(100), phone_no VARCHAR(40), support_id VARCHAR(50), support_key VARCHAR(50), support_contact_id INTEGER, auth_token VARCHAR(300), onboarding_confirmed VARCHAR(1), PRIMARY KEY (id), UNIQUE (email))''' + cur.execute(sql) + db_obj.commit() + sql = 'CREATE TABLE "AppConfig" (id INTEGER NOT NULL, first_access BOOLEAN NOT NULL,PRIMARY KEY (id))' + cur.execute(sql) + db_obj.commit() + sql = 'CREATE TABLE "EntityPermissions" ("ID" INTEGER NOT NULL, "AddDate" DATETIME NOT NULL, "UserID" INTEGER, "EntityType" VARCHAR(100), "EntityID" VARCHAR(100), PRIMARY KEY ("ID"), FOREIGN KEY("UserID") REFERENCES "User" (id) ON DELETE CASCADE)' + cur.execute(sql) + db_obj.commit() + sql = 'CREATE TABLE "SourceCodeFile" ("ID" INTEGER NOT NULL, "AddDate" DATETIME, "GitRepoId" INTEGER, "FileName" VARCHAR(300), "FileLocation" VARCHAR(300), "FileType" VARCHAR(300), PRIMARY KEY ("ID"))' + cur.execute(sql) + db_obj.commit() + sql = 'CREATE TABLE "TmControls" ("ID" INTEGER NOT NULL, "AddDate" DATETIME NOT NULL, "Control" TEXT, "Type" VARCHAR(8), "Description" TEXT, "Lambda" VARCHAR(1), "Process" VARCHAR(1), "Server" VARCHAR(1), "Dataflow" VARCHAR(1), "Datastore" VARCHAR(1), "ExternalEntity" VARCHAR(1), PRIMARY KEY ("ID"))' + cur.execute(sql) + db_obj.commit() + sql = 'CREATE TABLE "UserRoleAssignments" (id INTEGER NOT NULL, user_id INTEGER, role_id INTEGER, PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES "User" (id) ON DELETE CASCADE, FOREIGN KEY(role_id) REFERENCES "UserRoles" (id) ON DELETE CASCADE)' + cur.execute(sql) + db_obj.commit() + sql = 'CREATE TABLE "UserRoles" (id INTEGER NOT NULL, name VARCHAR(50), description VARCHAR(200), PRIMARY KEY (id), UNIQUE (name))' + cur.execute(sql) + db_obj.commit() + db_obj.close() + + + return "All tables dropped successfully", 200 + except Exception as e: + # Log the exception for debugging purposes + print(e) + db.session.rollback() + return "Error occurred during table deletion", 500 diff --git a/src/vr/api/__init__.py b/src/vr/api/__init__.py index 9c75fdb9..b6512480 100644 --- a/src/vr/api/__init__.py +++ b/src/vr/api/__init__.py @@ -10,5 +10,6 @@ from vr.api.vulns import vulnerabilities from vr.api.vulns import jenkins_webhook from vr.api.vulns import application_profiler +from vr.api.vulns import dora_extended from vr.api.integrations import servicenow diff --git a/src/vr/api/vulns/dora_extended.py b/src/vr/api/vulns/dora_extended.py new file mode 100644 index 00000000..1759a780 --- /dev/null +++ b/src/vr/api/vulns/dora_extended.py @@ -0,0 +1,378 @@ +import datetime +from vr import db, app +from flask import jsonify, request +from sqlalchemy import desc, text +from vr.api import api +from vr.admin.functions import db_connection_handler +from vr.admin.oauth2 import require_oauth +from authlib.integrations.flask_oauth2 import current_token +from vr.admin.auth_functions import verify_api_key, get_token_auth_header +from vr.functions.routing_functions import check_entity_permissions +from vr.assets.model.applicationprofiles import ApplicationProfiles, ApplicationProfilesSchema +from vr.assets.model.businessapplications import BusinessApplications +from vr.vulns.model.vulnerabilities import Vulnerabilities, MakeVulnerabilitiesSchema, VulnerabilitiesSchema +from vr.vulns.model.vulnerabilityscans import VulnerabilityScans +from vr.assessments.model.assessmentbenchmarkassessments import AssessmentBenchmarkAssessments +from vr.assessments.model.assessmentbenchmarkrules import AssessmentBenchmarkRules +from vr.assessments.model.assessmentbenchmarkruleaudits import AssessmentBenchmarkRuleAudits + + +ERROR_RESP = "Error: Invalid API Request" + +@api.route("/api/get_dora_grade/", methods=['POST', 'GET']) +@require_oauth('read:vulnerabilities') +def get_dora_grade(app_id): + token = current_token + auth, user_id, is_admin = verify_api_key(token) + response = jsonify({'response': ERROR_RESP}), 403 + if auth == 'valid': + permitted = check_entity_permissions(is_admin) + if permitted: + if request.method == 'POST': + form = request.get_json() + time_start = datetime.datetime.strptime(form['timeStart'], '%Y-%m-%d %H:%M:%S') + time_end = datetime.datetime.strptime(form['timeEnd'], '%Y-%m-%d %H:%M:%S') + else: + time_start = datetime.datetime.strptime('1990-01-01 12:00:00', '%Y-%m-%d %H:%M:%S') + time_end = datetime.datetime.utcnow() + time_dict = {'timeStart': time_start, 'timeEnd':time_end} + total_score = 0 + applicable_metrics = 0 + # calculate vulnerability resolution time + vr_score, total_score, applicable_metrics = calculate_vulnerability_resolution_time(time_dict, app_id, total_score, applicable_metrics) + # calculate frequency of security scans + scan_score, total_score, applicable_metrics = calculate_frequency_of_security_scans(time_dict, app_id, total_score, applicable_metrics) + # calculate mean time to detect (MTTD) security issues + all_vulns, detection_score, total_score, applicable_metrics = calculate_mean_time_to_detect(time_dict, app_id, total_score, applicable_metrics) + # calculate percentage of critical/high risk issues addressed + high_severity_score, total_score, applicable_metrics = calculate_percentage_of_high_risk_issues_addressed(all_vulns, total_score, applicable_metrics) + # calculate compliance with security standards + compliance_score, total_score, applicable_metrics = calculate_compliance_with_security_standards(time_dict, app_id, total_score, applicable_metrics) + # calculate risk profile adherence + risk_score, total_score, applicable_metrics = calculate_risk_profile_adherence(app_id, total_score, applicable_metrics) + + # calculate grade and final score + max_score, percent_score, grade = _calculate_grade(applicable_metrics, total_score) + + score = { + 'grade': grade, + 'total_score': total_score, + 'percent_score': (percent_score * 100), + 'max_score': max_score, + 'vulnerability_remediation_score': vr_score, + 'scan_frequency_score': scan_score, + 'time_to_detect_issues_score': detection_score, + 'critical_and_high_risk_mitigation_score': high_severity_score, + 'compliance_with_security_standards_score': compliance_score, + 'risk_profile_adherence_score': risk_score + } + response = jsonify(score), 200 + return response + + +def calculate_vulnerability_resolution_time(time_dict, app_id, total_score, applicable_metrics): + if '--' in app_id: + app_name = app_id.split('--')[0] + app_component = app_id.split('--')[1] + resolved_vulns = Vulnerabilities.query \ + .join(BusinessApplications, BusinessApplications.ID == Vulnerabilities.ApplicationId) \ + .filter(BusinessApplications.ApplicationName == app_name) \ + .filter(BusinessApplications.ApplicationAcronym == app_component) \ + .filter(Vulnerabilities.Status == 'Closed-Mitigated') \ + .filter(Vulnerabilities.MitigationDate >= time_dict['timeStart']) \ + .filter(Vulnerabilities.MitigationDate <= time_dict['timeEnd']) \ + .all() + else: + resolved_vulns = Vulnerabilities.query \ + .join(BusinessApplications, BusinessApplications.ID == Vulnerabilities.ApplicationId) \ + .filter(BusinessApplications.ApplicationName == app_id) \ + .filter(Vulnerabilities.Status == 'Closed-Mitigated') \ + .filter(Vulnerabilities.MitigationDate >= time_dict['timeStart']) \ + .filter(Vulnerabilities.MitigationDate <= time_dict['timeEnd']) \ + .all() + total_time = 0 + for i in resolved_vulns: + add_date = i.AddDate + resolve_date = i.MitigationDate + total = resolve_date - add_date + total_time += total.seconds / 60 / 60 + ave_resolved_hours = total_time / len(resolved_vulns) + vr_score = _calculate_dora_metric_score('resolved_vulns', ave_resolved_hours) + total_score += vr_score + applicable_metrics += 1 + return vr_score, total_score, applicable_metrics + + +def calculate_frequency_of_security_scans(time_dict, app_id, total_score, applicable_metrics): + if '--' in app_id: + app_name = app_id.split('--')[0] + app_component = app_id.split('--')[1] + all_scans = VulnerabilityScans.query \ + .join(BusinessApplications, BusinessApplications.ID == VulnerabilityScans.ApplicationId) \ + .filter(BusinessApplications.ApplicationName == app_name) \ + .filter(BusinessApplications.ApplicationAcronym == app_component) \ + .filter(VulnerabilityScans.ScanStartDate >= time_dict['timeStart']) \ + .filter(VulnerabilityScans.ScanStartDate <= time_dict['timeEnd']) \ + .all() + else: + all_scans = VulnerabilityScans.query \ + .join(BusinessApplications, BusinessApplications.ID == VulnerabilityScans.ApplicationId) \ + .filter(BusinessApplications.ApplicationName == app_id) \ + .filter(VulnerabilityScans.ScanStartDate >= time_dict['timeStart']) \ + .filter(VulnerabilityScans.ScanStartDate <= time_dict['timeEnd']) \ + .all() + if '--' in app_id: + app_name = app_id.split('--')[0] + app_component = app_id.split('--')[1] + reg_date = BusinessApplications\ + .query\ + .with_entities(BusinessApplications.RegDate)\ + .filter(BusinessApplications.ApplicationName == app_name) \ + .filter(BusinessApplications.ApplicationAcronym == app_component) \ + .first()[0] + else: + reg_date = BusinessApplications.query.with_entities(BusinessApplications.RegDate).filter( + BusinessApplications.ApplicationName == app_id).first()[0] + now = datetime.datetime.utcnow() + total_duration = (now - reg_date).days + unique_scan_dates = set() + for scan in all_scans: + unique_scan_dates.add(scan.ScanStartDate.date()) + unique_scan_count = len(unique_scan_dates) + if total_duration > 0: + frequency_of_scans = unique_scan_count / total_duration + else: + frequency_of_scans = 0 + scan_score = _calculate_dora_metric_score('scan_frequency', frequency_of_scans) + total_score += scan_score + applicable_metrics += 1 + return scan_score, total_score, applicable_metrics + + +def calculate_mean_time_to_detect(time_dict, app_id, total_score, applicable_metrics): + if '--' in app_id: + app_name = app_id.split('--')[0] + app_component = app_id.split('--')[1] + all_vulns = Vulnerabilities.query \ + .join(BusinessApplications, BusinessApplications.ID == Vulnerabilities.ApplicationId) \ + .filter(BusinessApplications.ApplicationName == app_name) \ + .filter(BusinessApplications.ApplicationAcronym == app_component) \ + .filter(Vulnerabilities.AddDate >= time_dict['timeStart']) \ + .filter(Vulnerabilities.AddDate <= time_dict['timeEnd']) \ + .all() + else: + all_vulns = Vulnerabilities.query \ + .join(BusinessApplications, BusinessApplications.ID == Vulnerabilities.ApplicationId) \ + .filter(BusinessApplications.ApplicationName == app_id) \ + .filter(Vulnerabilities.AddDate >= time_dict['timeStart']) \ + .filter(Vulnerabilities.AddDate <= time_dict['timeEnd']) \ + .all() + total_duration = 0 + for i in all_vulns: + detection_date = i.ReleaseDate + identification_date = i.AddDate + detection_time = detection_date - identification_date + total_duration += detection_time.seconds + if total_duration > 0: + detection_time_ave = total_duration / len(all_vulns) + else: + detection_time_ave = 0 + detection_score = _calculate_dora_metric_score('detection_time', detection_time_ave) + total_score += detection_score + applicable_metrics += 1 + return all_vulns, detection_score, total_score, applicable_metrics + + +def calculate_percentage_of_high_risk_issues_addressed(all_vulns, total_score, applicable_metrics): + total_issues = 0 + addressed_issues = 0 + for i in all_vulns: + if i.Severity == 'Critical' or i.Severity == 'High': + total_issues += 1 + if i.Status == 'Closed-Mitigated': + addressed_issues += 1 + if addressed_issues: + percent_addressed = (addressed_issues / total_issues) * 100 + else: + percent_addressed = 0 + high_severity_score = _calculate_dora_metric_score('high_severity', percent_addressed) + total_score += high_severity_score + applicable_metrics += 1 + return high_severity_score, total_score, applicable_metrics + + +def calculate_compliance_with_security_standards(time_dict, app_id, total_score, applicable_metrics): + if '--' in app_id: + app_name = app_id.split('--')[0] + app_component = app_id.split('--')[1] + latest_assessments = AssessmentBenchmarkAssessments.query \ + .join(BusinessApplications, BusinessApplications.ID == AssessmentBenchmarkAssessments.ApplicationID) \ + .filter(BusinessApplications.ApplicationName == app_name) \ + .filter(BusinessApplications.ApplicationAcronym == app_component) \ + .order_by(AssessmentBenchmarkAssessments.BenchmarkID, + desc(AssessmentBenchmarkAssessments.AddDate)) \ + .distinct(AssessmentBenchmarkAssessments.BenchmarkID) \ + .filter(AssessmentBenchmarkAssessments.AddDate >= time_dict['timeStart']) \ + .filter(AssessmentBenchmarkAssessments.AddDate <= time_dict['timeEnd']) \ + .all() + else: + latest_assessments = AssessmentBenchmarkAssessments.query \ + .join(BusinessApplications, BusinessApplications.ID == AssessmentBenchmarkAssessments.ApplicationID) \ + .filter(BusinessApplications.ApplicationName == app_id) \ + .order_by(AssessmentBenchmarkAssessments.BenchmarkID, + desc(AssessmentBenchmarkAssessments.AddDate)) \ + .distinct(AssessmentBenchmarkAssessments.BenchmarkID) \ + .filter(AssessmentBenchmarkAssessments.AddDate >= time_dict['timeStart']) \ + .filter(AssessmentBenchmarkAssessments.AddDate <= time_dict['timeEnd']) \ + .all() + all_rules = 0 + all_passed = 0 + for i in latest_assessments: + assessment_rules = AssessmentBenchmarkRules \ + .query \ + .filter(AssessmentBenchmarkRules.BenchmarkID == i.BenchmarkID) \ + .filter(text('AssessmentBenchmarkRules.ImplementationLevels LIKE "%1%"')) \ + .all() + assessment_rules_passed = AssessmentBenchmarkRuleAudits \ + .query \ + .filter(AssessmentBenchmarkRuleAudits.AssessmentID == i.ID) \ + .filter(text('AssessmentBenchmarkRuleAudits.PassingLevels LIKE "%1%"')) \ + .all() + all_rules += len(assessment_rules) + all_passed += len(assessment_rules_passed) + if all_passed > 0: + benchmark_pass_percent = all_passed / all_rules + else: + benchmark_pass_percent = 0 + compliance_score = _calculate_dora_metric_score('compliance', benchmark_pass_percent) + total_score += compliance_score + applicable_metrics += 1 + return compliance_score, total_score, applicable_metrics + + +def calculate_risk_profile_adherence(app_id, total_score, applicable_metrics): + if '--' in app_id: + app_name = app_id.split('--')[0] + app_component = app_id.split('--')[1] + score_query = BusinessApplications \ + .query \ + .with_entities(BusinessApplications.Criticality) \ + .filter(BusinessApplications.ApplicationName == app_name) \ + .filter(BusinessApplications.ApplicationAcronym == app_component) \ + .filter(text('BusinessApplications.Criticality LIKE "%(%"')) \ + .first() + else: + score_query = BusinessApplications \ + .query \ + .with_entities(BusinessApplications.Criticality) \ + .filter(BusinessApplications.ApplicationName == app_id) \ + .filter(text('BusinessApplications.Criticality LIKE "%(%"')) \ + .first() + if score_query is not None: + score = score_query.Criticality.split()[0] + else: + score = 0 + risk_score = _calculate_dora_metric_score('risk', score) + total_score += risk_score + applicable_metrics += 1 + return risk_score, total_score, applicable_metrics + + +def _calculate_grade(applicable_metrics, total_score): + max_score = applicable_metrics * 5 + percent_score = total_score / max_score + if percent_score >= 0.97: + grade = 'A+' + elif percent_score >= 0.93 and percent_score < 0.97: + grade = 'A' + elif percent_score >= 0.90 and percent_score < 0.93: + grade = 'A-' + elif percent_score >= 0.87 and percent_score < 0.90: + grade = 'B+' + elif percent_score >= 0.83 and percent_score < 0.87: + grade = 'B' + elif percent_score >= 0.80 and percent_score < 0.83: + grade = 'B-' + elif percent_score >= 0.77 and percent_score < 0.80: + grade = 'C+' + elif percent_score >= 0.73 and percent_score < 0.77: + grade = 'C' + elif percent_score >= 0.70 and percent_score < 0.73: + grade = 'C-' + elif percent_score >= 0.67 and percent_score < 0.70: + grade = 'D+' + elif percent_score >= 0.63 and percent_score < 0.67: + grade = 'D' + elif percent_score >= 0.60 and percent_score < 0.63: + grade = 'D-' + else: + grade = 'F' + return max_score, percent_score, grade + +def _calculate_dora_metric_score(metric_type, metric_data): + if metric_type == 'resolved_vulns': + if metric_data < 24: + points = 5 + elif metric_data >= 24 and metric_data < 72: + points = 4 + elif metric_data >= 72 and metric_data < 168: + points = 3 + elif metric_data: + points = 1 + else: + points = 0 + elif metric_type == 'scan_frequency': + if metric_data == 1: # means the scans occurred daily + points = 5 + elif metric_data > 0.1428571428571429: # means scans occurred at least weekly + points = 4 + elif metric_data > 0.07142857142857143: # means scans occurred at least monthly + points = 3 + elif metric_data: # means scans occurred less than monthly + points = 1 + else: # means no scans have be conducted + points = 0 + elif metric_type == 'detection_time': + if metric_data < 3600: # means less than 1 hour + points = 5 + elif metric_data < 21600: # means less than 6 hours + points = 4 + elif metric_data < 86400: # means less than 24 hours + points = 3 + elif metric_data: # means more than 24 hours + points = 1 + else: # means no scans have be conducted + points = 0 + elif metric_type == 'high_severity': + if metric_data > 95: + points = 5 + elif metric_data >= 80 and metric_data <= 95: + points = 4 + elif metric_data >= 60 and metric_data < 80: + points = 3 + elif metric_data: + points = 1 + else: # means no scans have be conducted + points = 0 + elif metric_type == 'compliance': + if metric_data > 95: + points = 5 + elif metric_data >= 80 and metric_data <= 95: + points = 4 + elif metric_data >= 60 and metric_data < 80: + points = 3 + elif metric_data: + points = 1 + else: # means no scans have be conducted + points = 0 + elif metric_type == 'risk': + if metric_data == 'low': + points = 5 + elif metric_data == 'medium': + points = 4 + elif metric_data == 'high': + points = 3 + else: # means no scans have be conducted + points = 0 + return points + diff --git a/src/vr/db_models/setup.py b/src/vr/db_models/setup.py index 31160bba..67adfc3b 100644 --- a/src/vr/db_models/setup.py +++ b/src/vr/db_models/setup.py @@ -34,7 +34,7 @@ def _init_db(db=None, app=None): class User(UserMixin, db.Model): __tablename__ = 'User' - extend_existing = True + __table_args__ = {'extend_existing': True} id = db.Column(db.Integer, primary_key=True) is_active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1') is_admin = db.Column('is_admin', db.Boolean(), nullable=False, server_default='0') @@ -78,6 +78,7 @@ def __repr__(self): # Define the Role data-model class UserRoles(db.Model): __tablename__ = 'UserRoles' + __table_args__ = {'extend_existing': True} id = db.Column(db.Integer(), primary_key=True) name = db.Column(db.String(50), unique=True) description = db.Column(db.String(200)) @@ -87,6 +88,7 @@ class UserRoles(db.Model): # Define the UserRoles association table class UserRoleAssignments(db.Model): __tablename__ = 'UserRoleAssignments' + __table_args__ = {'extend_existing': True} id = db.Column(db.Integer(), primary_key=True) user_id = db.Column(db.Integer(), db.ForeignKey(USER_ID, ondelete='CASCADE')) role_id = db.Column(db.Integer(), db.ForeignKey('UserRoles.id', ondelete='CASCADE')) @@ -99,6 +101,7 @@ class UserRoleAssignments(db.Model): class EntityPermissions(db.Model): __tablename__ = 'EntityPermissions' + __table_args__ = {'extend_existing': True} ID = db.Column(db.Integer, primary_key=True) AddDate = db.Column(db.DateTime, index=True, default=datetime.utcnow, nullable=False) UserID = db.Column(db.Integer, db.ForeignKey(USER_ID, ondelete='CASCADE')) @@ -111,6 +114,7 @@ class EntityPermissions(db.Model): class AppConfig(db.Model): __tablename__ = 'AppConfig' + __table_args__ = {'extend_existing': True} id = db.Column(db.Integer, primary_key=True) first_access = db.Column(db.Boolean, nullable=False, default=True) diff --git a/src/vr/db_models/setup_2.py b/src/vr/db_models/setup_2.py index 605d54ab..d47ba210 100644 --- a/src/vr/db_models/setup_2.py +++ b/src/vr/db_models/setup_2.py @@ -47,7 +47,7 @@ class Technologies(db.Model): UniqueIDType = db.Column(db.String(20)) Description = db.Column(db.String(200)) RegComplete = db.Column(db.String(1)) - RegDate = db.Column(db.DateTime, index=True, default=datetime.utcnow, nullable=False) + RegDate = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) Technologies() diff --git a/src/vr/templates/admin/settings.html b/src/vr/templates/admin/settings.html index b1d00a7a..06a4bee5 100644 --- a/src/vr/templates/admin/settings.html +++ b/src/vr/templates/admin/settings.html @@ -1,7 +1,48 @@ {% extends 'base_auth.html' %} {% block app_content %} + + + + {{ super() }} @@ -14,7 +55,61 @@

Application Settings

From fb5d50f2c373191b9f6288c61aa5ec222f4b02d5 Mon Sep 17 00:00:00 2001 From: bkaiserinfosec <49665796+bkaiserinfosec@users.noreply.github.com> Date: Mon, 8 Jan 2024 08:11:48 -0800 Subject: [PATCH 9/9] Feature/jenkinsfile updates (#455) * Update Jenkinsfile * added scores and grades api endpoint * Update Jenkinsfile --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index bb221109..7d21f73e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -51,7 +51,7 @@ pipeline { stage('Unit Testing') { agent { docker { - image 'securityuniversal/jenkins-python-agent:latest' + image 'securityuniversal/jenkins:latest' } } when {