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

io.codemodder.DefaultSarifParser Maven / Gradle / Ivy

There is a newer version: 0.97.9
Show newest version
package io.codemodder;

import com.contrastsecurity.sarif.Result;
import com.contrastsecurity.sarif.Run;
import com.contrastsecurity.sarif.SarifSchema210;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

final class DefaultSarifParser implements SarifParser {

  private Optional readSarifFile(final Path sarifFile) {
    try {
      return Optional.of(
          new ObjectMapper().readValue(Files.newInputStream(sarifFile), SarifSchema210.class));
    } catch (final IOException e) {
      LOG.error("Problem deserializing SARIF file: {}", sarifFile, e);
      return Optional.empty();
    }
  }

  /** Send the arguments to all factories and returns the first that built something. */
  private Optional> tryToBuild(
      final String toolName,
      final RuleDescriptor rule,
      final SarifSchema210 sarif,
      final CodeDirectory codeDirectory,
      final List factories) {
    for (final var factory : factories) {
      final var maybeRuleSarif =
          factory.build(toolName, rule.ruleId, rule.messageText, sarif, codeDirectory);
      if (maybeRuleSarif.isPresent()) {
        return Optional.of(Map.entry(toolName, maybeRuleSarif.get()));
      }
    }

    LOG.info("Found SARIF from unsupported tool: {}", toolName);
    return Optional.empty();
  }

  private record RuleDescriptor(String ruleId, String messageText) {}

  private RuleDescriptor extractRuleId(final Result result, final Run run) {
    if (result.getRuleId() == null) {
      var toolIndex = result.getRule().getToolComponent().getIndex();
      var ruleIndex = result.getRule().getIndex();
      var maybeRule =
          run.getTool().getExtensions().stream()
              .skip(toolIndex)
              .findFirst()
              .flatMap(tool -> tool.getRules().stream().skip(ruleIndex).findFirst())
              .map(descriptor -> new RuleDescriptor(descriptor.getId(), null));

      if (maybeRule.isPresent()) {
        return maybeRule.get();
      } else {
        LOG.info("Could not find rule id for result.");
        return null;
      }
    }
    return new RuleDescriptor(
        result.getRuleId(), result.getMessage() != null ? result.getMessage().getText() : null);
  }

  private Stream> fromSarif(
      final Run run, final SarifSchema210 sarif, final CodeDirectory codeDirectory) {
    // driver name
    final var toolName = run.getTool().getDriver().getName();
    final List factories =
        ServiceLoader.load(RuleSarifFactory.class).stream()
            .map(ServiceLoader.Provider::get)
            .toList();
    final var runResults = run.getResults();
    final var allResults =
        runResults != null
            ? runResults.stream()
                .map(result -> extractRuleId(result, run))
                .filter(Objects::nonNull)
                .filter(ruleDescriptor -> ruleDescriptor.ruleId != null)
                .collect(Collectors.toMap(r -> r.ruleId, r -> r, (r1, r2) -> r1))
                .values()
                .stream()
            : Stream.empty();

    return allResults.flatMap(
        rule -> tryToBuild(toolName, rule, sarif, codeDirectory, factories).stream());
  }

  /**
   * Parse a list of SARIF files and organize the obtained {@link RuleSarif}s by tool name with a
   * map .
   */
  @Override
  public Map> parseIntoMap(
      final List sarifFiles, final CodeDirectory codeDirectory) {
    final var map = new HashMap>();
    sarifFiles.stream()
        .flatMap(f -> readSarifFile(f).stream())
        .flatMap(
            sarif -> sarif.getRuns().stream().flatMap(run -> fromSarif(run, sarif, codeDirectory)))
        .forEach(
            p ->
                map.merge(
                    p.getKey(),
                    new ArrayList<>(Collections.singletonList(p.getValue())),
                    (l1, l2) -> {
                      l1.add(l2.get(0));
                      return l1;
                    }));
    return map;
  }

  private static final Logger LOG = LoggerFactory.getLogger(DefaultSarifParser.class);
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy