Python taint-tracking: Add documented example test.

This commit is contained in:
Mark Shannon
2019-08-29 13:03:58 +01:00
parent 10fddbc19b
commit 179f4ee88f
9 changed files with 413 additions and 0 deletions

View File

@@ -4,6 +4,12 @@ private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.Filters as Filters
import semmle.python.dataflow.Legacy
/* See tests/library-tests/taint/examples
* For examples of taint sources, sinks and flow,
* including attribute paths, contexts and edges.
*/
newtype TTaintTrackingContext =
TNoParam()
or

View File

@@ -0,0 +1,60 @@
/**
* @kind path-problem
*
* An example configuration.
* See ExampleConfiguration.expected for the results of running this query.
*/
import python
import semmle.python.dataflow.Configuration
/* First of all we set up some TaintKinds */
class Engineer extends TaintKind {
Engineer() { this = "Wally" or this = "Dilbert" }
}
class Wally extends Engineer {
Wally() { this = "Wally" }
}
/** Then the configuration */
class DilbertConfig extends TaintTracking::Configuration {
DilbertConfig() { this = "Dilbert config" }
override predicate isSource(DataFlow::Node node, TaintKind kind) {
node.asAstNode().(Name).getId() = "ENGINEER" and kind instanceof Engineer
}
override predicate isSink(DataFlow::Node node, TaintKind kind) {
/* Engineers hate meetings */
function_param("meeting", node) and kind instanceof Engineer
}
override predicate isBarrier(DataFlow::Node node, TaintKind kind) {
/* There is no way that Wally is working through lunch */
function_param("lunch", node) and kind instanceof Wally
}
override predicate isBarrier(DataFlow::Node node) {
/* Even the conscientious stop work if the building is on fire */
function_param("fire", node)
}
}
/** Helper predicate looking for `funcname(..., arg, ...)` */
private predicate function_param(string funcname, DataFlow::Node arg) {
exists(Call call |
call.getFunc().(Name).getId() = funcname and
arg.asAstNode() = call.getAnArg()
)
}

View File

@@ -0,0 +1,85 @@
| 5: SSA variable worker = Dilbert (p1 = Dilbert) | use | 6: worker = Dilbert (p1 = Dilbert) |
| 5: SSA variable worker = Wally (p1 = Wally) | use | 6: worker = Wally (p1 = Wally) |
| 5: worker = Dilbert (p1 = Dilbert) | [dataflow] | 5: SSA variable worker = Dilbert (p1 = Dilbert) |
| 5: worker = Wally (p1 = Wally) | [dataflow] | 5: SSA variable worker = Wally (p1 = Wally) |
| 6: worker = Dilbert (p1 = Dilbert) | [dataflow] | 6: SSA variable self.worker = Dilbert (p1 = Dilbert) |
| 6: worker = Wally (p1 = Wally) | [dataflow] | 6: SSA variable self.worker = Wally (p1 = Wally) |
| 8: SSA variable worker = Dilbert (p0 = Dilbert) | [dataflow] | 10: SSA variable worker = Dilbert (p0 = Dilbert) |
| 8: SSA variable worker = Dilbert (p0 = Dilbert) | use | 10: worker = Dilbert (p0 = Dilbert) |
| 8: SSA variable worker = Wally (p0 = Wally) | [dataflow] | 10: SSA variable worker = Wally (p0 = Wally) |
| 8: SSA variable worker = Wally (p0 = Wally) | use | 10: worker = Wally (p0 = Wally) |
| 8: worker = Dilbert (p0 = Dilbert) | [dataflow] | 8: SSA variable worker = Dilbert (p0 = Dilbert) |
| 8: worker = Wally (p0 = Wally) | [dataflow] | 8: SSA variable worker = Wally (p0 = Wally) |
| 10: worker = Dilbert (p0 = Dilbert) | parameter | 5: worker = Dilbert (p1 = Dilbert) |
| 10: worker = Wally (p0 = Wally) | parameter | 5: worker = Wally (p1 = Wally) |
| 12: SSA variable worker = Dilbert (p0 = Dilbert) | use | 13: worker = Dilbert (p0 = Dilbert) |
| 12: worker = Dilbert (p0 = Dilbert) | [dataflow] | 12: SSA variable worker = Dilbert (p0 = Dilbert) |
| 17: ENGINEER = Dilbert | [dataflow] | 17: SSA variable worker = Dilbert |
| 17: ENGINEER = Wally | [dataflow] | 17: SSA variable worker = Wally |
| 17: SSA variable worker = Dilbert | [dataflow] | 18: SSA variable worker = Dilbert |
| 17: SSA variable worker = Dilbert | use | 18: worker = Dilbert |
| 17: SSA variable worker = Wally | [dataflow] | 18: SSA variable worker = Wally |
| 17: SSA variable worker = Wally | use | 18: worker = Wally |
| 22: ENGINEER = Dilbert | [dataflow] | 22: SSA variable worker = Dilbert |
| 22: ENGINEER = Wally | [dataflow] | 22: SSA variable worker = Wally |
| 22: SSA variable worker = Dilbert | use | 23: worker = Dilbert |
| 23: SSA variable worker = Dilbert | [dataflow] | 24: SSA variable worker = Dilbert |
| 23: SSA variable worker = Dilbert | use | 24: worker = Dilbert |
| 23: lunch() = Dilbert | [dataflow] | 23: SSA variable worker = Dilbert |
| 23: worker = Dilbert | call | 23: lunch() = Dilbert |
| 23: worker = Dilbert | parameter | 12: worker = Dilbert (p0 = Dilbert) |
| 28: ENGINEER = Dilbert | [dataflow] | 28: SSA variable worker = Dilbert |
| 28: ENGINEER = Wally | [dataflow] | 28: SSA variable worker = Wally |
| 28: SSA variable worker = Dilbert | [dataflow] | 29: SSA variable worker = Dilbert |
| 28: SSA variable worker = Dilbert | use | 29: worker = Dilbert |
| 28: SSA variable worker = Wally | [dataflow] | 29: SSA variable worker = Wally |
| 28: SSA variable worker = Wally | use | 29: worker = Wally |
| 33: ENGINEER = Dilbert | [dataflow] | 33: SSA variable worker = Dilbert |
| 33: ENGINEER = Wally | [dataflow] | 33: SSA variable worker = Wally |
| 33: SSA variable worker = Dilbert | use | 34: worker = Dilbert |
| 33: SSA variable worker = Wally | use | 34: worker = Wally |
| 34: SSA variable task.worker = Dilbert | use | 37: task.worker = Dilbert |
| 34: SSA variable task.worker = Wally | use | 37: task.worker = Wally |
| 34: assign_task().worker = Dilbert | [dataflow] | 34: SSA variable task.worker = Dilbert |
| 34: assign_task().worker = Wally | [dataflow] | 34: SSA variable task.worker = Wally |
| 34: worker = Dilbert | call | 34: assign_task().worker = Dilbert |
| 34: worker = Dilbert | parameter | 8: worker = Dilbert (p0 = Dilbert) |
| 34: worker = Wally | call | 34: assign_task().worker = Wally |
| 34: worker = Wally | parameter | 8: worker = Wally (p0 = Wally) |
| 37: Attribute = Dilbert | call | 37: lunch() = Dilbert |
| 37: Attribute = Dilbert | parameter | 12: worker = Dilbert (p0 = Dilbert) |
| 37: SSA variable worker = Dilbert | [dataflow] | 39: SSA variable worker = Dilbert |
| 37: SSA variable worker = Dilbert | use | 39: worker = Dilbert |
| 37: lunch() = Dilbert | [dataflow] | 37: SSA variable worker = Dilbert |
| 43: ENGINEER = Dilbert | [dataflow] | 43: SSA variable worker = Dilbert |
| 43: ENGINEER = Wally | [dataflow] | 43: SSA variable worker = Wally |
| 48: SSA variable worker = Dilbert (p0 = Dilbert) | use | 53: worker = Dilbert (p0 = Dilbert) |
| 48: SSA variable worker = Wally (p0 = Wally) | use | 53: worker = Wally (p0 = Wally) |
| 48: worker = Dilbert (p0 = Dilbert) | [dataflow] | 48: SSA variable worker = Dilbert (p0 = Dilbert) |
| 48: worker = Wally (p0 = Wally) | [dataflow] | 48: SSA variable worker = Wally (p0 = Wally) |
| 57: ENGINEER = Dilbert | [dataflow] | 57: SSA variable worker = Dilbert |
| 57: ENGINEER = Wally | [dataflow] | 57: SSA variable worker = Wally |
| 57: SSA variable worker = Dilbert | use | 58: worker = Dilbert |
| 57: SSA variable worker = Wally | use | 58: worker = Wally |
| 58: SSA variable worker = Dilbert | [dataflow] | 60: SSA variable worker = Dilbert |
| 58: SSA variable worker = Dilbert | use | 60: worker = Dilbert |
| 58: SSA variable worker = Wally | [dataflow] | 60: SSA variable worker = Wally |
| 58: SSA variable worker = Wally | use | 60: worker = Wally |
| 58: cubical() = Dilbert | [dataflow] | 58: SSA variable worker = Dilbert |
| 58: cubical() = Wally | [dataflow] | 58: SSA variable worker = Wally |
| 58: worker = Dilbert | call | 58: cubical() = Dilbert |
| 58: worker = Dilbert | parameter | 48: worker = Dilbert (p0 = Dilbert) |
| 58: worker = Wally | call | 58: cubical() = Wally |
| 58: worker = Wally | parameter | 48: worker = Wally (p0 = Wally) |
| 64: ENGINEER = Dilbert | [dataflow] | 64: SSA variable worker = Dilbert |
| 64: ENGINEER = Wally | [dataflow] | 64: SSA variable worker = Wally |
| 64: SSA variable worker = Dilbert | use | 65: worker = Dilbert |
| 65: SSA variable worker = Dilbert | use | 66: worker = Dilbert |
| 65: lunch() = Dilbert | [dataflow] | 65: SSA variable worker = Dilbert |
| 65: worker = Dilbert | call | 65: lunch() = Dilbert |
| 65: worker = Dilbert | parameter | 12: worker = Dilbert (p0 = Dilbert) |
| 66: SSA variable worker = Dilbert | [dataflow] | 68: SSA variable worker = Dilbert |
| 66: SSA variable worker = Dilbert | use | 68: worker = Dilbert |
| 66: cubical() = Dilbert | [dataflow] | 66: SSA variable worker = Dilbert |
| 66: worker = Dilbert | call | 66: cubical() = Dilbert |
| 66: worker = Dilbert | parameter | 48: worker = Dilbert (p0 = Dilbert) |

View File

@@ -0,0 +1,34 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.Implementation
import DilbertConfig
string shortString(TaintTrackingNode n) {
if n.getContext().isTop() then
result = n.getLocation().getStartLine() + ": " + n.getNode().toString() + n.getPath().extension() + " = " + n.getTaintKind()
else
result = n.getLocation().getStartLine() + ": " + n.getNode().toString() + n.getPath().extension() + " = " + n.getTaintKind() + " (" + n.getContext().toString() + ")"
}
bindingset[s, len]
string ljust(string s, int len) {
result = s +
" ".prefix(len-s.length())
}
bindingset[s, len]
string format(string s, int len) {
exists(string label |
s = "" and label = "[dataflow]"
or
s != "" and label = s
|
result = ljust(label, len)
)
}
from TaintTrackingNode p, TaintTrackingNode s, string label
where any(DilbertConfig config).(TaintTrackingImplementation).flowStep(p, s, label)
select format(shortString(p), 50), format(label, 10), shortString(s)

View File

@@ -0,0 +1,35 @@
edges
| example.py:17:14:17:21 | Dilbert | example.py:18:13:18:18 | Dilbert |
| example.py:17:14:17:21 | Wally | example.py:18:13:18:18 | Wally |
| example.py:22:14:22:21 | Dilbert | example.py:23:20:23:25 | Dilbert |
| example.py:23:14:23:26 | Dilbert | example.py:24:13:24:18 | Dilbert |
| example.py:23:20:23:25 | Dilbert | example.py:23:14:23:26 | Dilbert |
| example.py:28:14:28:21 | Dilbert | example.py:29:13:29:18 | Dilbert |
| example.py:28:14:28:21 | Wally | example.py:29:13:29:18 | Wally |
| example.py:33:14:33:21 | Dilbert | example.py:34:24:34:29 | Dilbert |
| example.py:34:12:34:30 | .worker = Dilbert | example.py:37:20:37:23 | .worker = Dilbert |
| example.py:34:24:34:29 | Dilbert | example.py:34:12:34:30 | .worker = Dilbert |
| example.py:37:14:37:31 | Dilbert | example.py:39:13:39:18 | Dilbert |
| example.py:37:20:37:23 | .worker = Dilbert | example.py:37:20:37:30 | Dilbert |
| example.py:37:20:37:30 | Dilbert | example.py:37:14:37:31 | Dilbert |
| example.py:57:14:57:21 | Dilbert | example.py:58:22:58:27 | Dilbert |
| example.py:57:14:57:21 | Wally | example.py:58:22:58:27 | Wally |
| example.py:58:14:58:28 | Dilbert | example.py:60:13:60:18 | Dilbert |
| example.py:58:14:58:28 | Wally | example.py:60:13:60:18 | Wally |
| example.py:58:22:58:27 | Dilbert | example.py:58:14:58:28 | Dilbert |
| example.py:58:22:58:27 | Wally | example.py:58:14:58:28 | Wally |
| example.py:64:14:64:21 | Dilbert | example.py:65:20:65:25 | Dilbert |
| example.py:65:14:65:26 | Dilbert | example.py:66:22:66:27 | Dilbert |
| example.py:65:20:65:25 | Dilbert | example.py:65:14:65:26 | Dilbert |
| example.py:66:14:66:28 | Dilbert | example.py:68:13:68:18 | Dilbert |
| example.py:66:22:66:27 | Dilbert | example.py:66:14:66:28 | Dilbert |
#select
| example.py:18:13:18:18 | worker | example.py:17:14:17:21 | Dilbert | example.py:18:13:18:18 | Dilbert | $@ goes to a $@. | example.py:17:14:17:21 | ENGINEER | Dilbert | example.py:18:13:18:18 | worker | meeting |
| example.py:18:13:18:18 | worker | example.py:17:14:17:21 | Wally | example.py:18:13:18:18 | Wally | $@ goes to a $@. | example.py:17:14:17:21 | ENGINEER | Wally | example.py:18:13:18:18 | worker | meeting |
| example.py:24:13:24:18 | worker | example.py:22:14:22:21 | Dilbert | example.py:24:13:24:18 | Dilbert | $@ goes to a $@. | example.py:22:14:22:21 | ENGINEER | Dilbert | example.py:24:13:24:18 | worker | meeting |
| example.py:29:13:29:18 | worker | example.py:28:14:28:21 | Dilbert | example.py:29:13:29:18 | Dilbert | $@ goes to a $@. | example.py:28:14:28:21 | ENGINEER | Dilbert | example.py:29:13:29:18 | worker | meeting |
| example.py:29:13:29:18 | worker | example.py:28:14:28:21 | Wally | example.py:29:13:29:18 | Wally | $@ goes to a $@. | example.py:28:14:28:21 | ENGINEER | Wally | example.py:29:13:29:18 | worker | meeting |
| example.py:39:13:39:18 | worker | example.py:33:14:33:21 | Dilbert | example.py:39:13:39:18 | Dilbert | $@ goes to a $@. | example.py:33:14:33:21 | ENGINEER | Dilbert | example.py:39:13:39:18 | worker | meeting |
| example.py:60:13:60:18 | worker | example.py:57:14:57:21 | Dilbert | example.py:60:13:60:18 | Dilbert | $@ goes to a $@. | example.py:57:14:57:21 | ENGINEER | Dilbert | example.py:60:13:60:18 | worker | meeting |
| example.py:60:13:60:18 | worker | example.py:57:14:57:21 | Wally | example.py:60:13:60:18 | Wally | $@ goes to a $@. | example.py:57:14:57:21 | ENGINEER | Wally | example.py:60:13:60:18 | worker | meeting |
| example.py:68:13:68:18 | worker | example.py:64:14:64:21 | Dilbert | example.py:68:13:68:18 | Dilbert | $@ goes to a $@. | example.py:64:14:64:21 | ENGINEER | Dilbert | example.py:68:13:68:18 | worker | meeting |

