All files / src/rules sql_escape_host_variables.ts

95.56% Statements 86/90
87.88% Branches 29/33
100% Functions 5/5
95.56% Lines 86/90

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 901x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7653x 7653x 7653x 7653x 22822x 22822x 22822x 22822x 22822x 22822x 22822x 22822x 22822x 22822x 7653x 7653x 7346x 7346x 7653x 7653x 139x 139x 7653x 7653x 169x 169x 169x 10x 10x 159x 169x     159x 169x 814x 814x 814x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 8x 12x 4x 4x 4x 4x 814x 802x 802x 802x 6x     6x 7x 7x 4x 4x 4x 4x 4x 7x 6x 814x 159x 159x 159x 7653x
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) {
            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;
  }
}