Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 98691fe

Browse files
committed
Python: Model fabric Group execution (version 2.x)
This required some thought for how to model that we're interested in subclasses of `fabric.group.Group`, and not so much that class itself. Some thoughts: --- After initially using this in `module Group` /** A reference to a subclass of `fabric.group.Group` */ abstract class SubclassRef extends DataFlow::Node { } private class SubclassInstantiation extends SubclassInstanceSource, DataFlow::CfgNode { override CallNode node; SubclassInstantiation() { node.getFunction() = any(SubclassRef ref).asCfgNode() } } with this in `module SerialGroup` and `module ThreadingGroup`: class ClassRef extends DataFlow::Node, fabric::group::Group::SubclassRef { ClassRef() { this = classRef(DataFlow::TypeTracker::end()) } } I wasn't too much of fan of that approach. Since we probably need the `SubclassInstanceSource` anyway, and don't really have a specific use for `SubclassRef`, I just went with concrete (QL) subclasses of `SubclassInstanceSource` in each of the modules for the Python subclasses. I really don't know what the best approach is, so I'm very open to suggestions. I think we'll really have to flesh this out for handling Django responses, since we're interested in the fact that some subclasses provide default values for the content-type, and keeping track of that is important for XSS (since there is no XSS if response is `text/plain`)
1 parent f10456e commit 98691fe

2 files changed

Lines changed: 180 additions & 6 deletions

File tree

