@@ -428,6 +428,22 @@ private module StdlibPrivate {
428428 // ---------------------------------------------------------------------------
429429 // marshal
430430 // ---------------------------------------------------------------------------
431+ /**
432+ * A call to `marshal.load`
433+ * See https://docs.python.org/3/library/marshal.html#marshal.load
434+ */
435+ private class MarshalLoadCall extends Decoding:: Range , DataFlow:: CallCfgNode {
436+ MarshalLoadCall ( ) { this = API:: moduleImport ( "marshal" ) .getMember ( "load" ) .getACall ( ) }
437+
438+ override predicate mayExecuteInput ( ) { any ( ) }
439+
440+ override DataFlow:: Node getAnInput ( ) { result = this .getArg ( 0 ) }
441+
442+ override DataFlow:: Node getOutput ( ) { result = this }
443+
444+ override string getFormat ( ) { result = "marshal" }
445+ }
446+
431447 /**
432448 * A call to `marshal.loads`
433449 * See https://docs.python.org/3/library/marshal.html#marshal.loads
@@ -447,27 +463,87 @@ private module StdlibPrivate {
447463 // ---------------------------------------------------------------------------
448464 // pickle
449465 // ---------------------------------------------------------------------------
450- /** Gets a reference to the `pickle` module. */
451- DataFlow:: Node pickle ( ) { result = API:: moduleImport ( [ "pickle" , "cPickle" , "_pickle" ] ) .getAUse ( ) }
452-
453- /** Provides models for the `pickle` module. */
454- module pickle {
455- /** Gets a reference to the `pickle.loads` function. */
456- DataFlow:: Node loads ( ) {
457- result = API:: moduleImport ( [ "pickle" , "cPickle" , "_pickle" ] ) .getMember ( "loads" ) .getAUse ( )
458- }
466+ /** Gets a reference to any of the `pickle` modules. */
467+ API:: Node pickle ( ) { result = API:: moduleImport ( [ "pickle" , "cPickle" , "_pickle" ] ) }
468+
469+ /**
470+ * A call to `pickle.load`
471+ * See https://docs.python.org/3/library/pickle.html#pickle.load
472+ */
473+ private class PickleLoadCall extends Decoding:: Range , DataFlow:: CallCfgNode {
474+ PickleLoadCall ( ) { this = pickle ( ) .getMember ( "load" ) .getACall ( ) }
475+
476+ override predicate mayExecuteInput ( ) { any ( ) }
477+
478+ override DataFlow:: Node getAnInput ( ) { result in [ this .getArg ( 0 ) , this .getArgByName ( "file" ) ] }
479+
480+ override DataFlow:: Node getOutput ( ) { result = this }
481+
482+ override string getFormat ( ) { result = "pickle" }
459483 }
460484
461485 /**
462486 * A call to `pickle.loads`
463487 * See https://docs.python.org/3/library/pickle.html#pickle.loads
464488 */
465489 private class PickleLoadsCall extends Decoding:: Range , DataFlow:: CallCfgNode {
466- PickleLoadsCall ( ) { this . getFunction ( ) = pickle:: loads ( ) }
490+ PickleLoadsCall ( ) { this = pickle ( ) . getMember ( " loads" ) . getACall ( ) }
467491
468492 override predicate mayExecuteInput ( ) { any ( ) }
469493
470- override DataFlow:: Node getAnInput ( ) { result = this .getArg ( 0 ) }
494+ override DataFlow:: Node getAnInput ( ) { result in [ this .getArg ( 0 ) , this .getArgByName ( "data" ) ] }
495+
496+ override DataFlow:: Node getOutput ( ) { result = this }
497+
498+ override string getFormat ( ) { result = "pickle" }
499+ }
500+
501+ /**
502+ * A construction of a `pickle.Unpickler`
503+ * See https://docs.python.org/3/library/pickle.html#pickle.Unpickler
504+ */
505+ private class PickleUnpicklerCall extends Decoding:: Range , DataFlow:: CallCfgNode {
506+ PickleUnpicklerCall ( ) { this = pickle ( ) .getMember ( "Unpickler" ) .getACall ( ) }
507+
508+ override predicate mayExecuteInput ( ) { any ( ) }
509+
510+ override DataFlow:: Node getAnInput ( ) { result in [ this .getArg ( 0 ) , this .getArgByName ( "file" ) ] }
511+
512+ override DataFlow:: Node getOutput ( ) { result = this .getAMethodCall ( "load" ) }
513+
514+ override string getFormat ( ) { result = "pickle" }
515+ }
516+
517+ // ---------------------------------------------------------------------------
518+ // shelve
519+ // ---------------------------------------------------------------------------
520+ /**
521+ * A call to `shelve.open`
522+ * See https://docs.python.org/3/library/shelve.html#shelve.open
523+ *
524+ * Claiming there is decoding of the input to `shelve.open` is a bit questionable, since
525+ * it's not the filename, but the contents of the file that is decoded.
526+ *
527+ * However, we definitely want to be able to alert if a user is able to control what
528+ * file is used, since that can lead to code execution (even if that file is free of
529+ * path injection).
530+ *
531+ * So right now the best way we have of modeling this seems to be to treat the filename
532+ * argument as being deserialized...
533+ */
534+ private class ShelveOpenCall extends Decoding:: Range , FileSystemAccess:: Range ,
535+ DataFlow:: CallCfgNode {
536+ ShelveOpenCall ( ) { this = API:: moduleImport ( "shelve" ) .getMember ( "open" ) .getACall ( ) }
537+
538+ override predicate mayExecuteInput ( ) { any ( ) }
539+
540+ override DataFlow:: Node getAnInput ( ) {
541+ result in [ this .getArg ( 0 ) , this .getArgByName ( "filename" ) ]
542+ }
543+
544+ override DataFlow:: Node getAPathArgument ( ) {
545+ result in [ this .getArg ( 0 ) , this .getArgByName ( "filename" ) ]
546+ }
471547
472548 override DataFlow:: Node getOutput ( ) { result = this }
473549
0 commit comments