Skip to content

Commit

Permalink
add file hosting for feedback artifacts
Browse files Browse the repository at this point in the history
  • Loading branch information
nleach999 committed Sep 11, 2024
1 parent 02b3362 commit 1023f55
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 21 deletions.
Binary file added artifacts/checkmarx.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 11 additions & 7 deletions config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ def retrieve_services_by_route(clone_urls : str, scm_config_key : str) -> Tuple[
CxOneFlowConfig.log().error(f"No route matched for {clone_urls}")
raise RouteNotFoundException(clone_urls)


@staticmethod
def get_base_url():
return CxOneFlowConfig.__server_base_url

@staticmethod
def bootstrap(config_file_path = "./config.yaml"):

Expand All @@ -143,10 +148,8 @@ def bootstrap(config_file_path = "./config.yaml"):
with open(config_file_path, "rt") as cfg:
CxOneFlowConfig.__raw = yaml.safe_load(cfg)

if not "secret-root-path" in CxOneFlowConfig.__raw.keys():
raise ConfigurationException.missing_key_path("/secret-root-path")
else:
CxOneFlowConfig.__secret_root = CxOneFlowConfig.__raw['secret-root-path']
CxOneFlowConfig.__server_base_url = CxOneFlowConfig.__get_value_for_key_or_fail("", "server-base-url", CxOneFlowConfig.__raw)
CxOneFlowConfig.__secret_root = CxOneFlowConfig.__get_value_for_key_or_fail("", "secret-root-path", CxOneFlowConfig.__raw)

if len(CxOneFlowConfig.__raw.keys() - CxOneFlowConfig.__cloner_factories.keys()) == len(CxOneFlowConfig.__raw.keys()):
raise ConfigurationException.missing_at_least_one_key_path("/", CxOneFlowConfig.__cloner_factories.keys())
Expand Down Expand Up @@ -217,7 +220,8 @@ def __get_value_for_key_or_default(key, config_dict, default):
@staticmethod
def __workflow_service_client_factory(config_path, moniker, **kwargs):
if kwargs is None or len(kwargs.keys()) == 0:
return WorkflowStateService(moniker, CxOneFlowConfig.__default_amqp_url, None, None, True, PullRequestWorkflow())
return WorkflowStateService(moniker, CxOneFlowConfig.__default_amqp_url, None, None, True, CxOneFlowConfig.__server_base_url,
PullRequestWorkflow())
else:

pr_workflow_dict = CxOneFlowConfig.__get_value_for_key_or_default("pull-request", kwargs, {})
Expand Down Expand Up @@ -253,10 +257,10 @@ def __workflow_service_client_factory(config_path, moniker, **kwargs):
amqp_password = CxOneFlowConfig.__get_secret_from_value_of_key_or_default(amqp_dict, "amqp-password", None)
ssl_verify = CxOneFlowConfig.__get_value_for_key_or_default("ssl-verify", amqp_dict, CxOneFlowConfig.get_default_ssl_verify_value())

return WorkflowStateService(moniker, amqp_url, amqp_user, amqp_password, ssl_verify, pr_workflow, \
return WorkflowStateService(moniker, amqp_url, amqp_user, amqp_password, ssl_verify, CxOneFlowConfig.__server_base_url, pr_workflow, \
max_poll_interval, poll_backoff)
else:
return WorkflowStateService(moniker, CxOneFlowConfig.__default_amqp_url, None, None, True, pr_workflow, \
return WorkflowStateService(moniker, CxOneFlowConfig.__default_amqp_url, None, None, True, CxOneFlowConfig.__server_base_url, pr_workflow, \
max_poll_interval, poll_backoff)


Expand Down
31 changes: 22 additions & 9 deletions workflows/pr.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
from workflows.messaging import PRDetails
from typing import Callable, List, Type, Dict
from . import ResultSeverity, ResultStates
import re
import re, urllib


class PullRequestDecoration:
__cx_embed_header_img = "![CheckmarxOne](https://camo.githubusercontent.com/450121ab9d772ac3f1186c2dde5608322249cba9183cd43b34ac7a71e71584b9/68747470733a2f2f63646e2e6173742e636865636b6d6172782e6e65742f696e746567726174696f6e732f6c6f676f2f436865636b6d6172782e706e67)"

__comment = "[//]:#"

__identifier = __comment + "cxoneflow"
Expand Down Expand Up @@ -42,12 +40,14 @@ class PullRequestDecoration:
@staticmethod
def matches_identifier(text : str):
return PullRequestDecoration.__comment_match.match(text.replace("\n", ""))


def __init__(self, server_base_url : str):
self.__server_base_url = server_base_url

def __init__(self):
self.__elements = {
PullRequestDecoration.__identifier : [PullRequestDecoration.__identifier],
PullRequestDecoration.__header_begin : [PullRequestDecoration.__cx_embed_header_img],
PullRequestDecoration.__header_begin : [PullRequestDecoration.header_image(self.__server_base_url)],
PullRequestDecoration.__header_end : None,
PullRequestDecoration.__annotation_begin : [],
PullRequestDecoration.__annotation_end : None,
Expand All @@ -71,6 +71,18 @@ def sca_result_link(display_url : str, project_id : str, scanid : str, title : s
def link(url : str, display_name : str):
return f"[{display_name}]({url})"

@staticmethod
def image(url : str, display_name : str):
return f"![{display_name}]({url})"

@staticmethod
def header_image(server_base_url : str):
return PullRequestDecoration.image(PullRequestDecoration._form_artifact_url(server_base_url, "checkmarx.png"), "CheckmarxOne")

@staticmethod
def _form_artifact_url(server_base_url : str, artifact_path : str) -> str:
return f"{server_base_url.rstrip("/")}/artifacts/{urllib.parse.quote(artifact_path.lstrip("/"))}"

@staticmethod
def severity_indicator(severity : str):
return PullRequestDecoration.__severity_map[severity.lower()] \
Expand Down Expand Up @@ -158,8 +170,8 @@ def full_content(self):


class PullRequestAnnotation(PullRequestDecoration):
def __init__(self, display_url : str, project_id : str, scanid : str, annotation : str, branch : str):
super().__init__()
def __init__(self, display_url : str, project_id : str, scanid : str, annotation : str, branch : str, server_base_url : str):
super().__init__(server_base_url)
self.add_to_annotation(f"{annotation}: {PullRequestDecoration.scan_link(display_url, project_id, scanid, branch)}")

class PullRequestFeedback(PullRequestDecoration):
Expand All @@ -181,8 +193,9 @@ def __test_in_enum(clazz : Type, value : str, exclusions : List[Type]):
return False

def __init__(self, excluded_severities : List[ResultSeverity], excluded_states : List[ResultStates], display_url : str,
project_id : str, scanid : str, enhanced_report : dict, code_permalink_func : Callable, pr_details : PRDetails):
super().__init__()
project_id : str, scanid : str, enhanced_report : dict, code_permalink_func : Callable, pr_details : PRDetails,
server_base_url : str):
super().__init__(server_base_url)
self.__enhanced_report = enhanced_report
self.__permalink = code_permalink_func
self.__excluded_severities = excluded_severities
Expand Down
10 changes: 6 additions & 4 deletions workflows/state_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ class WorkflowStateService:
def log():
return logging.getLogger("WorkflowStateService")

def __init__(self, moniker, amqp_url , amqp_user, amqp_password, ssl_verify, pr_workflow : AbstractWorkflow, max_interval_seconds : timedelta = 600,
backoff_scalar : int = 2):
def __init__(self, moniker, amqp_url , amqp_user, amqp_password, ssl_verify, server_base_url : str, pr_workflow : AbstractWorkflow,
max_interval_seconds : timedelta = 600, backoff_scalar : int = 2):
self.__lock = asyncio.Lock()
self.__max_interval = timedelta(seconds=max_interval_seconds)
self.__backoff = backoff_scalar
Expand All @@ -47,6 +47,7 @@ def __init__(self, moniker, amqp_url , amqp_user, amqp_password, ssl_verify, pr_
self.__ssl_verify = ssl_verify
self.__client = None
self.__service_moniker = moniker
self.__server_base_url = server_base_url

self.__workflow_map = {
ScanWorkflow.PR : pr_workflow
Expand Down Expand Up @@ -153,7 +154,8 @@ async def execute_pr_annotate_workflow(self, msg : aio_pika.abc.AbstractIncoming
inspector = await cxone_service.load_scan_inspector(am.scanid)

if inspector is not None:
annotation = PullRequestAnnotation(cxone_service.display_link, inspector.project_id, am.scanid, am.annotation, pr_details.source_branch)
annotation = PullRequestAnnotation(cxone_service.display_link, inspector.project_id, am.scanid, am.annotation, pr_details.source_branch,
self.__server_base_url)
await scm_service.exec_pr_decorate(pr_details.organization, pr_details.repo_project, pr_details.repo_slug, pr_details.pr_id,
am.scanid, annotation.full_content, annotation.summary_content)
await msg.ack()
Expand All @@ -179,7 +181,7 @@ async def execute_pr_feedback_workflow(self, msg : aio_pika.abc.AbstractIncoming
else:
feedback = PullRequestFeedback(self.__workflow_map[ScanWorkflow.PR].excluded_severities,
self.__workflow_map[ScanWorkflow.PR].excluded_states, cxone_service.display_link, am.projectid, am.scanid, report,
scm_service.create_code_permalink, pr_details)
scm_service.create_code_permalink, pr_details, self.__server_base_url)
await scm_service.exec_pr_decorate(pr_details.organization, pr_details.repo_project, pr_details.repo_slug, pr_details.pr_id,
am.scanid, feedback.full_content, feedback.summary_content)
await msg.ack()
Expand Down
6 changes: 5 additions & 1 deletion wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
that is compatible with other methods of deployment.
"""
from _agent import __agent__
from flask import Flask, request, Response
from flask import Flask, request, Response, send_from_directory
from orchestration import OrchestrationDispatch, BitBucketDataCenterOrchestrator, AzureDevOpsEnterpriseOrchestrator
import json, logging, asyncio, os
from config import CxOneFlowConfig, ConfigurationException, get_config_path
Expand Down Expand Up @@ -71,3 +71,7 @@ async def adoe_webhook_endpoint():
__log.exception(ex)
return Response(status=400)

@app.get("/artifacts/<path:path>" )
async def artifacts(path):
__log.debug(f"Fetching artifact at {path}")
return send_from_directory("artifacts", path)

0 comments on commit 1023f55

Please sign in to comment.