/**
 * Provides a taint-tracking configuration for reasoning about command injection vulnerabilities.
 */
import csharp

module CommandInjection {
  import semmle.code.csharp.dataflow.flowsources.Remote
  import semmle.code.csharp.frameworks.system.Diagnostics
  import semmle.code.csharp.security.Sanitizers

  /**
   * A source specific to command injection vulnerabilities.
   */
  abstract class Source extends DataFlow::Node { }


  /**
   * A sink for command injection vulnerabilities.
   */
  abstract class Sink extends DataFlow::ExprNode { }

  /**
   * A sanitizer for user input treated as code vulnerabilities.
   */
  abstract class Sanitizer extends DataFlow::ExprNode { }

  /**
   * A taint-tracking configuration for command injection vulnerabilities.
   */
  class TaintTrackingConfiguration extends TaintTracking::Configuration {
    TaintTrackingConfiguration() {
      this = "CommandInjection"
    }

    override
    predicate isSource(DataFlow::Node source) {
      source instanceof Source
    }

    override
    predicate isSink(DataFlow::Node sink) {
      sink instanceof Sink
    }

    override
    predicate isSanitizer(DataFlow::Node node) {
      node instanceof Sanitizer
    }
  }

  /** A source of remote user input. */
  class RemoteSource extends Source {
    RemoteSource() {
      this instanceof RemoteFlowSource
    }
  }

  /**
   * A sink in `System.Diagnostic.Process` or its related classes.
   */
  class SystemProcessCommandInjectionSink extends Sink {
    SystemProcessCommandInjectionSink() {
      /*
       * Arguments passed directly to the `System.Diagnostics.Process.Start` method.
       */
      exists(SystemDiagnosticsProcessClass processClass |
        this.getExpr() = processClass.getAStartMethod().getAParameter().getAnAssignedArgument()
      ) or
      /*
       * Values set on a `System.Diagnostics.ProcessStartInfo` class.
       */
      exists(SystemDiagnosticsProcessStartInfoClass startInfoClass |
        this.getExpr() = startInfoClass.getAConstructor().getACall().getAnArgument() or
        exists(Property p |
          p = startInfoClass.getArgumentsProperty() or
          p = startInfoClass.getFileNameProperty() or
          p = startInfoClass.getWorkingDirectoryProperty() |
          this.getExpr() = p.getSetter().getParameter(0).getAnAssignedArgument()
        )
      )
    }
  }

  private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }

  private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
}
