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 9361x 9361x 9361x 9361x 27932x 27932x 27932x 27932x 27932x 27932x 27932x 27932x 27932x 27932x 9361x 9361x 8898x 8898x 9361x 9361x 216x 216x 9361x 9361x 251x 251x 251x 19x 19x 232x 251x     232x 251x 1290x 1290x 1290x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 9x 14x 5x 5x 5x 5x 1290x 1276x 1276x 1276x 6x     6x 7x 7x 7x 4x 4x 4x 4x 4x 7x 6x 1290x 232x 232x 232x 9361x
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;
  }
}