/**
 * @name SQLI Vulnerability
 * @description Using untrusted strings in a sql query allows sql injection attacks.
 * @kind path-problem
 * @id cpp/sqlivulnerable
 * @problem.severity warning
 */

import cpp

// from Call cl
// select cl
/*
 * int get_new_id() {
 *    int id = getpid();
 *    return id;
 * }
 */

// Goal: Find connection
// 1. reading user data -- source
//    count = read(STDIN_FILENO, buf, BUFSIZE - 1);
//                               ^^^
// from FunctionCall read, Expr buf
// where read.getTarget().getName() = "read"
// and buf = read.getArgument(1)
// select read, buf
predicate isSource(Expr buf) {
  exists(FunctionCall read |
    read.getTarget().getName() = "read" and
    buf = read.getArgument(1)
  )
}

// from Expr buf
// where isSource(buf)
// select buf
class MySource extends Expr {
  MySource() {
    exists(FunctionCall read |
      read.getTarget().getName() = "read" and
      this = read.getArgument(1) 
    )
  }
}

// from MySource buf
// select buf
// 2. writing sql -- sink
//     rc = sqlite3_exec(db, query, NULL, 0, &zErrMsg);
//                           ^^^^^
// from FunctionCall exec, Expr query
// where exec.getTarget().getName() = "sqlite3_exec"
// and query = exec.getArgument(1)
// select exec, query
predicate isSink(Expr query) {
  exists(FunctionCall exec |
    exec.getTarget().getName() = "sqlite3_exec" and
    query = exec.getArgument(1)
  )
}

// from Expr query
// where isSink(query)
// select query
class MySink extends Expr {
  FunctionCall exec;

  MySink() {
    exec.getTarget().getName() = "sqlite3_exec" and
    this = exec.getArgument(1)
  }

  FunctionCall getTheCall() { result = exec }
}

// from MySink query
// select query, query.getTheCall()
// 3. find call path between 1 and 2 them
import semmle.code.cpp.dataflow.new.TaintTracking

module SqliFlowConfig implements DataFlow::ConfigSig {
  predicate isSource(DataFlow::Node source) {
    exists(MySource ms | ms = source.asDefiningArgument())
  }

  predicate isSink(DataFlow::Node sink) { exists(MySink ms | ms = sink.asIndirectArgument()) }
}

module MyFlow = TaintTracking::Global<SqliFlowConfig>;

import MyFlow::PathGraph

from MyFlow::PathNode source, MyFlow::PathNode sink
where MyFlow::flowPath(source, sink)
select sink, source, sink, "Possible SQL injection"

// from MySource ms 
// select ms.getASuccessor*()