All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.codemodder.providers.sarif.appscan.AppScanRuleSarif Maven / Gradle / Ivy

There is a newer version: 0.98.0
Show newest version
package io.codemodder.providers.sarif.appscan;

import com.contrastsecurity.sarif.*;
import io.codemodder.CodeDirectory;
import io.codemodder.RuleSarif;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.*;

/** A {@link RuleSarif} for AppScan results. */
final class AppScanRuleSarif implements RuleSarif {

  private final SarifSchema210 sarif;
  private final String messageText;
  private final Map> resultsCache;
  private final List locations;

  /** A map of a AppScan SARIF "location" URIs mapped to their respective file paths. */
  private final Map> artifactLocationIndices;

  /**
   * Creates an {@link AppScanRuleSarif} that has already done the work of mapping AppScan SARIF
   * locations, which are strange combinations of class name and file path, into predictable paths.
   */
  AppScanRuleSarif(
      final String messageText, final SarifSchema210 sarif, final CodeDirectory codeDirectory) {
    this.sarif = Objects.requireNonNull(sarif);
    this.messageText = Objects.requireNonNull(messageText);
    this.resultsCache = new HashMap<>();
    this.locations =
        sarif.getRuns().get(0).getArtifacts().stream()
            .map(Artifact::getLocation)
            .map(ArtifactLocation::getUri)
            .map(u -> u.substring(8)) // trim the file:/// of all results
            .toList();
    Map> artifactLocationIndicesMap = new HashMap<>();

    for (int i = 0; i < locations.size(); i++) {
      final Integer index = i;
      String path = locations.get(i);
      path = path.replace('\\', '/');
      // we have a real but partial path, now we have to find it in the repository
      Optional existingRealPath;
      try {
        existingRealPath = codeDirectory.findFilesWithTrailingPath(path);
      } catch (IOException e) {
        throw new UncheckedIOException(e);
      }

      // add to the map if we found a matching file
      existingRealPath.ifPresent(
          p -> artifactLocationIndicesMap.computeIfAbsent(p, k -> new HashSet<>()).add(index));
    }
    this.artifactLocationIndices = Map.copyOf(artifactLocationIndicesMap);
  }

  @Override
  public List getRegionsFromResultsByRule(final Path path) {
    List resultsByLocationPath = getResultsByLocationPath(path);
    return resultsByLocationPath.stream()
        .map(result -> result.getLocations().get(0).getPhysicalLocation().getRegion())
        .toList();
  }

  /**
   * This call receives an actual source file path, whereas the AppScan results store a reference to
   * a fully qualified class name plus ".java", e.g.:
   *
   * 
file:///org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java
*/ @Override public List getResultsByLocationPath(final Path path) { return resultsCache.computeIfAbsent( path, p -> sarif.getRuns().stream() .flatMap(run -> run.getResults().stream()) .filter(result -> result.getMessage().getText().equals(messageText)) .filter( result -> artifactLocationIndices.get(path) != null && artifactLocationIndices .get(path) .contains( result .getLocations() .get(0) .getPhysicalLocation() .getArtifactLocation() .getIndex())) .toList()); } @Override public String getDriver() { return toolName; } /** * This returns the raw SARIF. This SARIF, for Java, contains binary analysis results. These * results may need a lot of massaging to act on. */ @Override public SarifSchema210 rawDocument() { return sarif; } /** * This returns the "message[text]" field from the SARIF results. This is a human-readable value * like "SQL Injection". We would ordinarily use this as the rule ID but this value is different * each time we retrieve the SARIF for a given scan */ @Override public String getRule() { return messageText; } static final String toolName = "HCL AppScan Static Analyzer"; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy