Security defects in custom ABAP fall into a small number of classes: injection through dynamic statements, missing or incomplete authorisation checks, unsafe database and operating system calls, unvalidated input reaching the file system or a web interface, hardcoded secrets, and hidden logic that changes behaviour for one user.
The RedRays ABAP Code Scanner ships a fixed catalogue of checks over those classes. What follows is the classes themselves, with what each one looks like in ABAP and what it does to an SAP system. The individual rules are not published, for the ordinary reason that a published rule is a rule that can be written around.
| Class | The pattern | Consequence | CWE |
|---|---|---|---|
| ABAP code injection | a dynamic CALL FUNCTION, CALL METHOD, PERFORM, SUBMIT, CREATE OBJECT or generated subroutine pool whose target comes from input |
the caller chooses what the application server executes | CWE-94, CWE-470 |
| Missing authorisation check | a sensitive read, write, transaction call or file operation with no AUTHORITY-CHECK in front of it |
anyone who can start the program does what the program does | CWE-862 |
| SQL injection | a WHERE clause built from a string, native SQL through EXEC SQL, or an ADBC statement assembled from input |
reads and writes outside what the program was meant to touch, including across clients | CWE-89 |
| OS command injection | external command execution on the application server where the command or its parameters come from input | code execution as the SAP system user on the host | CWE-78 |
| Directory traversal | OPEN DATASET and related file operations on a path the caller supplies |
reads and writes anywhere the SAP system user can reach on the file system | CWE-22 |
| Hardcoded credentials | a password, key, token or RFC destination credential written into the source | anyone who can read the program holds the credential, and it survives every password policy | CWE-798 |
| Cross-site scripting | unescaped output in a BSP application, a Web Dynpro component or an ICF handler | script execution in the browser of another SAP user | CWE-79 |
| Backdoors and hidden logic | a comparison against a specific user name, client or system id that switches behaviour | a control that is present for everybody except one person | CWE-912 |
| Information disclosure | sensitive table content written to a list, a spool, a log or a trace | data leaves the authorisation model through a channel nobody reviews | CWE-532 |
| Unsafe dynamic field access | ASSIGN with a dynamic component, or a field symbol built from input |
reads and writes of fields the program never named | CWE-470 |
Every finding is filed under the class of defect it belongs to, which is what keeps a long list of them readable.
The clearest example of why a scanner exists is also the simplest to state. A report declares a parameter on its selection screen and later uses that parameter as the name of the function module to call:
PARAMETERS: p_fm TYPE string LOWER CASE.
START-OF-SELECTION.
CALL FUNCTION p_fm
EXCEPTIONS OTHERS = 1.
Neither statement is a defect. The first is how every report on every SAP system takes input. The second is how every dispatcher in SAP's own code works, where the variable is filled from a table the caller does not control. What makes this a defect is that the value arrives from whoever runs the report and nothing between the two statements constrains it. Anyone who can start the report chooses what the application server executes.
A scan reports this as one finding with the two hops printed: the parameter declaration, then the call. The fix is the one the recommendation names: an authorisation check, and a whitelist of permitted function module or class names, with anything else rejected.
The second pattern is an absence rather than a statement:
PARAMETERS: p_pernr TYPE persno.
START-OF-SELECTION.
SELECT SINGLE ansal waers FROM pa0008
INTO (lv_salary, lv_currency)
WHERE pernr = p_pernr.
There is no AUTHORITY-CHECK on the HR structural or infotype authorisation object before the read. Any user who can start the report reads any employee's basic pay. The defect is that a statement is missing, so no search over source text finds it: the program looks exactly like a program that had no reason to check.
Detecting it means knowing what the table holds and what a check over it would have to cover. The same shape applies to file operations, to repository operations, to CALL TRANSACTION, to SUBMIT, and to remote-enabled function modules, which are callable from outside the system by anyone who reaches the gateway.
A remote-enabled function module is an entry point. The authorisation check that protects the transaction a user would otherwise go through is not automatically in front of it, and the caller may not be a person at all. A custom RFC-enabled module that reads a table, writes a document or runs a command, with no check of its own, is reachable by anything holding an RFC destination into the system.
Scans record the remote-callable flag on each object, so the findings on RFC-enabled modules can be read as a group. Whether the destinations pointing at that system are themselves trustworthy is a different question, answered in RFC trust and stored credentials.
No, and a tool that did would be useless. Dynamic dispatch is ordinary ABAP. What is reported is a dynamic call whose target can be traced back to input that the caller controls.
Yes, that is one of the classes in the catalogue. It is also one of the few classes where a text search gets part of the way, which is why the more interesting half of the result is the credentials that are not written as an obvious literal.
The classes above map onto CWE identifiers, which is the vocabulary most application security tooling and most auditors use. A finding names its class of defect; the mapping table on this page is how to translate that into CWE terms.
Yes. Those are the SAP web surfaces where unescaped output and missing checks matter most, and they are covered as their own classes rather than folded into the report and function module cases.
Open SQL is safe while the statement is static. A WHERE clause assembled into a string, or a dynamic table name, reopens exactly the hole the language was designed to close, and that is what the check looks for.