All files / src/rules sql_escape_host_variables.ts

95.6% Statements 87/91
82.85% Branches 29/35
100% Functions 5/5
95.6% Lines 87/91

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 911x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 8922x 8922x 8922x 8922x 26617x 26617x 26617x 26617x 26617x 26617x 26617x 26617x 26617x 26617x 8922x 8922x 8485x 8485x 8922x 8922x 202x 202x 8922x 8922x 233x 233x 233x 18x 18x 215x 233x     215x 233x 1159x 1159x 1159x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 9x 14x 5x 5x 5x 5x 1159x 1145x 1145x 1145x 6x     6x 7x 7x 7x 4x 4x 4x 4x 4x 7x 6x 1159x 215x 215x 215x 8922x
import * as Statements from "../abap/2_statements/statements";
import * as Expressions from "../abap/2_statements/expressions";
import {Issue} from "../issue";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {Version} from "../version";
import {RuleTag, IRuleMetadata} from "./_irule";
import {ABAPFile} from "../abap/abap_file";
import {ABAPObject} from "../objects/_abap_object";
 
export class SQLEscapeHostVariablesConf extends BasicRuleConfig {
}
 
export class SQLEscapeHostVariables extends ABAPRule {
  private conf = new SQLEscapeHostVariablesConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "sql_escape_host_variables",
      title: "Escape SQL host variables",
      shortDescription: `Escape SQL host variables, from 740sp05`,
      extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#avoid-obsolete-language-elements`,
      tags: [RuleTag.Upport, RuleTag.Styleguide],
      badExample: `SELECT * FROM tab INTO TABLE res WHERE field = val.`,
      goodExample: `SELECT * FROM tab INTO TABLE @res WHERE field = @val.`,
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: SQLEscapeHostVariablesConf) {
    this.conf = conf;
  }
 
  public runParsed(file: ABAPFile, obj: ABAPObject) {
    const issues: Issue[] = [];
 
    if (obj.getType() === "INTF") {
      return [];
    }
 
    if (this.reg.getConfig().getVersion() < Version.v740sp02 && this.reg.getConfig().getVersion() !== Version.Cloud) {
      return [];
    }
 
    for (const s of file.getStatements()) {
      const str = s.concatTokens().toUpperCase();
      if (s.get() instanceof Statements.Select
          || s.get() instanceof Statements.SelectLoop) {
// this is not completely correct and does not catch all, but okay for now
// todo: replace with logic from "else if" branch below, when/if it proves to work
        if (str.includes(" INTO ( @")
            || str.includes(" INTO (@")
            || str.includes(" INTO @")
            || str.includes(" INTO TABLE @")
            || str.includes(" INTO CORRESPONDING FIELDS OF @")
            || str.includes(" INTO CORRESPONDING FIELDS OF TABLE @")
            || str.includes(" APPENDING TABLE @")
            || ( str.includes(" APPENDING ") === false && str.includes(" INTO ") === false )
            || str.includes(" APPENDING CORRESPONDING FIELDS OF TABLE @")) {
          continue;
        } else {
          const message = "Escape SQL host variables";
          const issue = Issue.atToken(file, s.getFirstToken(), message, this.getMetadata().key, this.conf.severity);
          issues.push(issue);
        }
      } else if (s.get() instanceof Statements.UpdateDatabase
          || s.get() instanceof Statements.ModifyDatabase
          || s.get() instanceof Statements.InsertDatabase
          || s.get() instanceof Statements.DeleteDatabase) {
        if (str.startsWith("MODIFY SCREEN FROM ")) {
          continue;
        }
        for (const o of s.findAllExpressions(Expressions.SQLSource)) {
          const first = o.getFirstChild();
          if ((first?.get() instanceof Expressions.Source && first.getChildren()[0].get() instanceof Expressions.FieldChain)
              || (first?.get() instanceof Expressions.SimpleSource3 && first.getChildren()[0].get() instanceof Expressions.FieldChain)) {
            const message = "Escape SQL host variables";
            const issue = Issue.atToken(file, first.getFirstToken(), message, this.getMetadata().key, this.conf.severity);
            issues.push(issue);
            break;
          }
        }
      }
    }
 
    return issues;
  }
}