View File

@@ -0,0 +1,15 @@
/**
* @kind path-problem
*
* An example configuration.
* See ExampleConfiguration.expected for the results of running this query.
*/
import python
import DilbertConfig
import semmle.python.security.Paths
from DilbertConfig config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "$@ goes to a $@.", src.getNode(), src.getTaintKind().toString(), sink.getNode(), "meeting"

View File

@@ -0,0 +1,99 @@
| example.py:5 | SSA variable worker | no attribute | p1 = Dilbert | Dilbert |
| example.py:5 | SSA variable worker | no attribute | p1 = Wally | Wally |
| example.py:5 | worker | no attribute | p1 = Dilbert | Dilbert |
| example.py:5 | worker | no attribute | p1 = Wally | Wally |
| example.py:6 | SSA variable self | attribute worker | p1 = Dilbert | Dilbert |
| example.py:6 | SSA variable self | attribute worker | p1 = Wally | Wally |
| example.py:6 | worker | no attribute | p1 = Dilbert | Dilbert |
| example.py:6 | worker | no attribute | p1 = Wally | Wally |
| example.py:8 | SSA variable worker | no attribute | p0 = Dilbert | Dilbert |
| example.py:8 | SSA variable worker | no attribute | p0 = Wally | Wally |
| example.py:8 | worker | no attribute | p0 = Dilbert | Dilbert |
| example.py:8 | worker | no attribute | p0 = Wally | Wally |
| example.py:10 | SSA variable worker | no attribute | p0 = Dilbert | Dilbert |
| example.py:10 | SSA variable worker | no attribute | p0 = Wally | Wally |
| example.py:10 | Task() | attribute worker | p0 = Dilbert | Dilbert |
| example.py:10 | Task() | attribute worker | p0 = Wally | Wally |
| example.py:10 | worker | no attribute | p0 = Dilbert | Dilbert |
| example.py:10 | worker | no attribute | p0 = Wally | Wally |
| example.py:12 | SSA variable worker | no attribute | p0 = Dilbert | Dilbert |
| example.py:12 | worker | no attribute | p0 = Dilbert | Dilbert |
| example.py:13 | worker | no attribute | p0 = Dilbert | Dilbert |
| example.py:17 | ENGINEER | no attribute | | Dilbert |
| example.py:17 | ENGINEER | no attribute | | Wally |
| example.py:17 | SSA variable worker | no attribute | | Dilbert |
| example.py:17 | SSA variable worker | no attribute | | Wally |
| example.py:18 | SSA variable worker | no attribute | | Dilbert |
| example.py:18 | SSA variable worker | no attribute | | Wally |
| example.py:18 | worker | no attribute | | Dilbert |
| example.py:18 | worker | no attribute | | Wally |
| example.py:22 | ENGINEER | no attribute | | Dilbert |
| example.py:22 | ENGINEER | no attribute | | Wally |
| example.py:22 | SSA variable worker | no attribute | | Dilbert |
| example.py:22 | SSA variable worker | no attribute | | Wally |
| example.py:23 | SSA variable worker | no attribute | | Dilbert |
| example.py:23 | lunch() | no attribute | | Dilbert |
| example.py:23 | worker | no attribute | | Dilbert |
| example.py:24 | SSA variable worker | no attribute | | Dilbert |
| example.py:24 | worker | no attribute | | Dilbert |
| example.py:28 | ENGINEER | no attribute | | Dilbert |
| example.py:28 | ENGINEER | no attribute | | Wally |
| example.py:28 | SSA variable worker | no attribute | | Dilbert |
| example.py:28 | SSA variable worker | no attribute | | Wally |
| example.py:29 | SSA variable worker | no attribute | | Dilbert |
| example.py:29 | SSA variable worker | no attribute | | Wally |
| example.py:29 | worker | no attribute | | Dilbert |
| example.py:29 | worker | no attribute | | Wally |
| example.py:33 | ENGINEER | no attribute | | Dilbert |
| example.py:33 | ENGINEER | no attribute | | Wally |
| example.py:33 | SSA variable worker | no attribute | | Dilbert |
| example.py:33 | SSA variable worker | no attribute | | Wally |
| example.py:34 | SSA variable task | attribute worker | | Dilbert |
| example.py:34 | SSA variable task | attribute worker | | Wally |
| example.py:34 | assign_task() | attribute worker | | Dilbert |
| example.py:34 | assign_task() | attribute worker | | Wally |
| example.py:34 | worker | no attribute | | Dilbert |
| example.py:34 | worker | no attribute | | Wally |
| example.py:37 | Attribute | no attribute | | Dilbert |
| example.py:37 | SSA variable worker | no attribute | | Dilbert |
| example.py:37 | lunch() | no attribute | | Dilbert |
| example.py:37 | task | attribute worker | | Dilbert |
| example.py:37 | task | attribute worker | | Wally |
| example.py:39 | SSA variable worker | no attribute | | Dilbert |
| example.py:39 | worker | no attribute | | Dilbert |
| example.py:43 | ENGINEER | no attribute | | Dilbert |
| example.py:43 | ENGINEER | no attribute | | Wally |
| example.py:43 | SSA variable worker | no attribute | | Dilbert |
| example.py:43 | SSA variable worker | no attribute | | Wally |
| example.py:48 | SSA variable worker | no attribute | p0 = Dilbert | Dilbert |
| example.py:48 | SSA variable worker | no attribute | p0 = Wally | Wally |
| example.py:48 | worker | no attribute | p0 = Dilbert | Dilbert |
| example.py:48 | worker | no attribute | p0 = Wally | Wally |
| example.py:53 | worker | no attribute | p0 = Dilbert | Dilbert |
| example.py:53 | worker | no attribute | p0 = Wally | Wally |
| example.py:57 | ENGINEER | no attribute | | Dilbert |
| example.py:57 | ENGINEER | no attribute | | Wally |
| example.py:57 | SSA variable worker | no attribute | | Dilbert |
| example.py:57 | SSA variable worker | no attribute | | Wally |
| example.py:58 | SSA variable worker | no attribute | | Dilbert |
| example.py:58 | SSA variable worker | no attribute | | Wally |
| example.py:58 | cubical() | no attribute | | Dilbert |
| example.py:58 | cubical() | no attribute | | Wally |
| example.py:58 | worker | no attribute | | Dilbert |
| example.py:58 | worker | no attribute | | Wally |
| example.py:60 | SSA variable worker | no attribute | | Dilbert |
| example.py:60 | SSA variable worker | no attribute | | Wally |
| example.py:60 | worker | no attribute | | Dilbert |
| example.py:60 | worker | no attribute | | Wally |
| example.py:64 | ENGINEER | no attribute | | Dilbert |
| example.py:64 | ENGINEER | no attribute | | Wally |
| example.py:64 | SSA variable worker | no attribute | | Dilbert |
| example.py:64 | SSA variable worker | no attribute | | Wally |
| example.py:65 | SSA variable worker | no attribute | | Dilbert |
| example.py:65 | lunch() | no attribute | | Dilbert |
| example.py:65 | worker | no attribute | | Dilbert |
| example.py:66 | SSA variable worker | no attribute | | Dilbert |
| example.py:66 | cubical() | no attribute | | Dilbert |
| example.py:66 | worker | no attribute | | Dilbert |
| example.py:68 | SSA variable worker | no attribute | | Dilbert |
| example.py:68 | worker | no attribute | | Dilbert |