python/ql/src/experimental/semmle/python/frameworks/Fabric.qll

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,5 +394,185 @@ private module FabricV2 {
394394
)
395395
}
396396
}
397+
398+
// -------------------------------------------------------------------------
399+
// fabric.group
400+
// -------------------------------------------------------------------------
401+
/** Gets a reference to the `fabric.group` module. */
402+
DataFlow::Node group() { result = fabric_attr("group") }
403+
404+
/** Provides models for the `fabric.group` module */
405+
module group {
406+
/**
407+
* Gets a reference to the attribute `attr_name` of the `fabric.group` module.
408+
* WARNING: Only holds for a few predefined attributes.
409+
*/
410+
private DataFlow::Node group_attr(DataFlow::TypeTracker t, string attr_name) {
411+
attr_name in ["SerialGroup", "ThreadingGroup"] and
412+
(
413+
t.start() and
414+
result = DataFlow::importNode("fabric.group" + "." + attr_name)
415+
or
416+
t.startInAttr(attr_name) and
417+
result = group()
418+
)
419+
or
420+
// Due to bad performance when using normal setup with `group_attr(t2, attr_name).track(t2, t)`
421+
// we have inlined that code and forced a join
422+
exists(DataFlow::TypeTracker t2 |
423+
exists(DataFlow::StepSummary summary |
424+
group_attr_first_join(t2, attr_name, result, summary) and
425+
t = t2.append(summary)
426+
)
427+
)
428+
}
429+
430+
pragma[nomagic]
431+
private predicate group_attr_first_join(
432+
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
433+
DataFlow::StepSummary summary
434+
) {
435+
DataFlow::StepSummary::step(group_attr(t2, attr_name), res, summary)
436+
}
437+
438+
/**
439+
* Gets a reference to the attribute `attr_name` of the `fabric.group` module.
440+
* WARNING: Only holds for a few predefined attributes.
441+
*/
442+
private DataFlow::Node group_attr(string attr_name) {
443+
result = group_attr(DataFlow::TypeTracker::end(), attr_name)
444+
}
445+
446+
/**
447+
* Provides models for the `fabric.group.Group` class and its subclasses.
448+
*
449+
* `fabric.group.Group` is an abstract class, that has concrete implementations
450+
* `SerialGroup` and `ThreadingGroup`.
451+
*
452+
* See
453+
* - https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group
454+
* - https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.SerialGroup
455+
* - https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.ThreadingGroup
456+
*/
457+
module Group {
458+
/**
459+
* A source of an instance of a subclass of `fabric.group.Group`
460+
*
461+
* This can include instantiation of a class, return value from function
462+
* calls, or a special parameter that will be set when functions are call by external
463+
* library.
464+
*
465+
* Use `Group::subclassInstance()` predicate to get references to an instance of a subclass of `fabric.group.Group`.
466+
*/
467+
abstract class SubclassInstanceSource extends DataFlow::Node { }
468+
469+
/** Gets a reference to an instance of a subclass of `fabric.group.Group`. */
470+
private DataFlow::Node subclassInstance(DataFlow::TypeTracker t) {
471+
t.start() and
472+
result instanceof SubclassInstanceSource
473+
or
474+
exists(DataFlow::TypeTracker t2 | result = subclassInstance(t2).track(t2, t))
475+
}
476+
477+
/** Gets a reference to an instance of a subclass of `fabric.group.Group`. */
478+
DataFlow::Node subclassInstance() {
479+
result = subclassInstance(DataFlow::TypeTracker::end())
480+
}
481+
482+
/**
483+
* Gets a reference to the `run` method on an instance of a subclass of `fabric.group.Group`.
484+
*
485+
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
486+
*/
487+
private DataFlow::Node subclassInstanceRunMethod(DataFlow::TypeTracker t) {
488+
t.startInAttr("run") and
489+
result = subclassInstance()
490+
or
491+
exists(DataFlow::TypeTracker t2 | result = subclassInstanceRunMethod(t2).track(t2, t))
492+
}
493+
494+
/**
495+
* Gets a reference to the `run` method on an instance of a subclass of `fabric.group.Group`.
496+
*
497+
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
498+
*/
499+
DataFlow::Node subclassInstanceRunMethod() {
500+
result = subclassInstanceRunMethod(DataFlow::TypeTracker::end())
501+
}
502+
}
503+
504+
/**
505+
* A call to `run`, `sudo` on an instance of a subclass of `fabric.group.Group`.
506+
*
507+
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
508+
*/
509+
private class FabricGroupRunCall extends SystemCommandExecution::Range, DataFlow::CfgNode {
510+
override CallNode node;
511+
512+
FabricGroupRunCall() {
513+
node.getFunction() = fabric::group::Group::subclassInstanceRunMethod().asCfgNode()
514+
}
515+
516+
override DataFlow::Node getCommand() {
517+
result.asCfgNode() = [node.getArg(0), node.getArgByName("command")]
518+
}
519+
}
520+
521+
/**
522+
* Provides models for the `fabric.group.SerialGroup` class
523+
*
524+
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.SerialGroup.
525+
*/
526+
module SerialGroup {
527+
/** Gets a reference to the `fabric.group.SerialGroup` class. */
528+
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
529+
t.start() and
530+
result = group_attr("SerialGroup")
531+
or
532+
// Handle `fabric.SerialGroup` alias
533+
t.start() and
534+
result = fabric_attr("SerialGroup")
535+
or
536+
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
537+
}
538+
539+
/** Gets a reference to the `fabric.group.SerialGroup` class. */
540+
private DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
541+
542+
private class ClassInstantiation extends Group::SubclassInstanceSource, DataFlow::CfgNode {
543+
override CallNode node;
544+
545+
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
546+
}
547+
}
548+
549+
/**
550+
* Provides models for the `fabric.group.ThreadingGroup` class
551+
*
552+
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.ThreadingGroup.
553+
*/
554+
module ThreadingGroup {
555+
/** Gets a reference to the `fabric.group.ThreadingGroup` class. */
556+
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
557+
t.start() and
558+
result = group_attr("ThreadingGroup")
559+
or
560+
// Handle `fabric.ThreadingGroup` alias
561+
t.start() and
562+
result = fabric_attr("ThreadingGroup")
563+
or
564+
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
565+
}
566+
567+
/** Gets a reference to the `fabric.group.ThreadingGroup` class. */
568+
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
569+
570+
private class ClassInstantiation extends Group::SubclassInstanceSource, DataFlow::CfgNode {
571+
override CallNode node;
572+
573+
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
574+
}
575+
}
576+
}
397577
}
398578
}
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +0,0 @@
1-
| fabric_v2_test.py:29:66:29:91 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
2-
| fabric_v2_test.py:32:25:32:50 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
3-
| fabric_v2_test.py:35:62:35:87 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
4-
| fabric_v2_test.py:41:69:41:94 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
5-
| fabric_v2_test.py:44:25:44:50 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |
6-
| fabric_v2_test.py:47:65:47:90 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" |

0 commit comments

Comments
 (0)