├── .github ├── FUNDING.yml └── workflows │ ├── deploy.yml │ └── deploybeta.yml ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── Dockerfile ├── LICENSE ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml ├── src ├── main │ ├── java │ │ └── io │ │ │ └── mixeway │ │ │ └── scanner │ │ │ ├── ScannerApplication.java │ │ │ ├── config │ │ │ ├── SecurityConfig.java │ │ │ ├── SessionCredentials.java │ │ │ └── TokenAuthenticationFilter.java │ │ │ ├── db │ │ │ ├── entity │ │ │ │ ├── DependencyTrackEntity.java │ │ │ │ ├── ScanEntity.java │ │ │ │ └── ScannerTypeEntity.java │ │ │ └── repository │ │ │ │ ├── DependencyTrackRepository.java │ │ │ │ ├── ScanRepository.java │ │ │ │ └── ScannerTypeRepository.java │ │ │ ├── factory │ │ │ └── ScannerFactory.java │ │ │ ├── integrations │ │ │ ├── ScannerIntegrationFactory.java │ │ │ ├── model │ │ │ │ ├── BanditResponse.java │ │ │ │ ├── BanditResult.java │ │ │ │ ├── BugInstance.java │ │ │ │ ├── BugPattern.java │ │ │ │ ├── Component.java │ │ │ │ ├── Components.java │ │ │ │ ├── DTrackApiKeys.java │ │ │ │ ├── DTrackConfigProperty.java │ │ │ │ ├── DTrackCreateProject.java │ │ │ │ ├── DTrackCreateProjectResponse.java │ │ │ │ ├── DTrackGetVulnsForProject.java │ │ │ │ ├── DTrackProject.java │ │ │ │ ├── DTrackVuln.java │ │ │ │ ├── DependencyTrackConfiguration.java │ │ │ │ ├── ProgPilotVuln.java │ │ │ │ ├── SendBomRequest.java │ │ │ │ ├── SourceLine.java │ │ │ │ └── SpotbugReportXML.java │ │ │ └── scanner │ │ │ │ ├── Bandit.java │ │ │ │ ├── DependencyTrack.java │ │ │ │ ├── Progpilot.java │ │ │ │ └── Spotbug.java │ │ │ ├── rest │ │ │ ├── controller │ │ │ │ └── BaseController.java │ │ │ ├── model │ │ │ │ ├── ScanRequest.java │ │ │ │ └── Status.java │ │ │ └── service │ │ │ │ └── BaseService.java │ │ │ ├── standalone │ │ │ └── StandAloneService.java │ │ │ └── utils │ │ │ ├── CodeHelper.java │ │ │ ├── Constants.java │ │ │ ├── GetInfoRequest.java │ │ │ ├── GitInformations.java │ │ │ ├── GitOperations.java │ │ │ ├── GitResponse.java │ │ │ ├── MixewayConnector.java │ │ │ ├── Pom.java │ │ │ ├── PomBuild.java │ │ │ ├── PomConfiguration.java │ │ │ ├── PomPlugin.java │ │ │ ├── PomPlugins.java │ │ │ ├── PomProject.java │ │ │ ├── PrepareCIOperation.java │ │ │ ├── ScannerPluginType.java │ │ │ ├── ScannerType.java │ │ │ ├── SourceProjectType.java │ │ │ ├── StandaloneGitResponse.java │ │ │ ├── Status.java │ │ │ └── Vulnerability.java │ └── resources │ │ ├── application.properties │ │ └── db │ │ └── changelog │ │ └── changelog.sql └── test │ └── java │ └── io │ └── mixeway │ └── scanner │ └── ScannerApplicationTests.java └── startup.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [siewer] 4 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy prod version 2 | on: 3 | push: 4 | branches: 5 | - 'master' 6 | jobs: 7 | publishbrod: 8 | runs-on: ubuntu-16.04 9 | steps: 10 | - uses: actions/checkout@v1 11 | - name: docker actions build & deploy 12 | uses: docker/build-push-action@v1 13 | with: 14 | username: ${{ secrets.DOCKER_USER }} 15 | password: ${{ secrets.DOCKER_TOKEN }} 16 | repository: mixeway/scanner 17 | tags: 0.9.0 -------------------------------------------------------------------------------- /.github/workflows/deploybeta.yml: -------------------------------------------------------------------------------- 1 | name: Test Build & Deploy beta 2 | on: 3 | push: 4 | branches: 5 | - '*' 6 | - '!master' 7 | pull_request: 8 | branches: 9 | - '*' 10 | - '!master' 11 | jobs: 12 | publishbeta: 13 | runs-on: ubuntu-16.04 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: docker actions build & deploy 17 | uses: docker/build-push-action@v1 18 | with: 19 | username: ${{ secrets.DOCKER_USER }} 20 | password: ${{ secrets.DOCKER_TOKEN }} 21 | repository: mixeway/scanner 22 | tags: 0.9.0-beta -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | /mixeway_scan_sources/ 35 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import java.net.*; 18 | import java.io.*; 19 | import java.nio.channels.*; 20 | import java.util.Properties; 21 | 22 | public class MavenWrapperDownloader { 23 | 24 | private static final String WRAPPER_VERSION = "0.5.6"; 25 | /** 26 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 27 | */ 28 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 29 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 30 | 31 | /** 32 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 33 | * use instead of the default one. 34 | */ 35 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 36 | ".mvn/wrapper/maven-wrapper.properties"; 37 | 38 | /** 39 | * Path where the maven-wrapper.jar will be saved to. 40 | */ 41 | private static final String MAVEN_WRAPPER_JAR_PATH = 42 | ".mvn/wrapper/maven-wrapper.jar"; 43 | 44 | /** 45 | * Name of the property which should be used to override the default download url for the wrapper. 46 | */ 47 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 48 | 49 | public static void main(String args[]) { 50 | System.out.println("- Downloader started"); 51 | File baseDirectory = new File(args[0]); 52 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 53 | 54 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 55 | // wrapperUrl parameter. 56 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 57 | String url = DEFAULT_DOWNLOAD_URL; 58 | if (mavenWrapperPropertyFile.exists()) { 59 | FileInputStream mavenWrapperPropertyFileInputStream = null; 60 | try { 61 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 62 | Properties mavenWrapperProperties = new Properties(); 63 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 64 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 65 | } catch (IOException e) { 66 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 67 | } finally { 68 | try { 69 | if (mavenWrapperPropertyFileInputStream != null) { 70 | mavenWrapperPropertyFileInputStream.close(); 71 | } 72 | } catch (IOException e) { 73 | // Ignore ... 74 | } 75 | } 76 | } 77 | System.out.println("- Downloading from: " + url); 78 | 79 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 80 | if (!outputFile.getParentFile().exists()) { 81 | if (!outputFile.getParentFile().mkdirs()) { 82 | System.out.println( 83 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 84 | } 85 | } 86 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 87 | try { 88 | downloadFileFromURL(url, outputFile); 89 | System.out.println("Done"); 90 | System.exit(0); 91 | } catch (Throwable e) { 92 | System.out.println("- Error downloading"); 93 | e.printStackTrace(); 94 | System.exit(1); 95 | } 96 | } 97 | 98 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 99 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 100 | String username = System.getenv("MVNW_USERNAME"); 101 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 102 | Authenticator.setDefault(new Authenticator() { 103 | @Override 104 | protected PasswordAuthentication getPasswordAuthentication() { 105 | return new PasswordAuthentication(username, password); 106 | } 107 | }); 108 | } 109 | URL website = new URL(urlString); 110 | ReadableByteChannel rbc; 111 | rbc = Channels.newChannel(website.openStream()); 112 | FileOutputStream fos = new FileOutputStream(destination); 113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 114 | fos.close(); 115 | rbc.close(); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mixeway/MixewayScanner/98c0c57b48a2012db9bcbf2e5662b2394a7c674c/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:8 2 | MAINTAINER siewer 3 | 4 | # Required package installation 5 | RUN yum update -y 6 | RUN yum install java-11-openjdk-devel -y 7 | RUN yum install maven -y 8 | RUN yum install git -y 9 | RUN yum install epel-release -y 10 | RUN yum install python3-pip -y 11 | RUN pip3 install bandit 12 | RUN pip3 install pipreqs 13 | RUN yum install nodejs -y 14 | RUN yum install php-cli php-zip wget unzip php-json -y 15 | RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" 16 | RUN php composer-setup.php --install-dir=/usr/local/bin --filename=composer 17 | RUN php -r "copy('https://github.com/designsecurity/progpilot/releases/download/v0.8.0/progpilot_v0.8.0.phar', 'progpilot.phar');" 18 | RUN chmod +x progpilot.phar 19 | RUN mv progpilot.phar /bin/progpilot 20 | 21 | # TLS Support 22 | RUN mkdir /opt/pki 23 | RUN openssl req -new -newkey rsa:4096 -days 3650 -nodes -x509 -subj "/O=mixeway.io/CN=mixewayscanner" -keyout certificate.key -out certificate.crt 24 | RUN openssl pkcs12 -export -in certificate.crt -inkey certificate.key -out certificate.p12 -name "mixeway" -password pass:1qaz@WSX 25 | RUN mv certificate.p12 /opt/pki/certificate.p12 26 | 27 | # Download DTrack 28 | RUN mkdir /opt/dtrack && wget https://github.com/DependencyTrack/dependency-track/releases/download/3.8.0/dependency-track-embedded.war -O /opt/dtrack/dependency-track-embedded.war 29 | 30 | # Building Mixeway Scanner APP 31 | WORKDIR /app 32 | COPY ./pom.xml ./pom.xml 33 | RUN mvn dependency:go-offline -B 34 | 35 | COPY ./src ./src 36 | RUN mvn package -DskipTests && cp target/*.jar app.jar 37 | 38 | # Copy startup script 39 | COPY ./startup.sh ./startup.sh 40 | 41 | ENTRYPOINT ["/bin/bash", "/app/startup.sh"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mixeway 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mixeway Scanner Aggregator 2 | 3 | 4 | Mixeway Scanner Aggregator is Spring Boot application which aggregate and integrate the most popular OpenSurce Vulnrability scanners avaliable. 5 | 6 | ## Supported Scanners: 7 | * OWASP Dependency Track - https://dependencytrack.org 8 | * Spotbugs - https://spotbugs.github.io 9 | * Bandit - https://github.com/PyCQA/bandit 10 | 11 | 12 | ## Scope of integration 13 | MixewayScanner can be run as REST API or standalone. In REST mode it listen for scan request which contains GIT URL 14 | for repository to be scanned. Next it clone repo, create DTrack project and send SBOM. In next phase SAST scanner is executed. 15 | Detected vulnerabilities are pushed into console or to Mixeway if integration is configured. 16 | 17 | ## Requirements 18 | * Docker installed 19 | * Sonatype OSS username and key (for projects other then NPM) - https://ossindex.sonatype.org 20 | * If Maven require to download some custom libraries, link them via `-v ~/.m2:/root/.m2` 21 | 22 | ### Running options 23 | * In standalone mode, running container inside directory You want to scan 24 | ```shell script 25 | docker run -e MODE=STANDALONE \ 26 | -v ${PWD}:/opt/sources \ 27 | mixeway/scanner:latest 28 | ``` 29 | if source to be scaned is located in current direcory. Otherwise, use `-v /opt/sources` 30 | 31 | * In REST API mode, container is running and listetning on port :8443 32 | ```shell script 33 | docker run \ 34 | -e MODE=REST \ 35 | mixeway/scanner:latest 36 | ``` 37 | example usage: 38 | ```$xslt 39 | GET http://localhost:8443/run 40 | {"target":"https://github.com/mixeway/mixewaybackend", "branch":"master", "type":"SAST"} 41 | ``` 42 | where target is URL for repo, branch is branch name to be sanned and type is SAST (only this type is supported in current version) 43 | 44 | All options and descriptions 45 | 46 | | Option | Required | Default Value | Description | 47 | |-----------------------------------|--------------------------|------------------------|-------------------------------------------------------------------------------------------------------------------------------------| 48 | |-e OSS_USERNAME=\ | No | null | Sonatype OSS username - required to perform dependency check on projects other then NPM -to generate- https://ossindex.sonatype.org/| 49 | |-e OSS_KEY=\ | No | null | Sonatype OSS API Key - required to perform dependency check on projects other then NPM -to generate- https://ossindex.sonatype.org/ | 50 | |-e MODE=\ | No | REST | Mode of Scanner to run, in REST Mode API is started on :8443 port, in STANDALONE mode, full scan is performed in mounted directory | 51 | |-e MIXEWAY_URL=\ | No | https://hub.mixeway.io | URL to Mixeway to push results if no Mixeway data is passed results of scan will be print to console | 52 | |-e MIXEWAY_KEY=\ | No | null | CICD API Key - to generate in user profile of Mixeway | 53 | |-e MIXEWAY_PROJECT_ID=\ | No | null | ID of project in mixeway to which detected vulnerailities will be set. Required if You want enable Mixeway integration | 54 | |-e MIXEWAY_PROJECT_NAME=\ | No | null | Name of project. Required for Mixeway integration with STANDALONE scans. | 55 | |-v \:/opt/sources | Yes (STANDALONE version) | null | Passing files to scan to docker | 56 | 57 | ## Optimization 58 | * Maven projects - in scope of mvn project, task which takes the most of a time is dependency download. To skip this part just mount 59 | the `.m2` directory into docker with `-v ~/.m2:/root/.2` 60 | 61 | ## TLS support for REST API 62 | By default Mixeway Scanner use self-signed TLS certifiate generated during `docker build` action. 63 | If You want to use Your own certificate mount it as `certificate.p12` to `/opt/pki` location (e.g. `-v /etc/pki:/opt/pki`) and then 64 | during `docker run` pass `-e PKCS12_PASSWORD=` with PKCS12 password. 65 | 66 | ## Supported Languages 67 | 68 | | Scanner version | Languages | 69 | |---|---| 70 | |v0.9.0| JAVA-MAVEN | 71 | |v0.9.1| JAVA-MAVEN, Python3| 72 | |v0.9.2| JAVA-MAVEN, Python3, PHP| -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.3.3.RELEASE 9 | 10 | 11 | io.mixeway 12 | scanner 13 | 0.0.1-SNAPSHOT 14 | scanner 15 | Application which integrate number of OpenSource scanners with Mixeway via API 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-jdbc 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-security 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-web 33 | 34 | 35 | org.liquibase 36 | liquibase-core 37 | 38 | 39 | 40 | com.h2database 41 | h2 42 | runtime 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-test 47 | test 48 | 49 | 50 | org.junit.vintage 51 | junit-vintage-engine 52 | 53 | 54 | 55 | 56 | org.springframework.security 57 | spring-security-test 58 | test 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-data-jpa 63 | 64 | 65 | com.h2database 66 | h2 67 | runtime 68 | 69 | 70 | org.liquibase 71 | liquibase-core 72 | 3.8.0 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-starter-data-jpa 77 | RELEASE 78 | compile 79 | 80 | 81 | org.hobsoft.spring 82 | spring-rest-template-logger 83 | 2.0.0 84 | 85 | 86 | 87 | org.eclipse.jgit 88 | org.eclipse.jgit 89 | 5.8.1.202007141445-r 90 | 91 | 92 | org.hibernate 93 | hibernate-validator 94 | 6.0.13.Final 95 | 96 | 97 | 98 | commons-io 99 | commons-io 100 | 2.7 101 | 102 | 103 | junit 104 | junit 105 | 4.9 106 | test 107 | 108 | 109 | 110 | org.apache.commons 111 | commons-lang3 112 | 3.11 113 | 114 | 115 | org.apache.httpcomponents 116 | httpclient 117 | 4.5.3 118 | 119 | 120 | 121 | org.projectlombok 122 | lombok 123 | 1.18.12 124 | provided 125 | 126 | 127 | 128 | com.google.code.gson 129 | gson 130 | 2.8.5 131 | 132 | 133 | com.fasterxml.jackson.dataformat 134 | jackson-dataformat-xml 135 | 2.9.8 136 | 137 | 138 | org.codehaus.woodstox 139 | woodstox-core-asl 140 | 4.4.1 141 | 142 | 143 | 144 | 145 | 146 | 147 | org.springframework.boot 148 | spring-boot-maven-plugin 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/ScannerApplication.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import io.mixeway.scanner.factory.ScannerFactory; 5 | import io.mixeway.scanner.integrations.ScannerIntegrationFactory; 6 | import io.mixeway.scanner.standalone.StandAloneService; 7 | import io.mixeway.scanner.utils.ScannerPluginType; 8 | import io.mixeway.scanner.utils.ScannerType; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.SpringApplication; 11 | import org.springframework.boot.autoconfigure.SpringBootApplication; 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication; 13 | import org.springframework.boot.autoconfigure.domain.EntityScan; 14 | import org.springframework.boot.context.event.ApplicationReadyEvent; 15 | import org.springframework.context.ApplicationContext; 16 | import org.springframework.context.annotation.ComponentScan; 17 | import org.springframework.context.annotation.Profile; 18 | import org.springframework.context.event.EventListener; 19 | import org.springframework.core.env.Environment; 20 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 21 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 22 | import org.springframework.scheduling.annotation.EnableScheduling; 23 | import org.springframework.stereotype.Service; 24 | 25 | import java.io.IOException; 26 | import java.security.KeyManagementException; 27 | import java.security.KeyStoreException; 28 | import java.security.NoSuchAlgorithmException; 29 | 30 | @SpringBootApplication(scanBasePackages = "io.mixeway.scanner") 31 | @EnableScheduling 32 | @EnableJpaRepositories("io.mixeway.scanner.db.repository") 33 | @EntityScan(basePackages = "io.mixeway.scanner.db.entity") 34 | @EnableJpaAuditing 35 | public class ScannerApplication { 36 | 37 | public static void main(String[] args) { 38 | 39 | SpringApplication.run(ScannerApplication.class, args); 40 | } 41 | } 42 | 43 | @Service 44 | @ConditionalOnNotWebApplication 45 | class StandaloneMixewayApp { 46 | private final StandAloneService standAloneService; 47 | 48 | public StandaloneMixewayApp(StandAloneService standAloneService){ 49 | this.standAloneService = standAloneService; 50 | } 51 | @EventListener(ApplicationReadyEvent.class) 52 | public void runStandaloneMixewayScannerApp() throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, InterruptedException { 53 | standAloneService.runScan(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 21:47 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.config; 7 | 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 12 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 13 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 14 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 15 | import org.springframework.security.config.http.SessionCreationPolicy; 16 | import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; 17 | 18 | @Configuration 19 | @EnableWebSecurity 20 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 21 | 22 | @Value("${APIKEY:changeit}") 23 | private String apiKey; 24 | 25 | //Leave whatever you had here 26 | @Override 27 | public void configure(HttpSecurity http) throws Exception { 28 | http.addFilterBefore(new TokenAuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class); 29 | 30 | 31 | http.csrf().disable() 32 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 33 | .and().authorizeRequests() 34 | .antMatchers("/**").authenticated(); 35 | } 36 | 37 | 38 | 39 | 40 | //Add these two below. 41 | @Override 42 | public void configure(AuthenticationManagerBuilder auth) { 43 | auth.authenticationProvider(apiKeyAuthenticationProvider()); 44 | } 45 | 46 | @Bean 47 | public TokenAuthenticationProvider apiKeyAuthenticationProvider() { 48 | return new TokenAuthenticationProvider(apiKey); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/config/SessionCredentials.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 21:39 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.config; 7 | 8 | public class SessionCredentials { 9 | 10 | String apiKey; 11 | String accessToken; 12 | 13 | public SessionCredentials(String apiKey, String accessToken) { 14 | this.apiKey = apiKey; 15 | this.accessToken = accessToken; 16 | } 17 | 18 | public String getApiKey() { 19 | return apiKey; 20 | } 21 | 22 | public String getAccessToken() { 23 | return accessToken; 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/config/TokenAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 21:15 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.config; 7 | 8 | import org.springframework.security.authentication.*; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.core.AuthenticationException; 11 | import org.springframework.security.core.context.SecurityContextHolder; 12 | import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; 13 | import org.springframework.util.StringUtils; 14 | import org.springframework.web.filter.GenericFilterBean; 15 | 16 | import javax.servlet.FilterChain; 17 | import javax.servlet.ServletException; 18 | import javax.servlet.ServletRequest; 19 | import javax.servlet.ServletResponse; 20 | import javax.servlet.http.HttpServletRequest; 21 | import javax.servlet.http.HttpServletResponse; 22 | import java.io.IOException; 23 | 24 | 25 | public class TokenAuthenticationFilter extends GenericFilterBean { 26 | 27 | private final AuthenticationManager authenticationManager; 28 | 29 | public TokenAuthenticationFilter(AuthenticationManager authenticationManager) { 30 | this.authenticationManager = authenticationManager; 31 | } 32 | 33 | @Override 34 | public void doFilter(ServletRequest request, 35 | ServletResponse response, 36 | FilterChain chain) throws IOException, ServletException { 37 | 38 | HttpServletRequest httpRequest = (HttpServletRequest) request; 39 | HttpServletResponse httpResponse = (HttpServletResponse) response; 40 | 41 | String apiKey = httpRequest.getHeader("apiKey"); 42 | 43 | try { 44 | if (!StringUtils.isEmpty(apiKey)) { 45 | processTokenAuthentication(apiKey); 46 | } 47 | chain.doFilter(request, response); 48 | } catch (InternalAuthenticationServiceException internalAuthenticationServiceException) 49 | { 50 | SecurityContextHolder.clearContext(); 51 | logger.error("Internal authentication service exception", internalAuthenticationServiceException); 52 | httpResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 53 | } 54 | catch(AuthenticationException authenticationException) 55 | { 56 | SecurityContextHolder.clearContext(); 57 | httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage()); 58 | } 59 | } 60 | 61 | private void processTokenAuthentication(String apiKey) { 62 | SessionCredentials authCredentials = new SessionCredentials(apiKey,null); 63 | Authentication requestAuthentication = new PreAuthenticatedAuthenticationToken(authCredentials, authCredentials); 64 | Authentication resultOfAuthentication = tryToAuthenticate(requestAuthentication); 65 | SecurityContextHolder.getContext().setAuthentication(resultOfAuthentication); 66 | } 67 | 68 | private Authentication tryToAuthenticate(Authentication requestAuthentication) { 69 | Authentication responseAuthentication = authenticationManager.authenticate(requestAuthentication); 70 | if (responseAuthentication == null || !responseAuthentication.isAuthenticated()) { 71 | throw new InternalAuthenticationServiceException("Unable to authenticate Domain User for provided credentials"); 72 | } 73 | return responseAuthentication; 74 | } 75 | } 76 | 77 | class TokenAuthenticationProvider implements AuthenticationProvider { 78 | 79 | private final String apiKey; 80 | 81 | public TokenAuthenticationProvider(String apiKey) { 82 | this.apiKey = apiKey; 83 | } 84 | 85 | @Override 86 | public Authentication authenticate(Authentication authentication) throws AuthenticationException { 87 | SessionCredentials credentials = (SessionCredentials) authentication.getCredentials(); 88 | if (credentials != null && credentials.apiKey.equals(this.apiKey)) { 89 | 90 | //Also evaluate the token here 91 | 92 | Authentication newAuthentication = new PreAuthenticatedAuthenticationToken(apiKey, credentials); 93 | newAuthentication.setAuthenticated(true); 94 | return newAuthentication; 95 | } 96 | throw new BadCredentialsException("Incorrect ApiKey"); 97 | } 98 | 99 | @Override 100 | public boolean supports(Class aClass) { 101 | return aClass.equals(PreAuthenticatedAuthenticationToken.class); 102 | } 103 | } -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/db/entity/DependencyTrackEntity.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.db.entity; 2 | 3 | import io.mixeway.scanner.integrations.scanner.DependencyTrack; 4 | import org.springframework.boot.autoconfigure.domain.EntityScan; 5 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 6 | 7 | import javax.persistence.*; 8 | 9 | @Entity 10 | @EntityScan 11 | @Table(name = "dependencytrack") 12 | @EntityListeners(AuditingEntityListener.class) 13 | public class DependencyTrackEntity { 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.IDENTITY) 16 | private Long id; 17 | private boolean enabled; 18 | @Column(name = "apikey") 19 | private String apiKey; 20 | 21 | public Long getId() { 22 | return id; 23 | } 24 | 25 | public void setId(Long id) { 26 | this.id = id; 27 | } 28 | 29 | public boolean isEnabled() { 30 | return enabled; 31 | } 32 | 33 | public void setEnabled(boolean enabled) { 34 | this.enabled = enabled; 35 | } 36 | 37 | public String getApiKey() { 38 | return apiKey; 39 | } 40 | 41 | public void setApiKey(String apiKey) { 42 | this.apiKey = apiKey; 43 | } 44 | public DependencyTrackEntity() {} 45 | public DependencyTrackEntity(String apiKey){ 46 | this.enabled = false; 47 | this.apiKey =apiKey; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/db/entity/ScanEntity.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.db.entity; 2 | 3 | import org.hibernate.annotations.OnDelete; 4 | import org.hibernate.annotations.OnDeleteAction; 5 | import org.springframework.boot.autoconfigure.domain.EntityScan; 6 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 7 | 8 | import javax.persistence.*; 9 | import java.util.Date; 10 | 11 | @Entity 12 | @EntityScan 13 | @Table(name = "scan") 14 | @EntityListeners(AuditingEntityListener.class) 15 | public class ScanEntity { 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.IDENTITY) 18 | private Long id; 19 | private Date date; 20 | @ManyToOne(fetch = FetchType.LAZY, optional = false) 21 | @JoinColumn(name = "scannertype_id", nullable = false) 22 | @OnDelete(action = OnDeleteAction.CASCADE) 23 | private ScannerTypeEntity scannerType; 24 | private boolean running; 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/db/entity/ScannerTypeEntity.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.db.entity; 2 | 3 | import org.springframework.boot.autoconfigure.domain.EntityScan; 4 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 5 | 6 | import javax.persistence.*; 7 | 8 | @Entity 9 | @EntityScan 10 | @Table(name = "scannertype") 11 | @EntityListeners(AuditingEntityListener.class) 12 | public class ScannerTypeEntity { 13 | @Id 14 | @GeneratedValue(strategy = GenerationType.IDENTITY) 15 | private Long id; 16 | private String name; 17 | 18 | public Long getId() { 19 | return id; 20 | } 21 | 22 | public void setId(Long id) { 23 | this.id = id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/db/repository/DependencyTrackRepository.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.db.repository; 2 | 3 | import io.mixeway.scanner.db.entity.DependencyTrackEntity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.Optional; 8 | 9 | @Repository 10 | public interface DependencyTrackRepository extends JpaRepository { 11 | Optional findByEnabledAndApiKeyNotNull(boolean enabled); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/db/repository/ScanRepository.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.db.repository; 2 | 3 | import io.mixeway.scanner.db.entity.ScanEntity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface ScanRepository extends JpaRepository { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/db/repository/ScannerTypeRepository.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.db.repository; 2 | 3 | import io.mixeway.scanner.db.entity.ScannerTypeEntity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface ScannerTypeRepository extends JpaRepository { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/factory/ScannerFactory.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.factory; 2 | 3 | import io.mixeway.scanner.integrations.ScannerIntegrationFactory; 4 | import io.mixeway.scanner.integrations.scanner.Bandit; 5 | import io.mixeway.scanner.integrations.scanner.DependencyTrack; 6 | import io.mixeway.scanner.integrations.scanner.Progpilot; 7 | import io.mixeway.scanner.integrations.scanner.Spotbug; 8 | import io.mixeway.scanner.rest.model.ScanRequest; 9 | import io.mixeway.scanner.utils.*; 10 | import org.apache.commons.io.FileUtils; 11 | import org.apache.commons.io.filefilter.DirectoryFileFilter; 12 | import org.apache.commons.io.filefilter.RegexFileFilter; 13 | import org.springframework.stereotype.Service; 14 | 15 | import java.io.File; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.stream.Collectors; 19 | 20 | @Service 21 | public class ScannerFactory { 22 | private final DependencyTrack dependencyTrack; 23 | private final Spotbug spotbug; 24 | private final Bandit bandit; 25 | private final Progpilot progpilot; 26 | 27 | public ScannerFactory(DependencyTrack dependencyTrack, Spotbug spotbug, 28 | Bandit bandit, Progpilot progpilot){ 29 | this.dependencyTrack = dependencyTrack; 30 | this.spotbug = spotbug; 31 | this.bandit = bandit; 32 | this.progpilot = progpilot; 33 | } 34 | 35 | /** 36 | * Method which takes proper vulnerability scanner based on a type 37 | * 38 | * @param type type of a scanner 39 | * @return returning factory of given type 40 | */ 41 | public ScannerIntegrationFactory getProperScanner(ScannerPluginType type) { 42 | ScannerIntegrationFactory scanner = null; 43 | switch (type) { 44 | case DEPENDENCYTRACK: 45 | scanner = dependencyTrack; 46 | break; 47 | case SPOTBUG: 48 | scanner = spotbug; 49 | break; 50 | case BANDIT: 51 | scanner = bandit; 52 | break; 53 | case PROGPILOT: 54 | scanner = progpilot; 55 | break; 56 | } 57 | return scanner; 58 | } 59 | 60 | public List runScanForLanguage(SourceProjectType sourceProjectType) throws Exception { 61 | List vulnerabilityList = new ArrayList<>(); 62 | switch (sourceProjectType) { 63 | case NPM: 64 | List packagePaths= FileUtils.listFiles( 65 | new File(CodeHelper.getProjectPath(new ScanRequest(), true)), 66 | new RegexFileFilter(Constants.PACKAGE_FILENAME), 67 | DirectoryFileFilter.DIRECTORY 68 | ).stream() 69 | .map(File::getAbsoluteFile) 70 | .map(file -> file.toString() 71 | .split(File.separatorChar + Constants.PACKAGE_FILENAME)[0]) 72 | .collect(Collectors.toList()); 73 | vulnerabilityList.addAll(dependencyTrack.runScanStandalone(packagePaths)); 74 | break; 75 | case PHP: 76 | vulnerabilityList.addAll(dependencyTrack.runScanStandalone()); 77 | vulnerabilityList.addAll(progpilot.runScanStandalone()); 78 | break; 79 | case PIP: 80 | vulnerabilityList.addAll(dependencyTrack.runScanStandalone()); 81 | vulnerabilityList.addAll(bandit.runScanStandalone()); 82 | break; 83 | case MAVEN: 84 | List mvnPackagePaths= FileUtils.listFiles( 85 | new File(CodeHelper.getProjectPath(new ScanRequest(), true)), 86 | new RegexFileFilter(Constants.POM_FILENAME), 87 | DirectoryFileFilter.DIRECTORY 88 | ).stream() 89 | .map(File::getAbsoluteFile) 90 | .map(file -> file.toString() 91 | .split(File.separatorChar + Constants.POM_FILENAME)[0]) 92 | .collect(Collectors.toList()); 93 | vulnerabilityList.addAll(dependencyTrack.runScanStandalone()); 94 | for (String mvnPath : mvnPackagePaths) { 95 | vulnerabilityList.addAll(spotbug.runScanStandalone(mvnPath) 96 | .stream() 97 | .filter(vulnerability -> vulnerability.getCategory().equals(Constants.SPOTBUG_CATEGORY_SECURITY) || 98 | vulnerability.getCategory().equals(Constants.SPOTBUG_CATEGORY_MALICIOUS_CODE)) 99 | .collect(Collectors.toList())); 100 | } 101 | break; 102 | case GRADLE: 103 | break; 104 | } 105 | return vulnerabilityList.stream().distinct().collect(Collectors.toList()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/ScannerIntegrationFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 22:43 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.integrations; 7 | 8 | import io.mixeway.scanner.rest.model.ScanRequest; 9 | import io.mixeway.scanner.utils.Vulnerability; 10 | 11 | import java.util.List; 12 | 13 | public interface ScannerIntegrationFactory { 14 | void prepare() throws Exception; 15 | List runScan(ScanRequest scanRequest) throws Exception; 16 | List runScanStandalone() throws Exception; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/BanditResponse.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author gsiewruk 9 | */ 10 | @JsonIgnoreProperties(ignoreUnknown = true) 11 | public class BanditResponse { 12 | List results; 13 | 14 | public List getResults() { 15 | return results; 16 | } 17 | 18 | public void setResults(List results) { 19 | this.results = results; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/BanditResult.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | /** 6 | * @author gsiewruk 7 | */ 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class BanditResult { 10 | private String line_number; 11 | private String filename; 12 | private String issue_text; 13 | private String test_name; 14 | private String more_info; 15 | private String issue_severity; 16 | private String code; 17 | 18 | public String getLine_number() { 19 | return line_number; 20 | } 21 | 22 | public void setLine_number(String line_number) { 23 | this.line_number = line_number; 24 | } 25 | 26 | public String getFilename() { 27 | return filename; 28 | } 29 | 30 | public void setFilename(String filename) { 31 | this.filename = filename; 32 | } 33 | 34 | public String getIssue_text() { 35 | return issue_text; 36 | } 37 | 38 | public void setIssue_text(String issue_text) { 39 | this.issue_text = issue_text; 40 | } 41 | 42 | public String getTest_name() { 43 | return test_name; 44 | } 45 | 46 | public void setTest_name(String test_name) { 47 | this.test_name = test_name; 48 | } 49 | 50 | public String getMore_info() { 51 | return more_info; 52 | } 53 | 54 | public void setMore_info(String more_info) { 55 | this.more_info = more_info; 56 | } 57 | 58 | public String getIssue_severity() { 59 | return issue_severity; 60 | } 61 | 62 | public void setIssue_severity(String issue_severity) { 63 | this.issue_severity = issue_severity; 64 | } 65 | 66 | public String getCode() { 67 | return code; 68 | } 69 | 70 | public void setCode(String code) { 71 | this.code = code; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/BugInstance.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | import javax.xml.bind.annotation.*; 4 | 5 | /** 6 | * @author gsiewruk 7 | */ 8 | @XmlRootElement(name = "BugInstance") 9 | @XmlAccessorType(XmlAccessType.FIELD) 10 | public class BugInstance { 11 | @XmlAttribute 12 | private String cweid; 13 | @XmlAttribute 14 | private String rank; 15 | @XmlAttribute 16 | private String abbrev; 17 | @XmlAttribute 18 | private String category; 19 | @XmlAttribute 20 | private String priority; 21 | @XmlAttribute 22 | private String type; 23 | @XmlElement(name = "LongMessage") 24 | private String longMessage; 25 | @XmlElement(name = "ShortMessage") 26 | private String shortDescription; 27 | @XmlElement(name= "SourceLine") 28 | private SourceLine sourceLine; 29 | @XmlElement(name= "Details") 30 | private String details; 31 | 32 | public String getShortDescription() { 33 | return shortDescription; 34 | } 35 | 36 | public void setShortDescription(String shortDescription) { 37 | this.shortDescription = shortDescription; 38 | } 39 | 40 | public String getDetails() { 41 | return details; 42 | } 43 | 44 | public void setDetails(String details) { 45 | this.details = details; 46 | } 47 | 48 | public SourceLine getSourceLine() { 49 | return sourceLine; 50 | } 51 | 52 | public void setSourceLine(SourceLine sourceLine) { 53 | this.sourceLine = sourceLine; 54 | } 55 | 56 | public String getCweid() { 57 | return cweid; 58 | } 59 | 60 | public void setCweid(String cweid) { 61 | this.cweid = cweid; 62 | } 63 | 64 | public String getRank() { 65 | return rank; 66 | } 67 | 68 | public void setRank(String rank) { 69 | this.rank = rank; 70 | } 71 | 72 | public String getAbbrev() { 73 | return abbrev; 74 | } 75 | 76 | public void setAbbrev(String abbrev) { 77 | this.abbrev = abbrev; 78 | } 79 | 80 | public String getCategory() { 81 | return category; 82 | } 83 | 84 | public void setCategory(String category) { 85 | this.category = category; 86 | } 87 | 88 | public String getPriority() { 89 | return priority; 90 | } 91 | 92 | public void setPriority(String priority) { 93 | this.priority = priority; 94 | } 95 | 96 | public String getType() { 97 | return type; 98 | } 99 | 100 | public void setType(String type) { 101 | this.type = type; 102 | } 103 | 104 | public String getLongMessage() { 105 | return longMessage; 106 | } 107 | 108 | public void setLongMessage(String longMessage) { 109 | this.longMessage = longMessage; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/BugPattern.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | import javax.xml.bind.annotation.XmlAccessType; 4 | import javax.xml.bind.annotation.XmlAccessorType; 5 | import javax.xml.bind.annotation.XmlElement; 6 | import javax.xml.bind.annotation.XmlRootElement; 7 | 8 | /** 9 | * @author gsiewruk 10 | */ 11 | @XmlRootElement(name = "BugPattern") 12 | @XmlAccessorType(XmlAccessType.FIELD) 13 | public class BugPattern { 14 | @XmlElement(name = "ShortDescription") 15 | private String shortDescriptions; 16 | @XmlElement(name = "Details") 17 | private String details; 18 | 19 | 20 | public String getShortDescriptions() { 21 | return shortDescriptions; 22 | } 23 | 24 | public void setShortDescriptions(String shortDescriptions) { 25 | this.shortDescriptions = shortDescriptions; 26 | } 27 | 28 | public String getDetails() { 29 | return details; 30 | } 31 | 32 | public void setDetails(String details) { 33 | this.details = details; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/Component.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | public class Component { 4 | private String group; 5 | private String name; 6 | private String version; 7 | private String description; 8 | 9 | public String getGroup() { 10 | return group; 11 | } 12 | 13 | public void setGroup(String group) { 14 | this.group = group; 15 | } 16 | 17 | public String getName() { 18 | return name; 19 | } 20 | 21 | public void setName(String name) { 22 | this.name = name; 23 | } 24 | 25 | public String getVersion() { 26 | return version; 27 | } 28 | 29 | public void setVersion(String version) { 30 | this.version = version; 31 | } 32 | 33 | public String getDescription() { 34 | return description; 35 | } 36 | 37 | public void setDescription(String description) { 38 | this.description = description; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/Components.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | import java.util.List; 4 | 5 | public class Components { 6 | private List components; 7 | 8 | public List getComponents() { 9 | return components; 10 | } 11 | 12 | public void setComponents(List components) { 13 | this.components = components; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/DTrackApiKeys.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 22:18 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.integrations.model; 7 | 8 | public class DTrackApiKeys { 9 | String key; 10 | 11 | public String getKey() { 12 | return key; 13 | } 14 | 15 | public void setKey(String key) { 16 | this.key = key; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/DTrackConfigProperty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 22:43 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.integrations.model; 7 | 8 | public class DTrackConfigProperty { 9 | String groupName; 10 | String propertyName; 11 | Object propertyValue; 12 | 13 | public DTrackConfigProperty(String groupName, String propertyName, Object propertyValue){ 14 | this.groupName = groupName; 15 | this.propertyName = propertyName; 16 | this.propertyValue = propertyValue; 17 | } 18 | 19 | public String getGroupName() { 20 | return groupName; 21 | } 22 | 23 | public void setGroupName(String groupName) { 24 | this.groupName = groupName; 25 | } 26 | 27 | public String getPropertyName() { 28 | return propertyName; 29 | } 30 | 31 | public void setPropertyName(String propertyName) { 32 | this.propertyName = propertyName; 33 | } 34 | 35 | public Object getPropertyValue() { 36 | return propertyValue; 37 | } 38 | 39 | public void setPropertyValue(Object propertyValue) { 40 | this.propertyValue = propertyValue; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/DTrackCreateProject.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | public class DTrackCreateProject { 4 | private String name; 5 | 6 | public DTrackCreateProject(String name){ 7 | this.name = name; 8 | } 9 | 10 | public String getName() { 11 | return name; 12 | } 13 | 14 | public void setName(String name) { 15 | this.name = name; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/DTrackCreateProjectResponse.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | public class DTrackCreateProjectResponse { 4 | private String name; 5 | private String uuid; 6 | 7 | public String getName() { 8 | return name; 9 | } 10 | 11 | public void setName(String name) { 12 | this.name = name; 13 | } 14 | 15 | public String getUuid() { 16 | return uuid; 17 | } 18 | 19 | public void setUuid(String uuid) { 20 | this.uuid = uuid; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/DTrackGetVulnsForProject.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | import java.util.List; 4 | 5 | public class DTrackGetVulnsForProject { 6 | private List dTrackVulns; 7 | 8 | public List getdTrackVulns() { 9 | return dTrackVulns; 10 | } 11 | 12 | public void setdTrackVulns(List dTrackVulns) { 13 | this.dTrackVulns = dTrackVulns; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/DTrackProject.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | public class DTrackProject { 4 | private String name; 5 | private String uuid; 6 | 7 | public String getName() { 8 | return name; 9 | } 10 | 11 | public void setName(String name) { 12 | this.name = name; 13 | } 14 | 15 | public String getUuid() { 16 | return uuid; 17 | } 18 | 19 | public void setUuid(String uuid) { 20 | this.uuid = uuid; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/DTrackVuln.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | import java.sql.Timestamp; 4 | import java.util.List; 5 | 6 | public class DTrackVuln { 7 | private String vulnId; 8 | private String source; 9 | private String description; 10 | private Timestamp published; 11 | private String recommendation; 12 | private String references; 13 | private String Severity; 14 | private List components; 15 | 16 | public String getVulnId() { 17 | return vulnId; 18 | } 19 | 20 | public void setVulnId(String vulnId) { 21 | this.vulnId = vulnId; 22 | } 23 | 24 | public String getSource() { 25 | return source; 26 | } 27 | 28 | public void setSource(String source) { 29 | this.source = source; 30 | } 31 | 32 | public String getDescription() { 33 | return description; 34 | } 35 | 36 | public void setDescription(String description) { 37 | this.description = description; 38 | } 39 | 40 | public Timestamp getPublished() { 41 | return published; 42 | } 43 | 44 | public void setPublished(Timestamp published) { 45 | this.published = published; 46 | } 47 | 48 | public String getSeverity() { 49 | return Severity; 50 | } 51 | 52 | public void setSeverity(String severity) { 53 | Severity = severity; 54 | } 55 | 56 | public List getComponents() { 57 | return components; 58 | } 59 | 60 | public void setComponents(List components) { 61 | this.components = components; 62 | } 63 | 64 | public String getRecommendation() { 65 | return recommendation; 66 | } 67 | 68 | public void setRecommendation(String recommendation) { 69 | this.recommendation = recommendation; 70 | } 71 | 72 | public String getReferences() { 73 | return references; 74 | } 75 | 76 | public void setReferences(String references) { 77 | this.references = references; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/DependencyTrackConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 18:19 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.integrations.model; 7 | 8 | import java.util.List; 9 | 10 | public class DependencyTrackConfiguration { 11 | String uuid; 12 | String name; 13 | List apiKeys; 14 | 15 | public List getApiKeys() { 16 | return apiKeys; 17 | } 18 | 19 | public void setApiKeys(List apiKeys) { 20 | this.apiKeys = apiKeys; 21 | } 22 | 23 | public String getUuid() { 24 | return uuid; 25 | } 26 | 27 | public void setUuid(String uuid) { 28 | this.uuid = uuid; 29 | } 30 | 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/ProgPilotVuln.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | /** 6 | * @author gsiewruk 7 | */ 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class ProgPilotVuln { 10 | private int[] source_line; 11 | private String[] source_file; 12 | private String vuln_name; 13 | private String vuln_cwe; 14 | 15 | 16 | 17 | public String getVuln_name() { 18 | return vuln_name; 19 | } 20 | 21 | public void setVuln_name(String vuln_name) { 22 | this.vuln_name = vuln_name; 23 | } 24 | 25 | public String getVuln_cwe() { 26 | return vuln_cwe; 27 | } 28 | 29 | public void setVuln_cwe(String vuln_cwe) { 30 | this.vuln_cwe = vuln_cwe; 31 | } 32 | 33 | public int[] getSource_line() { 34 | return source_line; 35 | } 36 | 37 | public void setSource_line(int[] source_line) { 38 | this.source_line = source_line; 39 | } 40 | 41 | public String[] getSource_file() { 42 | return source_file; 43 | } 44 | 45 | public void setSource_file(String[] source_file) { 46 | this.source_file = source_file; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/SendBomRequest.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | /** 4 | * @author gsiewruk 5 | */ 6 | public class SendBomRequest { 7 | 8 | private String project; 9 | private String bom; 10 | 11 | 12 | public SendBomRequest(String project, String bom) { 13 | this.project = project; 14 | this.bom = bom; 15 | } 16 | 17 | public String getProject() { 18 | return project; 19 | } 20 | 21 | public void setProject(String project) { 22 | this.project = project; 23 | } 24 | 25 | public String getBom() { 26 | return bom; 27 | } 28 | 29 | public void setBom(String bom) { 30 | this.bom = bom; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/SourceLine.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | import javax.xml.bind.annotation.XmlAccessType; 4 | import javax.xml.bind.annotation.XmlAccessorType; 5 | import javax.xml.bind.annotation.XmlAttribute; 6 | import javax.xml.bind.annotation.XmlRootElement; 7 | 8 | /** 9 | * @author gsiewruk 10 | */ 11 | @XmlRootElement(name = "SourceLine") 12 | @XmlAccessorType(XmlAccessType.FIELD) 13 | public class SourceLine { 14 | @XmlAttribute 15 | private String start; 16 | @XmlAttribute 17 | private String end; 18 | @XmlAttribute 19 | private String sourcefile; 20 | @XmlAttribute 21 | private String classname; 22 | @XmlAttribute 23 | private String sourcepath; 24 | 25 | public String getStart() { 26 | return start; 27 | } 28 | 29 | public void setStart(String start) { 30 | this.start = start; 31 | } 32 | 33 | public String getEnd() { 34 | return end; 35 | } 36 | 37 | public void setEnd(String end) { 38 | this.end = end; 39 | } 40 | 41 | public String getSourcefile() { 42 | return sourcefile; 43 | } 44 | 45 | public void setSourcefile(String sourcefile) { 46 | this.sourcefile = sourcefile; 47 | } 48 | 49 | public String getClassname() { 50 | return classname; 51 | } 52 | 53 | public void setClassname(String classname) { 54 | this.classname = classname; 55 | } 56 | 57 | public String getSourcepath() { 58 | return sourcepath; 59 | } 60 | 61 | public void setSourcepath(String sourcepath) { 62 | this.sourcepath = sourcepath; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/model/SpotbugReportXML.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.model; 2 | 3 | import javax.xml.bind.annotation.XmlAccessType; 4 | import javax.xml.bind.annotation.XmlAccessorType; 5 | import javax.xml.bind.annotation.XmlElement; 6 | import javax.xml.bind.annotation.XmlRootElement; 7 | import java.util.List; 8 | 9 | /** 10 | * @author gsiewruk 11 | */ 12 | @XmlRootElement(name = "BugCollection") 13 | @XmlAccessorType(XmlAccessType.FIELD) 14 | public class SpotbugReportXML { 15 | @XmlElement(name="BugInstance") 16 | List bugInstanceList; 17 | @XmlElement(name="BugPattern") 18 | List bugPatterns; 19 | 20 | public List getBugPatterns() { 21 | return bugPatterns; 22 | } 23 | 24 | public void setBugPatterns(List bugPatterns) { 25 | this.bugPatterns = bugPatterns; 26 | } 27 | 28 | public List getBugInstanceList() { 29 | return bugInstanceList; 30 | } 31 | 32 | public void setBugInstanceList(List bugInstanceList) { 33 | this.bugInstanceList = bugInstanceList; 34 | } 35 | 36 | public SpotbugReportXML processSeverity() { 37 | if (this.bugInstanceList != null) { 38 | this.bugInstanceList.stream().filter(bi -> bi.getPriority() == null).forEach(info -> info.setPriority("Info")); 39 | this.bugInstanceList.stream().filter(bi -> bi.getPriority().equals("1")).forEach(high -> high.setPriority("High")); 40 | this.bugInstanceList.stream().filter(bi -> bi.getPriority().equals("2")).forEach(high -> high.setPriority("Medium")); 41 | this.bugInstanceList.stream().filter(bi -> bi.getPriority().equals("3")).forEach(high -> high.setPriority("Low")); 42 | this.bugInstanceList.stream().filter(bi -> bi.getPriority().equals("4")).forEach(high -> high.setPriority("Info")); 43 | } 44 | return this; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/scanner/Bandit.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.scanner; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import io.mixeway.scanner.integrations.ScannerIntegrationFactory; 5 | import io.mixeway.scanner.integrations.model.BanditResponse; 6 | import io.mixeway.scanner.integrations.model.BanditResult; 7 | import io.mixeway.scanner.rest.model.ScanRequest; 8 | import io.mixeway.scanner.utils.CodeHelper; 9 | import io.mixeway.scanner.utils.Vulnerability; 10 | import org.apache.commons.io.IOUtils; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.security.core.parameters.P; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.io.File; 17 | import java.io.IOException; 18 | import java.nio.charset.StandardCharsets; 19 | import java.util.ArrayList; 20 | import java.util.Collection; 21 | import java.util.List; 22 | 23 | /** 24 | * Bandit Python scanner integration - https://github.com/PyCQA/bandit 25 | * @author gsiewruk 26 | */ 27 | @Component 28 | public class Bandit implements ScannerIntegrationFactory { 29 | private final static Logger log = LoggerFactory.getLogger(Spotbug.class); 30 | @Override 31 | public void prepare() throws Exception { 32 | 33 | } 34 | 35 | /** 36 | * Running Bandit scan with 'bandit -r . --format json' and then parse json response into object 37 | * 38 | * @param scanRequest to get directory 39 | * @throws Exception 40 | */ 41 | @Override 42 | public List runScan(ScanRequest scanRequest) throws Exception { 43 | log.info("[Bandit] Starting to Scan app {}", scanRequest.getTarget()); 44 | String projectDirectory = CodeHelper.getProjectPath(scanRequest, false); 45 | ProcessBuilder packageApp = new ProcessBuilder("bash", "-c", "bandit -r . --format json > bandit.vulns"); 46 | packageApp.directory(new File(projectDirectory)); 47 | Process packageAppProcess = packageApp.start(); 48 | packageAppProcess.waitFor(); 49 | 50 | BanditResponse banditResponse = processBanditReport(projectDirectory + File.separatorChar + "bandit.vulns"); 51 | log.info("[Bandit] Scan completed"); 52 | return convertBanditResposeIntoVulnerabilities(banditResponse); 53 | } 54 | 55 | private List convertBanditResposeIntoVulnerabilities(BanditResponse banditResponse) { 56 | List vulnerabilities = new ArrayList<>(); 57 | for (BanditResult banditResult : banditResponse.getResults()){ 58 | vulnerabilities.add(new Vulnerability(banditResult)); 59 | } 60 | return vulnerabilities; 61 | } 62 | 63 | /** 64 | * Parsing bandit's json response into object 65 | * 66 | * @param reportPath path to bandit report 67 | */ 68 | private BanditResponse processBanditReport(String reportPath) throws IOException { 69 | ObjectMapper objectMapper = new ObjectMapper(); 70 | return objectMapper.readValue(new File(reportPath), BanditResponse.class); 71 | } 72 | 73 | @Override 74 | public List runScanStandalone() throws Exception { 75 | log.info("[Bandit] Starting to Scan app "); 76 | String projectDirectory = CodeHelper.getProjectPath(new ScanRequest(), true); 77 | ProcessBuilder packageApp = new ProcessBuilder("bash", "-c", "bandit -r . --format json > bandit.vulns"); 78 | packageApp.directory(new File(projectDirectory)); 79 | Process packageAppProcess = packageApp.start(); 80 | packageAppProcess.waitFor(); 81 | 82 | BanditResponse banditResponse = processBanditReport(projectDirectory + File.separatorChar + "bandit.vulns"); 83 | log.info("[Bandit] Scan completed"); 84 | return convertBanditResposeIntoVulnerabilities(banditResponse); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/scanner/DependencyTrack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 22:43 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.integrations.scanner; 7 | 8 | import io.mixeway.scanner.db.entity.DependencyTrackEntity; 9 | import io.mixeway.scanner.db.repository.DependencyTrackRepository; 10 | import io.mixeway.scanner.integrations.ScannerIntegrationFactory; 11 | import io.mixeway.scanner.integrations.model.*; 12 | import io.mixeway.scanner.rest.model.ScanRequest; 13 | import io.mixeway.scanner.utils.Constants; 14 | import io.mixeway.scanner.utils.CodeHelper; 15 | import io.mixeway.scanner.utils.SourceProjectType; 16 | import io.mixeway.scanner.utils.Vulnerability; 17 | import org.apache.commons.io.FileUtils; 18 | import org.apache.tomcat.util.codec.binary.Base64; 19 | import org.hobsoft.spring.resttemplatelogger.LoggingCustomizer; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.beans.factory.annotation.Value; 24 | import org.springframework.boot.web.client.RestTemplateBuilder; 25 | import org.springframework.core.ParameterizedTypeReference; 26 | import org.springframework.http.*; 27 | import org.springframework.stereotype.Component; 28 | import org.springframework.web.client.HttpClientErrorException; 29 | import org.springframework.web.client.HttpServerErrorException; 30 | import org.springframework.web.client.ResourceAccessException; 31 | import org.springframework.web.client.RestTemplate; 32 | 33 | import java.io.File; 34 | import java.io.IOException; 35 | import java.nio.charset.StandardCharsets; 36 | import java.util.*; 37 | import java.util.concurrent.TimeUnit; 38 | 39 | @Component 40 | public class DependencyTrack implements ScannerIntegrationFactory { 41 | private final static Logger log = LoggerFactory.getLogger(DependencyTrack.class); 42 | private DependencyTrackRepository dependencyTrackRepository; 43 | @Value("${sonatype.oss.username}") 44 | private String ossUsername; 45 | @Value("${sonatype.oss.key}") 46 | private String ossKey; 47 | 48 | @Autowired 49 | public DependencyTrack(DependencyTrackRepository dependencyTrackRepository){ 50 | this.dependencyTrackRepository = dependencyTrackRepository; 51 | } 52 | 53 | public DependencyTrack(){} 54 | 55 | /** 56 | * Verify if Dependency Track is initialized - DependencyTrack has enabled=true and apiKey not null 57 | */ 58 | @Override 59 | public void prepare() throws Exception { 60 | Optional dependencyTrackEntity = dependencyTrackRepository.findByEnabledAndApiKeyNotNull(true); 61 | if (!dependencyTrackEntity.isPresent()){ 62 | changePassword(); 63 | getApiKey(); 64 | setOssIntegration(); 65 | } 66 | } 67 | 68 | /** 69 | * Loading vulnerabilities from dependency-track 70 | * @param dependencyTrack to get url and api 71 | * @param uuid of project to check 72 | */ 73 | private List loadVulnerabilities(DependencyTrackEntity dependencyTrack, String uuid ){ 74 | RestTemplate restTemplate = new RestTemplate(); 75 | HttpHeaders headers = new HttpHeaders(); 76 | headers.set(Constants.DEPENDENCYTRACK_APIKEY_HEADER, dependencyTrack.getApiKey()); 77 | HttpEntity entity = new HttpEntity<>(headers); 78 | ResponseEntity> response = restTemplate.exchange(Constants.DEPENDENCYTRACK_URL + 79 | Constants.DEPENDENCYTRACK_URL_VULNS + uuid, HttpMethod.GET, entity, new ParameterizedTypeReference>() { 80 | }); 81 | if (response.getStatusCode() == HttpStatus.OK) { 82 | return response.getBody(); 83 | } else { 84 | log.error("[Dependency Track] Unable to get Findings from Dependency Track for project {}", uuid); 85 | } 86 | return null; 87 | } 88 | 89 | /** 90 | * Setting sonatype integration 91 | * 92 | */ 93 | private void setOssIntegration() throws Exception { 94 | Optional dependencyTrack = dependencyTrackRepository.findByEnabledAndApiKeyNotNull(false); 95 | if (!dependencyTrack.isPresent()) { 96 | throw new Exception("[Dependency Track] Cannot change config for not initialized scanner"); 97 | } 98 | RestTemplate restTemplate = new RestTemplate(); 99 | HttpHeaders headers = new HttpHeaders(); 100 | headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + getOAuthToken()); 101 | headers.setContentType(MediaType.APPLICATION_JSON); 102 | HttpEntity entity = new HttpEntity<>(prepareOssIntegration(ossUsername,ossKey),headers); 103 | ResponseEntity response = restTemplate.exchange(Constants.DEPENDENCYTRACK_URL + 104 | Constants.DEPENDENCYTRACK_URL_OSS_CONFIG, HttpMethod.POST, entity, String.class); 105 | if (response.getStatusCode().equals(HttpStatus.OK)){ 106 | dependencyTrack.get().setEnabled(true); 107 | dependencyTrackRepository.save(dependencyTrack.get()); 108 | log.info("[Dependency Track] Successfully set OSS integration. DependencyTrack activated"); 109 | } 110 | } 111 | 112 | /** 113 | * Preparing request body for seting sonatype oss integration 114 | * 115 | * @param ossUsername username taken from ENV variable 116 | * @param ossKey apiKey taken from ENV variable 117 | * @return array of elements 118 | */ 119 | private DTrackConfigProperty[] prepareOssIntegration(String ossUsername, String ossKey) { 120 | return new DTrackConfigProperty[]{ 121 | new DTrackConfigProperty("scanner","ossindex.enabled", true), 122 | new DTrackConfigProperty("scanner","ossindex.api.username", ossUsername), 123 | new DTrackConfigProperty("scanner","ossindex.api.token", ossKey) 124 | }; 125 | } 126 | 127 | /** 128 | * Using obtained oAuth token to check apiKey which is set for Automation team and then save it to DB 129 | */ 130 | private void getApiKey() { 131 | RestTemplate restTemplate = new RestTemplate(); 132 | HttpHeaders headers = new HttpHeaders(); 133 | headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + getOAuthToken()); 134 | HttpEntity entity = new HttpEntity<>(headers); 135 | ResponseEntity response = restTemplate.exchange(Constants.DEPENDENCYTRACK_URL + 136 | Constants.DEPENDENCYTRACK_URL_APIKEY, HttpMethod.GET, entity, DependencyTrackConfiguration[].class); 137 | if (response.getStatusCode().equals(HttpStatus.OK)){ 138 | List dependencyTrackConfigurations = Arrays.asList(response.getBody()); 139 | String apiKey = dependencyTrackConfigurations 140 | .stream() 141 | .filter(c -> c.getName().equals(Constants.DEPENDENCYTRACK_AUTOMATION)) 142 | .findFirst() 143 | .orElse(null) 144 | .getApiKeys() 145 | .stream().findFirst().orElse(null) 146 | .getKey(); 147 | String automationTeamUuid = dependencyTrackConfigurations 148 | .stream() 149 | .filter(c -> c.getName().equals(Constants.DEPENDENCYTRACK_AUTOMATION)) 150 | .findFirst() 151 | .orElse(null) 152 | .getUuid(); 153 | setPermissions(automationTeamUuid); 154 | dependencyTrackRepository.save(new DependencyTrackEntity(apiKey)); 155 | log.info("[Dependency Track] Successfully saved apiKey"); 156 | } 157 | 158 | } 159 | 160 | /** 161 | * Default API user doesn't have permission to view or create projects, those hase to be added 162 | * 163 | * @param automationTeamUuid 164 | */ 165 | private void setPermissions(String automationTeamUuid) { 166 | RestTemplate restTemplate = new RestTemplateBuilder() 167 | .customizers(new LoggingCustomizer()) 168 | .build(); 169 | HttpHeaders headers = new HttpHeaders(); 170 | headers.setBearerAuth(getOAuthToken()); 171 | headers.setContentType(MediaType.APPLICATION_JSON); 172 | HttpEntity entity = new HttpEntity<>("{}",headers); 173 | String[] permissions = new String[] {"ACCESS_MANAGEMENT", "BOM_UPLOAD","PORTFOLIO_MANAGEMENT","PROJECT_CREATION_UPLOAD","SYSTEM_CONFIGURATION","VIEW_PORTFOLIO","VULNERABILITY_ANALYSIS"}; 174 | for (String permision : permissions) { 175 | ResponseEntity response = restTemplate.exchange(Constants.DEPENDENCYTRACK_URL + 176 | Constants.DEPENDENCYTRACK_URL_PERMISSIONS + permision + "/team/" + automationTeamUuid, HttpMethod.POST, entity, String.class); 177 | 178 | } 179 | log.info("[Dependency Track] Permission for API enabled"); 180 | } 181 | 182 | 183 | /** 184 | * whole scan logic, get UUID or create project, generate BOM, upload it to DTrack and then load vulnerabilities 185 | * 186 | * @throws Exception 187 | */ 188 | @Override 189 | public List runScan(ScanRequest scanRequest) throws Exception { 190 | this.prepare(); 191 | Optional dependencyTrack = dependencyTrackRepository.findByEnabledAndApiKeyNotNull(true); 192 | if(dependencyTrack.isPresent()) { 193 | String uuid = getDTrackProjectUuid(dependencyTrack.get(),scanRequest, false); 194 | SourceProjectType sourceProjectType = CodeHelper.getSourceProjectTypeFromDirectory(scanRequest, false); 195 | if (sourceProjectType == null){ 196 | throw new Exception("Unknown project type. Supported: MVN, NPM, Composer, PIP"); 197 | } 198 | log.info("[Dependency Track] Get UUID {} and type of project {}", uuid, sourceProjectType); 199 | String bomPath = generateBom(scanRequest, sourceProjectType, false, null); 200 | if (bomPath == null) { 201 | throw new Exception("SBOM path appears to be null"); 202 | } 203 | sendBomToDTrack(dependencyTrack.get(), uuid, bomPath); 204 | //Sleep untill DTrack audit the bom 205 | TimeUnit.SECONDS.sleep(50); 206 | log.info("[Dependency Track] Scan completed"); 207 | return convertDTrackResponseToVulnerabilities(loadVulnerabilities(dependencyTrack.get(),uuid)); 208 | } else { 209 | log.error("[Dependency Track] Trying to run scan on not properly initialized scanner. " + 210 | "This should not happen, please collect log and issue ticket."); 211 | } 212 | return new ArrayList<>(); 213 | } 214 | 215 | 216 | private List convertDTrackResponseToVulnerabilities(List loadVulnerabilities) { 217 | List vulnerabilities = new ArrayList<>(); 218 | for (DTrackVuln dTrackVuln : loadVulnerabilities){ 219 | vulnerabilities.add(new Vulnerability(dTrackVuln)); 220 | } 221 | return vulnerabilities; 222 | } 223 | 224 | /** 225 | * Running standalone scan 226 | */ 227 | @Override 228 | public List runScanStandalone() throws Exception { 229 | this.prepare(); 230 | Optional dependencyTrack = dependencyTrackRepository.findByEnabledAndApiKeyNotNull(true); 231 | if(dependencyTrack.isPresent()) { 232 | String uuid = getDTrackProjectUuid(dependencyTrack.get(),new ScanRequest(), true); 233 | SourceProjectType sourceProjectType = CodeHelper.getSourceProjectTypeFromDirectory(new ScanRequest(), true); 234 | if (sourceProjectType == null){ 235 | throw new Exception("Unknown project type. Supported: MVN, NPM, Composer, PIP"); 236 | } 237 | log.info("[Dependency Track] Get UUID {} and type of project {}", uuid, sourceProjectType); 238 | String bomPath = generateBom(new ScanRequest(), sourceProjectType, true,null); 239 | if (bomPath == null) { 240 | throw new Exception("SBOM path appears to be null"); 241 | } 242 | sendBomToDTrack(dependencyTrack.get(), uuid, bomPath); 243 | //Sleep untill DTrack audit the bom 244 | TimeUnit.SECONDS.sleep(60); 245 | log.info("[Dependency Track] Scan completed"); 246 | return convertDTrackResponseToVulnerabilities(loadVulnerabilities(dependencyTrack.get(),uuid)); 247 | } else { 248 | log.error("[Dependency Track] Trying to run scan on not properly initialized scanner. " + 249 | "This should not happen, please collect log and issue ticket."); 250 | } 251 | return new ArrayList<>(); 252 | } 253 | 254 | /** 255 | * Method which uploads SBOM to Dependency Track 256 | * 257 | * @param dependencyTrackEntity for URL and api key 258 | * @param uuid UUID of project on DTrack to attach SBOM 259 | * @param bomPath location of file to upload 260 | */ 261 | private void sendBomToDTrack(DependencyTrackEntity dependencyTrackEntity, String uuid, String bomPath) throws IOException { 262 | RestTemplate restTemplate = new RestTemplate(); 263 | HttpHeaders headers = new HttpHeaders(); 264 | headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); 265 | headers.set(Constants.DEPENDENCYTRACK_APIKEY_HEADER, dependencyTrackEntity.getApiKey()); 266 | HttpEntity entity = new HttpEntity<>(new SendBomRequest(uuid, encodeFileToBase64Binary(bomPath)), headers); 267 | ResponseEntity response = restTemplate.exchange(Constants.DEPENDENCYTRACK_URL + 268 | Constants.DEPENDENCYTRACK_URL_UPLOAD_BOM, HttpMethod.PUT, entity, String.class); 269 | if (response.getStatusCode().equals(HttpStatus.OK)){ 270 | log.info("[Dependency Track] SBOM for {} uploaded successfully", uuid); 271 | } 272 | } 273 | 274 | /** 275 | * Encodes file content to base64 276 | * @param fileName file name to encode 277 | * @return return base64 string 278 | * @throws IOException 279 | */ 280 | private static String encodeFileToBase64Binary(String fileName) throws IOException { 281 | File file = new File(fileName); 282 | byte[] encoded = Base64.encodeBase64(FileUtils.readFileToByteArray(file)); 283 | return new String(encoded, StandardCharsets.US_ASCII); 284 | } 285 | 286 | /** 287 | * Generation of SBOM using CycloneDX plugin which depend on technology. Required for this method is language of project and path to location 288 | * 289 | * @param scanRequest needed to get location of source code 290 | * @param sourceProjectType needed to determine which type of execution to be done 291 | * @return return path to SBOM file 292 | */ 293 | private String generateBom(ScanRequest scanRequest, SourceProjectType sourceProjectType, boolean standalone, String path) throws IOException, InterruptedException { 294 | String directory = path!=null ? path : CodeHelper.getProjectPath(scanRequest, standalone); 295 | ProcessBuilder install, generate; 296 | Process installProcess, generateProcess; 297 | 298 | switch (sourceProjectType) { 299 | case PIP: 300 | ProcessBuilder freeze = new ProcessBuilder("bash", "-c", "pipreqs . --force"); 301 | install = new ProcessBuilder("bash", "-c", "pip3 install cyclonedx-bom"); 302 | generate = new ProcessBuilder("bash", "-c", "cyclonedx-py -i requirements.txt -o bom.xml"); 303 | freeze.directory(new File(directory)); 304 | Process freezeProcess = freeze.start(); 305 | freezeProcess.waitFor(); 306 | log.info("[Dependency Track] Freezing PIP dependencies for {}", directory); 307 | install.directory(new File(directory)); 308 | installProcess = install.start(); 309 | installProcess.waitFor(); 310 | log.info("[Dependency Track] Installed CycloneDX PIP for {}", directory); 311 | generate.directory(new File(directory)); 312 | generateProcess = generate.start(); 313 | generateProcess.waitFor(); 314 | log.info("[Dependency Track] Generated SBOM for {}", directory); 315 | return directory + File.separatorChar + "bom.xml"; 316 | case NPM: 317 | install = new ProcessBuilder("bash", "-c", "npm install -g @cyclonedx/bom"); 318 | install.directory(new File(directory)); 319 | installProcess = install.start(); 320 | installProcess.waitFor(); 321 | log.info("[Dependency Track] Installed CycloneDX NPM for {}", directory); 322 | generate = new ProcessBuilder("bash", "-c", "cyclonedx-bom -o bom.xml"); 323 | generate.directory(new File(directory)); 324 | generateProcess = generate.start(); 325 | generateProcess.waitFor(); 326 | log.info("[Dependency Track] Generated SBOM for {}", directory); 327 | return directory + File.separatorChar + "bom.xml"; 328 | case MAVEN: 329 | generate = new ProcessBuilder("bash", "-c", "mvn -DskipTests -DSPDXParser.OnlyUseLocalLicenses=true org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom").inheritIO(); 330 | generate.directory(new File(directory)); 331 | generateProcess = generate.start(); 332 | generateProcess.waitFor(); 333 | log.info("[Dependency Track] Generated SBOM for {}", directory); 334 | return directory + File.separatorChar + "target" + File.separatorChar + "bom.xml"; 335 | case GRADLE: 336 | log.error("[Dependency Track] GRADLE not yet supported"); 337 | break; 338 | case PHP: 339 | install = new ProcessBuilder("bash", "-c", "composer require --dev cyclonedx/cyclonedx-php-composer"); 340 | install.directory(new File(directory)); 341 | installProcess = install.start(); 342 | installProcess.waitFor(); 343 | log.info("[Dependency Track] Installed CycloneDX COMPOSER for {}", directory); 344 | generate = new ProcessBuilder("bash", "-c", "composer make-bom"); 345 | generate.directory(new File(directory)); 346 | generateProcess = generate.start(); 347 | generateProcess.waitFor(); 348 | log.info("[Dependency Track] Generated SBOM for {}", directory); 349 | return directory + File.separatorChar + "bom.xml"; 350 | default: 351 | return null; 352 | } 353 | return null; 354 | } 355 | 356 | /** 357 | * Getting oAuth token for Dependency track using default username and password 358 | * 359 | * @return oAuth token 360 | */ 361 | private String getOAuthToken(){ 362 | RestTemplate restTemplate = new RestTemplate(); 363 | HttpHeaders headers = new HttpHeaders(); 364 | headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); 365 | HttpEntity entity = new HttpEntity<>(Constants.DEPENDENCYTRACK_LOGIN_STRING, headers); 366 | ResponseEntity response = restTemplate.exchange(Constants.DEPENDENCYTRACK_URL + 367 | Constants.DEPENDENCYTRACK_URL_LOGIN, HttpMethod.POST, entity, String.class); 368 | if (response.getStatusCode().equals(HttpStatus.OK)){ 369 | return response.getBody(); 370 | } 371 | return null; 372 | } 373 | 374 | /** 375 | * Loading projects from DependencyTrack to check if project is already created on platform 376 | * 377 | * @return list of projects 378 | */ 379 | private List getProjects(DependencyTrackEntity dependencyTrackEntity) { 380 | RestTemplate restTemplate = new RestTemplate(); 381 | HttpHeaders headers = new HttpHeaders(); 382 | headers.set(Constants.DEPENDENCYTRACK_APIKEY_HEADER, dependencyTrackEntity.getApiKey()); 383 | HttpEntity entity = new HttpEntity<>(headers); 384 | try { 385 | ResponseEntity> response = restTemplate.exchange(Constants.DEPENDENCYTRACK_URL + 386 | Constants.DEPENDENCYTRACK_GET_PROJECTS, HttpMethod.GET, entity, new ParameterizedTypeReference>() {}); 387 | if (response.getStatusCode() == HttpStatus.OK) { 388 | return response.getBody(); 389 | } else { 390 | log.error("[Dependency Track] Unable to load Dependency Track projects"); 391 | } 392 | } catch (HttpClientErrorException | HttpServerErrorException | ResourceAccessException e){ 393 | log.error("[Dependency Track] Error during getting Dependency Track project list {}", e.getLocalizedMessage()); 394 | } 395 | return null; 396 | } 397 | 398 | /** 399 | * Saving project to DependencyTrack 400 | * 401 | * @param dependencyTrackEntity needed for ApiKey 402 | * @param name name of project to be created 403 | * @return uuid of project 404 | */ 405 | private String createProject(DependencyTrackEntity dependencyTrackEntity, String name, String branch, boolean standalone) { 406 | RestTemplate restTemplate = new RestTemplate(); 407 | HttpHeaders headers = new HttpHeaders(); 408 | headers.set(Constants.DEPENDENCYTRACK_APIKEY_HEADER, dependencyTrackEntity.getApiKey()); 409 | headers.setContentType(MediaType.APPLICATION_JSON); 410 | HttpEntity entity = new HttpEntity<>(new DTrackCreateProject(standalone? UUID.randomUUID().toString() : name + "_" + branch),headers); 411 | try { 412 | ResponseEntity response = restTemplate.exchange(Constants.DEPENDENCYTRACK_URL + 413 | Constants.DEPENDENCYTRACK_GET_PROJECTS, HttpMethod.PUT, entity, DTrackCreateProjectResponse.class); 414 | if (response.getStatusCode() == HttpStatus.CREATED) { 415 | log.info("[Dependency Track] Successfully created Dependency Track project for {} with UUID {}", name ,response.getBody().getUuid()); 416 | return response.getBody().getUuid(); 417 | } else { 418 | log.error("[Dependency Track] Unable to to create project Dependency Track for project {}", name); 419 | } 420 | } catch (HttpClientErrorException | HttpServerErrorException e){ 421 | log.error("[Dependency Track] Error during Creation of project for {} with code {}", name, e.getStatusCode()); 422 | } 423 | return null; 424 | } 425 | 426 | /** 427 | * Change default admin password - DTrack require admin after first login to force password change 428 | * 429 | */ 430 | private void changePassword() { 431 | RestTemplate restTemplate = new RestTemplate(); 432 | HttpHeaders headers = new HttpHeaders(); 433 | headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); 434 | HttpEntity entity = new HttpEntity<>(Constants.DEPENDENCYTRACK_CHANGE_PASSWORD_STRING, headers); 435 | ResponseEntity response = restTemplate.exchange(Constants.DEPENDENCYTRACK_URL + 436 | Constants.DEPENDENCYTRACK_URL_CHANGE_PASSWORD, HttpMethod.POST, entity, String.class); 437 | if (response.getStatusCode().equals(HttpStatus.OK)){ 438 | log.info("[Dependency Track] Default admin password changed"); 439 | } 440 | } 441 | 442 | /** 443 | * Method which return UUID for project on DependencyTrack by ScanRequest.class. 444 | * If project is present it simply return its UUID, if project is not present it is being created 445 | * 446 | * @param dependencyTrackEntity entity for apiKey usage 447 | * @param scanRequest request with name and url repo 448 | * @return UUID on dependency track 449 | */ 450 | private String getDTrackProjectUuid(DependencyTrackEntity dependencyTrackEntity, ScanRequest scanRequest, boolean standalone){ 451 | List dTrackProjects = getProjects(dependencyTrackEntity); 452 | String projectName; 453 | if (standalone){ 454 | projectName = UUID.randomUUID().toString(); 455 | } else { 456 | projectName = CodeHelper.getNameFromRepoUrlforSAST(scanRequest.getTarget(), standalone) + "_" + scanRequest.getBranch(); 457 | } 458 | if (dTrackProjects!= null && dTrackProjects.size() > 0) { 459 | Optional dTrackProject = dTrackProjects 460 | .stream() 461 | .filter(p -> p.getName().equals(projectName)) 462 | .findFirst(); 463 | if (dTrackProject.isPresent()){ 464 | return dTrackProject.get().getUuid(); 465 | } else { 466 | return createProject(dependencyTrackEntity, CodeHelper.getNameFromRepoUrlforSAST(scanRequest.getTarget(), standalone), scanRequest.getBranch(), standalone); 467 | } 468 | } else { 469 | return createProject(dependencyTrackEntity, CodeHelper.getNameFromRepoUrlforSAST(scanRequest.getTarget(), standalone), scanRequest.getBranch(), standalone); 470 | } 471 | } 472 | 473 | /** 474 | * Running DTrack scan for given path, NPM only supported 475 | * @param packagePath path where package.json is located 476 | * @return 477 | */ 478 | public List runScanStandalone(List packagePath) throws Exception { 479 | this.prepare(); 480 | List vulns = new ArrayList<>(); 481 | List uuids = new ArrayList<>(); 482 | Optional dependencyTrack = dependencyTrackRepository.findByEnabledAndApiKeyNotNull(true); 483 | if(dependencyTrack.isPresent()) { 484 | for (String path : packagePath) { 485 | String uuid = getDTrackProjectUuid(dependencyTrack.get(), new ScanRequest(), true); 486 | //Only NPM supported ATM 487 | //SourceProjectType sourceProjectType = CodeHelper.getSourceProjectTypeFromDirectory(new ScanRequest(), true); 488 | 489 | log.info("[Dependency Track] Get UUID {} and type of project {}", uuid, SourceProjectType.NPM); 490 | String bomPath = generateBom(new ScanRequest(), SourceProjectType.NPM, true, path); 491 | if (bomPath == null) { 492 | throw new Exception("SBOM path appears to be null"); 493 | } 494 | sendBomToDTrack(dependencyTrack.get(), uuid, bomPath); 495 | uuids.add(uuid); 496 | log.info("[Dependency Track] Scan completed for {}", path); 497 | } 498 | //Sleep untill DTrack audit the bom 499 | TimeUnit.SECONDS.sleep(60); 500 | for (String uuid : uuids){ 501 | vulns.addAll(convertDTrackResponseToVulnerabilities(loadVulnerabilities(dependencyTrack.get(), uuid))); 502 | } 503 | return vulns; 504 | 505 | } else { 506 | log.error("[Dependency Track] Trying to run scan on not properly initialized scanner. " + 507 | "This should not happen, please collect log and issue ticket."); 508 | } 509 | return new ArrayList<>(); 510 | } 511 | } 512 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/scanner/Progpilot.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.integrations.scanner; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import io.mixeway.scanner.integrations.ScannerIntegrationFactory; 6 | import io.mixeway.scanner.integrations.model.BanditResponse; 7 | import io.mixeway.scanner.integrations.model.ProgPilotVuln; 8 | import io.mixeway.scanner.rest.model.ScanRequest; 9 | import io.mixeway.scanner.utils.CodeHelper; 10 | import io.mixeway.scanner.utils.Vulnerability; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.security.core.parameters.P; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.io.File; 17 | import java.io.IOException; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * Progpilot PHP scanner integration - https://github.com/designsecurity/progpilot 23 | * @author gsiewruk 24 | */ 25 | @Component 26 | public class Progpilot implements ScannerIntegrationFactory { 27 | private final static Logger log = LoggerFactory.getLogger(Progpilot.class); 28 | @Override 29 | public void prepare() throws Exception { 30 | 31 | } 32 | 33 | @Override 34 | public List runScan(ScanRequest scanRequest) throws Exception { 35 | log.info("[Progpilot] Starting to package app {}", scanRequest.getTarget()); 36 | String projectDirectory = CodeHelper.getProjectPath(scanRequest, false); 37 | ProcessBuilder packageApp = new ProcessBuilder("bash", "-c", "progpilot . > progpilot.vulns"); 38 | packageApp.directory(new File(projectDirectory)); 39 | Process packageAppProcess = packageApp.start(); 40 | packageAppProcess.waitFor(); 41 | log.info("[Progpilot] Scan completed"); 42 | List progPilotVulns = processProgPilotReport(projectDirectory + File.separatorChar + "progpilot.vulns"); 43 | return convertProgpilotReportToVulns(progPilotVulns); 44 | } 45 | 46 | private List convertProgpilotReportToVulns(List progPilotVulns) { 47 | List vulnerabilities = new ArrayList<>(); 48 | for (ProgPilotVuln progPilotVuln : progPilotVulns) { 49 | vulnerabilities.add(new Vulnerability(progPilotVuln)); 50 | } 51 | return vulnerabilities; 52 | } 53 | 54 | @Override 55 | public List runScanStandalone() throws Exception { 56 | log.info("[Progpilot] Starting to package app "); 57 | String projectDirectory = CodeHelper.getProjectPath(new ScanRequest(), true); 58 | ProcessBuilder packageApp = new ProcessBuilder("bash", "-c", "progpilot . > progpilot.vulns"); 59 | packageApp.directory(new File(projectDirectory)); 60 | Process packageAppProcess = packageApp.start(); 61 | packageAppProcess.waitFor(); 62 | log.info("[Progpilot] Scan completed"); 63 | List progPilotVulns = processProgPilotReport(projectDirectory + File.separatorChar + "progpilot.vulns"); 64 | return convertProgpilotReportToVulns(progPilotVulns); 65 | } 66 | /** 67 | * Parsing bandit's json response into object 68 | * 69 | * @param reportPath path to bandit report 70 | */ 71 | private List processProgPilotReport(String reportPath) throws IOException { 72 | ObjectMapper objectMapper = new ObjectMapper(); 73 | return objectMapper.readValue(new File(reportPath), new TypeReference>(){}); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/integrations/scanner/Spotbug.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 22:43 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.integrations.scanner; 7 | 8 | 9 | import com.fasterxml.jackson.annotation.JsonInclude; 10 | import com.fasterxml.jackson.core.JsonProcessingException; 11 | import com.fasterxml.jackson.databind.DeserializationFeature; 12 | import com.fasterxml.jackson.dataformat.xml.XmlMapper; 13 | import io.mixeway.scanner.integrations.ScannerIntegrationFactory; 14 | import io.mixeway.scanner.integrations.model.BugInstance; 15 | import io.mixeway.scanner.integrations.model.SpotbugReportXML; 16 | import io.mixeway.scanner.rest.model.ScanRequest; 17 | import io.mixeway.scanner.rest.service.BaseService; 18 | import io.mixeway.scanner.utils.*; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.springframework.stereotype.Component; 22 | import org.w3c.dom.Document; 23 | import org.w3c.dom.Element; 24 | import org.w3c.dom.Node; 25 | import org.w3c.dom.NodeList; 26 | import org.xml.sax.InputSource; 27 | import org.xml.sax.SAXException; 28 | 29 | import javax.xml.bind.JAXBContext; 30 | import javax.xml.bind.JAXBException; 31 | import javax.xml.bind.Unmarshaller; 32 | import javax.xml.parsers.DocumentBuilder; 33 | import javax.xml.parsers.DocumentBuilderFactory; 34 | import javax.xml.parsers.ParserConfigurationException; 35 | import javax.xml.transform.Transformer; 36 | import javax.xml.transform.TransformerConfigurationException; 37 | import javax.xml.transform.TransformerException; 38 | import javax.xml.transform.TransformerFactory; 39 | import javax.xml.transform.dom.DOMSource; 40 | import javax.xml.transform.stream.StreamResult; 41 | import java.io.*; 42 | import java.nio.charset.StandardCharsets; 43 | import java.nio.file.Files; 44 | import java.nio.file.Path; 45 | import java.nio.file.Paths; 46 | import java.util.ArrayList; 47 | import java.util.List; 48 | import java.util.concurrent.TimeUnit; 49 | import java.util.stream.Collectors; 50 | import java.util.stream.Stream; 51 | 52 | @Component 53 | public class Spotbug implements ScannerIntegrationFactory { 54 | private final static Logger log = LoggerFactory.getLogger(Spotbug.class); 55 | @Override 56 | public void prepare() { 57 | 58 | } 59 | 60 | /** 61 | * Running proper command 62 | * @param scanRequest 63 | */ 64 | @Override 65 | public List runScan(ScanRequest scanRequest) throws IOException, InterruptedException { 66 | log.info("[Spotbug] Starting to package app {}", scanRequest.getTarget()); 67 | List spotbugReportXMLS = new ArrayList<>(); 68 | List reportPaths = new ArrayList<>(); 69 | String projectDirectory = CodeHelper.getProjectPath(scanRequest, false); 70 | log.info("[Spotbug] Starting to generate Spotbug report for {}", scanRequest.getTarget()); 71 | ProcessBuilder spotbug = new ProcessBuilder("bash", 72 | "-c", 73 | "mvn compile -DskipTests com.github.spotbugs:spotbugs-maven-plugin:spotbugs").inheritIO(); 74 | spotbug.directory(new File(projectDirectory)); 75 | Process spotbugProcess = spotbug.inheritIO().start(); 76 | spotbugProcess.waitFor(); 77 | log.info("[Spotbug] Report ready to process {}", scanRequest.getTarget()); 78 | SpotbugReportXML spotbugReportXML = processXmlReport(projectDirectory + File.separatorChar + "target" + File.separatorChar + "spotbugsXml.xml"); 79 | log.info("[Spotbug] Scan completed"); 80 | searchForReports(spotbugReportXMLS, projectDirectory); 81 | return convertSpotbugReportIntoVulnList(spotbugReportXMLS); 82 | } 83 | 84 | private void searchForReports(List spotbugReportXMLS, String projectDirectory) throws IOException { 85 | List reportPaths; 86 | try (Stream paths = Files.walk(Paths.get(projectDirectory))) { 87 | reportPaths = paths 88 | .filter(Files::isRegularFile) 89 | .filter(f -> f.getFileName().toString().equals("spotbugsXml.xml")) 90 | .collect(Collectors.toList()); 91 | } 92 | for (Path path : reportPaths){ 93 | spotbugReportXMLS.add(processXmlReport(path.toString())); 94 | } 95 | } 96 | 97 | /** 98 | * Converts spotbug XML report into Vulnerabilities 99 | * @param spotbugReportXMLs report to parse 100 | * @return list of shared items 101 | */ 102 | private List convertSpotbugReportIntoVulnList(List spotbugReportXMLs) { 103 | List vulnerabilities = new ArrayList<>(); 104 | for (SpotbugReportXML reportXML : spotbugReportXMLs) { 105 | if (reportXML.getBugInstanceList() !=null) { 106 | for (BugInstance bugInstance : reportXML.getBugInstanceList()) { 107 | vulnerabilities.add(new Vulnerability(bugInstance, 108 | reportXML 109 | .getBugPatterns() 110 | .stream() 111 | .filter(bugPattern -> bugPattern.getShortDescriptions().equals(bugInstance.getShortDescription())) 112 | .findFirst() 113 | .orElse(null))); 114 | } 115 | } 116 | } 117 | return vulnerabilities; 118 | } 119 | 120 | @Override 121 | public List runScanStandalone() throws IOException, InterruptedException { 122 | try { 123 | List spotbugReportXMLS = new ArrayList<>(); 124 | List reportPaths = new ArrayList<>(); 125 | String projectDirectory = CodeHelper.getProjectPath(new ScanRequest(), true); 126 | log.info("[Spotbug] Preparing POM, create backup and generate new"); 127 | preparePomforSpotbugAnalysis(projectDirectory); 128 | log.info("[Spotbug] Starting to generate Spotbug report for {}", projectDirectory); 129 | ProcessBuilder spotbug = new ProcessBuilder("bash", 130 | "-c", 131 | "mvn compile -DskipTests spotbugs:spotbugs").inheritIO(); 132 | spotbug.directory(new File(projectDirectory)); 133 | Process spotbugProcess = spotbug.start(); 134 | spotbugProcess.waitFor(5, TimeUnit.MINUTES); 135 | spotbugProcess.destroy(); 136 | spotbugProcess.waitFor(); 137 | log.info("[Spotbug] Report ready to process {}", projectDirectory); 138 | searchForReports(spotbugReportXMLS, projectDirectory); 139 | clearAfterPomManipulation(projectDirectory); 140 | log.info("[Spotbug] Scan completed, artifact cleared"); 141 | return convertSpotbugReportIntoVulnList(spotbugReportXMLS); 142 | } catch (Exception e) { 143 | e.printStackTrace(); 144 | log.error("[Spotbug] Error occuredd during scanning reason {} on line {}", e.getLocalizedMessage(), e.getStackTrace()[0].getLineNumber()); 145 | return new ArrayList<>(); 146 | } 147 | } 148 | public List runScanStandalone(String directory) throws IOException, InterruptedException { 149 | try { 150 | List spotbugReportXMLS = new ArrayList<>(); 151 | List reportPaths = new ArrayList<>(); 152 | String projectDirectory = directory!=null ? directory : CodeHelper.getProjectPath(new ScanRequest(), true); 153 | log.info("[Spotbug] Preparing POM, create backup and generate new"); 154 | preparePomforSpotbugAnalysis(projectDirectory); 155 | log.info("[Spotbug] Starting to generate Spotbug report for {}", projectDirectory); 156 | ProcessBuilder spotbug = new ProcessBuilder("bash", 157 | "-c", 158 | "mvn compile -DskipTests spotbugs:spotbugs").inheritIO(); 159 | spotbug.directory(new File(projectDirectory)); 160 | Process spotbugProcess = spotbug.start(); 161 | spotbugProcess.waitFor(5, TimeUnit.MINUTES); 162 | spotbugProcess.destroy(); 163 | spotbugProcess.waitFor(); 164 | log.info("[Spotbug] Report ready to process {}", projectDirectory); 165 | searchForReports(spotbugReportXMLS, projectDirectory); 166 | clearAfterPomManipulation(projectDirectory); 167 | log.info("[Spotbug] Scan completed, artifact cleared"); 168 | return convertSpotbugReportIntoVulnList(spotbugReportXMLS); 169 | } catch (Exception e) { 170 | e.printStackTrace(); 171 | log.error("[Spotbug] Error occuredd during scanning reason {} on line {}", e.getLocalizedMessage(), e.getStackTrace()[0].getLineNumber()); 172 | return new ArrayList<>(); 173 | } 174 | } 175 | 176 | /** 177 | * Method which takes directory for spotbugXml.xml report file and convert it into readable object 178 | * 179 | * @param directory to spotbugXml.xml file 180 | * @return object with defects 181 | */ 182 | private SpotbugReportXML processXmlReport(String directory){ 183 | File xmlFile = new File(directory); 184 | JAXBContext jaxbContext; 185 | try 186 | { 187 | jaxbContext = JAXBContext.newInstance(SpotbugReportXML.class); 188 | Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); 189 | return ((SpotbugReportXML) jaxbUnmarshaller.unmarshal(xmlFile)).processSeverity(); 190 | } 191 | catch (JAXBException e) 192 | { 193 | e.printStackTrace(); 194 | } 195 | return null; 196 | } 197 | 198 | /** 199 | * First builds spotbugs build with plugins completed 200 | * Second replace build tag in pom with new one 201 | * third create backup of old pom.xml 202 | * end create new pom 203 | * @param directory to look for pom 204 | */ 205 | private void preparePomforSpotbugAnalysis(String directory) throws IOException, ParserConfigurationException, SAXException, TransformerException, InterruptedException { 206 | File file = new File(directory + File.separatorChar + "pom.xml"); 207 | XmlMapper xmlMapper = new XmlMapper(); 208 | xmlMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 209 | xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); 210 | 211 | List plugins = new ArrayList<>(); 212 | plugins.add(buildSpotbugPlugin()); 213 | 214 | String replaceWith = "" + xmlMapper. 215 | writer() 216 | .withRootName("plugins") 217 | .writeValueAsString(plugins).replaceAll("item","plugin") + ""; 218 | 219 | DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); 220 | DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); 221 | Document doc = dBuilder.parse(file); 222 | NodeList buildNode = doc.getElementsByTagName("project"); 223 | Element element = dbFactory.newDocumentBuilder().parse(new InputSource(new StringReader( 224 | replaceWith))).getDocumentElement(); 225 | Node firstDocImportedNode = doc.importNode(element, true); 226 | 227 | boolean spotbugsAdded = false; 228 | for(int i=0; i"); 286 | } 287 | //Create spotbugs-security-exclude.xml 288 | try (Writer writer = new BufferedWriter(new OutputStreamWriter( 289 | new FileOutputStream(s + File.separatorChar + "spotbugs-security-exclude.xml"), StandardCharsets.UTF_8))) { 290 | writer.write(""); 291 | } 292 | } 293 | 294 | 295 | /** 296 | * Create Spotbug element which will be appended into Pom.xml 297 | * @return configured spotbugs with find-sec-bugs 298 | */ 299 | public PomPlugin buildSpotbugPlugin(){ 300 | List findSecBugList = new ArrayList<>(); 301 | PomPlugin findSecBugs = PomPlugin.builder() 302 | .groupId("com.h3xstream.findsecbugs") 303 | .artifactId("findsecbugs-plugin") 304 | .version("1.10.1") 305 | .build(); 306 | findSecBugList.add(findSecBugs); 307 | PomConfiguration configuration = PomConfiguration.builder() 308 | .effort("Max") 309 | .threshold("Low") 310 | .failOnError("true") 311 | .plugins(findSecBugList) 312 | .build(); 313 | return PomPlugin.builder() 314 | .groupId("com.github.spotbugs") 315 | .artifactId("spotbugs-maven-plugin") 316 | .version("4.0.4") 317 | .configuration(configuration) 318 | .build(); 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/rest/controller/BaseController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 16:43 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.rest.controller; 7 | 8 | import io.mixeway.scanner.rest.model.ScanRequest; 9 | import io.mixeway.scanner.rest.model.Status; 10 | import io.mixeway.scanner.rest.service.BaseService; 11 | import io.mixeway.scanner.utils.Vulnerability; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.web.bind.annotation.PostMapping; 14 | import org.springframework.web.bind.annotation.RequestBody; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import javax.validation.Valid; 18 | import java.util.List; 19 | 20 | @RestController 21 | public class BaseController { 22 | BaseService baseService; 23 | 24 | public BaseController(BaseService baseService){ 25 | this.baseService = baseService; 26 | } 27 | @PostMapping("/run") 28 | public ResponseEntity> runScan(@Valid @RequestBody ScanRequest scanRequest) throws Exception { 29 | return baseService.runScan(scanRequest); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/rest/model/ScanRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 16:44 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.rest.model; 7 | 8 | import io.mixeway.scanner.utils.ScannerPluginType; 9 | import io.mixeway.scanner.utils.ScannerType; 10 | 11 | import javax.validation.constraints.NotNull; 12 | 13 | public class ScanRequest { 14 | @NotNull(message = "Scanner Type cannot be null possible options 'SAST' or 'DAST'") 15 | public ScannerType type; 16 | @NotNull(message = "Target cannot be null. In scope of SAST scan target is GIT repo, in scope of DAST scan target is URL to scan") 17 | public String target; 18 | public String username; 19 | public String password; 20 | public String exclusions; 21 | public String branch; 22 | public String projectName; 23 | 24 | public String getProjectName() { 25 | return projectName; 26 | } 27 | 28 | public void setProjectName(String projectName) { 29 | this.projectName = projectName; 30 | } 31 | 32 | public String getBranch() { 33 | return branch; 34 | } 35 | 36 | public void setBranch(String branch) { 37 | this.branch = branch; 38 | } 39 | 40 | public ScannerType getType() { 41 | return type; 42 | } 43 | 44 | public void setType(ScannerType type) { 45 | this.type = type; 46 | } 47 | 48 | public String getTarget() { 49 | return target; 50 | } 51 | 52 | public void setTarget(String target) { 53 | this.target = target; 54 | } 55 | 56 | public String getUsername() { 57 | return username; 58 | } 59 | 60 | public void setUsername(String username) { 61 | this.username = username; 62 | } 63 | 64 | public String getPassword() { 65 | return password; 66 | } 67 | 68 | public void setPassword(String password) { 69 | this.password = password; 70 | } 71 | 72 | public String getExclusions() { 73 | return exclusions; 74 | } 75 | 76 | public void setExclusions(String exclusions) { 77 | this.exclusions = exclusions; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/rest/model/Status.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 23:35 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.rest.model; 7 | 8 | public class Status { 9 | String result; 10 | 11 | public Status(String result) { 12 | this.result = result; 13 | } 14 | 15 | public String getResult() { 16 | return result; 17 | } 18 | 19 | public void setResult(String result) { 20 | this.result = result; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/rest/service/BaseService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 23:34 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.rest.service; 7 | 8 | import io.mixeway.scanner.factory.ScannerFactory; 9 | import io.mixeway.scanner.integrations.ScannerIntegrationFactory; 10 | import io.mixeway.scanner.rest.model.ScanRequest; 11 | import io.mixeway.scanner.utils.*; 12 | import org.aspectj.apache.bcel.classfile.Code; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.http.HttpStatus; 16 | import org.springframework.http.ResponseEntity; 17 | import org.springframework.stereotype.Service; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | @Service 23 | public class BaseService { 24 | private final static Logger log = LoggerFactory.getLogger(BaseService.class); 25 | ScannerFactory scannerFactory; 26 | GitOperations gitOperations; 27 | MixewayConnector mixewayConnector; 28 | 29 | 30 | public BaseService(ScannerFactory scannerFactory, GitOperations gitOperations, MixewayConnector mixewayConnector){ 31 | this.scannerFactory = scannerFactory; 32 | this.gitOperations = gitOperations; 33 | this.mixewayConnector = mixewayConnector; 34 | } 35 | 36 | /** 37 | * Running scan for given request 38 | * 39 | * @param scanRequest sent by client 40 | * @return status 41 | */ 42 | public ResponseEntity> runScan(ScanRequest scanRequest) { 43 | List vulnerabilities = new ArrayList<>(); 44 | try { 45 | GitResponse gitResponse = null; 46 | String projectName = null; 47 | if (scanRequest.getType().equals(ScannerType.SAST)) { 48 | if (gitOperations.isProjectPresent(scanRequest)) { 49 | gitResponse = gitOperations.pull(scanRequest); 50 | } else { 51 | gitResponse = gitOperations.clone(scanRequest); 52 | } 53 | projectName = CodeHelper.getNameFromRepoUrlforSAST(scanRequest.getTarget(),false); 54 | SourceProjectType sourceProjectType = CodeHelper.getSourceProjectTypeFromDirectory(scanRequest, false); 55 | if (sourceProjectType == null) { 56 | log.error("Repository doesnt contain any of the known types of projects. Current version support only JAVA-Maven projects."); 57 | return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED); 58 | } 59 | 60 | 61 | ScannerIntegrationFactory openSourceScan = scannerFactory.getProperScanner(ScannerPluginType.DEPENDENCYTRACK); 62 | vulnerabilities.addAll(openSourceScan.runScan(scanRequest)); 63 | 64 | switch (sourceProjectType) { 65 | case MAVEN: 66 | ScannerIntegrationFactory spotbug = scannerFactory.getProperScanner(ScannerPluginType.SPOTBUG); 67 | vulnerabilities.addAll(spotbug.runScan(scanRequest)); 68 | break; 69 | case PIP: 70 | ScannerIntegrationFactory bandit = scannerFactory.getProperScanner(ScannerPluginType.BANDIT); 71 | vulnerabilities.addAll(bandit.runScan(scanRequest)); 72 | break; 73 | case PHP: 74 | ScannerIntegrationFactory progpilot = scannerFactory.getProperScanner(ScannerPluginType.PROGPILOT); 75 | vulnerabilities.addAll(progpilot.runScan(scanRequest)); 76 | break; 77 | default: 78 | log.error("Source Code Language not supported"); 79 | return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED); 80 | } 81 | // TODO get SAST SCAN 82 | } else if (scanRequest.getType().equals(ScannerType.DAST)) { 83 | // TODO run DAST SCAN 84 | } else { 85 | log.error("[REST] Got request with unknown scan type {}", scanRequest.getType().toString()); 86 | } 87 | mixewayConnector.sendRequestToMixeway(vulnerabilities, projectName, scanRequest.branch, gitResponse.getCommitId()); 88 | } catch (Exception e){ 89 | e.printStackTrace(); 90 | log.error(e.getLocalizedMessage()); 91 | } 92 | 93 | return new ResponseEntity<>(vulnerabilities,HttpStatus.OK); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/standalone/StandAloneService.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.standalone; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.SerializationFeature; 7 | import com.google.gson.Gson; 8 | import io.mixeway.scanner.factory.ScannerFactory; 9 | import io.mixeway.scanner.integrations.ScannerIntegrationFactory; 10 | import io.mixeway.scanner.rest.model.ScanRequest; 11 | import io.mixeway.scanner.utils.*; 12 | import org.apache.commons.lang3.StringUtils; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.factory.annotation.Value; 16 | import org.springframework.stereotype.Service; 17 | 18 | import java.io.File; 19 | import java.io.FileWriter; 20 | import java.io.IOException; 21 | import java.io.Writer; 22 | import java.nio.file.Files; 23 | import java.nio.file.Paths; 24 | import java.security.KeyManagementException; 25 | import java.security.KeyStoreException; 26 | import java.security.NoSuchAlgorithmException; 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | /** 31 | * Class which run Standalone test. Gets current location as mounted application, check for language used and then 32 | * run full SAST scan 33 | * 34 | * @author gsiewruk 35 | */ 36 | @Service 37 | public class StandAloneService { 38 | 39 | @Value("${BRANCH:}") 40 | String branch; 41 | @Value("${COMMIT_ID:}") 42 | String commitId; 43 | @Value("${MIXEWAY_PROJECT_NAME:}") 44 | String mixewayProjectName; 45 | private final static Logger log = LoggerFactory.getLogger(StandAloneService.class); 46 | ScannerFactory scannerFactory; 47 | MixewayConnector mixewayConnector; 48 | GitOperations gitOperations; 49 | public StandAloneService(ScannerFactory scannerFactory, 50 | MixewayConnector mixewayConnector, 51 | GitOperations gitOperations){ 52 | this.scannerFactory = scannerFactory; 53 | this.mixewayConnector = mixewayConnector; 54 | this.gitOperations = gitOperations; 55 | } 56 | 57 | /** 58 | * Running scan in hardcoded location, first it check if location exists (is mounted during docker run), 59 | * and if yes it go full SAST scan, if not log info and exit. 60 | */ 61 | public void runScan() throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, InterruptedException { 62 | List vulnerabilityList = new ArrayList<>(); 63 | String directory = CodeHelper.getProjectPath(new ScanRequest(), true); 64 | 65 | try { 66 | checkMountPoint(); 67 | for (SourceProjectType projectType : SourceProjectType.values()){ 68 | if (CodeHelper.isProjectInLanguage(directory, projectType)){ 69 | vulnerabilityList.addAll(scannerFactory.runScanForLanguage(projectType)); 70 | } 71 | } 72 | } catch (Exception e ){ 73 | e.printStackTrace(); 74 | log.error("[Standalone Mixeway App] Fatal error: {}", e.getLocalizedMessage()); 75 | } 76 | printResults(vulnerabilityList); 77 | writeResultsToFile(vulnerabilityList, CodeHelper.getProjectPath(new ScanRequest(), true)); 78 | processMixeway(vulnerabilityList); 79 | } 80 | 81 | /** 82 | * Send vulnerabilities to Mixeway and waits for results if result is success exit with success, if failure exit with code 1 83 | * 84 | */ 85 | private void processMixeway(List vulnerabilityList) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException, InterruptedException { 86 | GitInformations gitInformations = GitOperations.getGitInformations(); 87 | Status status; 88 | if (gitInformations != null){ 89 | PrepareCIOperation prepareCiOperations = mixewayConnector.getCIInfo(new GetInfoRequest(gitInformations)); 90 | status = mixewayConnector.sendRequestToMixewayWithGitInfo(gitInformations, prepareCiOperations, vulnerabilityList); 91 | } else if (StringUtils.isNotBlank(mixewayProjectName) && StringUtils.isNotBlank(commitId) && StringUtils.isNotBlank(branch)){ 92 | status = mixewayConnector.sendRequestToMixewayStandalone(vulnerabilityList, mixewayProjectName, branch, commitId); 93 | } else { 94 | status = mixewayConnector.sendAnonymousRequestToMixeway(vulnerabilityList); 95 | } 96 | 97 | if (status != null && status.getStatus().equals(Constants.GATEWAY_SUCCESS)){ 98 | System.exit(0); 99 | } else if (status == null) { 100 | System.exit(0); 101 | } else { 102 | System.exit(1); 103 | } 104 | } 105 | 106 | /** 107 | * Save results to file mixeway_sast_report.json 108 | * @param vulnerabilityList 109 | * @param directory 110 | */ 111 | private void writeResultsToFile(List vulnerabilityList, String directory) { 112 | try { 113 | Gson gson = new Gson(); 114 | Writer writer = null; 115 | try { 116 | writer = new FileWriter(directory + File.separator + "mixeway_sast_report.json"); 117 | gson.toJson(vulnerabilityList, writer); 118 | 119 | } catch (IOException e) { 120 | e.printStackTrace(); 121 | } finally { 122 | try { 123 | if (writer != null) { 124 | writer.close(); 125 | } else { 126 | log.error("Buffer has not been initialized!"); 127 | } 128 | } catch (IOException e) { 129 | e.printStackTrace(); 130 | } 131 | } 132 | } catch (Exception e) { 133 | log.error("Cannot write to {} check permission or use vulnerabilities from console", directory); 134 | } 135 | } 136 | 137 | /** 138 | * Printing results 139 | * 140 | * @param vulnerabilityList 141 | */ 142 | private void printResults(List vulnerabilityList) throws JsonProcessingException { 143 | ObjectMapper mapper = new ObjectMapper(); 144 | mapper.enable(SerializationFeature.INDENT_OUTPUT); 145 | mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 146 | String json = mapper.writeValueAsString(vulnerabilityList); 147 | System.out.println(json); 148 | } 149 | 150 | /** 151 | * Checking if source directory is mounted properly: 152 | * -v ${PWD}:/opt/sources 153 | */ 154 | public void checkMountPoint(){ 155 | log.info("Running Standalone Mixeway Scanner App"); 156 | if (Files.isDirectory(Paths.get(Constants.STANDALONE_DEFAULT_SOURCE_PATH))) { 157 | log.info("Directory is properly mounted proceeding..."); 158 | } else { 159 | log.error("No location mounted exiting. Please make sure You have used '-v ${PWD}:/opt/sources'"); 160 | System.exit(1); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/CodeHelper.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | import io.mixeway.scanner.rest.model.ScanRequest; 4 | import org.apache.commons.io.FileUtils; 5 | import org.apache.commons.io.filefilter.DirectoryFileFilter; 6 | import org.apache.commons.io.filefilter.RegexFileFilter; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.io.File; 11 | import java.nio.file.Path; 12 | import java.util.Collection; 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | 16 | /** 17 | * @author gsiewruk 18 | */ 19 | @Component 20 | public class CodeHelper { 21 | public static String sourceLocation; 22 | 23 | @Value( "${sources.location}" ) 24 | public void setSourceLocation(String sourcesPath) { 25 | sourceLocation = sourcesPath; 26 | } 27 | 28 | /** 29 | * Gettig repo name from URL 30 | * e.g: https://github.com/mixeway/mixewayhub.git should return mixewayhub 31 | * 32 | * @param repoUrl URL for repository 33 | * @return name of repository 34 | */ 35 | public static String getNameFromRepoUrlforSAST(String repoUrl, boolean standalone){ 36 | if (standalone) { 37 | return "standaloneApp"; 38 | } else { 39 | String[] partsOfUrl = repoUrl.split("/"); 40 | String repoName = partsOfUrl[partsOfUrl.length - 1]; 41 | return repoName.split("\\.")[0]; 42 | } 43 | } 44 | /** 45 | * Determine type of source code in given location - JAVA-MVN, JAVA-Gradle, NPM, PIP or PHP-COMPOSER 46 | * 47 | */ 48 | public static SourceProjectType getSourceProjectTypeFromDirectory(ScanRequest scanRequest, boolean standalone){ 49 | String projectLocation; 50 | if (standalone){ 51 | projectLocation = Constants.STANDALONE_DEFAULT_SOURCE_PATH; 52 | } else { 53 | projectLocation = sourceLocation + File.separatorChar + getNameFromRepoUrlforSAST(scanRequest.getTarget(), standalone); 54 | } 55 | File pom = new File(projectLocation + File.separatorChar + "pom.xml"); 56 | if(pom.exists()){ 57 | return SourceProjectType.MAVEN; 58 | } 59 | File gradle = new File(projectLocation + File.separatorChar + "build.xml"); 60 | File gradle2 = new File(projectLocation + File.separatorChar + "build.gradle"); 61 | if (gradle.exists() || gradle2.exists()) { 62 | prepareGradle(gradle); 63 | return SourceProjectType.GRADLE; 64 | } 65 | File npm = new File(projectLocation + File.separatorChar + "package.json"); 66 | if(npm.exists()){ 67 | return SourceProjectType.NPM; 68 | } 69 | File composer = new File(projectLocation + File.separatorChar + "composer.json"); 70 | if(composer.exists() || directoryContainsPhp(projectLocation)){ 71 | return SourceProjectType.PHP; 72 | } 73 | Collection pip = FileUtils.listFiles( 74 | new File(projectLocation), 75 | new RegexFileFilter(".*\\.py"), 76 | DirectoryFileFilter.DIRECTORY 77 | ).stream().map(File::getName).collect(Collectors.toList()); 78 | if (pip.size() > 3) { 79 | return SourceProjectType.PIP; 80 | } 81 | return null; 82 | } 83 | /** 84 | * Check if project is is particular language 85 | */ 86 | public static boolean isProjectInLanguage(String directory, SourceProjectType sourceProjectType){ 87 | switch (sourceProjectType) { 88 | case PIP: 89 | Collection pip = FileUtils.listFiles( 90 | new File(directory), 91 | new RegexFileFilter(".*\\.py"), 92 | DirectoryFileFilter.DIRECTORY 93 | ).stream().map(File::getName).collect(Collectors.toList()); 94 | return pip.size() > 3; 95 | case GRADLE: 96 | File gradle = new File(directory + File.separatorChar + "build.xml"); 97 | File gradle2 = new File(directory + File.separatorChar + "build.gradle"); 98 | if (gradle.exists() || gradle2.exists()) { 99 | prepareGradle(gradle); 100 | return true; 101 | } else { 102 | return false; 103 | } 104 | case PHP: 105 | File composer = new File(directory + File.separatorChar + "composer.json"); 106 | return composer.exists() || directoryContainsPhp(directory); 107 | case NPM: 108 | List packagePaths= FileUtils.listFiles( 109 | new File(directory), 110 | new RegexFileFilter(Constants.PACKAGE_FILENAME), 111 | DirectoryFileFilter.DIRECTORY 112 | ).stream() 113 | .map(File::getAbsoluteFile) 114 | .map(file -> file.toString() 115 | .split(File.separatorChar + Constants.PACKAGE_FILENAME)[0]) 116 | .collect(Collectors.toList()); 117 | return packagePaths.size()>0; 118 | case MAVEN: 119 | File pom = new File(directory + File.separatorChar + "pom.xml"); 120 | return pom.exists(); 121 | } 122 | return false; 123 | } 124 | 125 | 126 | 127 | /** 128 | * Check if directory contains php files 129 | * @param projectLocation 130 | * @return 131 | */ 132 | private static boolean directoryContainsPhp(String projectLocation) { 133 | Collection php = FileUtils.listFiles( 134 | new File(projectLocation), 135 | new RegexFileFilter(".*\\.php"), 136 | DirectoryFileFilter.DIRECTORY 137 | ).stream().map(File::getName).collect(Collectors.toList()); 138 | return php.size() > 5; 139 | } 140 | 141 | /** 142 | * Method which preare build.xml file to use CycloneDX plugin to generate SBOM 143 | * 144 | * @param gradle path to gradle file 145 | */ 146 | private static void prepareGradle(File gradle) { 147 | //TODO edit of build.xml 148 | } 149 | 150 | /** 151 | * Method which return path to project git downloaded 152 | * 153 | * @param scanRequest to process 154 | * @return path to code 155 | */ 156 | public static String getProjectPath(ScanRequest scanRequest, boolean standalone){ 157 | if (standalone){ 158 | return Constants.STANDALONE_DEFAULT_SOURCE_PATH; 159 | } else { 160 | return sourceLocation + File.separatorChar + getNameFromRepoUrlforSAST(scanRequest.getTarget(), standalone); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-18 : 16:55 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.utils; 7 | 8 | import java.net.URI; 9 | 10 | public final class Constants { 11 | public static final String DEPENDENCYTRACK_USERNAME = "admin"; 12 | public static final String DEPENDENCYTRACK_PASSWORD = "admin"; 13 | public static final String DEPENDENCYTRACK_URL = "http://localhost:8080"; 14 | public static final String DEPENDENCYTRACK_URL_LOGIN = "/api/v1/user/login"; 15 | public static final String DEPENDENCYTRACK_URL_APIKEY = "/api/v1/team?searchText=&sortOrder=asc"; 16 | public static final String DEPENDENCYTRACK_LOGIN_STRING = "username=admin&password=admin1"; 17 | public static final String DEPENDENCYTRACK_CHANGE_PASSWORD_STRING = "username=admin&password=admin&newPassword=admin1&confirmPassword=admin1"; 18 | public static final String DEPENDENCYTRACK_AUTOMATION = "Automation"; 19 | public static final String DEPENDENCYTRACK_URL_OSS_CONFIG = "/api/v1/configProperty/aggregate"; 20 | public static final String DEPENDENCYTRACK_APIKEY_HEADER = "X-Api-Key"; 21 | public static final String DEPENDENCYTRACK_GET_PROJECTS = "/api/v1/project"; 22 | public static final String DEPENDENCYTRACK_URL_PERMISSIONS = "/api/v1/permission/"; 23 | public static final String GIT_BRANCH_MASTER = "master"; 24 | public static final String DEPENDENCYTRACK_URL_CHANGE_PASSWORD = "/api/v1/user/forceChangePassword"; 25 | public static final String DEPENDENCYTRACK_URL_UPLOAD_BOM = "/api/v1/bom"; 26 | 27 | public static final String DEPENDENCYTRACK_URL_VULNS = "/api/v1/vulnerability/project/"; 28 | public static final String STANDALONE_DEFAULT_SOURCE_PATH = "/opt/sources"; 29 | public static final String MIXEWAY_URL_SAAS = "https://hub.mixeway.io"; 30 | public static final String MIXEWAY_API_KEY = "apikey"; 31 | public static final String MIXEWAY_PUSH_VULN_URL = "/v2/api/cicd/loadvulnerabilities"; 32 | 33 | public static final String GATEWAY_SUCCESS = "Ok"; 34 | public static final String PACKAGE_FILENAME = "package.json"; 35 | public static final String POM_FILENAME = "pom.xml"; 36 | public static final String SPOTBUG_CATEGORY_SECURITY = "SECURITY"; 37 | public static final String SPOTBUG_CATEGORY_MALICIOUS_CODE = "MALICIOUS_CODE"; 38 | public static final String MIXEWAY_GET_SCANNER_INFO_URL = "/v2/api/cicd/getscannerinfo"; 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/GetInfoRequest.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | /** 4 | * @author gsiewruk 5 | */ 6 | public class GetInfoRequest { 7 | String repoUrl; 8 | String branch; 9 | String scope; 10 | String repoName; 11 | 12 | public GetInfoRequest(GitInformations gitInformations) { 13 | this.repoUrl = gitInformations.getRepoUrl(); 14 | this.branch = gitInformations.getBranchName(); 15 | this.scope = "opensource"; 16 | this.repoName=gitInformations.getProjectName(); 17 | } 18 | 19 | public String getRepoName() { 20 | return repoName; 21 | } 22 | 23 | public void setRepoName(String repoName) { 24 | this.repoName = repoName; 25 | } 26 | 27 | public String getBranch() { 28 | return branch; 29 | } 30 | 31 | public void setBranch(String branch) { 32 | this.branch = branch; 33 | } 34 | 35 | public String getRepoUrl() { 36 | return repoUrl; 37 | } 38 | 39 | public void setRepoUrl(String repoUrl) { 40 | this.repoUrl = repoUrl; 41 | } 42 | 43 | public String getScope() { 44 | return scope; 45 | } 46 | 47 | public void setScope(String scope) { 48 | this.scope = scope; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/GitInformations.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author gsiewruk 8 | */ 9 | @Builder 10 | @Getter 11 | public class GitInformations { 12 | private String projectName; 13 | private String commitId; 14 | private String branchName; 15 | private String repoUrl; 16 | 17 | public GitInformations(){} 18 | public GitInformations(String projectName, String commitId, String branchName, String repoUrl) { 19 | this.projectName = projectName; 20 | this.commitId = commitId; 21 | this.branchName = branchName; 22 | this.repoUrl = repoUrl; 23 | } 24 | 25 | public String getRepoUrl() { 26 | return repoUrl; 27 | } 28 | 29 | public void setRepoUrl(String repoUrl) { 30 | this.repoUrl = repoUrl; 31 | } 32 | 33 | public String getProjectName() { 34 | return projectName; 35 | } 36 | 37 | public void setProjectName(String projectName) { 38 | this.projectName = projectName; 39 | } 40 | 41 | public String getCommitId() { 42 | return commitId; 43 | } 44 | 45 | public void setCommitId(String commitId) { 46 | this.commitId = commitId; 47 | } 48 | 49 | public String getBranchName() { 50 | return branchName; 51 | } 52 | 53 | public void setBranchName(String branchName) { 54 | this.branchName = branchName; 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/GitOperations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-19 : 18:30 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.utils; 7 | 8 | import io.mixeway.scanner.rest.model.ScanRequest; 9 | import org.eclipse.jgit.api.ListBranchCommand; 10 | import org.eclipse.jgit.revwalk.RevCommit; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.stereotype.Component; 15 | import org.eclipse.jgit.api.Git; 16 | import org.eclipse.jgit.api.ResetCommand; 17 | import org.eclipse.jgit.api.errors.GitAPIException; 18 | import org.eclipse.jgit.lib.Ref; 19 | import org.eclipse.jgit.lib.Repository; 20 | import org.eclipse.jgit.merge.MergeStrategy; 21 | import org.eclipse.jgit.storage.file.FileRepositoryBuilder; 22 | import org.eclipse.jgit.transport.CredentialsProvider; 23 | import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; 24 | 25 | import java.io.File; 26 | import java.io.IOException; 27 | import java.nio.file.Files; 28 | import java.nio.file.Path; 29 | import java.nio.file.Paths; 30 | import java.util.List; 31 | import java.util.stream.Stream; 32 | 33 | /** 34 | * Class which enable integration with GIT Repositories 35 | */ 36 | @Component 37 | public class GitOperations { 38 | @Value( "${sources.location}" ) 39 | private String sourceLocation; 40 | private static String LOCATION_STATIC; 41 | @Value("${sources.location}") 42 | public void setSourceLocationStatic(String name){ 43 | GitOperations.LOCATION_STATIC = name; 44 | } 45 | private final Logger log = LoggerFactory.getLogger(GitOperations.class); 46 | 47 | /** 48 | * Get default directory of /opt/sources and try to load active branch, last commitID and repo name from it 49 | * 50 | * @return git info object containing above data 51 | */ 52 | public static GitInformations getGitInformations() { 53 | final Logger log = LoggerFactory.getLogger(GitOperations.class); 54 | try { 55 | FileRepositoryBuilder builder = new FileRepositoryBuilder(); 56 | Repository repository = builder.setGitDir(Paths.get(LOCATION_STATIC + File.separatorChar + ".git").toFile()) 57 | .readEnvironment() 58 | .findGitDir() 59 | .build(); 60 | RevCommit latestCommit = new Git(repository).log().setMaxCount(1).call().iterator().next(); 61 | String latestCommitHash = latestCommit.getName(); 62 | 63 | GitInformations gitInformations = GitInformations 64 | .builder() 65 | .branchName(Stream.of(repository.getFullBranch().split("/")).reduce((first, last) -> last).get()) 66 | .commitId(latestCommitHash) 67 | .projectName(Stream.of(repository.getConfig().getString("remote", "origin", "url").split("/")).reduce((first, last) -> last).get()) 68 | .repoUrl(repository.getConfig().getString("remote", "origin", "url")) 69 | .build(); 70 | log.info("[GIT] Processing scan for {} with active branch {} and latest commit {}", gitInformations.getProjectName(), gitInformations.getBranchName(), gitInformations.getCommitId()); 71 | return gitInformations; 72 | } catch (IOException | GitAPIException e){ 73 | log.error("[GIT] Unable to load GIT informations reason - {}", e.getLocalizedMessage()); 74 | } 75 | return null; 76 | } 77 | 78 | 79 | /** 80 | * Method which is pulling git repo based on scanRequest 81 | * 82 | * @param scanRequest with repo url and auth credentials 83 | * @return object with commit id for given branch 84 | */ 85 | public GitResponse pull(ScanRequest scanRequest) throws Exception { 86 | try { 87 | verifyLocation(); 88 | } catch (Exception e){ 89 | throw new Exception (e.getLocalizedMessage()); 90 | } 91 | try { 92 | FileRepositoryBuilder builder = new FileRepositoryBuilder(); 93 | CredentialsProvider credentialsProvider = prepareCredentails(scanRequest); 94 | 95 | Repository repository = builder.setGitDir(Paths.get(sourceLocation + File.separatorChar + CodeHelper.getNameFromRepoUrlforSAST(scanRequest.getTarget(), false) + File.separatorChar + ".git").toFile()) 96 | .readEnvironment() 97 | .findGitDir() 98 | .build(); 99 | Git git = new Git(repository); 100 | git.reset().setMode(ResetCommand.ResetType.HARD).call(); 101 | git.pull() 102 | .setRemote("origin") 103 | .setStrategy(MergeStrategy.THEIRS) 104 | .setCredentialsProvider(credentialsProvider) 105 | .setRemoteBranchName(scanRequest.getBranch() != null ? scanRequest.getBranch() : Constants.GIT_BRANCH_MASTER) 106 | .call(); 107 | Ref call = git.checkout().setName("origin/" + (scanRequest.getBranch() != null ? scanRequest.getBranch() : Constants.GIT_BRANCH_MASTER)).call(); 108 | String commitId = git.log().setMaxCount(1).call().iterator().next().getName(); 109 | log.info("[GIT] Successfully fetched repo for {} commitId id is {} branch {}", scanRequest.getTarget(), commitId, scanRequest.getBranch()); 110 | return new GitResponse(true,commitId); 111 | } catch (GitAPIException | IOException e){ 112 | log.error("[GIT] Error during fetching repo {}", e.getLocalizedMessage()); 113 | } 114 | return new GitResponse(false,""); 115 | } 116 | 117 | /** 118 | * Method which clone repository based on scanRequest 119 | * @param scanRequest given scanRequest with repo url and auth credentials 120 | * @return commitid of given branch 121 | */ 122 | public GitResponse clone(ScanRequest scanRequest) throws Exception { 123 | try { 124 | verifyLocation(); 125 | } catch (Exception e){ 126 | throw new Exception (e.getLocalizedMessage()); 127 | } 128 | try { 129 | Git git = Git.cloneRepository() 130 | .setCredentialsProvider(prepareCredentails(scanRequest)) 131 | .setURI(scanRequest.getTarget() + ".git") 132 | .setDirectory(Paths.get(sourceLocation + File.separatorChar + CodeHelper.getNameFromRepoUrlforSAST(scanRequest.getTarget(), false)).toFile()) 133 | .call(); 134 | Ref call = git.checkout().setName("origin/" + (scanRequest.getBranch() != null ? scanRequest.getBranch() : Constants.GIT_BRANCH_MASTER)).call(); 135 | String commitId = git.log().setMaxCount(1).call().iterator().next().getName(); 136 | log.info("[GIT] Successfully cloned repo for {} commitId is {} branch {}", scanRequest.getTarget(), commitId, scanRequest.getBranch()); 137 | return new GitResponse(true, commitId); 138 | } catch (GitAPIException e){ 139 | log.error("[GIT] Error cloning repo {}", e.getLocalizedMessage()); 140 | } 141 | return new GitResponse(false, ""); 142 | } 143 | 144 | /** 145 | * Preparing CredentialsProvider for given auth info: 146 | * - No auth if no pass and user given 147 | * - Token auth if only pass provided 148 | * - basic auth if both pass and user provided 149 | * @param scanRequest scan request to prepare credentials for 150 | * @return credentials provided object 151 | */ 152 | private CredentialsProvider prepareCredentails(ScanRequest scanRequest) { 153 | if (scanRequest.getUsername() ==null && scanRequest.getPassword()!=null) { 154 | return new UsernamePasswordCredentialsProvider("PRIVATE-TOKEN", scanRequest.getPassword()); 155 | } else if (scanRequest.getUsername() !=null && scanRequest.getPassword()!=null ) { 156 | return new UsernamePasswordCredentialsProvider(scanRequest.getUsername(), scanRequest.getPassword()); 157 | } else { 158 | return null; 159 | } 160 | } 161 | 162 | /** 163 | * Method which verify if location and configuration for given request is already configured 164 | * 165 | * @param scanRequest scan request with target to verify 166 | * @return boolean if location exists or not 167 | */ 168 | public boolean isProjectPresent(ScanRequest scanRequest){ 169 | Path path = Paths.get(sourceLocation + File.separatorChar + CodeHelper.getNameFromRepoUrlforSAST(scanRequest.getTarget(), false)); 170 | return Files.exists(path); 171 | } 172 | 173 | /** 174 | * Method which verify if sourcesLocation exist on FileSystem. Directory is created if needed. 175 | * Permissions to write on given location is required. 176 | */ 177 | private void verifyLocation() throws Exception { 178 | Path path = Paths.get(sourceLocation); 179 | if (!Files.exists(path)) { 180 | File file = new File(sourceLocation); 181 | if (!file.exists()) { 182 | if (file.mkdir()) { 183 | log.info("[GIT] Directory is created! {}", sourceLocation); 184 | } else { 185 | log.warn("[GIT] Failed to create directory {}", sourceLocation); 186 | throw new Exception("Cannot create directory"); 187 | } 188 | } 189 | } 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/GitResponse.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | public class GitResponse { 4 | private Boolean status; 5 | private String commitId; 6 | 7 | public GitResponse(Boolean status, String commitId){ 8 | this.commitId = commitId; 9 | this.status = status; 10 | } 11 | public Boolean getStatus() { 12 | return status; 13 | } 14 | 15 | public void setStatus(Boolean status) { 16 | this.status = status; 17 | } 18 | 19 | public String getCommitId() { 20 | return commitId; 21 | } 22 | 23 | public void setCommitId(String commitId) { 24 | this.commitId = commitId; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/MixewayConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * @created 2020-08-27 : 16:53 3 | * @project MixewayScanner 4 | * @author siewer 5 | */ 6 | package io.mixeway.scanner.utils; 7 | 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.apache.http.conn.ssl.NoopHostnameVerifier; 10 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 11 | import org.apache.http.impl.client.CloseableHttpClient; 12 | import org.apache.http.impl.client.HttpClients; 13 | import org.apache.http.ssl.TrustStrategy; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import org.springframework.beans.factory.annotation.Value; 17 | import org.springframework.http.*; 18 | import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; 19 | import org.springframework.stereotype.Service; 20 | import org.springframework.web.client.HttpClientErrorException; 21 | import org.springframework.web.client.HttpServerErrorException; 22 | import org.springframework.web.client.RestTemplate; 23 | 24 | import javax.net.ssl.SSLContext; 25 | import java.security.KeyManagementException; 26 | import java.security.KeyStoreException; 27 | import java.security.NoSuchAlgorithmException; 28 | import java.util.List; 29 | 30 | @Service 31 | public class MixewayConnector { 32 | private final static Logger log = LoggerFactory.getLogger(MixewayConnector.class); 33 | @Value("${mixeway.url}") 34 | String mixewayUrl; 35 | @Value("${mixeway.key}") 36 | String mixewayKey; 37 | @Value("${mixeway.project}") 38 | int mixewayProject; 39 | @Value("${mixeway.project.name}") 40 | String mixewayProjectName; 41 | int tries = 0; 42 | public Status sendRequestToMixeway(List vulnerabilities, String projectName, String branch, String commit) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { 43 | if (StringUtils.isNoneBlank(mixewayKey) && mixewayProject > 0){ 44 | log.info("[Mixeway Connector] Mixeway integraiton is enabled. Starting to push the results to {}", mixewayUrl); 45 | RestTemplate restTemplate = getRestTemplate(); 46 | HttpHeaders headers = new HttpHeaders(); 47 | headers.set(Constants.MIXEWAY_API_KEY, mixewayKey); 48 | headers.setContentType(MediaType.APPLICATION_JSON); 49 | HttpEntity> entity = new HttpEntity<>(vulnerabilities,headers); 50 | try { 51 | ResponseEntity response = restTemplate.exchange(mixewayUrl + 52 | Constants.MIXEWAY_PUSH_VULN_URL 53 | + "/" + mixewayProject + "/" + projectName + "/" + branch + "/" + commit, 54 | HttpMethod.POST, entity, Status.class); 55 | if (response.getStatusCode() == HttpStatus.OK) { 56 | log.info("[Mixeway Connector] Results pushed and already visible at {}", mixewayUrl); 57 | return response.getBody(); 58 | } else { 59 | log.error("[Mixeway Connector] Push results to Mixeway - {}", response.getStatusCodeValue()); 60 | } 61 | } catch (HttpServerErrorException | HttpClientErrorException e) { 62 | log.error("[Mixeway Connector] Problem with contaictint with Mixeway - {}",e.getLocalizedMessage()); 63 | } 64 | } else { 65 | log.info("[Mixeway Connector] Mixeway integration is not enabled, if You want to push results into mixeway please set MIXEWAY_URL and MIXEWAY_KEY. Read more at docs."); 66 | } 67 | return null; 68 | } 69 | public Status sendRequestToMixewayStandalone(List vulnerabilities, String projectName, String branch, String commit) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { 70 | if (StringUtils.isNoneBlank(mixewayKey) ){ 71 | log.info("[Mixeway Connector] Mixeway integraiton is enabled. Starting to push the results to {}", mixewayUrl); 72 | RestTemplate restTemplate = getRestTemplate(); 73 | HttpHeaders headers = new HttpHeaders(); 74 | headers.set(Constants.MIXEWAY_API_KEY, mixewayKey); 75 | headers.setContentType(MediaType.APPLICATION_JSON); 76 | HttpEntity> entity = new HttpEntity<>(vulnerabilities,headers); 77 | try { 78 | ResponseEntity response = restTemplate.exchange(mixewayUrl + 79 | Constants.MIXEWAY_PUSH_VULN_URL 80 | + (mixewayProject > 0 ? "/"+mixewayProject:"") 81 | + "/" + projectName + "/" + branch + "/" + commit, 82 | HttpMethod.POST, entity, Status.class); 83 | if (response.getStatusCode() == HttpStatus.OK) { 84 | log.info("[Mixeway Connector] Results pushed and already visible at {}", mixewayUrl); 85 | return response.getBody(); 86 | } else { 87 | log.error("[Mixeway Connector] Push results to Mixeway - {}", response.getStatusCodeValue()); 88 | } 89 | } catch (HttpServerErrorException | HttpClientErrorException e) { 90 | log.error("[Mixeway Connector] Problem with contaictint with Mixeway - {}",e.getLocalizedMessage()); 91 | } 92 | } else { 93 | log.info("[Mixeway Connector] Mixeway integration is not enabled, if You want to push results into mixeway please set MIXEWAY_URL and MIXEWAY_KEY. Read more at docs."); 94 | } 95 | return null; 96 | } 97 | 98 | public Status sendAnonymousRequestToMixeway(List vulnerabilities) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { 99 | if (StringUtils.isNoneBlank(mixewayProjectName) && StringUtils.isNoneBlank(mixewayKey)){ 100 | log.info("[Mixeway Connector] Mixeway integraiton is enabled. Starting to push the results to {}", mixewayUrl); 101 | RestTemplate restTemplate = getRestTemplate(); 102 | HttpHeaders headers = new HttpHeaders(); 103 | headers.set(Constants.MIXEWAY_API_KEY, mixewayKey); 104 | headers.setContentType(MediaType.APPLICATION_JSON); 105 | try{ 106 | HttpEntity> entity = new HttpEntity<>(vulnerabilities, headers); 107 | ResponseEntity response = restTemplate.exchange(mixewayUrl + 108 | Constants.MIXEWAY_PUSH_VULN_URL + "/" + mixewayProjectName, 109 | HttpMethod.POST, entity, Status.class); 110 | if (response.getStatusCode() == HttpStatus.OK) { 111 | log.info("[Mixeway Connector] Results pushed and already visible at {}", mixewayUrl); 112 | return response.getBody(); 113 | } else { 114 | log.error("[Mixeway Connector] Push results to Mixeway - {}", response.getStatusCodeValue()); 115 | } 116 | } catch (HttpServerErrorException | HttpClientErrorException e) { 117 | log.error("[Mixeway Connector] Problem with contaictint with Mixeway - {}",e.getLocalizedMessage()); 118 | } 119 | } else { 120 | log.info("[Mixeway Connector] Mixeway integration is not enabled, if You want to push results into mixeway please set MIXEWAY_URL, MIXEWAY_KEY and MIXEWAY_PROJECT_NAME. Read more at docs."); 121 | } 122 | return null; 123 | } 124 | 125 | /** 126 | * Geting information about OpenSource scan infos 127 | */ 128 | public PrepareCIOperation getCIInfo(GetInfoRequest getInfoRequest) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException, InterruptedException { 129 | 130 | RestTemplate restTemplate = getRestTemplate(); 131 | HttpHeaders headers = new HttpHeaders(); 132 | headers.set(Constants.MIXEWAY_API_KEY, mixewayKey); 133 | headers.setContentType(MediaType.APPLICATION_JSON); 134 | HttpEntity entity = new HttpEntity<>(getInfoRequest,headers); 135 | try { 136 | ResponseEntity response = restTemplate.exchange(mixewayUrl + 137 | Constants.MIXEWAY_GET_SCANNER_INFO_URL, 138 | HttpMethod.POST, entity, PrepareCIOperation.class); 139 | return response.getBody(); 140 | } catch (HttpClientErrorException | HttpServerErrorException e){ 141 | tries++; 142 | log.error("[Mixeway] Cannot get info for Mixeway configuration... try {} ... reason - {}",tries, e.getLocalizedMessage()); 143 | if (tries < 4){ 144 | Thread.sleep(3000); 145 | return getCIInfo(getInfoRequest); 146 | } 147 | } 148 | return null; 149 | } 150 | 151 | public RestTemplate getRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { 152 | TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true; 153 | SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build(); 154 | SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier()); 155 | CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build(); 156 | HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); 157 | requestFactory.setHttpClient(httpClient); 158 | return new RestTemplate(requestFactory); 159 | } 160 | 161 | public Status sendRequestToMixewayWithGitInfo(GitInformations gitInformations, PrepareCIOperation prepareCiOperations, List vulnerabilityList) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { 162 | if (StringUtils.isNoneBlank(mixewayKey) ){ 163 | log.info("[Mixeway Connector] Mixeway integraiton is enabled. Starting to push the results to {}", mixewayUrl); 164 | RestTemplate restTemplate = getRestTemplate(); 165 | HttpHeaders headers = new HttpHeaders(); 166 | headers.set(Constants.MIXEWAY_API_KEY, mixewayKey); 167 | headers.setContentType(MediaType.APPLICATION_JSON); 168 | HttpEntity> entity = new HttpEntity<>(vulnerabilityList,headers); 169 | try { 170 | ResponseEntity response = restTemplate.exchange(mixewayUrl + 171 | Constants.MIXEWAY_PUSH_VULN_URL 172 | + "/" + gitInformations.getProjectName() + "/" + gitInformations.getBranchName() + "/" + gitInformations.getCommitId(), 173 | HttpMethod.POST, entity, Status.class); 174 | if (response.getStatusCode() == HttpStatus.OK) { 175 | log.info("[Mixeway Connector] Results pushed and already visible at {}", mixewayUrl); 176 | return response.getBody(); 177 | } else { 178 | log.error("[Mixeway Connector] Push results to Mixeway - {}", response.getStatusCodeValue()); 179 | } 180 | } catch (HttpServerErrorException | HttpClientErrorException e) { 181 | log.error("[Mixeway Connector] Problem with contaictint with Mixeway - {}",e.getLocalizedMessage()); 182 | } 183 | } else { 184 | log.info("[Mixeway Connector] Mixeway integration is not enabled, if You want to push results into mixeway please set MIXEWAY_URL and MIXEWAY_KEY. Read more at docs."); 185 | } 186 | return null; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/Pom.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | import javax.xml.bind.annotation.XmlElement; 4 | 5 | /** 6 | * @author gsiewruk 7 | */ 8 | public class Pom { 9 | @XmlElement 10 | PomBuild build; 11 | 12 | public PomBuild getBuild() { 13 | return build; 14 | } 15 | 16 | public void setBuild(PomBuild build) { 17 | this.build = build; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/PomBuild.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; 4 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 5 | import com.sun.xml.txw2.annotation.XmlElement; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | 9 | import javax.xml.bind.annotation.XmlRootElement; 10 | import java.util.List; 11 | 12 | /** 13 | * @author gsiewruk 14 | */ 15 | @Builder 16 | @AllArgsConstructor 17 | @XmlRootElement(name = "build") 18 | @XmlElement("build") 19 | public class PomBuild { 20 | List plugins; 21 | 22 | public PomBuild(){} 23 | 24 | public List getPlugins() { 25 | return plugins; 26 | } 27 | 28 | public void setPlugins(List plugins) { 29 | this.plugins = plugins; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/PomConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | 7 | import javax.xml.bind.annotation.XmlRootElement; 8 | import java.util.List; 9 | 10 | /** 11 | * @author gsiewruk 12 | */ 13 | @Builder 14 | @AllArgsConstructor 15 | @JsonInclude(JsonInclude.Include.NON_EMPTY) 16 | @XmlRootElement(name = "configuration") 17 | public class PomConfiguration { 18 | String effort; 19 | String threshold; 20 | String failOnError; 21 | List plugins; 22 | 23 | public PomConfiguration(){} 24 | 25 | 26 | public String getThreshold() { 27 | return threshold; 28 | } 29 | 30 | public void setThreshold(String threshold) { 31 | this.threshold = threshold; 32 | } 33 | 34 | public String getEffort() { 35 | return effort; 36 | } 37 | 38 | public void setEffort(String effort) { 39 | this.effort = effort; 40 | } 41 | 42 | public String getFailOnError() { 43 | return failOnError; 44 | } 45 | 46 | public void setFailOnError(String failOnError) { 47 | this.failOnError = failOnError; 48 | } 49 | 50 | public List getPlugins() { 51 | return plugins; 52 | } 53 | 54 | public void setPlugins(List plugins) { 55 | this.plugins = plugins; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/PomPlugin.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.sun.xml.txw2.annotation.XmlElement; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Getter; 8 | 9 | import javax.xml.bind.annotation.XmlRootElement; 10 | 11 | /** 12 | * @author gsiewruk 13 | */ 14 | @Builder 15 | @AllArgsConstructor 16 | @JsonInclude(JsonInclude.Include.NON_EMPTY) 17 | @XmlRootElement(name = "plugin") 18 | @XmlElement("build") 19 | public class PomPlugin { 20 | String groupId; 21 | String artifactId; 22 | String version; 23 | PomConfiguration configuration; 24 | 25 | public PomPlugin(){} 26 | 27 | public String getGroupId() { 28 | return groupId; 29 | } 30 | 31 | public void setGroupId(String groupId) { 32 | this.groupId = groupId; 33 | } 34 | 35 | public String getArtifactId() { 36 | return artifactId; 37 | } 38 | 39 | public void setArtifactId(String artifactId) { 40 | this.artifactId = artifactId; 41 | } 42 | 43 | public String getVersion() { 44 | return version; 45 | } 46 | 47 | public void setVersion(String version) { 48 | this.version = version; 49 | } 50 | 51 | public PomConfiguration getConfiguration() { 52 | return configuration; 53 | } 54 | 55 | public void setConfiguration(PomConfiguration configuration) { 56 | this.configuration = configuration; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/PomPlugins.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | import javax.xml.bind.annotation.XmlRootElement; 4 | import java.util.List; 5 | 6 | /** 7 | * @author gsiewruk 8 | */ 9 | @XmlRootElement(name = "plugins") 10 | public class PomPlugins { 11 | List plugins; 12 | 13 | public List getPlugins() { 14 | return plugins; 15 | } 16 | 17 | public void setPlugins(List plugins) { 18 | this.plugins = plugins; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/PomProject.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | import javax.xml.bind.annotation.XmlRootElement; 4 | 5 | /** 6 | * @author gsiewruk 7 | */ 8 | @XmlRootElement(name = "project") 9 | public class PomProject { 10 | PomBuild build; 11 | 12 | public PomBuild getBuild() { 13 | return build; 14 | } 15 | 16 | public void setBuild(PomBuild build) { 17 | this.build = build; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/PrepareCIOperation.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | /** 4 | * @author gsiewruk 5 | */ 6 | public class PrepareCIOperation { 7 | Long projectId; 8 | Long codeProjectId; 9 | String openSourceScannerProjectId; 10 | String openSourceScannerCredentials; 11 | String openSourceScannerApiUrl; 12 | boolean openSourceScannerIntegration; 13 | String scannerType; 14 | 15 | public Long getCodeProjectId() { 16 | return codeProjectId; 17 | } 18 | 19 | public void setCodeProjectId(Long codeProjectId) { 20 | this.codeProjectId = codeProjectId; 21 | } 22 | 23 | public String getScannerType() { 24 | return scannerType; 25 | } 26 | 27 | public void setScannerType(String scannerType) { 28 | this.scannerType = scannerType; 29 | } 30 | 31 | public boolean isOpenSourceScannerIntegration() { 32 | return openSourceScannerIntegration; 33 | } 34 | 35 | public void setOpenSourceScannerIntegration(boolean openSourceScannerIntegration) { 36 | this.openSourceScannerIntegration = openSourceScannerIntegration; 37 | } 38 | 39 | public Long getProjectId() { 40 | return projectId; 41 | } 42 | 43 | public void setProjectId(Long projectId) { 44 | this.projectId = projectId; 45 | } 46 | 47 | public String getOpenSourceScannerProjectId() { 48 | return openSourceScannerProjectId; 49 | } 50 | 51 | public void setOpenSourceScannerProjectId(String openSourceScannerProjectId) { 52 | this.openSourceScannerProjectId = openSourceScannerProjectId; 53 | } 54 | 55 | public String getOpenSourceScannerCredentials() { 56 | return openSourceScannerCredentials; 57 | } 58 | 59 | public void setOpenSourceScannerCredentials(String openSourceScannerCredentials) { 60 | this.openSourceScannerCredentials = openSourceScannerCredentials; 61 | } 62 | 63 | public String getOpenSourceScannerApiUrl() { 64 | return openSourceScannerApiUrl; 65 | } 66 | 67 | public void setOpenSourceScannerApiUrl(String openSourceScannerApiUrl) { 68 | this.openSourceScannerApiUrl = openSourceScannerApiUrl; 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/ScannerPluginType.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | public enum ScannerPluginType { 4 | DEPENDENCYTRACK, 5 | SPOTBUG, 6 | BANDIT, 7 | PROGPILOT; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/ScannerType.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | public enum ScannerType { 4 | OPENSOURCE, 5 | SAST, 6 | DAST, 7 | PROGPILOT 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/SourceProjectType.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | /** 4 | * @author gsiewruk 5 | */ 6 | public enum SourceProjectType { 7 | MAVEN, 8 | GRADLE, 9 | NPM, 10 | PIP, 11 | PHP 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/StandaloneGitResponse.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @author gsiewruk 8 | */ 9 | @Builder 10 | @Getter 11 | public class StandaloneGitResponse { 12 | private String branch; 13 | private String commitId; 14 | private String projectName; 15 | 16 | public StandaloneGitResponse(String branch, String commitId, String projectName) { 17 | this.branch = branch; 18 | this.commitId = commitId; 19 | this.projectName = projectName; 20 | } 21 | 22 | public StandaloneGitResponse() { 23 | 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "StandaloneGitResponse{" + 29 | "branch='" + branch + '\'' + 30 | ", commitId='" + commitId + '\'' + 31 | ", projectName='" + projectName + '\'' + 32 | '}'; 33 | } 34 | 35 | public String getBranch() { 36 | return branch; 37 | } 38 | 39 | public void setBranch(String branch) { 40 | this.branch = branch; 41 | } 42 | 43 | public String getCommitId() { 44 | return commitId; 45 | } 46 | 47 | public void setCommitId(String commitId) { 48 | this.commitId = commitId; 49 | } 50 | 51 | public String getProjectName() { 52 | return projectName; 53 | } 54 | 55 | public void setProjectName(String projectName) { 56 | this.projectName = projectName; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/Status.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public class Status { 7 | private final static Logger log = LoggerFactory.getLogger(Status.class); 8 | private String status; 9 | private String requestId; 10 | 11 | public String getRequestId() { 12 | return requestId; 13 | } 14 | 15 | public void setRequestId(String requestId) { 16 | this.requestId = requestId; 17 | } 18 | 19 | public String getStatus() { 20 | return status; 21 | } 22 | 23 | public void setStatus(String status) { 24 | this.status = status; 25 | } 26 | 27 | public Status(){} 28 | 29 | public Status(String status) { 30 | this.status = status; 31 | } 32 | 33 | public Status(String status, String requestId) { 34 | this.status = status; 35 | this.requestId = requestId; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/mixeway/scanner/utils/Vulnerability.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner.utils; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import io.mixeway.scanner.integrations.model.*; 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | /** 8 | * @author gsiewruk 9 | */ 10 | @JsonInclude(JsonInclude.Include.NON_NULL) 11 | public class Vulnerability { 12 | private String name; 13 | private ScannerType scannerType; 14 | private String description; 15 | private String filename; 16 | private String line; 17 | private String severity; 18 | private String packageName; 19 | private String packageVersion; 20 | private String recomendations; 21 | private String references; 22 | private String category; 23 | 24 | /** 25 | * for Bandit integration 26 | * @param banditResult bandit report 27 | */ 28 | public Vulnerability(BanditResult banditResult) { 29 | this.name = banditResult.getTest_name(); 30 | this.description = banditResult.getIssue_text(); 31 | this.filename = banditResult.getFilename(); 32 | this.severity = StringUtils.capitalize(banditResult.getIssue_severity()); 33 | this.line = banditResult.getLine_number(); 34 | this.references = banditResult.getMore_info(); 35 | this.scannerType = ScannerType.SAST; 36 | } 37 | 38 | /** 39 | * For Dependency Track Integraiton 40 | * @param dTrackVuln dTrack report 41 | */ 42 | public Vulnerability(DTrackVuln dTrackVuln) { 43 | this.name = dTrackVuln.getVulnId(); 44 | this.scannerType = ScannerType.OPENSOURCE; 45 | this.packageName = dTrackVuln.getComponents().stream().findFirst().orElse(null).getName(); 46 | this.packageVersion = dTrackVuln.getComponents().stream().findFirst().orElse(null).getVersion(); 47 | this.severity = dTrackVuln.getSeverity(); 48 | this.description = dTrackVuln.getDescription(); 49 | this.references = dTrackVuln.getReferences(); 50 | this.recomendations = dTrackVuln.getRecommendation(); 51 | } 52 | 53 | /** 54 | * For spotbug integration 55 | * @param bugInstance spotbug report 56 | */ 57 | public Vulnerability(BugInstance bugInstance, BugPattern bugPattern) { 58 | this.name = bugPattern != null? bugPattern.getShortDescriptions() : bugInstance.getShortDescription(); 59 | this.severity = getSeverityFromBugInstanceRank(bugInstance.getRank()); 60 | this.filename = bugInstance.getSourceLine().getSourcepath(); 61 | this.line = bugInstance.getSourceLine().getStart(); 62 | this.description = bugPattern != null? bugPattern.getDetails() : bugInstance.getLongMessage(); 63 | this.scannerType = ScannerType.SAST; 64 | this.category = bugInstance.getCategory(); 65 | } 66 | 67 | public String getCategory() { 68 | return category; 69 | } 70 | 71 | public void setCategory(String category) { 72 | this.category = category; 73 | } 74 | 75 | private String getSeverityFromBugInstanceRank(String rank) { 76 | int rankValue = Integer.parseInt(rank); 77 | String severity = "Info"; 78 | if (rankValue < 5){ 79 | severity = "Critical"; 80 | } else if (rankValue >=5 && rankValue < 10){ 81 | severity = "High"; 82 | } else if ( rankValue >= 10 && rankValue < 15){ 83 | severity = "Medium"; 84 | } else { 85 | severity = "Low"; 86 | } 87 | return severity; 88 | } 89 | 90 | /** 91 | * For Progpilot integration 92 | * @param progPilotVuln 93 | */ 94 | public Vulnerability(ProgPilotVuln progPilotVuln) { 95 | this.name = progPilotVuln.getVuln_name(); 96 | this.scannerType = ScannerType.SAST; 97 | this.line = String.valueOf(progPilotVuln.getSource_line()[0]); 98 | this.filename =progPilotVuln.getSource_file()[0]; 99 | } 100 | 101 | public String getRecomendations() { 102 | return recomendations; 103 | } 104 | 105 | public void setRecomendations(String recomendations) { 106 | this.recomendations = recomendations; 107 | } 108 | 109 | public String getName() { 110 | return name; 111 | } 112 | 113 | public void setName(String name) { 114 | this.name = name; 115 | } 116 | 117 | public ScannerType getScannerType() { 118 | return scannerType; 119 | } 120 | 121 | public void setScannerType(ScannerType scannerType) { 122 | this.scannerType = scannerType; 123 | } 124 | 125 | public String getDescription() { 126 | return description; 127 | } 128 | 129 | public void setDescription(String description) { 130 | this.description = description; 131 | } 132 | 133 | public String getFilename() { 134 | return filename; 135 | } 136 | 137 | public void setFilename(String filename) { 138 | this.filename = filename; 139 | } 140 | 141 | public String getLine() { 142 | return line; 143 | } 144 | 145 | public void setLine(String line) { 146 | this.line = line; 147 | } 148 | 149 | public String getSeverity() { 150 | return severity; 151 | } 152 | 153 | public void setSeverity(String severity) { 154 | this.severity = severity; 155 | } 156 | 157 | public String getPackageName() { 158 | return packageName; 159 | } 160 | 161 | public void setPackageName(String packageName) { 162 | this.packageName = packageName; 163 | } 164 | 165 | public String getPackageVersion() { 166 | return packageVersion; 167 | } 168 | 169 | public void setPackageVersion(String packageVersion) { 170 | this.packageVersion = packageVersion; 171 | } 172 | 173 | public String getReferences() { 174 | return references; 175 | } 176 | 177 | public void setReferences(String references) { 178 | this.references = references; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=mixeway 2 | server.port: 8443 3 | # SSL Configuration 4 | server.ssl.key-store:${PKCS12:/opt/pki/certificate.p12} 5 | server.ssl.key-store-password: ${PKCS12_PASSWORD:1qaz@WSX} 6 | server.ssl.keyStoreType: PKCS12 7 | spring.datasource.driverClassName=org.h2.Driver 8 | spring.datasource.username=sa 9 | spring.datasource.password=password 10 | spring.jpa.database-platform=org.hibernate.dialect.H2Dialect 11 | spring.datasource.url=jdbc:h2:file:~/data/scannerdb 12 | spring.liquibase.change-log=classpath:db/changelog/changelog.sql 13 | sources.location=${SOURCES_LOCATION:mixeway_scan_sources} 14 | sonatype.oss.username=${OSS_USERNAME:blank} 15 | sonatype.oss.key=${OSS_KEY:blank} 16 | source.branch=${BRANCH:master} 17 | mixeway.url=${MIXEWAY_URL:https://hub.mixeway.io} 18 | mixeway.key=${MIXEWAY_KEY:} 19 | mixeway.project=${MIXEWAY_PROJECT_ID:0} 20 | mixeway.project.name=${MIXEWAY_PROJECT_NAME:} -------------------------------------------------------------------------------- /src/main/resources/db/changelog/changelog.sql: -------------------------------------------------------------------------------- 1 | --liquibase formatted sql 2 | 3 | --changeset gsiewruk:prerelease 4 | create table dependencytrack 5 | ( 6 | id serial primary key, 7 | enabled boolean, 8 | apikey text 9 | ); 10 | create table scannertype( 11 | id serial primary key, 12 | name text 13 | ); 14 | insert into scannertype (name) values ('DependencyTrack'), ('Spotbug'); 15 | create table scan ( 16 | id serial primary key, 17 | scannertype_id int references scannertype(id), 18 | inserted date, 19 | running boolean 20 | ); -------------------------------------------------------------------------------- /src/test/java/io/mixeway/scanner/ScannerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.mixeway.scanner; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.databind.DeserializationFeature; 6 | import com.fasterxml.jackson.dataformat.xml.XmlMapper; 7 | import io.mixeway.scanner.utils.Pom; 8 | import io.mixeway.scanner.utils.PomBuild; 9 | import io.mixeway.scanner.utils.PomConfiguration; 10 | import io.mixeway.scanner.utils.PomPlugin; 11 | import org.apache.commons.io.FileUtils; 12 | import org.apache.commons.io.filefilter.DirectoryFileFilter; 13 | import org.apache.commons.io.filefilter.RegexFileFilter; 14 | import org.dom4j.DocumentException; 15 | import org.dom4j.io.SAXReader; 16 | import org.junit.jupiter.api.Test; 17 | import org.junit.runner.RunWith; 18 | import org.springframework.test.context.junit4.SpringRunner; 19 | import org.w3c.dom.Document; 20 | import org.w3c.dom.Element; 21 | import org.w3c.dom.Node; 22 | import org.w3c.dom.NodeList; 23 | import org.xml.sax.InputSource; 24 | import org.xml.sax.SAXException; 25 | 26 | 27 | import javax.xml.parsers.DocumentBuilder; 28 | import javax.xml.parsers.DocumentBuilderFactory; 29 | import javax.xml.parsers.ParserConfigurationException; 30 | import javax.xml.transform.Transformer; 31 | import javax.xml.transform.TransformerConfigurationException; 32 | import javax.xml.transform.TransformerException; 33 | import javax.xml.transform.TransformerFactory; 34 | import javax.xml.transform.dom.DOMSource; 35 | import javax.xml.transform.stream.StreamResult; 36 | import java.io.*; 37 | import java.util.ArrayList; 38 | import java.util.Collection; 39 | import java.util.List; 40 | import java.util.Optional; 41 | import java.util.function.Predicate; 42 | import java.util.stream.Collectors; 43 | 44 | @RunWith(SpringRunner.class) 45 | class ScannerApplicationTests { 46 | 47 | @Test 48 | void contextLoads() throws IOException, InterruptedException, ParserConfigurationException, SAXException, DocumentException, TransformerException { 49 | List packagePaths= FileUtils.listFiles( 50 | new File("/tmp/testjs"), 51 | new RegexFileFilter("package.json"), 52 | DirectoryFileFilter.DIRECTORY 53 | ).stream().map(File::getAbsoluteFile).map(file -> file.toString().split("/package.json")[0]).collect(Collectors.toList()); 54 | packagePaths.forEach(s -> System.out.println(s)); 55 | 56 | 57 | } 58 | 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PROFILES=("REST" "STANDALONE") 3 | # Run DTrack 4 | 5 | echo "Starting Dependency-Track" 6 | cd /opt/dtrack/ && nohup java -Xmx4G -XX:ActiveProcessorCount=2 -jar dependency-track-embedded.war > /opt/dtrack/dtrack.log 2>&1 & 7 | echo "Waiting for NVD to load" 8 | sleep 25 9 | echo "Starting Mixeway Scanner APP" 10 | 11 | if [ -n "$MODE" ]; then 12 | if [[ "$MODE" = "REST" ]] ; then 13 | echo "Selected mode: REST" 14 | cd /app && java -jar /app/app.jar 15 | elif [[ "$MODE" = "STANDALONE" ]] ; then 16 | echo "Selected mode: STANDALONE" 17 | cd /app && java -jar -Dspring.main.web-application-type=NONE /app/app.jar 18 | else 19 | echo "Unknown MODE - $MODE, quitting.." 20 | exit 1 21 | fi 22 | else 23 | echo "MODE variable is not set, running REST which is default mode" 24 | cd /app && java -jar /app/app.jar 25 | fi 26 | 27 | --------------------------------------------------------------------------------