Hello all,
I am trying to use code analysis rule 5903.
Below I would identify as literals. Is there a different rule to use to identify them or would i need a custom rule?
AND RL.ROLE_ID = ORM.ROLE_ID;
P_TRAN_STATUS := 'SEARCH EXECUTED SUCCESSFULLY';
P_TRAN_DESC := 'SEARCH EXECUTED SUCCESSFULLY FOR REQ: ' || P_REQ_ID;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
P_TRAS_STATUS := 'SEARCH FAILED';
P_TRAN_DESC := 'DID NOT FIND REQ: ' || P_REQ_ID;
Thanks
Dave
Hi Dave,
Currently the 5903 rule has been implemented for queries only, as the rule’s title suggests. (“All queries (dynamic and fixed) must leverage bind variables for performance, security, and maintainability purposes.”)
The current XPath expression …
//SELECT_ITEM//LITERAL,
//(WHERE, HAVING)//LITERAL
[not(parent::EXPR[count(LITERAL) = 2 and LITERAL[1]/@value = LITERAL[2]/@value and @cat = ("eq", "gt", "lt", "ne")])]
(::: exclude comparisons between equal literal values, like 1 = 1, 'a' < 'a' ::)
… will work for literals inside select items and WHERE and HAVING clauses only. The tail of the expression excludes literals found in expressions such as WHERE 1 = 1, a widespread practice.
If you’d like to target all literals (in a custom rule 7000+) then you could catch them all using an expression as simple as
//LITERAL
but that’s quite rude. It may be better to compose it from more specific searches such as the RHS in assignments:
//ASSIGN/RHS/LITERAL
and grab literals from all default clauses:
//DEFAULT/LITERAL
which combined would give you a single rule:
//ASSIGN/RHS/LITERAL, //DEFAULT/LITERAL
Let me know what else you’re thinking of which could be added to this rule, I’ll be glad to help.
Thanks,
Andre
Cool thanks, so I was trying to create my own rule but the line numbers are not coming back.
Matches Case-Insensitively: The matches(@dbvalue, '^[CGLV]_', 'i') function uses the 'i' flag to ensure c_, C_, v_, and V_ are all correctly ignored.
Identify Three Specific Areas:
-
General Assignments: Catches strings assigned to variables that aren't constants.
-
Specific RHS Literals: Finds hard-coded values assigned to variables, provided they don't have your specific "safe" prefixes (C, G, L, V).
-
Default Values: Catches hard-coded values in variable declarations (e.g., v_date DATE := '01-JAN-2026') unless the variable name starts with your chosen prefixes.
//ASSIGN_STMT[not(ancestor::CONST_DECL)]//LITERAL_STRING
|
//ASSIGN[not(LHS//IDENTIFIER[matches(@dbvalue, ‘ [CGLV]_', 'i')])] /RHS/LITERAL*
|
//DEFAULT[not(ancestor::VARIABLE_DECLARATION/IDENTIFIER[matches(@dbvalue, '‘*[CGLV]_’, 'i')])] /LITERAL
I assume below is what’s being flagged but without a line number I am not sure.
Items 1 and 2 seem overlapping, 1 catches them all, 2 is selective. For 1 did you mean variables that are constants, instead?
Well, for the fun, let’s assume that for now. Then the following seems to work for me:
//ASSIGN[RHS/LITERAL]/LHS/QNAME/IDENTIFIER[ancestor::BLOCK[1]/STMT/DECL_SET/CONST_DECL/QNAME/IDENTIFIER/@dbvalue = @dbvalue]
,
//ASSIGN[not(LHS//IDENTIFIER[matches(@dbvalue, '^CGLV')])]/RHS/LITERAL
,
//VAR_DECL/DEFAULT[not(../QNAME/IDENTIFIER[matches(@dbvalue, '^CGLV')])]/LITERAL
Please don’t use | to combine results, use comma instead.
The first expression (item 1) looks up the constant declaration in the current block only ( BLOCK[1] ).
You can also rewrite expression 1 to obtain the following:
declare function ConstantDecl()
{
./(
let $name := ./IDENTIFIER/@dbvalue
return ./ancestor::BLOCK[1][STMT/DECL_SET/CONST_DECL/QNAME/IDENTIFIER/@dbvalue = $name]
)
}
//ASSIGN[RHS/LITERAL]/LHS/QNAME[ConstantDecl()]
,
//ASSIGN[not(LHS//IDENTIFIER[matches(@dbvalue, '^CGLV')])]/RHS/LITERAL
,
//VAR_DECL/DEFAULT[not(../QNAME/IDENTIFIER[matches(@dbvalue, '^CGLV')])]/LITERAL
which decomposes things, hence should be a bit more clear. That will also ease future code expansion.
Andre