@@ -3,6 +3,8 @@ private import codeql.ruby.Concepts
33private import codeql.ruby.DataFlow
44private import codeql.ruby.ApiGraphs
55private import codeql.ruby.dataflow.FlowSummary
6+ private import codeql.ruby.dataflow.internal.DataFlowDispatch
7+ private import codeql.ruby.CFG
68
79/**
810 * The `Kernel` module is included by the `Object` class, so its methods are available
@@ -347,3 +349,103 @@ class RegexpEscapeSummary extends SummarizedCallable {
347349 preservesValue = false
348350 }
349351}
352+
353+ /** A reference to a `Logger` instance */
354+ private DataFlow:: Node loggerInstance ( ) {
355+ result = API:: getTopLevelMember ( "Logger" ) .getAnInstantiation ( )
356+ or
357+ exists ( DataFlow:: Node inst |
358+ inst = loggerInstance ( ) and
359+ inst .( DataFlow:: LocalSourceNode ) .flowsTo ( result )
360+ )
361+ or
362+ // Assume that a variable assigned as a `Logger` instance is always a
363+ // `Logger` instance. This covers class and instance variables where we can't
364+ // necessarily trace a dataflow path from assignment to use.
365+ exists ( Variable v , Assignment a |
366+ a .getLeftOperand ( ) .getAVariable ( ) = v and
367+ a .getRightOperand ( ) = loggerInstance ( ) .asExpr ( ) .getExpr ( ) and
368+ result .asExpr ( ) .getExpr ( ) .( VariableReadAccess ) .getVariable ( ) = v
369+ )
370+ }
371+
372+ /**
373+ * A call to a `Logger` instance method that causes a message to be logged.
374+ */
375+ abstract class LoggerLoggingCall extends Logging:: Range , DataFlow:: CallNode {
376+ LoggerLoggingCall ( ) { this .getReceiver ( ) = loggerInstance ( ) }
377+ }
378+
379+ /**
380+ * A call to `Logger#add` or its alias `Logger#log`.
381+ */
382+ private class LoggerAddCall extends LoggerLoggingCall {
383+ LoggerAddCall ( ) { this .getMethodName ( ) = [ "add" , "log" ] }
384+
385+ override DataFlow:: Node getAnInput ( ) {
386+ // Both the message and the progname are form part of the log output:
387+ // Logger#add(severity, message) / Logger#add(severity, message, progname)
388+ result = this .getArgument ( 1 )
389+ or
390+ result = this .getArgument ( 2 )
391+ or
392+ // a return value from the block in Logger#add(severity) <block> or in
393+ // Logger#add(severity, nil, progname) <block>
394+ (
395+ this .getNumberOfArguments ( ) = 1
396+ or
397+ // TODO: this could track the value of the `message` argument to make
398+ // this check more accurate
399+ this .getArgument ( 1 ) .asExpr ( ) .getExpr ( ) instanceof NilLiteral
400+ ) and
401+ exprNodeReturnedFrom ( result , this .getBlock ( ) .asExpr ( ) .getExpr ( ) )
402+ }
403+ }
404+
405+ /**
406+ * A call to `Logger#<<`.
407+ */
408+ private class LoggerPushCall extends LoggerLoggingCall {
409+ LoggerPushCall ( ) { this .getMethodName ( ) = "<<" }
410+
411+ override DataFlow:: Node getAnInput ( ) {
412+ // Logger#<<(msg)
413+ result = this .getArgument ( 0 )
414+ }
415+ }
416+
417+ /**
418+ * A call to a `Logger` method that logs at a preset severity level.
419+ *
420+ * Specifically, these methods are `debug`, `error`, `fatal`, `info`,
421+ * `unknown`, and `warn`.
422+ */
423+ private class LoggerInfoStyleCall extends LoggerLoggingCall {
424+ LoggerInfoStyleCall ( ) {
425+ this .getMethodName ( ) = [ "debug" , "error" , "fatal" , "info" , "unknown" , "warn" ]
426+ }
427+
428+ override DataFlow:: Node getAnInput ( ) {
429+ // `msg` from `Logger#info(msg)`,
430+ // or `progname` from `Logger#info(progname) <block>`
431+ result = this .getArgument ( 0 )
432+ or
433+ // a return value from the block in `Logger#info(progname) <block>`
434+ exprNodeReturnedFrom ( result , this .getBlock ( ) .asExpr ( ) .getExpr ( ) )
435+ }
436+ }
437+
438+ /**
439+ * A call to `Logger#progname=`. This sets a default progname.
440+ * This call does not log anything directly, but the assigned value can appear
441+ * in future log messages that do not specify a `progname` argument.
442+ */
443+ private class LoggerSetPrognameCall extends LoggerLoggingCall {
444+ LoggerSetPrognameCall ( ) { this .getMethodName ( ) = "progname=" }
445+
446+ override DataFlow:: Node getAnInput ( ) {
447+ exists ( CfgNodes:: ExprNodes:: AssignExprCfgNode a | this .getArgument ( 0 ) .asExpr ( ) = a |
448+ result .asExpr ( ) = a .getRhs ( )
449+ )
450+ }
451+ }
0 commit comments