View File

@@ -0,0 +1,10 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.dataflow.Implementation
import DilbertConfig
from TaintTrackingNode n
where n.getConfiguration() instanceof DilbertConfig
select n.getLocation().toString(), n.getNode().toString(), n.getPath().toString(), n.getContext().toString(), n.getTaintKind()

View File

@@ -0,0 +1,69 @@
# A class to demonstrate tracking of tainted attributes.
class Task(object):
def __init__(self, worker):
self.worker = worker
def assign_task(worker):
# The Task object will have its .worker attribute with whatever taint `worker`
return Task(worker)
def lunch(worker):
return worker
# The engineers go to a meeting
def example1():
worker = ENGINEER
meeting(worker)
# The engineers go to a meeting, but might need to skip lunch
def example2():
worker = ENGINEER
worker = lunch(worker)
meeting(worker)
# Everyone goes to a meeting (but that's OK for the managers)
def example3():
worker = ENGINEER
meeting(worker)
#Tracking taint of an attribute.
def example4():
worker = ENGINEER
task = assign_task(worker)
#Here 'task' has its .worker attribute "tainted"
#Task team lunch
worker = lunch(task.worker)
#And meeting
meeting(worker)
#A fire -- A barrier to all kinds of taint.
def example5():
worker = ENGINEER
worker = fire(worker)
meeting(worker)
#Some context sensitive flow
def cubical(worker):
''' The flow here is context sensitive.
In example6 the worker can be any engineer,
but in example7 is cannot be Wally.
'''
return worker
# Workers go back to their cubicals
def example6():
worker = ENGINEER
worker = cubical(worker)
#And meeting
meeting(worker)
# Workers have lunch, then go back to their cubicals
def example7():
worker = ENGINEER
worker = lunch(worker)
worker = cubical(worker)
#And meeting
meeting(worker)