@@ -57,16 +57,43 @@ module CleartextLogging {
5757 /** A piece of data printed, considered as a flow sink. */
5858 class PrintedDataAsSink extends Sink {
5959 PrintedDataAsSink ( ) {
60- this = API:: builtin ( "print" ) .getACall ( ) .getArg ( _)
61- or
62- // special handling of writing to `sys.stdout` and `sys.stderr`, which is
63- // essentially the same as printing
64- this =
65- API:: moduleImport ( "sys" )
66- .getMember ( [ "stdout" , "stderr" ] )
67- .getMember ( "write" )
68- .getACall ( )
69- .getArg ( 0 )
60+ (
61+ this = API:: builtin ( "print" ) .getACall ( ) .getArg ( _)
62+ or
63+ // special handling of writing to `sys.stdout` and `sys.stderr`, which is
64+ // essentially the same as printing
65+ this =
66+ API:: moduleImport ( "sys" )
67+ .getMember ( [ "stdout" , "stderr" ] )
68+ .getMember ( "write" )
69+ .getACall ( )
70+ .getArg ( 0 )
71+ ) and
72+ // since some of the inner error handling implementation of the logging module is
73+ // ```py
74+ // sys.stderr.write('Message: %r\n'
75+ // 'Arguments: %s\n' % (record.msg,
76+ // record.args))
77+ // ```
78+ // any time we would report flow to such a logging sink, we can ALSO report
79+ // the flow to the `record.msg`/`record.args` sinks -- obviously we
80+ // don't want that.
81+ //
82+ // However, simply removing taint edges out of a sink is not a good enough solution,
83+ // since we would only flag one of the `logging.info` calls in the following example
84+ // due to use-use flow
85+ // ```py
86+ // logging.info(user_controlled)
87+ // logging.info(user_controlled)
88+ // ```
89+ //
90+ // The same approach is used in the command injection query.
91+ not exists ( Module loggingInit |
92+ loggingInit .getName ( ) = "logging.__init__" and
93+ this .getScope ( ) .getEnclosingModule ( ) = loggingInit and
94+ // do allow this call if we're analyzing logging/__init__.py as part of CPython though
95+ not exists ( loggingInit .getFile ( ) .getRelativePath ( ) )
96+ )
7097 }
7198 }
7299}
0 commit comments