11/**
22 * @name Hard-coded credentials
33 * @description Credentials are hard coded in the source code of the application.
4- * @kind problem
4+ * @kind path- problem
55 * @problem.severity error
66 * @precision medium
77 * @id py/hardcoded-credentials
1212 */
1313
1414import python
15+ import semmle.python.security.Paths
1516import semmle.python.security.TaintTracking
1617import semmle.python.filters.Tests
1718
1819class HardcodedValue extends TaintKind {
19-
20- HardcodedValue ( ) {
21- this = "hard coded value"
22- }
23-
20+ HardcodedValue ( ) { this = "hard coded value" }
2421}
2522
2623bindingset [ char, fraction]
2724predicate fewer_characters_than ( StrConst str , string char , float fraction ) {
2825 exists ( string text , int chars |
2926 text = str .getText ( ) and
30- chars = count ( int i | text .charAt ( i ) = char ) |
27+ chars = count ( int i | text .charAt ( i ) = char )
28+ |
3129 /* Allow one character */
3230 chars = 1 or
3331 chars < text .length ( ) * fraction
@@ -46,118 +44,88 @@ predicate possible_reflective_name(string name) {
4644 exists ( Object:: builtin ( name ) )
4745}
4846
49- int char_count ( StrConst str ) {
50- result = count ( string c | c = str .getText ( ) .charAt ( _) )
51- }
47+ int char_count ( StrConst str ) { result = count ( string c | c = str .getText ( ) .charAt ( _) ) }
5248
53- predicate capitalized_word ( StrConst str ) {
54- str .getText ( ) .regexpMatch ( "[A-Z][a-z]+" )
55- }
49+ predicate capitalized_word ( StrConst str ) { str .getText ( ) .regexpMatch ( "[A-Z][a-z]+" ) }
5650
57- predicate format_string ( StrConst str ) {
58- str .getText ( ) .matches ( "%{%}%" )
59- }
51+ predicate format_string ( StrConst str ) { str .getText ( ) .matches ( "%{%}%" ) }
6052
6153predicate maybeCredential ( ControlFlowNode f ) {
6254 /* A string that is not too short and unlikely to be text or an identifier. */
63- exists ( StrConst str |
64- str = f .getNode ( ) |
55+ exists ( StrConst str | str = f .getNode ( ) |
6556 /* At least 10 characters */
6657 str .getText ( ) .length ( ) > 9 and
6758 /* Not too much whitespace */
6859 fewer_characters_than ( str , " " , 0.05 ) and
6960 /* or underscores */
7061 fewer_characters_than ( str , "_" , 0.2 ) and
7162 /* Not too repetitive */
72- exists ( int chars |
73- chars = char_count ( str ) |
63+ exists ( int chars | chars = char_count ( str ) |
7464 chars > 15 or
75- chars * 3 > str .getText ( ) .length ( ) * 2
65+ chars * 3 > str .getText ( ) .length ( ) * 2
7666 ) and
7767 not possible_reflective_name ( str .getText ( ) ) and
7868 not capitalized_word ( str ) and
7969 not format_string ( str )
8070 )
8171 or
8272 /* Or, an integer with over 32 bits */
83- exists ( IntegerLiteral lit |
84- f .getNode ( ) = lit
85- |
73+ exists ( IntegerLiteral lit | f .getNode ( ) = lit |
8674 not exists ( lit .getValue ( ) ) and
8775 /* Not a set of flags or round number */
8876 not lit .getN ( ) .matches ( "%00%" )
8977 )
9078}
9179
9280class HardcodedValueSource extends TaintSource {
81+ HardcodedValueSource ( ) { maybeCredential ( this ) }
9382
94- HardcodedValueSource ( ) {
95- maybeCredential ( this )
96- }
97-
98- override predicate isSourceOf ( TaintKind kind ) {
99- kind instanceof HardcodedValue
100- }
101-
83+ override predicate isSourceOf ( TaintKind kind ) { kind instanceof HardcodedValue }
10284}
10385
10486class CredentialSink extends TaintSink {
105-
10687 CredentialSink ( ) {
10788 exists ( string name |
10889 name .regexpMatch ( getACredentialRegex ( ) ) and
109- not name .suffix ( name .length ( ) - 4 ) = "file"
110- |
90+ not name .suffix ( name .length ( ) - 4 ) = "file"
91+ |
11192 any ( FunctionObject func ) .getNamedArgumentForCall ( _, name ) = this
11293 or
113- exists ( Keyword k |
114- k .getArg ( ) = name and k .getValue ( ) .getAFlowNode ( ) = this
115- )
94+ exists ( Keyword k | k .getArg ( ) = name and k .getValue ( ) .getAFlowNode ( ) = this )
11695 or
117- exists ( CompareNode cmp , NameNode n |
118- n .getId ( ) = name
119- |
96+ exists ( CompareNode cmp , NameNode n | n .getId ( ) = name |
12097 cmp .operands ( this , any ( Eq eq ) , n )
12198 or
12299 cmp .operands ( n , any ( Eq eq ) , this )
123100 )
124101 )
125102 }
126103
127-
128- override predicate sinks ( TaintKind kind ) {
129- kind instanceof HardcodedValue
130- }
131-
104+ override predicate sinks ( TaintKind kind ) { kind instanceof HardcodedValue }
132105}
133106
134107/**
135- * Gets a regular expression for matching names of locations (variables, parameters, keys) that
136- * indicate the value being held is a credential.
137- */
108+ * Gets a regular expression for matching names of locations (variables, parameters, keys) that
109+ * indicate the value being held is a credential.
110+ */
138111private string getACredentialRegex ( ) {
139- result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or
140- result = "(?i).*(puid|username|userid).*" or
141- result = "(?i).*(cert)(?!.*(format|name)).*"
112+ result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or
113+ result = "(?i).*(puid|username|userid).*" or
114+ result = "(?i).*(cert)(?!.*(format|name)).*"
142115}
143116
144117class HardcodedCredentialsConfiguration extends TaintTracking:: Configuration {
145-
146118 HardcodedCredentialsConfiguration ( ) { this = "Hardcoded coredentials configuration" }
147119
148- override predicate isSource ( TaintTracking:: Source source ) { source instanceof HardcodedValueSource }
149-
150- override predicate isSink ( TaintTracking:: Sink sink ) {
151- sink instanceof CredentialSink
120+ override predicate isSource ( TaintTracking:: Source source ) {
121+ source instanceof HardcodedValueSource
152122 }
153123
124+ override predicate isSink ( TaintTracking:: Sink sink ) { sink instanceof CredentialSink }
154125}
155126
156-
157-
158- from HardcodedCredentialsConfiguration config , TaintSource src , TaintSink sink
159-
160- where config .hasFlow ( src , sink ) and
161- not any ( TestScope test ) .contains ( src .( ControlFlowNode ) .getNode ( ) )
162-
163- select sink , "Use of hardcoded credentials from $@." , src , src .toString ( )
127+ from HardcodedCredentialsConfiguration config , TaintedPathSource src , TaintedPathSink sink
128+ where
129+ config .hasFlowPath ( src , sink ) and
130+ not any ( TestScope test ) .contains ( src .getAstNode ( ) )
131+ select sink .getSink ( ) , src , sink , "Use of $@." , src .getSource ( ) , "hardcoded credentials"
0 commit comments