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

Skip to content

Commit f6af248

Browse files
authored
python: recover isPackageUsed
- add `unknownAttribute` to pre-compute negation - add `Node`-less formulation of "is imported"
1 parent 71583bf commit f6af248

4 files changed

Lines changed: 61 additions & 25 deletions

File tree

python/ql/lib/semmle/python/ApiGraphs.qll

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,21 @@ module API {
291291
not m.matches("%.%")
292292
}
293293

294+
/**
295+
* Holds if an import of module `m` exists.
296+
*
297+
* This is determined without referring to `Node`,
298+
* allowing this predicate to be used in a negative
299+
* context when constructing new nodes.
300+
*/
301+
predicate moduleImportExists(string m) {
302+
Impl::isImported(m) and
303+
// restrict `moduleImport` so it will never give results for a dotted name. Note
304+
// that we cannot move this logic to the `MkModuleImport` construction, since we
305+
// need the intermediate API graph nodes for the prefixes in `import foo.bar.baz`.
306+
not m.matches("%.%")
307+
}
308+
294309
/** Gets a node corresponding to the built-in with the given name, if any. */
295310
Node builtin(string n) { result = moduleImport("builtins").getMember(n) }
296311

@@ -492,14 +507,38 @@ module API {
492507
*
493508
* Ignores relative imports, such as `from ..foo.bar import baz`.
494509
*/
495-
private predicate imports(DataFlow::Node imp, string name) {
510+
private predicate imports(DataFlow::CfgNode imp, string name) {
496511
exists(PY::ImportExprNode iexpr |
497-
imp.asCfgNode() = iexpr and
512+
imp.getNode() = iexpr and
498513
not iexpr.getNode().isRelative() and
499514
name = iexpr.getNode().getImportedModuleName()
500515
)
501516
}
502517

518+
/**
519+
* Holds is them module `name` is imported.
520+
*
521+
* This is determined syntactically.
522+
*/
523+
cached
524+
predicate isImported(string name) {
525+
// Ignore the following module name for Python 2, as we alias `__builtin__` to `builtins` elsewhere
526+
(name != "__builtin__" or PY::major_version() = 3) and
527+
(
528+
exists(PY::ImportExpr iexpr |
529+
not iexpr.isRelative() and
530+
name = iexpr.getImportedModuleName()
531+
)
532+
or
533+
// When we `import foo.bar.baz` we want to create API graph nodes also for the prefixes
534+
// `foo` and `foo.bar`:
535+
name = any(PY::ImportExpr e | not e.isRelative()).getAnImportedModuleName()
536+
)
537+
or
538+
// The `builtins` module should always be implicitly available
539+
name = "builtins"
540+
}
541+
503542
private import semmle.python.dataflow.new.internal.Builtins
504543
private import semmle.python.dataflow.new.internal.ImportStar
505544

@@ -518,7 +557,7 @@ module API {
518557
*/
519558
private TApiNode potential_import_star_base(PY::Scope s) {
520559
exists(DataFlow::Node n |
521-
n.asCfgNode() = ImportStar::potentialImportStarBase(s) and
560+
n.(DataFlow::CfgNode).getNode() = ImportStar::potentialImportStarBase(s) and
522561
use(result, n)
523562
)
524563
}
@@ -540,17 +579,17 @@ module API {
540579
or
541580
// TODO: I had expected `DataFlow::AttrWrite` to contain the attribute writes from a dict, that's how JS works.
542581
exists(PY::Dict dict, PY::KeyValuePair item |
543-
dict = pred.asExpr() and
582+
dict = pred.(DataFlow::ExprNode).getNode().getNode() and
544583
dict.getItem(_) = item and
545584
lbl = Label::member(item.getKey().(PY::StrConst).getS()) and
546-
rhs.asExpr() = item.getValue()
585+
rhs.(DataFlow::ExprNode).getNode().getNode() = item.getValue()
547586
)
548587
or
549-
exists(PY::CallableExpr fn | fn = pred.asExpr() |
588+
exists(PY::CallableExpr fn | fn = pred.(DataFlow::ExprNode).getNode().getNode() |
550589
not fn.getInnerScope().isAsync() and
551590
lbl = Label::return() and
552591
exists(PY::Return ret |
553-
rhs.asExpr() = ret.getValue() and
592+
rhs.(DataFlow::ExprNode).getNode().getNode() = ret.getValue() and
554593
ret.getScope() = fn.getInnerScope()
555594
)
556595
)
@@ -592,7 +631,8 @@ module API {
592631
// Subclassing a node
593632
lbl = Label::subclass() and
594633
exists(DataFlow::Node superclass | pred.flowsTo(superclass) |
595-
ref.asExpr().(PY::ClassExpr).getABase() = superclass.asExpr()
634+
ref.(DataFlow::ExprNode).getNode().getNode().(PY::ClassExpr).getABase() =
635+
superclass.(DataFlow::ExprNode).getNode().getNode()
596636
)
597637
or
598638
// awaiting
@@ -604,26 +644,27 @@ module API {
604644
)
605645
or
606646
exists(DataFlow::Node def, PY::CallableExpr fn |
607-
rhs(base, def) and fn = trackDefNode(def).asExpr()
647+
rhs(base, def) and fn = trackDefNode(def).(DataFlow::ExprNode).getNode().getNode()
608648
|
609649
exists(int i, int offset |
610650
if exists(PY::Parameter p | p = fn.getInnerScope().getAnArg() and p.isSelf())
611651
then offset = 1
612652
else offset = 0
613653
|
614654
lbl = Label::parameter(i - offset) and
615-
ref.asExpr() = fn.getInnerScope().getArg(i)
655+
ref.(DataFlow::ExprNode).getNode().getNode() = fn.getInnerScope().getArg(i)
616656
)
617657
or
618658
exists(string name, PY::Parameter param |
619659
lbl = Label::keywordParameter(name) and
620660
param = fn.getInnerScope().getArgByName(name) and
621661
not param.isSelf() and
622-
ref.asExpr() = param
662+
ref.(DataFlow::ExprNode).getNode().getNode() = param
623663
)
624664
or
625665
lbl = Label::selfParameter() and
626-
ref.asExpr() = any(PY::Parameter p | p = fn.getInnerScope().getAnArg() and p.isSelf())
666+
ref.(DataFlow::ExprNode).getNode().getNode() =
667+
any(PY::Parameter p | p = fn.getInnerScope().getAnArg() and p.isSelf())
627668
)
628669
or
629670
// Built-ins, treated as members of the module `builtins`
@@ -635,7 +676,7 @@ module API {
635676
base = potential_import_star_base(s) and
636677
lbl =
637678
Label::member(any(string name |
638-
ImportStar::namePossiblyDefinedInImportStar(ref.asCfgNode(), name, s)
679+
ImportStar::namePossiblyDefinedInImportStar(ref.(DataFlow::CfgNode).getNode(), name, s)
639680
))
640681
)
641682
}
@@ -901,7 +942,7 @@ module API {
901942
ApiLabel memberFromRef(DataFlow::AttrRef ref) {
902943
result = member(ref.getAttributeName())
903944
or
904-
not exists(ref.getAttributeName()) and
945+
ref.unknownAttribute() and
905946
result = unknownMember()
906947
}
907948

python/ql/lib/semmle/python/dataflow/new/internal/Attributes.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ abstract class AttrRef extends Node {
5050
* better results.
5151
*/
5252
abstract string getAttributeName();
53+
54+
/** Holds if a name could not be determined for this attribute. */
55+
predicate unknownAttribute() { not exists(this.getAttributeName()) }
5356
}
5457

5558
/**

python/ql/lib/semmle/python/dataflow/new/internal/Builtins.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ module Builtins {
6060
* Currently this is an over-approximation, and may not account for things like overwriting a
6161
* built-in with a different value.
6262
*/
63-
DataFlow::Node likelyBuiltin(string name) {
63+
DataFlow::CfgNode likelyBuiltin(string name) {
6464
exists(Module m |
65-
result.asCfgNode() =
65+
result.getNode() =
6666
any(NameNode n |
6767
possible_builtin_accessed_in_module(n, name, m) and
6868
not possible_builtin_defined_in_module(name, m)

python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,7 @@ private import AccessPathSyntax
3333
/**
3434
* Holds if models describing `package` may be relevant for the analysis of this database.
3535
*/
36-
bindingset[package]
37-
predicate isPackageUsed(string package) {
38-
// We would like to ask for imports here,
39-
// but that entails non-monotonic recursion.
40-
// For now, we follow Ruby and just allow all packages.
41-
//
42-
// exists(API::moduleImport(package))
43-
any()
44-
}
36+
predicate isPackageUsed(string package) { API::moduleImportExists(package) }
4537

4638
/** Gets a Python-specific interpretation of the `(package, type, path)` tuple after resolving the first `n` access path tokens. */
4739
bindingset[package, type, path]

0 commit comments

Comments
 (0)