From f07a7bf8cff4152845a013fa44001247e796b3a0 Mon Sep 17 00:00:00 2001 From: Taus Brock-Nannestad Date: Tue, 7 Jul 2020 15:43:52 +0200 Subject: [PATCH] Python: Autoformat everything using `qlformat`. Will need subsequent PRs fixing up test failures (due to deprecated methods moving around), but other than that everything should be straight-forward. --- .../ql/examples/snippets/catch_exception.ql | 4 +- .../snippets/conditional_expression.ql | 6 +- python/ql/examples/snippets/emptythen.ql | 8 +- python/ql/examples/snippets/extend_class.ql | 4 +- python/ql/examples/snippets/method_call.ql | 4 +- python/ql/examples/snippets/new_instance.ql | 4 +- .../ql/examples/snippets/override_method.ql | 4 +- python/ql/examples/snippets/print.ql | 10 +- python/ql/examples/snippets/private_access.ql | 8 +- .../ql/examples/snippets/raise_exception.ql | 4 +- python/ql/examples/snippets/store_none.ql | 4 +- python/ql/examples/snippets/tryfinally.ql | 4 +- python/ql/src/Classes/ClassAttributes.qll | 240 +- .../ConflictingAttributesInBaseClasses.ql | 62 +- .../DefineEqualsWhenAddingAttributes.ql | 48 +- python/ql/src/Classes/Equality.qll | 102 +- python/ql/src/Classes/EqualsOrHash.ql | 54 +- python/ql/src/Classes/EqualsOrNotEquals.ql | 30 +- python/ql/src/Classes/IncompleteOrdering.ql | 72 +- python/ql/src/Classes/InconsistentMRO.ql | 14 +- .../ql/src/Classes/InitCallsSubclassMethod.ql | 24 +- .../Classes/MaybeUndefinedClassAttribute.ql | 38 +- python/ql/src/Classes/MethodCallOrder.qll | 84 +- python/ql/src/Classes/MissingCallToDel.ql | 12 +- python/ql/src/Classes/MissingCallToInit.ql | 20 +- python/ql/src/Classes/MutatingDescriptor.ql | 24 +- .../OverwritingAttributeInSuperClass.ql | 114 +- .../ql/src/Classes/PropertyInOldStyleClass.ql | 4 +- .../ql/src/Classes/ShouldBeContextManager.ql | 4 +- python/ql/src/Classes/SubclassShadowing.ql | 34 +- python/ql/src/Classes/SuperInOldStyleClass.ql | 14 +- .../SuperclassDelCalledMultipleTimes.ql | 20 +- .../SuperclassInitCalledMultipleTimes.ql | 22 +- .../ql/src/Classes/UndefinedClassAttribute.ql | 18 +- python/ql/src/Classes/UselessClass.ql | 106 +- ...rongNameForArgumentInClassInstantiation.ql | 6 +- ...rongNumberArgumentsInClassInstantiation.ql | 22 +- .../src/Exceptions/CatchingBaseException.ql | 10 +- python/ql/src/Exceptions/EmptyExcept.ql | 104 +- .../Exceptions/IllegalExceptionHandlerType.ql | 22 +- python/ql/src/Exceptions/IllegalRaise.ql | 10 +- .../ql/src/Exceptions/IncorrectExceptOrder.ql | 20 +- python/ql/src/Exceptions/NotImplemented.qll | 10 +- python/ql/src/Exceptions/Raising.qll | 14 +- python/ql/src/Exceptions/RaisingTuple.ql | 10 +- .../Exceptions/UnguardedNextInGenerator.ql | 42 +- python/ql/src/Expressions/CallArgs.qll | 292 +- .../src/Expressions/CallToSuperWrongClass.ql | 18 +- python/ql/src/Expressions/CompareConstants.ql | 8 +- .../Comparisons/UselessComparisonTest.ql | 26 +- .../src/Expressions/ContainsNonContainer.ql | 26 +- .../DuplicateKeyInDictionaryLiteral.ql | 42 +- .../ExpectedMappingForFormatString.ql | 16 +- .../ql/src/Expressions/ExplicitCallToDel.ql | 26 +- .../Formatting/AdvancedFormatting.qll | 164 +- .../Formatting/UnusedArgumentIn3101Format.ql | 14 +- .../UnusedNamedArgumentIn3101Format.ql | 26 +- .../WrongNameInArgumentsFor3101Format.ql | 12 +- .../WrongNumberArgumentsFor3101Format.ql | 20 +- python/ql/src/Expressions/HashedButNoHash.ql | 60 +- .../Expressions/IncorrectComparisonUsingIs.ql | 18 +- python/ql/src/Expressions/IsComparisons.qll | 180 +- .../ql/src/Expressions/NonCallableCalled.ql | 16 +- .../NonPortableComparisonUsingIs.ql | 14 +- .../src/Expressions/RedundantComparison.qll | 73 +- .../src/Expressions/Regex/BackspaceEscape.ql | 6 +- .../Regex/DuplicateCharacterInSet.ql | 42 +- .../src/Expressions/Regex/UnmatchableCaret.ql | 10 +- .../Expressions/Regex/UnmatchableDollar.ql | 10 +- .../ql/src/Expressions/TruncatedDivision.ql | 36 +- ...nintentionalImplicitStringConcatenation.ql | 24 +- .../ql/src/Expressions/UnnecessaryLambda.ql | 68 +- python/ql/src/Expressions/UseofInput.ql | 8 +- .../Expressions/WrongNameForArgumentInCall.ql | 12 +- .../WrongNumberArgumentsForFormat.ql | 42 +- .../Expressions/WrongNumberArgumentsInCall.ql | 19 +- python/ql/src/Filters/ClassifyFiles.ql | 6 +- python/ql/src/Functions/ConsistentReturns.ql | 20 +- .../ql/src/Functions/DeprecatedSliceMethod.ql | 10 +- .../ql/src/Functions/ExplicitReturnInInit.ql | 12 +- .../IncorrectRaiseInSpecialMethod.ql | 212 +- .../Functions/IncorrectlyOverriddenMethod.ql | 26 +- .../IncorrectlySpecifiedOverriddenMethod.ql | 36 +- python/ql/src/Functions/InitIsGenerator.ql | 4 +- .../src/Functions/IterReturnsNonIterator.ql | 12 +- python/ql/src/Functions/IterReturnsNonSelf.ql | 12 +- .../ModificationOfParameterWithDefault.ql | 104 +- python/ql/src/Functions/NonCls.ql | 44 +- python/ql/src/Functions/NonSelf.ql | 58 +- .../src/Functions/OverlyComplexDelMethod.ql | 8 +- .../Functions/ReturnConsistentTupleSizes.ql | 20 +- python/ql/src/Functions/ReturnValueIgnored.ql | 92 +- .../Functions/SignatureOverriddenMethod.ql | 32 +- .../src/Functions/SignatureSpecialMethods.ql | 330 +- .../Functions/UseImplicitNoneReturnValue.ql | 36 +- python/ql/src/Imports/Cyclic.qll | 132 +- python/ql/src/Imports/CyclicImport.ql | 14 +- python/ql/src/Imports/DeprecatedModule.ql | 110 +- .../Imports/FromImportOfMutableAttribute.ql | 30 +- .../ql/src/Imports/ImportShadowedByLoopVar.ql | 10 +- python/ql/src/Imports/ImportandImportFrom.ql | 12 +- python/ql/src/Imports/ModuleImportsItself.ql | 16 +- .../ql/src/Imports/ModuleLevelCyclicImport.ql | 8 +- python/ql/src/Imports/MultipleImports.ql | 46 +- python/ql/src/Imports/UnintentionalImport.ql | 16 +- python/ql/src/Imports/UnusedImport.ql | 160 +- python/ql/src/Lexical/CommentedOutCode.qll | 432 +- python/ql/src/Lexical/OldOctalLiteral.ql | 20 +- python/ql/src/Metrics/CommentRatio.ql | 2 +- .../Dependencies/ExternalDependencies.ql | 14 +- .../ExternalDependenciesSourceLinks.ql | 10 +- python/ql/src/Metrics/DocStringRatio.ql | 4 +- python/ql/src/Metrics/FLinesOfComments.ql | 2 +- .../ql/src/Metrics/FLinesOfDuplicatedCode.ql | 14 +- python/ql/src/Metrics/FLinesOfSimilarCode.ql | 14 +- python/ql/src/Metrics/History/HChurn.ql | 14 +- python/ql/src/Metrics/History/HLinesAdded.ql | 14 +- .../ql/src/Metrics/History/HLinesDeleted.ql | 14 +- .../src/Metrics/History/HNumberOfCoCommits.ql | 10 +- .../src/Metrics/History/HNumberOfReCommits.ql | 24 +- .../Metrics/History/HNumberOfRecentAuthors.ql | 14 +- .../History/HNumberOfRecentChangedFiles.ql | 8 +- python/ql/src/Metrics/Internal/Extents.qll | 50 +- python/ql/src/Numerics/Pythagorean.ql | 34 +- .../ql/src/Resources/FileNotAlwaysClosed.ql | 78 +- python/ql/src/Resources/FileOpen.qll | 196 +- .../CVE-2018-1281/BindToAllInterfaces.ql | 22 +- .../CWE-020/IncompleteHostnameRegExp.ql | 26 +- .../IncompleteUrlSubstringSanitization.ql | 44 +- .../ql/src/Security/CWE-022/PathInjection.ql | 26 +- python/ql/src/Security/CWE-022/TarSlip.ql | 200 +- .../src/Security/CWE-078/CommandInjection.ql | 22 +- .../Security/CWE-079/Jinja2WithoutEscaping.ql | 26 +- .../ql/src/Security/CWE-079/ReflectedXss.ql | 22 +- .../ql/src/Security/CWE-089/SqlInjection.ql | 22 +- .../ql/src/Security/CWE-094/CodeInjection.ql | 12 +- .../Security/CWE-209/StackTraceExposure.ql | 8 +- python/ql/src/Security/CWE-215/FlaskDebug.ql | 8 +- .../CWE-295/MissingHostKeyValidation.ql | 20 +- .../CWE-295/RequestWithoutValidation.ql | 8 +- .../src/Security/CWE-312/CleartextLogging.ql | 18 +- .../src/Security/CWE-312/CleartextStorage.ql | 18 +- python/ql/src/Security/CWE-326/WeakCrypto.ql | 94 +- .../Security/CWE-327/BrokenCryptoAlgorithm.ql | 12 +- .../CWE-327/InsecureDefaultProtocol.ql | 20 +- .../src/Security/CWE-327/InsecureProtocol.ql | 94 +- .../Security/CWE-377/InsecureTemporaryFile.ql | 20 +- .../Security/CWE-502/UnsafeDeserialization.ql | 10 +- python/ql/src/Security/CWE-601/UrlRedirect.ql | 20 +- .../Security/CWE-732/WeakFilePermissions.ql | 28 +- .../Security/CWE-798/HardcodedCredentials.ql | 142 +- .../src/Statements/AssertLiteralConstant.ql | 22 +- python/ql/src/Statements/AssertOnTuple.ql | 16 +- .../src/Statements/BreakOrReturnInFinally.ql | 16 +- .../ql/src/Statements/C_StyleParentheses.ql | 34 +- .../src/Statements/ConstantInConditional.ql | 24 +- python/ql/src/Statements/DocStrings.ql | 40 +- python/ql/src/Statements/ExecUsed.ql | 8 +- .../Statements/IterableStringOrSequence.ql | 32 +- .../MismatchInMultipleAssignment.ql | 68 +- .../ql/src/Statements/ModificationOfLocals.ql | 24 +- .../src/Statements/NestedLoopsSameVariable.ql | 14 +- .../NestedLoopsSameVariableWithReuse.ql | 24 +- .../ql/src/Statements/NonIteratorInForLoop.ql | 14 +- .../ql/src/Statements/RedundantAssignment.ql | 82 +- .../ReturnOrYieldOutsideFunction.ql | 16 +- .../src/Statements/ShouldUseWithStatement.ql | 24 +- .../ql/src/Statements/SideEffectInAssert.ql | 50 +- python/ql/src/Statements/StatementNoEffect.ql | 132 +- .../Statements/StringConcatenationInLoop.ql | 16 +- python/ql/src/Statements/TopLevelPrint.ql | 32 +- python/ql/src/Statements/UnnecessaryDelete.ql | 26 +- .../src/Statements/UnnecessaryElseClause.ql | 14 +- python/ql/src/Statements/UnnecessaryPass.ql | 18 +- python/ql/src/Statements/UnreachableCode.ql | 56 +- .../src/Statements/UnusedExceptionObject.ql | 6 +- python/ql/src/Statements/UseOfExit.ql | 4 +- python/ql/src/Testing/ImpreciseAssert.ql | 130 +- python/ql/src/Testing/Mox.qll | 20 +- python/ql/src/Variables/Definition.qll | 218 +- .../src/Variables/LeakingListComprehension.ql | 26 +- python/ql/src/Variables/Loop.qll | 40 +- .../ql/src/Variables/LoopVariableCapture.ql | 42 +- python/ql/src/Variables/MonkeyPatched.qll | 40 +- python/ql/src/Variables/MultiplyDefined.ql | 62 +- python/ql/src/Variables/ShadowBuiltin.ql | 74 +- python/ql/src/Variables/ShadowGlobal.ql | 64 +- python/ql/src/Variables/Shadowing.qll | 8 +- .../SuspiciousUnusedLoopIterationVariable.ql | 126 +- python/ql/src/Variables/Undefined.qll | 154 +- python/ql/src/Variables/UndefinedExport.ql | 95 +- python/ql/src/Variables/UndefinedGlobal.ql | 148 +- .../ql/src/Variables/UndefinedPlaceHolder.ql | 28 +- python/ql/src/Variables/UninitializedLocal.ql | 22 +- .../ql/src/Variables/UnusedLocalVariable.ql | 24 +- .../ql/src/Variables/UnusedModuleVariable.ql | 72 +- python/ql/src/Variables/UnusedParameter.ql | 26 +- python/ql/src/analysis/AlertSuppression.ql | 114 +- python/ql/src/analysis/CallGraphEfficiency.ql | 26 +- .../analysis/CallGraphMarginalEfficiency.ql | 34 +- python/ql/src/analysis/Consistency.ql | 464 +- python/ql/src/analysis/ContextEfficiency.ql | 28 +- .../src/analysis/ContextMarginalEfficiency.ql | 28 +- .../src/analysis/CrossProjectDefinitions.qll | 144 +- python/ql/src/analysis/DefinitionTracking.qll | 545 +- python/ql/src/analysis/Definitions.ql | 2 +- python/ql/src/analysis/Efficiency.ql | 38 +- python/ql/src/analysis/ImportFailure.ql | 98 +- python/ql/src/analysis/KeyPointsToFailure.ql | 14 +- python/ql/src/analysis/LocalDefinitions.ql | 9 +- python/ql/src/analysis/LocalReferences.ql | 7 +- python/ql/src/analysis/RatioOfDefinitions.ql | 26 +- python/ql/src/analysis/Summary.ql | 68 +- .../ql/src/analysis/TypeInferenceFailure.ql | 4 +- .../experimental/dataflow/TaintTracking.qll | 2 +- .../internal/DataFlowImplSpecific.qll | 2 +- .../dataflow/internal/TaintTrackingPublic.qll | 6 +- .../tainttracking1/TaintTrackingParameter.qll | 2 +- python/ql/src/external/CodeDuplication.qll | 288 +- python/ql/src/external/DefectFilter.qll | 94 +- python/ql/src/external/DuplicateBlock.ql | 16 +- python/ql/src/external/DuplicateFunction.ql | 10 +- python/ql/src/external/ExternalArtifact.qll | 94 +- .../ql/src/external/MostlyDuplicateClass.ql | 6 +- python/ql/src/external/SimilarFunction.ql | 12 +- python/ql/src/external/Thrift.qll | 216 +- python/ql/src/external/VCS.qll | 86 +- python/ql/src/semmle/crypto/Crypto.qll | 216 +- python/ql/src/semmle/python/AstExtended.qll | 164 +- python/ql/src/semmle/python/AstGenerated.qll | 1542 ++--- python/ql/src/semmle/python/Class.qll | 244 +- python/ql/src/semmle/python/Comment.qll | 143 +- python/ql/src/semmle/python/Comparisons.qll | 758 +-- .../ql/src/semmle/python/Comprehensions.qll | 136 +- python/ql/src/semmle/python/Constants.qll | 32 +- python/ql/src/semmle/python/Exprs.qll | 866 +-- python/ql/src/semmle/python/Files.qll | 794 +-- python/ql/src/semmle/python/Flow.qll | 1739 +++--- python/ql/src/semmle/python/Function.qll | 544 +- .../src/semmle/python/GuardedControlFlow.qll | 120 +- python/ql/src/semmle/python/Import.qll | 378 +- python/ql/src/semmle/python/Keywords.qll | 56 +- python/ql/src/semmle/python/Metrics.qll | 536 +- python/ql/src/semmle/python/Module.qll | 350 +- python/ql/src/semmle/python/Operations.qll | 152 +- python/ql/src/semmle/python/SSA.qll | 354 +- python/ql/src/semmle/python/Scope.qll | 244 +- python/ql/src/semmle/python/SelfAttribute.qll | 100 +- python/ql/src/semmle/python/Stmts.qll | 554 +- python/ql/src/semmle/python/TestUtils.qll | 22 +- python/ql/src/semmle/python/Variables.qll | 104 +- .../semmle/python/dataflow/Configuration.qll | 246 +- .../ql/src/semmle/python/dataflow/Files.qll | 16 +- .../semmle/python/dataflow/Implementation.qll | 1700 +++--- .../ql/src/semmle/python/dataflow/Legacy.qll | 100 +- .../semmle/python/dataflow/StateTracking.qll | 274 +- .../semmle/python/dataflow/TaintTracking.qll | 874 +-- .../python/dependencies/Dependencies.qll | 222 +- .../python/dependencies/DependencyKind.qll | 20 +- .../python/dependencies/TechInventory.qll | 150 +- .../ql/src/semmle/python/essa/Definitions.qll | 526 +- python/ql/src/semmle/python/essa/Essa.qll | 956 ++-- .../ql/src/semmle/python/essa/SsaCompute.qll | 492 +- .../src/semmle/python/essa/SsaDefinitions.qll | 250 +- .../semmle/python/filters/GeneratedCode.qll | 238 +- python/ql/src/semmle/python/filters/Tests.qll | 46 +- .../ql/src/semmle/python/libraries/Zope.qll | 62 +- .../src/semmle/python/objects/Callables.qll | 652 +-- .../ql/src/semmle/python/objects/Classes.qll | 492 +- .../src/semmle/python/objects/Constants.qll | 364 +- .../src/semmle/python/objects/Descriptors.qll | 498 +- .../src/semmle/python/objects/Instances.qll | 674 +-- .../ql/src/semmle/python/objects/Modules.qll | 528 +- .../src/semmle/python/objects/ObjectAPI.qll | 1598 +++--- .../semmle/python/objects/ObjectInternal.qll | 794 +-- .../src/semmle/python/objects/Sequences.qll | 372 +- .../ql/src/semmle/python/objects/TObject.qll | 805 ++- python/ql/src/semmle/python/pointsto/Base.qll | 406 +- .../src/semmle/python/pointsto/CallGraph.qll | 86 +- .../ql/src/semmle/python/pointsto/Filters.qll | 48 +- python/ql/src/semmle/python/pointsto/MRO.qll | 654 +-- .../src/semmle/python/pointsto/PointsTo.qll | 4936 ++++++++--------- .../python/pointsto/PointsToContext.qll | 306 +- python/ql/src/semmle/python/regex.qll | 1344 +++-- .../src/semmle/python/security/ClearText.qll | 80 +- .../ql/src/semmle/python/security/Crypto.qll | 210 +- .../src/semmle/python/security/Exceptions.qll | 76 +- .../ql/src/semmle/python/security/Paths.qll | 18 +- .../semmle/python/security/SensitiveData.qll | 230 +- .../semmle/python/security/flow/AnyCall.qll | 4 +- .../python/security/injection/Command.qll | 322 +- .../security/injection/Deserialization.qll | 4 +- .../semmle/python/security/injection/Exec.qll | 18 +- .../python/security/injection/Marshal.qll | 16 +- .../semmle/python/security/injection/Path.qll | 84 +- .../python/security/injection/Pickle.qll | 26 +- .../semmle/python/security/injection/Sql.qll | 64 +- .../semmle/python/security/injection/Xml.qll | 42 +- .../semmle/python/security/injection/Yaml.qll | 16 +- .../semmle/python/security/strings/Basic.qll | 150 +- .../semmle/python/security/strings/Common.qll | 16 +- .../python/security/strings/External.qll | 472 +- .../python/security/strings/Untrusted.qll | 2 +- python/ql/src/semmle/python/strings.qll | 38 +- .../src/semmle/python/templates/PyxlTags.qll | 78 +- .../src/semmle/python/templates/Templates.qll | 12 +- .../ql/src/semmle/python/types/Builtins.qll | 200 +- .../src/semmle/python/types/ClassObject.qll | 666 +-- .../src/semmle/python/types/Descriptors.qll | 30 +- .../ql/src/semmle/python/types/Exceptions.qll | 786 +-- .../ql/src/semmle/python/types/Extensions.qll | 220 +- .../semmle/python/types/FunctionObject.qll | 456 +- .../ql/src/semmle/python/types/ImportTime.qll | 42 +- .../ql/src/semmle/python/types/ModuleKind.qll | 52 +- .../src/semmle/python/types/ModuleObject.qll | 314 +- python/ql/src/semmle/python/types/Object.qll | 589 +- .../ql/src/semmle/python/types/Properties.qll | 128 +- python/ql/src/semmle/python/types/Version.qll | 14 +- .../semmle/python/values/StringAttributes.qll | 108 +- python/ql/src/semmle/python/web/Http.qll | 132 +- .../src/semmle/python/web/HttpConstants.qll | 14 +- .../src/semmle/python/web/bottle/General.qll | 48 +- .../src/semmle/python/web/bottle/Redirect.qll | 16 +- .../src/semmle/python/web/bottle/Request.qll | 86 +- .../src/semmle/python/web/bottle/Response.qll | 44 +- .../semmle/python/web/cherrypy/General.qll | 60 +- .../semmle/python/web/cherrypy/Request.qll | 52 +- .../semmle/python/web/cherrypy/Response.qll | 16 +- .../src/semmle/python/web/client/Requests.qll | 18 +- .../src/semmle/python/web/client/StdLib.qll | 88 +- python/ql/src/semmle/python/web/django/Db.qll | 34 +- .../src/semmle/python/web/django/General.qll | 160 +- .../ql/src/semmle/python/web/django/Model.qll | 102 +- .../src/semmle/python/web/django/Redirect.qll | 24 +- .../src/semmle/python/web/django/Request.qll | 90 +- .../src/semmle/python/web/django/Response.qll | 72 +- .../src/semmle/python/web/django/Shared.qll | 112 +- .../src/semmle/python/web/falcon/General.qll | 38 +- .../src/semmle/python/web/falcon/Request.qll | 58 +- .../src/semmle/python/web/falcon/Response.qll | 22 +- .../src/semmle/python/web/flask/General.qll | 98 +- .../src/semmle/python/web/flask/Redirect.qll | 14 +- .../src/semmle/python/web/flask/Request.qll | 86 +- .../src/semmle/python/web/flask/Response.qll | 60 +- .../semmle/python/web/pyramid/Redirect.qll | 26 +- .../src/semmle/python/web/pyramid/Request.qll | 20 +- .../semmle/python/web/pyramid/Response.qll | 34 +- .../ql/src/semmle/python/web/pyramid/View.qll | 2 +- .../src/semmle/python/web/stdlib/Request.qll | 170 +- .../src/semmle/python/web/stdlib/Response.qll | 46 +- .../semmle/python/web/tornado/Redirect.qll | 18 +- .../src/semmle/python/web/tornado/Request.qll | 96 +- .../semmle/python/web/tornado/Response.qll | 48 +- .../src/semmle/python/web/tornado/Tornado.qll | 48 +- .../semmle/python/web/turbogears/Request.qll | 30 +- .../semmle/python/web/turbogears/Response.qll | 32 +- .../python/web/turbogears/TurboGears.qll | 40 +- .../src/semmle/python/web/twisted/Request.qll | 40 +- .../semmle/python/web/twisted/Response.qll | 48 +- .../src/semmle/python/web/twisted/Twisted.qll | 50 +- .../src/semmle/python/web/webob/Request.qll | 52 +- .../ControlFlow/Exceptions/Likely.ql | 4 +- .../PointsTo/class_properties/ClassValues.ql | 30 +- .../PointsTo/import_time/Pruned.ql | 10 +- .../library-tests/PointsTo/imports/Runtime.ql | 10 +- .../PointsTo/origin_uniqueness/Origin.ql | 8 +- .../library-tests/classes/attr/class_attr.ql | 8 +- .../classes/attr/class_has_attr.ql | 8 +- .../2/library-tests/classes/attr/list_attr.ql | 10 +- .../ql/test/2/library-tests/classes/mro/C3.ql | 2 +- .../test/2/library-tests/classes/mro/mro.ql | 4 +- .../comprehensions/ConsistencyCheck.ql | 4 +- .../locations/general/AllLocations.ql | 6 +- .../modules/general/import_test.ql | 8 +- .../test/2/library-tests/objects/Literals.ql | 6 +- .../ql/test/2/library-tests/six/pointsto.ql | 6 +- .../library-tests/types/classes/new_style.ql | 6 +- .../library-tests/types/exceptions/Raises.ql | 14 +- .../types/properties/BuiltinProperties.ql | 6 +- .../ControlFlow/Exceptions/Likely.ql | 4 +- .../PointsTo/attributes/TestWithType.ql | 2 +- .../PointsTo/class_properties/ClassValues.ql | 30 +- .../PointsTo/consts/BooleanConstants.ql | 6 +- .../PointsTo/import_time/Pruned.ql | 6 +- .../subprocess-assert/ClassValue.ql | 10 +- .../PointsTo/typehints/Values.ql | 4 +- .../library-tests/classes/attr/class_attr.ql | 8 +- .../classes/attr/class_has_attr.ql | 8 +- .../test/3/library-tests/classes/mro/mro.ql | 4 +- .../3/library-tests/classes/mro/mro_index.ql | 4 +- .../locations/general/AllLocations.ql | 6 +- .../modules/general/import_test.ql | 8 +- .../3/library-tests/parameters/Special.ql | 10 +- .../ql/test/3/library-tests/six/pointsto.ql | 6 +- .../3/library-tests/taint/unpacking/Taint.qll | 18 +- .../taint/unpacking/TestTaint.ql | 22 +- .../library-tests/types/exceptions/Raises.ql | 14 +- .../library-tests/types/exceptions/Viable.ql | 2 +- .../types/namespaces/NameSpace.ql | 22 +- .../types/properties/BuiltinProperties.ql | 6 +- .../experimental/dataflow/basic/callGraph.ql | 10 +- .../dataflow/basic/callGraphConfig.qll | 2 +- .../dataflow/basic/callGraphSinks.ql | 2 +- .../dataflow/basic/callGraphSources.ql | 2 +- .../experimental/dataflow/basic/global.ql | 9 +- .../experimental/dataflow/basic/globalStep.ql | 10 +- .../test/experimental/dataflow/basic/local.ql | 10 +- .../experimental/dataflow/basic/localStep.ql | 10 +- .../dataflow/basic/maximalFlows.ql | 9 +- .../dataflow/basic/maximalFlowsConfig.qll | 4 +- .../test/experimental/dataflow/basic/sinks.ql | 2 +- .../experimental/dataflow/basic/sources.ql | 2 +- .../dataflow/coverage/dataflow.ql | 10 +- .../dataflow/regression/dataflow.ql | 10 +- .../test/experimental/dataflow/testConfig.qll | 8 +- .../PointsToSupport/UseFromDefinition.ql | 12 +- .../ControlFlow/augassign/AugAssignFlow.ql | 6 +- .../ControlFlow/augassign/Kind.ql | 14 +- .../ControlFlow/comparison/Compare.ql | 12 +- .../library-tests/ControlFlow/delete/test.ql | 2 +- .../dominators/DominatesConsistency.ql | 10 +- .../ControlFlow/dominators/idom.ql | 4 +- .../general/ImmediateDominatorCheck.ql | 20 +- .../ControlFlow/general/Lines.ql | 6 +- .../ControlFlow/general/Reaches.ql | 8 +- .../ControlFlow/raising_stmts/RaisingFlow.ql | 10 +- .../ControlFlow/splitting/NodeCount.ql | 12 +- .../ControlFlow/splitting/SuccessorCount.ql | 6 +- .../ControlFlow/ssa/defns/test.ql | 2 +- .../ControlFlow/ssa/deletions/test.ql | 16 +- .../ssa/phi-nodes/phi_input_test.ql | 2 +- .../ControlFlow/ssa/phi-nodes/test.ql | 2 +- .../ControlFlow/ssa/uses/test.ql | 2 +- .../ControlFlow/successors/Successors.ql | 22 +- .../truefalse/ExceptionalSuccessors.ql | 8 +- .../truefalse/TrueFalseSuccessors.ql | 8 +- .../library-tests/ControlFlow/try/test_ssa.ql | 2 +- .../library-tests/DuplicateCode/Duplicate.ql | 14 +- .../DuplicateCode/DuplicateStatements.ql | 20 +- .../library-tests/DuplicateCode/Similar.ql | 12 +- .../library-tests/PointsTo/api/ClassValue.ql | 18 +- .../library-tests/PointsTo/api/Constants.ql | 20 +- .../PointsTo/api/QualifedNames.ql | 20 +- .../test/library-tests/PointsTo/api/Value.ql | 16 +- .../PointsTo/calls/getArgumentForCall.ql | 2 +- .../PointsTo/calls/getNamedArgumentForCall.ql | 2 +- .../PointsTo/comparisons/PointsTo.ql | 6 +- .../library-tests/PointsTo/customise/test.ql | 40 +- .../library-tests/PointsTo/decorators/Test.ql | 6 +- .../PointsTo/decorators/Values.ql | 4 +- .../PointsTo/extensions/Extend.ql | 70 +- .../library-tests/PointsTo/functions/Calls.ql | 12 +- .../library-tests/PointsTo/functions/test.ql | 4 +- .../PointsTo/general/GlobalPointsTo.ql | 6 +- .../PointsTo/general/LocalPointsTo.ql | 4 +- .../PointsTo/general/LocalPointsToType.ql | 4 +- .../library-tests/PointsTo/general/Util.qll | 12 +- .../PointsTo/general/interesting.qll | 16 +- .../library-tests/PointsTo/global/Global.ql | 4 +- .../PointsTo/guarded/PointsTo.ql | 6 +- .../PointsTo/guarded/PointsToWithType.ql | 6 +- .../library-tests/PointsTo/imports/Runtime.ql | 8 +- .../PointsTo/imports/RuntimeWithType.ql | 10 +- .../library-tests/PointsTo/indexing/Test.ql | 4 +- .../PointsTo/indexing/TestWithType.ql | 6 +- .../PointsTo/inheritance/BaseTypes.ql | 4 +- .../PointsTo/inheritance/MetaClass.ql | 4 +- .../library-tests/PointsTo/inheritance/Mro.ql | 2 +- .../PointsTo/inheritance/SuperTypes.ql | 4 +- .../PointsTo/local/LocalPointsTo.ql | 4 +- .../library-tests/PointsTo/lookup/Lookup.ql | 16 +- .../PointsTo/metaclass/Failed.ql | 4 +- .../library-tests/PointsTo/metaclass/Mro.ql | 2 +- .../library-tests/PointsTo/metaclass/Style.ql | 12 +- .../library-tests/PointsTo/metaclass/test.ql | 2 +- .../library-tests/PointsTo/new/ClassMethod.ql | 2 +- .../library-tests/PointsTo/new/Consistency.ql | 252 +- .../library-tests/PointsTo/new/Dataflow.ql | 2 +- .../test/library-tests/PointsTo/new/Live.ql | 6 +- .../library-tests/PointsTo/new/NameSpace.ql | 22 +- .../PointsTo/new/PointsToMissing.ql | 26 +- .../PointsTo/new/PointsToWithContext.ql | 2 +- .../PointsTo/new/PointsToWithType.ql | 2 +- .../library-tests/PointsTo/new/Precedes.ql | 2 +- .../ql/test/library-tests/PointsTo/new/SSA.ql | 8 +- .../PointsTo/new/SourceNodeDefinitions.ql | 12 +- .../library-tests/PointsTo/new/SsaAttr.ql | 6 +- .../PointsTo/new/TestEvaluate.ql | 22 +- .../test/library-tests/PointsTo/new/Util.qll | 68 +- .../test/library-tests/PointsTo/new/Values.ql | 2 +- .../library-tests/PointsTo/new/VarUses.ql | 4 +- .../PointsTo/properties/Values.ql | 8 +- .../missing/if-urlsplit-access/Test.ql | 10 +- .../regressions/missing/re-compile/Test.ql | 10 +- .../missing/uncalled-function/Test.ql | 10 +- .../regressions/wrong/classmethod/Test.ql | 10 +- .../PointsTo/subclass/TestEvaluate.ql | 8 +- .../PointsTo/super/SuperMethodCall.ql | 6 +- .../library-tests/attributes/SelfAttribute.ql | 6 +- .../classes/abstract/Abstract.ql | 4 +- .../library-tests/classes/attr/class_attr.ql | 8 +- .../classes/attr/class_defined_attr.ql | 8 +- .../classes/attr/class_defines_attr.ql | 8 +- .../classes/attr/class_has_attr.ql | 8 +- .../test/library-tests/classes/attr/hash.ql | 10 +- .../ql/test/library-tests/comments/length.ql | 4 +- .../library-tests/comparisons/Compare2.ql | 12 +- .../comparisons/CompareControls.ql | 2 +- .../dependencies/Dependencies.ql | 2 +- .../library-tests/descriptors/Descriptors.ql | 8 +- .../test/library-tests/descriptors/Methods.ql | 6 +- .../library-tests/descriptors/Properties.ql | 10 +- .../library-tests/encoding/CheckEncoding.ql | 6 +- .../examples/custom-sanitizer/Taint.qll | 80 +- .../examples/custom-sanitizer/TestTaint.ql | 44 +- .../ql/test/library-tests/exceptions/Legal.ql | 10 +- .../test/library-tests/exprs/ast/AstParent.ql | 2 +- .../library-tests/filters/generated/Filter.ql | 6 +- .../formatting/FormatArguments.ql | 6 +- .../library-tests/jump_to_defn/Consistency.ql | 16 +- .../test/library-tests/jump_to_defn/Remote.ql | 6 +- .../test/library-tests/jump_to_defn/test.ql | 4 +- .../implicit_concatenation/part_locations.ql | 4 +- .../locations/implicit_concatenation/parts.ql | 2 +- .../locations/implicit_concatenation/test.ql | 10 +- .../locations/negative_numbers/negative.ql | 4 +- .../modules/usage/ModuleUsage.ql | 22 +- .../ql/test/library-tests/objects/Literals.ql | 6 +- python/ql/test/library-tests/objects/Name.ql | 32 +- .../overrides/FunctionOverrides.ql | 4 +- .../test/library-tests/parameters/Special.ql | 10 +- .../test/library-tests/regex/Alternation.ql | 2 +- .../ql/test/library-tests/regex/FirstLast.ql | 6 +- .../test/library-tests/regex/GroupContents.ql | 2 +- python/ql/test/library-tests/regex/Regex.ql | 30 +- .../security/fabric-v1-execute/Taint.qll | 18 +- .../security/fabric-v1-execute/TestTaint.ql | 45 +- .../test/library-tests/state_tracking/Lib.qll | 10 +- .../test/library-tests/state_tracking/Test.ql | 12 +- .../state_tracking/Violations.ql | 12 +- .../library-tests/stmts/general/AstParent.ql | 2 +- .../stmts/general/SubExpressions.ql | 2 +- .../library-tests/stmts/raise_stmt/AST.ql | 2 +- .../test/library-tests/stmts/try_stmt/AST.ql | 2 +- .../test/library-tests/stmts/with_stmt/AST.ql | 2 +- .../library-tests/taint/collections/Taint.qll | 18 +- .../taint/collections/TestStep.ql | 8 +- .../taint/collections/TestTaint.ql | 22 +- .../taint/config/RockPaperScissors.ql | 2 +- .../test/library-tests/taint/config/Simple.ql | 2 +- .../library-tests/taint/config/TaintLib.qll | 290 +- .../taint/config/TaintedArgument.ql | 8 +- .../library-tests/taint/config/TestNode.ql | 2 +- .../library-tests/taint/config/TestSource.ql | 2 +- .../library-tests/taint/config/TestStep.ql | 4 +- .../library-tests/taint/dataflow/Config.qll | 16 +- .../library-tests/taint/dataflow/TestNode.ql | 2 +- .../taint/example/DilbertConfig.qll | 44 +- .../test/library-tests/taint/example/Edges.ql | 40 +- .../taint/example/ExampleConfig.ql | 2 +- .../test/library-tests/taint/example/Nodes.ql | 2 +- .../taint/exception_traceback/TestSource.ql | 4 +- .../taint/exception_traceback/TestStep.ql | 10 +- .../taint/extensions/ExtensionsLib.qll | 92 +- .../taint/extensions/TestNode.ql | 2 +- .../taint/extensions/TestStep.ql | 4 +- .../taint/flowpath_regression/Config.qll | 44 +- .../library-tests/taint/general/Contexts.ql | 4 +- .../taint/general/ParamSource.ql | 36 +- .../taint/general/TaintConsistency.ql | 40 +- .../library-tests/taint/general/TaintLib.qll | 316 +- .../library-tests/taint/general/TestDefn.ql | 2 +- .../library-tests/taint/general/TestSink.ql | 2 +- .../library-tests/taint/general/TestStep.ql | 2 +- .../library-tests/taint/general/TestTaint.ql | 22 +- .../library-tests/taint/general/TestVar.ql | 2 +- .../library-tests/taint/namedtuple/Taint.qll | 42 +- .../taint/namedtuple/TestTaint.ql | 22 +- .../library-tests/taint/strings/Taint.qll | 30 +- .../library-tests/taint/strings/TestStep.ql | 8 +- .../library-tests/taint/strings/TestTaint.ql | 22 +- .../library-tests/taint/unpacking/Taint.qll | 18 +- .../library-tests/taint/unpacking/TestStep.ql | 8 +- .../taint/unpacking/TestTaint.ql | 22 +- .../ql/test/library-tests/thrift/Function.ql | 10 +- .../library-tests/types/attributes/Test.ql | 2 +- .../types/classattr/ClassAttribute.ql | 14 +- .../types/classattr/ClassMember.ql | 14 +- .../types/classattr/SpecialAttribute.ql | 16 +- .../types/exceptions/Impossible.ql | 28 +- .../types/exceptions/LineRaises.ql | 14 +- .../library-tests/types/exceptions/Raises.ql | 14 +- .../library-tests/types/exceptions/Viable.ql | 2 +- .../library-tests/variables/scopes/free.ql | 6 +- .../library-tests/variables/scopes/locals.ql | 6 +- .../library-tests/variables/scopes/lookup.ql | 26 +- .../library-tests/web/stdlib/HttpSources.ql | 4 +- .../library-tests/web/stdlib/TestTaint.ql | 44 +- python/ql/test/library-tests/web/zope/Test.ql | 8 +- .../query-tests/Metrics/ratios/CodeRatio.ql | 2 +- .../ql/test/query-tests/Resources/Dataflow.ql | 16 +- .../py_exprs.ql | 186 +- 602 files changed, 26777 insertions(+), 26790 deletions(-) diff --git a/python/ql/examples/snippets/catch_exception.ql b/python/ql/examples/snippets/catch_exception.ql index c117267d112..9d67d0056b6 100644 --- a/python/ql/examples/snippets/catch_exception.ql +++ b/python/ql/examples/snippets/catch_exception.ql @@ -11,6 +11,6 @@ import python from ExceptStmt ex, ClassValue cls where - cls.getName() = "MyExceptionClass" and - ex.getType().pointsTo(cls) + cls.getName() = "MyExceptionClass" and + ex.getType().pointsTo(cls) select ex diff --git a/python/ql/examples/snippets/conditional_expression.ql b/python/ql/examples/snippets/conditional_expression.ql index ee519aedb06..8af55ca104f 100644 --- a/python/ql/examples/snippets/conditional_expression.ql +++ b/python/ql/examples/snippets/conditional_expression.ql @@ -12,7 +12,7 @@ import python from IfExp e, ClassObject cls1, ClassObject cls2 where - e.getBody().refersTo(_, cls1, _) and - e.getOrelse().refersTo(_, cls2, _) and - cls1 != cls2 + e.getBody().refersTo(_, cls1, _) and + e.getOrelse().refersTo(_, cls2, _) and + cls1 != cls2 select e diff --git a/python/ql/examples/snippets/emptythen.ql b/python/ql/examples/snippets/emptythen.ql index bc017d4707a..e6c5b0f290e 100644 --- a/python/ql/examples/snippets/emptythen.ql +++ b/python/ql/examples/snippets/emptythen.ql @@ -14,8 +14,8 @@ import python from If i where - not exists(Stmt s | - i.getStmt(_) = s and - not s instanceof Pass - ) + not exists(Stmt s | + i.getStmt(_) = s and + not s instanceof Pass + ) select i diff --git a/python/ql/examples/snippets/extend_class.ql b/python/ql/examples/snippets/extend_class.ql index cc4dd62647d..805929ab709 100644 --- a/python/ql/examples/snippets/extend_class.ql +++ b/python/ql/examples/snippets/extend_class.ql @@ -14,6 +14,6 @@ import python from ClassObject sub, ClassObject base where - base.getName() = "MyClass" and - sub.getABaseType() = base + base.getName() = "MyClass" and + sub.getABaseType() = base select sub diff --git a/python/ql/examples/snippets/method_call.ql b/python/ql/examples/snippets/method_call.ql index 9f78a4bb22f..f78a9e43be0 100644 --- a/python/ql/examples/snippets/method_call.ql +++ b/python/ql/examples/snippets/method_call.ql @@ -10,6 +10,6 @@ import python from AstNode call, PythonFunctionValue method where - method.getQualifiedName() = "MyClass.methodName" and - method.getACall().getNode() = call + method.getQualifiedName() = "MyClass.methodName" and + method.getACall().getNode() = call select call diff --git a/python/ql/examples/snippets/new_instance.ql b/python/ql/examples/snippets/new_instance.ql index c1293d6638c..75a1ea635d5 100644 --- a/python/ql/examples/snippets/new_instance.ql +++ b/python/ql/examples/snippets/new_instance.ql @@ -11,6 +11,6 @@ import python from Call new, ClassValue cls where - cls.getName() = "MyClass" and - new.getFunc().pointsTo(cls) + cls.getName() = "MyClass" and + new.getFunc().pointsTo(cls) select new diff --git a/python/ql/examples/snippets/override_method.ql b/python/ql/examples/snippets/override_method.ql index 75c276df627..cb0c7d57680 100644 --- a/python/ql/examples/snippets/override_method.ql +++ b/python/ql/examples/snippets/override_method.ql @@ -10,6 +10,6 @@ import python from FunctionObject override, FunctionObject base where - base.getQualifiedName() = "MyClass.methodName" and - override.overrides(base) + base.getQualifiedName() = "MyClass.methodName" and + override.overrides(base) select override diff --git a/python/ql/examples/snippets/print.ql b/python/ql/examples/snippets/print.ql index 1a560d48e3d..f0ba47eafeb 100644 --- a/python/ql/examples/snippets/print.ql +++ b/python/ql/examples/snippets/print.ql @@ -9,9 +9,9 @@ import python from AstNode print where - /* Python 2 without `from __future__ import print_function` */ - print instanceof Print - or - /* Python 3 or with `from __future__ import print_function` */ - print.(Call).getFunc().pointsTo(Value::named("print")) + /* Python 2 without `from __future__ import print_function` */ + print instanceof Print + or + /* Python 3 or with `from __future__ import print_function` */ + print.(Call).getFunc().pointsTo(Value::named("print")) select print diff --git a/python/ql/examples/snippets/private_access.ql b/python/ql/examples/snippets/private_access.ql index 14548864579..d9e28db2875 100644 --- a/python/ql/examples/snippets/private_access.ql +++ b/python/ql/examples/snippets/private_access.ql @@ -9,12 +9,12 @@ import python predicate is_private(Attribute a) { - a.getName().matches("\\_%") and - not a.getName().matches("\\_\\_%\\_\\_") + a.getName().matches("\\_%") and + not a.getName().matches("\\_\\_%\\_\\_") } from Attribute access where - is_private(access) and - not access.getObject().(Name).getId() = "self" + is_private(access) and + not access.getObject().(Name).getId() = "self" select access diff --git a/python/ql/examples/snippets/raise_exception.ql b/python/ql/examples/snippets/raise_exception.ql index ce69c353780..12e4f93a349 100644 --- a/python/ql/examples/snippets/raise_exception.ql +++ b/python/ql/examples/snippets/raise_exception.ql @@ -11,6 +11,6 @@ import python from Raise raise, ClassValue ex where - ex.getName() = "AnException" and - raise.getException().pointsTo(ex.getASuperType()) + ex.getName() = "AnException" and + raise.getException().pointsTo(ex.getASuperType()) select raise, "Don't raise instances of 'AnException'" diff --git a/python/ql/examples/snippets/store_none.ql b/python/ql/examples/snippets/store_none.ql index 88aaac47f56..57be82f229d 100644 --- a/python/ql/examples/snippets/store_none.ql +++ b/python/ql/examples/snippets/store_none.ql @@ -13,6 +13,6 @@ import python from SubscriptNode store where - store.isStore() and - store.getIndex().pointsTo(Value::named("None")) + store.isStore() and + store.getIndex().pointsTo(Value::named("None")) select store diff --git a/python/ql/examples/snippets/tryfinally.ql b/python/ql/examples/snippets/tryfinally.ql index bf5ea3c61a6..b95b386f962 100644 --- a/python/ql/examples/snippets/tryfinally.ql +++ b/python/ql/examples/snippets/tryfinally.ql @@ -11,6 +11,6 @@ import python from Try t where - exists(t.getFinalbody()) and - not exists(t.getAHandler()) + exists(t.getFinalbody()) and + not exists(t.getAHandler()) select t diff --git a/python/ql/src/Classes/ClassAttributes.qll b/python/ql/src/Classes/ClassAttributes.qll index 5da7d29ace3..82c573f8f72 100644 --- a/python/ql/src/Classes/ClassAttributes.qll +++ b/python/ql/src/Classes/ClassAttributes.qll @@ -3,140 +3,140 @@ private import semmle.python.pointsto.PointsTo /** Helper class for UndefinedClassAttribute.ql and MaybeUndefinedClassAttribute.ql */ class CheckClass extends ClassObject { - private predicate ofInterest() { - not this.unknowableAttributes() and - not this.getPyClass().isProbableMixin() and - this.getPyClass().isPublic() and - not this.getPyClass().getScope() instanceof Function and - not this.probablyAbstract() and - not this.declaresAttribute("__new__") and - not this.selfDictAssigns() and - not this.lookupAttribute("__getattribute__") != object_getattribute() and - not this.hasAttribute("__getattr__") and - not this.selfSetattr() and - /* If class overrides object.__init__, but we can't resolve it to a Python function then give up */ - forall(ClassObject sup | - sup = this.getAnImproperSuperType() and - sup.declaresAttribute("__init__") and - not sup = theObjectType() - | - sup.declaredAttribute("__init__") instanceof PyFunctionObject - ) - } + private predicate ofInterest() { + not this.unknowableAttributes() and + not this.getPyClass().isProbableMixin() and + this.getPyClass().isPublic() and + not this.getPyClass().getScope() instanceof Function and + not this.probablyAbstract() and + not this.declaresAttribute("__new__") and + not this.selfDictAssigns() and + not this.lookupAttribute("__getattribute__") != object_getattribute() and + not this.hasAttribute("__getattr__") and + not this.selfSetattr() and + /* If class overrides object.__init__, but we can't resolve it to a Python function then give up */ + forall(ClassObject sup | + sup = this.getAnImproperSuperType() and + sup.declaresAttribute("__init__") and + not sup = theObjectType() + | + sup.declaredAttribute("__init__") instanceof PyFunctionObject + ) + } - predicate alwaysDefines(string name) { - auto_name(name) or - this.hasAttribute(name) or - this.getAnImproperSuperType().assignedInInit(name) or - this.getMetaClass().assignedInInit(name) - } + predicate alwaysDefines(string name) { + auto_name(name) or + this.hasAttribute(name) or + this.getAnImproperSuperType().assignedInInit(name) or + this.getMetaClass().assignedInInit(name) + } - predicate sometimesDefines(string name) { - this.alwaysDefines(name) + predicate sometimesDefines(string name) { + this.alwaysDefines(name) + or + exists(SelfAttributeStore sa | + sa.getScope().getScope+() = this.getAnImproperSuperType().getPyClass() + | + name = sa.getName() + ) + } + + private predicate selfDictAssigns() { + exists(Assign a, SelfAttributeRead self_dict, Subscript sub | + self_dict.getName() = "__dict__" and + ( + self_dict = sub.getObject() or - exists(SelfAttributeStore sa | - sa.getScope().getScope+() = this.getAnImproperSuperType().getPyClass() - | - name = sa.getName() + /* Indirect assignment via temporary variable */ + exists(SsaVariable v | + v.getAUse() = sub.getObject().getAFlowNode() and + v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode() ) - } + ) and + a.getATarget() = sub and + exists(FunctionObject meth | + meth = this.lookupAttribute(_) and a.getScope() = meth.getFunction() + ) + ) + } - private predicate selfDictAssigns() { - exists(Assign a, SelfAttributeRead self_dict, Subscript sub | - self_dict.getName() = "__dict__" and - ( - self_dict = sub.getObject() - or - /* Indirect assignment via temporary variable */ - exists(SsaVariable v | - v.getAUse() = sub.getObject().getAFlowNode() and - v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode() - ) - ) and - a.getATarget() = sub and - exists(FunctionObject meth | - meth = this.lookupAttribute(_) and a.getScope() = meth.getFunction() - ) - ) - } + pragma[nomagic] + private predicate monkeyPatched(string name) { + exists(Attribute a | + a.getCtx() instanceof Store and + PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and + a.getName() = name + ) + } - pragma[nomagic] - private predicate monkeyPatched(string name) { - exists(Attribute a | - a.getCtx() instanceof Store and - PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and - a.getName() = name - ) - } + private predicate selfSetattr() { + exists(Call c, Name setattr, Name self, Function method | + ( + method.getScope() = this.getPyClass() or + method.getScope() = this.getASuperType().getPyClass() + ) and + c.getScope() = method and + c.getFunc() = setattr and + setattr.getId() = "setattr" and + c.getArg(0) = self and + self.getId() = "self" + ) + } - private predicate selfSetattr() { - exists(Call c, Name setattr, Name self, Function method | - ( - method.getScope() = this.getPyClass() or - method.getScope() = this.getASuperType().getPyClass() - ) and - c.getScope() = method and - c.getFunc() = setattr and - setattr.getId() = "setattr" and - c.getArg(0) = self and - self.getId() = "self" - ) - } + predicate interestingUndefined(SelfAttributeRead a) { + exists(string name | name = a.getName() | + interestingContext(a, name) and + not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name) + ) + } - predicate interestingUndefined(SelfAttributeRead a) { - exists(string name | name = a.getName() | - interestingContext(a, name) and - not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name) - ) - } + private predicate interestingContext(SelfAttributeRead a, string name) { + name = a.getName() and + this.ofInterest() and + this.getPyClass() = a.getScope().getScope() and + not a.locallyDefined() and + not a.guardedByHasattr() and + a.getScope().isPublic() and + not this.monkeyPatched(name) and + not attribute_assigned_in_method(lookupAttribute("setUp"), name) + } - private predicate interestingContext(SelfAttributeRead a, string name) { - name = a.getName() and - this.ofInterest() and - this.getPyClass() = a.getScope().getScope() and - not a.locallyDefined() and - not a.guardedByHasattr() and - a.getScope().isPublic() and - not this.monkeyPatched(name) and - not attribute_assigned_in_method(lookupAttribute("setUp"), name) - } + private predicate probablyAbstract() { + this.getName().matches("Abstract%") + or + this.isAbstract() + } - private predicate probablyAbstract() { - this.getName().matches("Abstract%") - or - this.isAbstract() - } + pragma[nomagic] + private predicate definitionInBlock(BasicBlock b, string name) { + exists(SelfAttributeStore sa | + sa.getAFlowNode().getBasicBlock() = b and + sa.getName() = name and + sa.getClass() = this.getPyClass() + ) + or + exists(FunctionObject method | this.lookupAttribute(_) = method | + attribute_assigned_in_method(method, name) and + b = method.getACall().getBasicBlock() + ) + } - pragma[nomagic] - private predicate definitionInBlock(BasicBlock b, string name) { - exists(SelfAttributeStore sa | - sa.getAFlowNode().getBasicBlock() = b and - sa.getName() = name and - sa.getClass() = this.getPyClass() - ) - or - exists(FunctionObject method | this.lookupAttribute(_) = method | - attribute_assigned_in_method(method, name) and - b = method.getACall().getBasicBlock() - ) - } - - pragma[nomagic] - private predicate definedInBlock(BasicBlock b, string name) { - // manual specialisation: this is only called from interestingUndefined, - // so we can push the context in from there, which must apply to a - // SelfAttributeRead in the same scope - exists(SelfAttributeRead a | a.getScope() = b.getScope() and name = a.getName() | - interestingContext(a, name) - ) and - this.definitionInBlock(b, name) - or - exists(BasicBlock prev | this.definedInBlock(prev, name) and prev.getASuccessor() = b) - } + pragma[nomagic] + private predicate definedInBlock(BasicBlock b, string name) { + // manual specialisation: this is only called from interestingUndefined, + // so we can push the context in from there, which must apply to a + // SelfAttributeRead in the same scope + exists(SelfAttributeRead a | a.getScope() = b.getScope() and name = a.getName() | + interestingContext(a, name) + ) and + this.definitionInBlock(b, name) + or + exists(BasicBlock prev | this.definedInBlock(prev, name) and prev.getASuccessor() = b) + } } private Object object_getattribute() { - result.asBuiltin() = theObjectType().asBuiltin().getMember("__getattribute__") + result.asBuiltin() = theObjectType().asBuiltin().getMember("__getattribute__") } private predicate auto_name(string name) { name = "__class__" or name = "__dict__" } diff --git a/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql index e1f95e36eb6..34a9e133075 100644 --- a/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql +++ b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql @@ -14,48 +14,48 @@ import python predicate does_nothing(PyFunctionObject f) { - not exists(Stmt s | s.getScope() = f.getFunction() | - not s instanceof Pass and not s.(ExprStmt).getValue() = f.getFunction().getDocString() - ) + not exists(Stmt s | s.getScope() = f.getFunction() | + not s instanceof Pass and not s.(ExprStmt).getValue() = f.getFunction().getDocString() + ) } /* If a method performs a super() call then it is OK as the 'overridden' method will get called */ predicate calls_super(FunctionObject f) { - exists(Call sup, Call meth, Attribute attr, GlobalVariable v | - meth.getScope() = f.getFunction() and - meth.getFunc() = attr and - attr.getObject() = sup and - attr.getName() = f.getName() and - sup.getFunc() = v.getAnAccess() and - v.getId() = "super" - ) + exists(Call sup, Call meth, Attribute attr, GlobalVariable v | + meth.getScope() = f.getFunction() and + meth.getFunc() = attr and + attr.getObject() = sup and + attr.getName() = f.getName() and + sup.getFunc() = v.getAnAccess() and + v.getId() = "super" + ) } /** Holds if the given name is allowed for some reason */ predicate allowed(string name) { - /* - * The standard library specifically recommends this :( - * See https://docs.python.org/3/library/socketserver.html#asynchronous-mixins - */ + /* + * The standard library specifically recommends this :( + * See https://docs.python.org/3/library/socketserver.html#asynchronous-mixins + */ - name = "process_request" + name = "process_request" } from - ClassObject c, ClassObject b1, ClassObject b2, string name, int i1, int i2, Object o1, Object o2 + ClassObject c, ClassObject b1, ClassObject b2, string name, int i1, int i2, Object o1, Object o2 where - c.getBaseType(i1) = b1 and - c.getBaseType(i2) = b2 and - i1 < i2 and - o1 != o2 and - o1 = b1.lookupAttribute(name) and - o2 = b2.lookupAttribute(name) and - not name.matches("\\_\\_%\\_\\_") and - not calls_super(o1) and - not does_nothing(o2) and - not allowed(name) and - not o1.overrides(o2) and - not o2.overrides(o1) and - not c.declaresAttribute(name) + c.getBaseType(i1) = b1 and + c.getBaseType(i2) = b2 and + i1 < i2 and + o1 != o2 and + o1 = b1.lookupAttribute(name) and + o2 = b2.lookupAttribute(name) and + not name.matches("\\_\\_%\\_\\_") and + not calls_super(o1) and + not does_nothing(o2) and + not allowed(name) and + not o1.overrides(o2) and + not o2.overrides(o1) and + not c.declaresAttribute(name) select c, "Base classes have conflicting values for attribute '" + name + "': $@ and $@.", o1, - o1.toString(), o2, o2.toString() + o1.toString(), o2, o2.toString() diff --git a/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql index a0fff36b344..15d44f9a1eb 100644 --- a/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql +++ b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql @@ -15,21 +15,21 @@ import semmle.python.SelfAttribute import Equality predicate class_stores_to_attribute(ClassValue cls, SelfAttributeStore store, string name) { - exists(FunctionValue f | - f = cls.declaredAttribute(_) and store.getScope() = f.getScope() and store.getName() = name - ) and - /* Exclude classes used as metaclasses */ - not cls.getASuperType() = ClassValue::type() + exists(FunctionValue f | + f = cls.declaredAttribute(_) and store.getScope() = f.getScope() and store.getName() = name + ) and + /* Exclude classes used as metaclasses */ + not cls.getASuperType() = ClassValue::type() } predicate should_override_eq(ClassValue cls, Value base_eq) { - not cls.declaresAttribute("__eq__") and - exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | - not exists(GenericEqMethod eq | eq.getScope() = sup.getScope()) and - not exists(IdentityEqMethod eq | eq.getScope() = sup.getScope()) and - not base_eq.(FunctionValue).getScope() instanceof IdentityEqMethod and - not base_eq = ClassValue::object().declaredAttribute("__eq__") - ) + not cls.declaresAttribute("__eq__") and + exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | + not exists(GenericEqMethod eq | eq.getScope() = sup.getScope()) and + not exists(IdentityEqMethod eq | eq.getScope() = sup.getScope()) and + not base_eq.(FunctionValue).getScope() instanceof IdentityEqMethod and + not base_eq = ClassValue::object().declaredAttribute("__eq__") + ) } /** @@ -37,21 +37,21 @@ predicate should_override_eq(ClassValue cls, Value base_eq) { * which implies that the __eq__ method does not need to be overridden. */ predicate superclassEqExpectsAttribute(ClassValue cls, FunctionValue base_eq, string attrname) { - not cls.declaresAttribute("__eq__") and - exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | - exists(SelfAttributeRead store | store.getName() = attrname | - store.getScope() = base_eq.getScope() - ) + not cls.declaresAttribute("__eq__") and + exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | + exists(SelfAttributeRead store | store.getName() = attrname | + store.getScope() = base_eq.getScope() ) + ) } from ClassValue cls, SelfAttributeStore store, Value base_eq where - class_stores_to_attribute(cls, store, _) and - should_override_eq(cls, base_eq) and - /* Don't report overridden unittest.TestCase. -- TestCase overrides __eq__, but subclasses do not really need to. */ - not cls.getASuperType().getName() = "TestCase" and - not superclassEqExpectsAttribute(cls, base_eq, store.getName()) + class_stores_to_attribute(cls, store, _) and + should_override_eq(cls, base_eq) and + /* Don't report overridden unittest.TestCase. -- TestCase overrides __eq__, but subclasses do not really need to. */ + not cls.getASuperType().getName() = "TestCase" and + not superclassEqExpectsAttribute(cls, base_eq, store.getName()) select cls, - "The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq, - "'__eq__'", store, store.getName() + "The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq, + "'__eq__'", store, store.getName() diff --git a/python/ql/src/Classes/Equality.qll b/python/ql/src/Classes/Equality.qll index 27d17d863a4..347f5057c38 100644 --- a/python/ql/src/Classes/Equality.qll +++ b/python/ql/src/Classes/Equality.qll @@ -1,14 +1,14 @@ import python private Attribute dictAccess(LocalVariable var) { - result.getName() = "__dict__" and - result.getObject() = var.getAnAccess() + result.getName() = "__dict__" and + result.getObject() = var.getAnAccess() } private Call getattr(LocalVariable obj, LocalVariable attr) { - result.getFunc().(Name).getId() = "getattr" and - result.getArg(0) = obj.getAnAccess() and - result.getArg(1) = attr.getAnAccess() + result.getFunc().(Name).getId() = "getattr" and + result.getArg(0) = obj.getAnAccess() and + result.getArg(1) = attr.getAnAccess() } /** @@ -16,59 +16,59 @@ private Call getattr(LocalVariable obj, LocalVariable attr) { * or compares attributes using `getattr`. */ class GenericEqMethod extends Function { - GenericEqMethod() { - this.getName() = "__eq__" and - exists(LocalVariable self, LocalVariable other | - self.getAnAccess() = this.getArg(0) and - self.getId() = "self" and - other.getAnAccess() = this.getArg(1) and - exists(Compare eq | - eq.getOp(0) instanceof Eq or - eq.getOp(0) instanceof NotEq - | - // `self.__dict__ == other.__dict__` - eq.getAChildNode() = dictAccess(self) and - eq.getAChildNode() = dictAccess(other) - or - // `getattr(self, var) == getattr(other, var)` - exists(Variable var | - eq.getAChildNode() = getattr(self, var) and - eq.getAChildNode() = getattr(other, var) - ) - ) + GenericEqMethod() { + this.getName() = "__eq__" and + exists(LocalVariable self, LocalVariable other | + self.getAnAccess() = this.getArg(0) and + self.getId() = "self" and + other.getAnAccess() = this.getArg(1) and + exists(Compare eq | + eq.getOp(0) instanceof Eq or + eq.getOp(0) instanceof NotEq + | + // `self.__dict__ == other.__dict__` + eq.getAChildNode() = dictAccess(self) and + eq.getAChildNode() = dictAccess(other) + or + // `getattr(self, var) == getattr(other, var)` + exists(Variable var | + eq.getAChildNode() = getattr(self, var) and + eq.getAChildNode() = getattr(other, var) ) - } + ) + ) + } } /** An `__eq__` method that just does `self is other` */ class IdentityEqMethod extends Function { - IdentityEqMethod() { - this.getName() = "__eq__" and - exists(LocalVariable self, LocalVariable other | - self.getAnAccess() = this.getArg(0) and - self.getId() = "self" and - other.getAnAccess() = this.getArg(1) and - exists(Compare eq | eq.getOp(0) instanceof Is | - eq.getAChildNode() = self.getAnAccess() and - eq.getAChildNode() = other.getAnAccess() - ) - ) - } + IdentityEqMethod() { + this.getName() = "__eq__" and + exists(LocalVariable self, LocalVariable other | + self.getAnAccess() = this.getArg(0) and + self.getId() = "self" and + other.getAnAccess() = this.getArg(1) and + exists(Compare eq | eq.getOp(0) instanceof Is | + eq.getAChildNode() = self.getAnAccess() and + eq.getAChildNode() = other.getAnAccess() + ) + ) + } } /** An (in)equality method that delegates to its complement */ class DelegatingEqualityMethod extends Function { - DelegatingEqualityMethod() { - exists(Return ret, UnaryExpr not_, Compare comp, Cmpop op, Parameter p0, Parameter p1 | - ret.getScope() = this and - ret.getValue() = not_ and - not_.getOp() instanceof Not and - not_.getOperand() = comp and - comp.compares(p0.getVariable().getAnAccess(), op, p1.getVariable().getAnAccess()) - | - this.getName() = "__eq__" and op instanceof NotEq - or - this.getName() = "__ne__" and op instanceof Eq - ) - } + DelegatingEqualityMethod() { + exists(Return ret, UnaryExpr not_, Compare comp, Cmpop op, Parameter p0, Parameter p1 | + ret.getScope() = this and + ret.getValue() = not_ and + not_.getOp() instanceof Not and + not_.getOperand() = comp and + comp.compares(p0.getVariable().getAnAccess(), op, p1.getVariable().getAnAccess()) + | + this.getName() = "__eq__" and op instanceof NotEq + or + this.getName() = "__ne__" and op instanceof Eq + ) + } } diff --git a/python/ql/src/Classes/EqualsOrHash.ql b/python/ql/src/Classes/EqualsOrHash.ql index 795e7f4c0ff..26be0c2ec43 100644 --- a/python/ql/src/Classes/EqualsOrHash.ql +++ b/python/ql/src/Classes/EqualsOrHash.ql @@ -14,49 +14,49 @@ import python CallableValue defines_equality(ClassValue c, string name) { - ( - name = "__eq__" - or - major_version() = 2 and name = "__cmp__" - ) and - result = c.declaredAttribute(name) + ( + name = "__eq__" + or + major_version() = 2 and name = "__cmp__" + ) and + result = c.declaredAttribute(name) } CallableValue implemented_method(ClassValue c, string name) { - result = defines_equality(c, name) - or - result = c.declaredAttribute("__hash__") and name = "__hash__" + result = defines_equality(c, name) + or + result = c.declaredAttribute("__hash__") and name = "__hash__" } string unimplemented_method(ClassValue c) { - not exists(defines_equality(c, _)) and - ( - result = "__eq__" and major_version() = 3 - or - major_version() = 2 and result = "__eq__ or __cmp__" - ) + not exists(defines_equality(c, _)) and + ( + result = "__eq__" and major_version() = 3 or - /* Python 3 automatically makes classes unhashable if __eq__ is defined, but __hash__ is not */ - not c.declaresAttribute(result) and result = "__hash__" and major_version() = 2 + major_version() = 2 and result = "__eq__ or __cmp__" + ) + or + /* Python 3 automatically makes classes unhashable if __eq__ is defined, but __hash__ is not */ + not c.declaresAttribute(result) and result = "__hash__" and major_version() = 2 } /** Holds if this class is unhashable */ predicate unhashable(ClassValue cls) { - cls.lookup("__hash__") = Value::named("None") - or - cls.lookup("__hash__").(CallableValue).neverReturns() + cls.lookup("__hash__") = Value::named("None") + or + cls.lookup("__hash__").(CallableValue).neverReturns() } predicate violates_hash_contract(ClassValue c, string present, string missing, Value method) { - not unhashable(c) and - missing = unimplemented_method(c) and - method = implemented_method(c, present) and - not c.failedInference(_) + not unhashable(c) and + missing = unimplemented_method(c) and + method = implemented_method(c, present) and + not c.failedInference(_) } from ClassValue c, string present, string missing, CallableValue method where - violates_hash_contract(c, present, missing, method) and - exists(c.getScope()) // Suppress results that aren't from source + violates_hash_contract(c, present, missing, method) and + exists(c.getScope()) // Suppress results that aren't from source select method, "Class $@ implements " + present + " but does not define " + missing + ".", c, - c.getName() + c.getName() diff --git a/python/ql/src/Classes/EqualsOrNotEquals.ql b/python/ql/src/Classes/EqualsOrNotEquals.ql index 7457de441b0..adac5a20e87 100644 --- a/python/ql/src/Classes/EqualsOrNotEquals.ql +++ b/python/ql/src/Classes/EqualsOrNotEquals.ql @@ -16,33 +16,33 @@ import Equality string equals_or_ne() { result = "__eq__" or result = "__ne__" } predicate total_ordering(Class cls) { - exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") - or - exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") + exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") + or + exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") } CallableValue implemented_method(ClassValue c, string name) { - result = c.declaredAttribute(name) and name = equals_or_ne() + result = c.declaredAttribute(name) and name = equals_or_ne() } string unimplemented_method(ClassValue c) { - not c.declaresAttribute(result) and result = equals_or_ne() + not c.declaresAttribute(result) and result = equals_or_ne() } predicate violates_equality_contract( - ClassValue c, string present, string missing, CallableValue method + ClassValue c, string present, string missing, CallableValue method ) { - missing = unimplemented_method(c) and - method = implemented_method(c, present) and - not c.failedInference(_) and - not total_ordering(c.getScope()) and - /* Python 3 automatically implements __ne__ if __eq__ is defined, but not vice-versa */ - not (major_version() = 3 and present = "__eq__" and missing = "__ne__") and - not method.getScope() instanceof DelegatingEqualityMethod and - not c.lookup(missing).(CallableValue).getScope() instanceof DelegatingEqualityMethod + missing = unimplemented_method(c) and + method = implemented_method(c, present) and + not c.failedInference(_) and + not total_ordering(c.getScope()) and + /* Python 3 automatically implements __ne__ if __eq__ is defined, but not vice-versa */ + not (major_version() = 3 and present = "__eq__" and missing = "__ne__") and + not method.getScope() instanceof DelegatingEqualityMethod and + not c.lookup(missing).(CallableValue).getScope() instanceof DelegatingEqualityMethod } from ClassValue c, string present, string missing, CallableValue method where violates_equality_contract(c, present, missing, method) select method, "Class $@ implements " + present + " but does not implement " + missing + ".", c, - c.getName() + c.getName() diff --git a/python/ql/src/Classes/IncompleteOrdering.ql b/python/ql/src/Classes/IncompleteOrdering.ql index 7755696bd45..d6cd1230ece 100644 --- a/python/ql/src/Classes/IncompleteOrdering.ql +++ b/python/ql/src/Classes/IncompleteOrdering.ql @@ -13,61 +13,61 @@ import python predicate total_ordering(Class cls) { - exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") - or - exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") + exists(Attribute a | a = cls.getADecorator() | a.getName() = "total_ordering") + or + exists(Name n | n = cls.getADecorator() | n.getId() = "total_ordering") } string ordering_name(int n) { - result = "__lt__" and n = 1 - or - result = "__le__" and n = 2 - or - result = "__gt__" and n = 3 - or - result = "__ge__" and n = 4 + result = "__lt__" and n = 1 + or + result = "__le__" and n = 2 + or + result = "__gt__" and n = 3 + or + result = "__ge__" and n = 4 } predicate overrides_ordering_method(ClassValue c, string name) { - name = ordering_name(_) and - ( - c.declaresAttribute(name) - or - exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | - sup.declaresAttribute(name) - ) + name = ordering_name(_) and + ( + c.declaresAttribute(name) + or + exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | + sup.declaresAttribute(name) ) + ) } string unimplemented_ordering(ClassValue c, int n) { - not c = Value::named("object") and - not overrides_ordering_method(c, result) and - result = ordering_name(n) + not c = Value::named("object") and + not overrides_ordering_method(c, result) and + result = ordering_name(n) } string unimplemented_ordering_methods(ClassValue c, int n) { - n = 0 and result = "" and exists(unimplemented_ordering(c, _)) + n = 0 and result = "" and exists(unimplemented_ordering(c, _)) + or + exists(string prefix, int nm1 | n = nm1 + 1 and prefix = unimplemented_ordering_methods(c, nm1) | + prefix = "" and result = unimplemented_ordering(c, n) or - exists(string prefix, int nm1 | n = nm1 + 1 and prefix = unimplemented_ordering_methods(c, nm1) | - prefix = "" and result = unimplemented_ordering(c, n) - or - result = prefix and not exists(unimplemented_ordering(c, n)) and n < 5 - or - prefix != "" and result = prefix + " or " + unimplemented_ordering(c, n) - ) + result = prefix and not exists(unimplemented_ordering(c, n)) and n < 5 + or + prefix != "" and result = prefix + " or " + unimplemented_ordering(c, n) + ) } Value ordering_method(ClassValue c, string name) { - /* If class doesn't declare a method then don't blame this class (the superclass will be blamed). */ - name = ordering_name(_) and result = c.declaredAttribute(name) + /* If class doesn't declare a method then don't blame this class (the superclass will be blamed). */ + name = ordering_name(_) and result = c.declaredAttribute(name) } from ClassValue c, Value ordering, string name where - not c.failedInference(_) and - not total_ordering(c.getScope()) and - ordering = ordering_method(c, name) and - exists(unimplemented_ordering(c, _)) + not c.failedInference(_) and + not total_ordering(c.getScope()) and + ordering = ordering_method(c, name) and + exists(unimplemented_ordering(c, _)) select c, - "Class " + c.getName() + " implements $@, but does not implement " + - unimplemented_ordering_methods(c, 4) + ".", ordering, name + "Class " + c.getName() + " implements $@, but does not implement " + + unimplemented_ordering_methods(c, 4) + ".", ordering, name diff --git a/python/ql/src/Classes/InconsistentMRO.ql b/python/ql/src/Classes/InconsistentMRO.ql index a9541bc9023..90c1d386938 100644 --- a/python/ql/src/Classes/InconsistentMRO.ql +++ b/python/ql/src/Classes/InconsistentMRO.ql @@ -13,18 +13,18 @@ import python ClassObject left_base(ClassObject type, ClassObject base) { - exists(int i | i > 0 and type.getBaseType(i) = base and result = type.getBaseType(i - 1)) + exists(int i | i > 0 and type.getBaseType(i) = base and result = type.getBaseType(i - 1)) } predicate invalid_mro(ClassObject t, ClassObject left, ClassObject right) { - t.isNewStyle() and - left = left_base(t, right) and - left = right.getAnImproperSuperType() + t.isNewStyle() and + left = left_base(t, right) and + left = right.getAnImproperSuperType() } from ClassObject t, ClassObject left, ClassObject right where invalid_mro(t, left, right) select t, - "Construction of class " + t.getName() + - " can fail due to invalid method resolution order(MRO) for bases $@ and $@.", left, - left.getName(), right, right.getName() + "Construction of class " + t.getName() + + " can fail due to invalid method resolution order(MRO) for bases $@ and $@.", left, + left.getName(), right, right.getName() diff --git a/python/ql/src/Classes/InitCallsSubclassMethod.ql b/python/ql/src/Classes/InitCallsSubclassMethod.ql index b0e1cc0d8f0..19865751c53 100644 --- a/python/ql/src/Classes/InitCallsSubclassMethod.ql +++ b/python/ql/src/Classes/InitCallsSubclassMethod.ql @@ -14,17 +14,17 @@ import python from - ClassObject supercls, string method, Call call, FunctionObject overriding, - FunctionObject overridden + ClassObject supercls, string method, Call call, FunctionObject overriding, + FunctionObject overridden where - exists(FunctionObject init, SelfAttribute sa | - supercls.declaredAttribute("__init__") = init and - call.getScope() = init.getFunction() and - call.getFunc() = sa - | - sa.getName() = method and - overridden = supercls.declaredAttribute(method) and - overriding.overrides(overridden) - ) + exists(FunctionObject init, SelfAttribute sa | + supercls.declaredAttribute("__init__") = init and + call.getScope() = init.getFunction() and + call.getFunc() = sa + | + sa.getName() = method and + overridden = supercls.declaredAttribute(method) and + overriding.overrides(overridden) + ) select call, "Call to self.$@ in __init__ method, which is overridden by $@.", overridden, method, - overriding, overriding.descriptiveString() + overriding, overriding.descriptiveString() diff --git a/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql b/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql index ca8a260b863..7f4e27801c2 100644 --- a/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql +++ b/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql @@ -15,30 +15,30 @@ import semmle.python.SelfAttribute import ClassAttributes predicate guarded_by_other_attribute(SelfAttributeRead a, CheckClass c) { - c.sometimesDefines(a.getName()) and - exists(SelfAttributeRead guard, If i | - i.contains(a) and - c.assignedInInit(guard.getName()) - | - i.getTest() = guard - or - i.getTest().contains(guard) - ) + c.sometimesDefines(a.getName()) and + exists(SelfAttributeRead guard, If i | + i.contains(a) and + c.assignedInInit(guard.getName()) + | + i.getTest() = guard + or + i.getTest().contains(guard) + ) } predicate maybe_undefined_class_attribute(SelfAttributeRead a, CheckClass c) { - c.sometimesDefines(a.getName()) and - not c.alwaysDefines(a.getName()) and - c.interestingUndefined(a) and - not guarded_by_other_attribute(a, c) + c.sometimesDefines(a.getName()) and + not c.alwaysDefines(a.getName()) and + c.interestingUndefined(a) and + not guarded_by_other_attribute(a, c) } from Attribute a, ClassObject c, SelfAttributeStore sa where - maybe_undefined_class_attribute(a, c) and - sa.getClass() = c.getPyClass() and - sa.getName() = a.getName() + maybe_undefined_class_attribute(a, c) and + sa.getClass() = c.getPyClass() and + sa.getName() = a.getName() select a, - "Attribute '" + a.getName() + - "' is not defined in the class body nor in the __init__() method, but it is defined $@", sa, - "here" + "Attribute '" + a.getName() + + "' is not defined in the class body nor in the __init__() method, but it is defined $@", sa, + "here" diff --git a/python/ql/src/Classes/MethodCallOrder.qll b/python/ql/src/Classes/MethodCallOrder.qll index 494c234da3c..620cb802878 100644 --- a/python/ql/src/Classes/MethodCallOrder.qll +++ b/python/ql/src/Classes/MethodCallOrder.qll @@ -3,73 +3,73 @@ import python // Helper predicates for multiple call to __init__/__del__ queries. pragma[noinline] private predicate multiple_invocation_paths_helper( - FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi + FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi ) { - i1 != i2 and - i1 = top.getACallee+() and - i2 = top.getACallee+() and - i1.getFunction() = multi + i1 != i2 and + i1 = top.getACallee+() and + i2 = top.getACallee+() and + i1.getFunction() = multi } pragma[noinline] private predicate multiple_invocation_paths( - FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi + FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi ) { - multiple_invocation_paths_helper(top, i1, i2, multi) and - i2.getFunction() = multi + multiple_invocation_paths_helper(top, i1, i2, multi) and + i2.getFunction() = multi } /** Holds if `self.name` calls `multi` by multiple paths, and thus calls it more than once. */ predicate multiple_calls_to_superclass_method(ClassObject self, FunctionObject multi, string name) { - exists(FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2 | - multiple_invocation_paths(top, i1, i2, multi) and - top.runtime(self.declaredAttribute(name)) and - self.getASuperType().declaredAttribute(name) = multi - | - // Only called twice if called from different functions, - // or if one call-site can reach the other. - i1.getCall().getScope() != i2.getCall().getScope() - or - i1.getCall().strictlyReaches(i2.getCall()) - ) + exists(FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2 | + multiple_invocation_paths(top, i1, i2, multi) and + top.runtime(self.declaredAttribute(name)) and + self.getASuperType().declaredAttribute(name) = multi + | + // Only called twice if called from different functions, + // or if one call-site can reach the other. + i1.getCall().getScope() != i2.getCall().getScope() + or + i1.getCall().strictlyReaches(i2.getCall()) + ) } /** Holds if all attributes called `name` can be inferred to be methods. */ private predicate named_attributes_not_method(ClassObject cls, string name) { - cls.declaresAttribute(name) and not cls.declaredAttribute(name) instanceof FunctionObject + cls.declaresAttribute(name) and not cls.declaredAttribute(name) instanceof FunctionObject } /** Holds if `f` actually does something. */ private predicate does_something(FunctionObject f) { - f.isBuiltin() and not f = theObjectType().lookupAttribute("__init__") - or - exists(Stmt s | s = f.getFunction().getAStmt() and not s instanceof Pass) + f.isBuiltin() and not f = theObjectType().lookupAttribute("__init__") + or + exists(Stmt s | s = f.getFunction().getAStmt() and not s instanceof Pass) } /** Holds if `meth` looks like it should have a call to `name`, but does not */ private predicate missing_call(FunctionObject meth, string name) { - exists(CallNode call, AttrNode attr | - call.getScope() = meth.getFunction() and - call.getFunction() = attr and - attr.getName() = name and - not exists(FunctionObject f | f.getACall() = call) - ) + exists(CallNode call, AttrNode attr | + call.getScope() = meth.getFunction() and + call.getFunction() = attr and + attr.getName() = name and + not exists(FunctionObject f | f.getACall() = call) + ) } /** Holds if `self.name` does not call `missing`, even though it is expected to. */ predicate missing_call_to_superclass_method( - ClassObject self, FunctionObject top, FunctionObject missing, string name + ClassObject self, FunctionObject top, FunctionObject missing, string name ) { - missing = self.getASuperType().declaredAttribute(name) and - top = self.lookupAttribute(name) and - /* There is no call to missing originating from top */ - not top.getACallee*() = missing and - /* Make sure that all named 'methods' are objects that we can understand. */ - not exists(ClassObject sup | - sup = self.getAnImproperSuperType() and - named_attributes_not_method(sup, name) - ) and - not self.isAbstract() and - does_something(missing) and - not missing_call(top, name) + missing = self.getASuperType().declaredAttribute(name) and + top = self.lookupAttribute(name) and + /* There is no call to missing originating from top */ + not top.getACallee*() = missing and + /* Make sure that all named 'methods' are objects that we can understand. */ + not exists(ClassObject sup | + sup = self.getAnImproperSuperType() and + named_attributes_not_method(sup, name) + ) and + not self.isAbstract() and + does_something(missing) and + not missing_call(top, name) } diff --git a/python/ql/src/Classes/MissingCallToDel.ql b/python/ql/src/Classes/MissingCallToDel.ql index b54a9b8c782..e658dba1389 100644 --- a/python/ql/src/Classes/MissingCallToDel.ql +++ b/python/ql/src/Classes/MissingCallToDel.ql @@ -15,10 +15,10 @@ import MethodCallOrder from ClassObject self, FunctionObject missing where - missing_call_to_superclass_method(self, _, missing, "__del__") and - not missing.neverReturns() and - not self.failedInference() and - not missing.isBuiltin() + missing_call_to_superclass_method(self, _, missing, "__del__") and + not missing.neverReturns() and + not self.failedInference() and + not missing.isBuiltin() select self, - "Class " + self.getName() + " may not be cleaned up properly as $@ is not called during deletion.", - missing, missing.descriptiveString() + "Class " + self.getName() + " may not be cleaned up properly as $@ is not called during deletion.", + missing, missing.descriptiveString() diff --git a/python/ql/src/Classes/MissingCallToInit.ql b/python/ql/src/Classes/MissingCallToInit.ql index bb6121e33b6..6daa06de79c 100644 --- a/python/ql/src/Classes/MissingCallToInit.ql +++ b/python/ql/src/Classes/MissingCallToInit.ql @@ -15,14 +15,14 @@ import MethodCallOrder from ClassObject self, FunctionObject initializer, FunctionObject missing where - self.lookupAttribute("__init__") = initializer and - missing_call_to_superclass_method(self, initializer, missing, "__init__") and - // If a superclass is incorrect, don't flag this class as well. - not missing_call_to_superclass_method(self.getASuperType(), _, missing, "__init__") and - not missing.neverReturns() and - not self.failedInference() and - not missing.isBuiltin() and - not self.isAbstract() + self.lookupAttribute("__init__") = initializer and + missing_call_to_superclass_method(self, initializer, missing, "__init__") and + // If a superclass is incorrect, don't flag this class as well. + not missing_call_to_superclass_method(self.getASuperType(), _, missing, "__init__") and + not missing.neverReturns() and + not self.failedInference() and + not missing.isBuiltin() and + not self.isAbstract() select self, - "Class " + self.getName() + " may not be initialized properly as $@ is not called from its $@.", - missing, missing.descriptiveString(), initializer, "__init__ method" + "Class " + self.getName() + " may not be initialized properly as $@ is not called from its $@.", + missing, missing.descriptiveString(), initializer, "__init__ method" diff --git a/python/ql/src/Classes/MutatingDescriptor.ql b/python/ql/src/Classes/MutatingDescriptor.ql index 1f1188c2830..a7118883951 100644 --- a/python/ql/src/Classes/MutatingDescriptor.ql +++ b/python/ql/src/Classes/MutatingDescriptor.ql @@ -13,20 +13,20 @@ import python predicate mutates_descriptor(ClassObject cls, SelfAttributeStore s) { - cls.isDescriptorType() and - exists(PyFunctionObject f, PyFunctionObject get_set | - exists(string name | cls.lookupAttribute(name) = get_set | - name = "__get__" or name = "__set__" or name = "__delete__" - ) and - cls.lookupAttribute(_) = f and - get_set.getACallee*() = f and - not f.getName() = "__init__" and - s.getScope() = f.getFunction() - ) + cls.isDescriptorType() and + exists(PyFunctionObject f, PyFunctionObject get_set | + exists(string name | cls.lookupAttribute(name) = get_set | + name = "__get__" or name = "__set__" or name = "__delete__" + ) and + cls.lookupAttribute(_) = f and + get_set.getACallee*() = f and + not f.getName() = "__init__" and + s.getScope() = f.getFunction() + ) } from ClassObject cls, SelfAttributeStore s where mutates_descriptor(cls, s) select s, - "Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties.", - cls, cls.getName() + "Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties.", + cls, cls.getName() diff --git a/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql b/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql index 168348e7b1c..e77d5ec481f 100644 --- a/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql +++ b/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql @@ -14,79 +14,79 @@ import python class InitCallStmt extends ExprStmt { - InitCallStmt() { - exists(Call call, Attribute attr | call = this.getValue() and attr = call.getFunc() | - attr.getName() = "__init__" - ) - } + InitCallStmt() { + exists(Call call, Attribute attr | call = this.getValue() and attr = call.getFunc() | + attr.getName() = "__init__" + ) + } } predicate overwrites_which(Function subinit, AssignStmt write_attr, string which) { - write_attr.getScope() = subinit and - self_write_stmt(write_attr, _) and - exists(Stmt top | top.contains(write_attr) or top = write_attr | - ( - exists(int i, int j, InitCallStmt call | call.getScope() = subinit | - i > j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "superclass" - ) - or - exists(int i, int j, InitCallStmt call | call.getScope() = subinit | - i < j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "subclass" - ) - ) + write_attr.getScope() = subinit and + self_write_stmt(write_attr, _) and + exists(Stmt top | top.contains(write_attr) or top = write_attr | + ( + exists(int i, int j, InitCallStmt call | call.getScope() = subinit | + i > j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "superclass" + ) + or + exists(int i, int j, InitCallStmt call | call.getScope() = subinit | + i < j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "subclass" + ) ) + ) } predicate self_write_stmt(Stmt s, string attr) { - exists(Attribute a, Name self | - self = a.getObject() and - s.contains(a) and - self.getId() = "self" and - a.getCtx() instanceof Store and - a.getName() = attr - ) + exists(Attribute a, Name self | + self = a.getObject() and + s.contains(a) and + self.getId() = "self" and + a.getCtx() instanceof Store and + a.getName() = attr + ) } predicate both_assign_attribute(Stmt s1, Stmt s2, Function f1, Function f2) { - exists(string name | - s1.getScope() = f1 and - s2.getScope() = f2 and - self_write_stmt(s1, name) and - self_write_stmt(s2, name) - ) + exists(string name | + s1.getScope() = f1 and + s2.getScope() = f2 and + self_write_stmt(s1, name) and + self_write_stmt(s2, name) + ) } predicate attribute_overwritten( - AssignStmt overwrites, AssignStmt overwritten, string name, string classtype, string classname + AssignStmt overwrites, AssignStmt overwritten, string name, string classtype, string classname ) { - exists( - FunctionObject superinit, FunctionObject subinit, ClassObject superclass, ClassObject subclass, - AssignStmt subattr, AssignStmt superattr - | - ( - classtype = "superclass" and - classname = superclass.getName() and - overwrites = subattr and - overwritten = superattr - or - classtype = "subclass" and - classname = subclass.getName() and - overwrites = superattr and - overwritten = subattr - ) and - /* OK if overwritten in subclass and is a class attribute */ - (not exists(superclass.declaredAttribute(name)) or classtype = "subclass") and - superclass.declaredAttribute("__init__") = superinit and - subclass.declaredAttribute("__init__") = subinit and - superclass = subclass.getASuperType() and - overwrites_which(subinit.getFunction(), subattr, classtype) and - both_assign_attribute(subattr, superattr, subinit.getFunction(), superinit.getFunction()) and - self_write_stmt(superattr, name) - ) + exists( + FunctionObject superinit, FunctionObject subinit, ClassObject superclass, ClassObject subclass, + AssignStmt subattr, AssignStmt superattr + | + ( + classtype = "superclass" and + classname = superclass.getName() and + overwrites = subattr and + overwritten = superattr + or + classtype = "subclass" and + classname = subclass.getName() and + overwrites = superattr and + overwritten = subattr + ) and + /* OK if overwritten in subclass and is a class attribute */ + (not exists(superclass.declaredAttribute(name)) or classtype = "subclass") and + superclass.declaredAttribute("__init__") = superinit and + subclass.declaredAttribute("__init__") = subinit and + superclass = subclass.getASuperType() and + overwrites_which(subinit.getFunction(), subattr, classtype) and + both_assign_attribute(subattr, superattr, subinit.getFunction(), superinit.getFunction()) and + self_write_stmt(superattr, name) + ) } from string classtype, AssignStmt overwrites, AssignStmt overwritten, string name, string classname where attribute_overwritten(overwrites, overwritten, name, classtype, classname) select overwrites, - "Assignment overwrites attribute " + name + ", which was previously defined in " + classtype + - " $@.", overwritten, classname + "Assignment overwrites attribute " + name + ", which was previously defined in " + classtype + + " $@.", overwritten, classname diff --git a/python/ql/src/Classes/PropertyInOldStyleClass.ql b/python/ql/src/Classes/PropertyInOldStyleClass.ql index ff2bf13a9f8..6e1b9a063c0 100644 --- a/python/ql/src/Classes/PropertyInOldStyleClass.ql +++ b/python/ql/src/Classes/PropertyInOldStyleClass.ql @@ -15,5 +15,5 @@ import python from PropertyObject prop, ClassObject cls where cls.declaredAttribute(_) = prop and not cls.failedInference() and not cls.isNewStyle() select prop, - "Property " + prop.getName() + " will not work properly, as class " + cls.getName() + - " is an old-style class." + "Property " + prop.getName() + " will not work properly, as class " + cls.getName() + + " is an old-style class." diff --git a/python/ql/src/Classes/ShouldBeContextManager.ql b/python/ql/src/Classes/ShouldBeContextManager.ql index fa7cebf9c77..bdcc6dc2863 100644 --- a/python/ql/src/Classes/ShouldBeContextManager.ql +++ b/python/ql/src/Classes/ShouldBeContextManager.ql @@ -17,5 +17,5 @@ import python from ClassValue c where not c.isBuiltin() and not c.isContextManager() and exists(c.declaredAttribute("__del__")) select c, - "Class " + c.getName() + - " implements __del__ (presumably to release some resource). Consider making it a context manager." + "Class " + c.getName() + + " implements __del__ (presumably to release some resource). Consider making it a context manager." diff --git a/python/ql/src/Classes/SubclassShadowing.ql b/python/ql/src/Classes/SubclassShadowing.ql index ed1a79869b0..6594f5eee12 100644 --- a/python/ql/src/Classes/SubclassShadowing.ql +++ b/python/ql/src/Classes/SubclassShadowing.ql @@ -20,27 +20,27 @@ import python predicate shadowed_by_super_class( - ClassObject c, ClassObject supercls, Assign assign, FunctionObject f + ClassObject c, ClassObject supercls, Assign assign, FunctionObject f ) { - c.getASuperType() = supercls and - c.declaredAttribute(_) = f and - exists(FunctionObject init, Attribute attr | - supercls.declaredAttribute("__init__") = init and - attr = assign.getATarget() and - attr.getObject().(Name).getId() = "self" and - attr.getName() = f.getName() and - assign.getScope() = init.getOrigin().(FunctionExpr).getInnerScope() - ) and - /* - * It's OK if the super class defines the method as well. - * We assume that the original method must have been defined for a reason. - */ + c.getASuperType() = supercls and + c.declaredAttribute(_) = f and + exists(FunctionObject init, Attribute attr | + supercls.declaredAttribute("__init__") = init and + attr = assign.getATarget() and + attr.getObject().(Name).getId() = "self" and + attr.getName() = f.getName() and + assign.getScope() = init.getOrigin().(FunctionExpr).getInnerScope() + ) and + /* + * It's OK if the super class defines the method as well. + * We assume that the original method must have been defined for a reason. + */ - not supercls.hasAttribute(f.getName()) + not supercls.hasAttribute(f.getName()) } from ClassObject c, ClassObject supercls, Assign assign, FunctionObject shadowed where shadowed_by_super_class(c, supercls, assign, shadowed) select shadowed.getOrigin(), - "Method " + shadowed.getName() + " is shadowed by $@ in super class '" + supercls.getName() + "'.", - assign, "an attribute" + "Method " + shadowed.getName() + " is shadowed by $@ in super class '" + supercls.getName() + "'.", + assign, "an attribute" diff --git a/python/ql/src/Classes/SuperInOldStyleClass.ql b/python/ql/src/Classes/SuperInOldStyleClass.ql index aa4c62c6f08..f309c025fec 100644 --- a/python/ql/src/Classes/SuperInOldStyleClass.ql +++ b/python/ql/src/Classes/SuperInOldStyleClass.ql @@ -13,13 +13,13 @@ import python predicate uses_of_super_in_old_style_class(Call s) { - exists(Function f, ClassObject c | - s.getScope() = f and - f.getScope() = c.getPyClass() and - not c.failedInference() and - not c.isNewStyle() and - s.getFunc().(Name).getId() = "super" - ) + exists(Function f, ClassObject c | + s.getScope() = f and + f.getScope() = c.getPyClass() and + not c.failedInference() and + not c.isNewStyle() and + s.getFunc().(Name).getId() = "super" + ) } from Call c diff --git a/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql index cd4c74a5e86..f1ef04bb89a 100644 --- a/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql +++ b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql @@ -15,14 +15,14 @@ import MethodCallOrder from ClassObject self, FunctionObject multi where - multiple_calls_to_superclass_method(self, multi, "__del__") and - not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__del__") and - not exists(FunctionObject better | - multiple_calls_to_superclass_method(self, better, "__del__") and - better.overrides(multi) - ) and - not self.failedInference() + multiple_calls_to_superclass_method(self, multi, "__del__") and + not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__del__") and + not exists(FunctionObject better | + multiple_calls_to_superclass_method(self, better, "__del__") and + better.overrides(multi) + ) and + not self.failedInference() select self, - "Class " + self.getName() + - " may not be cleaned up properly as $@ may be called multiple times during destruction.", multi, - multi.descriptiveString() + "Class " + self.getName() + + " may not be cleaned up properly as $@ may be called multiple times during destruction.", multi, + multi.descriptiveString() diff --git a/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql index 71d05533fde..cb90cb32510 100644 --- a/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql +++ b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql @@ -15,15 +15,15 @@ import MethodCallOrder from ClassObject self, FunctionObject multi where - multi != theObjectType().lookupAttribute("__init__") and - multiple_calls_to_superclass_method(self, multi, "__init__") and - not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__init__") and - not exists(FunctionObject better | - multiple_calls_to_superclass_method(self, better, "__init__") and - better.overrides(multi) - ) and - not self.failedInference() + multi != theObjectType().lookupAttribute("__init__") and + multiple_calls_to_superclass_method(self, multi, "__init__") and + not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__init__") and + not exists(FunctionObject better | + multiple_calls_to_superclass_method(self, better, "__init__") and + better.overrides(multi) + ) and + not self.failedInference() select self, - "Class " + self.getName() + - " may not be initialized properly as $@ may be called multiple times during initialization.", - multi, multi.descriptiveString() + "Class " + self.getName() + + " may not be initialized properly as $@ may be called multiple times during initialization.", + multi, multi.descriptiveString() diff --git a/python/ql/src/Classes/UndefinedClassAttribute.ql b/python/ql/src/Classes/UndefinedClassAttribute.ql index bdbbcbf2496..348daa7f9fb 100644 --- a/python/ql/src/Classes/UndefinedClassAttribute.ql +++ b/python/ql/src/Classes/UndefinedClassAttribute.ql @@ -15,18 +15,18 @@ import semmle.python.SelfAttribute import ClassAttributes predicate undefined_class_attribute(SelfAttributeRead a, CheckClass c, int line, string name) { - name = a.getName() and - not c.sometimesDefines(name) and - c.interestingUndefined(a) and - line = a.getLocation().getStartLine() and - not attribute_assigned_in_method(c.getAMethodCalledFromInit(), name) + name = a.getName() and + not c.sometimesDefines(name) and + c.interestingUndefined(a) and + line = a.getLocation().getStartLine() and + not attribute_assigned_in_method(c.getAMethodCalledFromInit(), name) } predicate report_undefined_class_attribute(Attribute a, ClassObject c, string name) { - exists(int line | - undefined_class_attribute(a, c, line, name) and - line = min(int x | undefined_class_attribute(_, c, x, name)) - ) + exists(int line | + undefined_class_attribute(a, c, line, name) and + line = min(int x | undefined_class_attribute(_, c, x, name)) + ) } from Attribute a, ClassObject c, string name diff --git a/python/ql/src/Classes/UselessClass.ql b/python/ql/src/Classes/UselessClass.ql index e4e3f31f3a2..24bf446fc6a 100644 --- a/python/ql/src/Classes/UselessClass.ql +++ b/python/ql/src/Classes/UselessClass.ql @@ -13,76 +13,76 @@ import python predicate fewer_than_two_public_methods(Class cls, int methods) { - (methods = 0 or methods = 1) and - methods = count(Function f | f = cls.getAMethod() and not f = cls.getInitMethod()) + (methods = 0 or methods = 1) and + methods = count(Function f | f = cls.getAMethod() and not f = cls.getInitMethod()) } predicate does_not_define_special_method(Class cls) { - not exists(Function f | f = cls.getAMethod() and f.isSpecialMethod()) + not exists(Function f | f = cls.getAMethod() and f.isSpecialMethod()) } predicate no_inheritance(Class c) { - not exists(ClassValue cls, ClassValue other | - cls.getScope() = c and - other != ClassValue::object() - | - other.getABaseType() = cls or - cls.getABaseType() = other - ) and - not exists(Expr base | base = c.getABase() | - not base instanceof Name or base.(Name).getId() != "object" - ) + not exists(ClassValue cls, ClassValue other | + cls.getScope() = c and + other != ClassValue::object() + | + other.getABaseType() = cls or + cls.getABaseType() = other + ) and + not exists(Expr base | base = c.getABase() | + not base instanceof Name or base.(Name).getId() != "object" + ) } predicate is_decorated(Class c) { exists(c.getADecorator()) } predicate is_stateful(Class c) { - exists(Function method, ExprContext ctx | - method.getScope() = c and - (ctx instanceof Store or ctx instanceof AugStore) - | - exists(Subscript s | s.getScope() = method and s.getCtx() = ctx) - or - exists(Attribute a | a.getScope() = method and a.getCtx() = ctx) - ) + exists(Function method, ExprContext ctx | + method.getScope() = c and + (ctx instanceof Store or ctx instanceof AugStore) + | + exists(Subscript s | s.getScope() = method and s.getCtx() = ctx) or - exists(Function method, Call call, Attribute a, string name | - method.getScope() = c and - call.getScope() = method and - call.getFunc() = a and - a.getName() = name - | - name = "pop" or - name = "remove" or - name = "discard" or - name = "extend" or - name = "append" - ) + exists(Attribute a | a.getScope() = method and a.getCtx() = ctx) + ) + or + exists(Function method, Call call, Attribute a, string name | + method.getScope() = c and + call.getScope() = method and + call.getFunc() = a and + a.getName() = name + | + name = "pop" or + name = "remove" or + name = "discard" or + name = "extend" or + name = "append" + ) } predicate useless_class(Class c, int methods) { - c.isTopLevel() and - c.isPublic() and - no_inheritance(c) and - fewer_than_two_public_methods(c, methods) and - does_not_define_special_method(c) and - not c.isProbableMixin() and - not is_decorated(c) and - not is_stateful(c) + c.isTopLevel() and + c.isPublic() and + no_inheritance(c) and + fewer_than_two_public_methods(c, methods) and + does_not_define_special_method(c) and + not c.isProbableMixin() and + not is_decorated(c) and + not is_stateful(c) } from Class c, int methods, string msg where - useless_class(c, methods) and - ( - methods = 1 and - msg = - "Class " + c.getName() + - " defines only one public method, which should be replaced by a function." - or - methods = 0 and - msg = - "Class " + c.getName() + - " defines no public methods and could be replaced with a namedtuple or dictionary." - ) + useless_class(c, methods) and + ( + methods = 1 and + msg = + "Class " + c.getName() + + " defines only one public method, which should be replaced by a function." + or + methods = 0 and + msg = + "Class " + c.getName() + + " defines no public methods and could be replaced with a namedtuple or dictionary." + ) select c, msg diff --git a/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql index f3276ac61ef..73631a134c8 100644 --- a/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql +++ b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql @@ -18,7 +18,7 @@ import Expressions.CallArgs from Call call, ClassValue cls, string name, FunctionValue init where - illegally_named_parameter(call, cls, name) and - init = get_function_or_initializer(cls) + illegally_named_parameter(call, cls, name) and + init = get_function_or_initializer(cls) select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", init, - init.getQualifiedName() + init.getQualifiedName() diff --git a/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql index c8763bd8aa7..7afd344eacb 100644 --- a/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql +++ b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql @@ -17,15 +17,15 @@ import Expressions.CallArgs from Call call, ClassValue cls, string too, string should, int limit, FunctionValue init where - ( - too_many_args(call, cls, limit) and - too = "too many arguments" and - should = "no more than " - or - too_few_args(call, cls, limit) and - too = "too few arguments" and - should = "no fewer than " - ) and - init = get_function_or_initializer(cls) + ( + too_many_args(call, cls, limit) and + too = "too many arguments" and + should = "no more than " + or + too_few_args(call, cls, limit) and + too = "too few arguments" and + should = "no fewer than " + ) and + init = get_function_or_initializer(cls) select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", init, - init.getQualifiedName() + init.getQualifiedName() diff --git a/python/ql/src/Exceptions/CatchingBaseException.ql b/python/ql/src/Exceptions/CatchingBaseException.ql index 04a95a8e827..828cbf46e4b 100644 --- a/python/ql/src/Exceptions/CatchingBaseException.ql +++ b/python/ql/src/Exceptions/CatchingBaseException.ql @@ -17,13 +17,13 @@ import python predicate doesnt_reraise(ExceptStmt ex) { ex.getAFlowNode().getBasicBlock().reachesExit() } predicate catches_base_exception(ExceptStmt ex) { - ex.getType().pointsTo(ClassValue::baseException()) - or - not exists(ex.getType()) + ex.getType().pointsTo(ClassValue::baseException()) + or + not exists(ex.getType()) } from ExceptStmt ex where - catches_base_exception(ex) and - doesnt_reraise(ex) + catches_base_exception(ex) and + doesnt_reraise(ex) select ex, "Except block directly handles BaseException." diff --git a/python/ql/src/Exceptions/EmptyExcept.ql b/python/ql/src/Exceptions/EmptyExcept.ql index fd656755c1c..207ba6dd247 100755 --- a/python/ql/src/Exceptions/EmptyExcept.ql +++ b/python/ql/src/Exceptions/EmptyExcept.ql @@ -14,88 +14,88 @@ import python predicate empty_except(ExceptStmt ex) { - not exists(Stmt s | s = ex.getAStmt() and not s instanceof Pass) + not exists(Stmt s | s = ex.getAStmt() and not s instanceof Pass) } predicate no_else(ExceptStmt ex) { not exists(ex.getTry().getOrelse()) } predicate no_comment(ExceptStmt ex) { - not exists(Comment c | - c.getLocation().getFile() = ex.getLocation().getFile() and - c.getLocation().getStartLine() >= ex.getLocation().getStartLine() and - c.getLocation().getEndLine() <= ex.getBody().getLastItem().getLocation().getEndLine() - ) + not exists(Comment c | + c.getLocation().getFile() = ex.getLocation().getFile() and + c.getLocation().getStartLine() >= ex.getLocation().getStartLine() and + c.getLocation().getEndLine() <= ex.getBody().getLastItem().getLocation().getEndLine() + ) } predicate non_local_control_flow(ExceptStmt ex) { - ex.getType().pointsTo(ClassValue::stopIteration()) + ex.getType().pointsTo(ClassValue::stopIteration()) } predicate try_has_normal_exit(Try try) { - exists(ControlFlowNode pred, ControlFlowNode succ | - /* Exists a non-exception predecessor, successor pair */ - pred.getASuccessor() = succ and - not pred.getAnExceptionalSuccessor() = succ - | - /* Successor is either a normal flow node or a fall-through exit */ - not exists(Scope s | s.getReturnNode() = succ) and - /* Predecessor is in try body and successor is not */ - pred.getNode().getParentNode*() = try.getAStmt() and - not succ.getNode().getParentNode*() = try.getAStmt() - ) + exists(ControlFlowNode pred, ControlFlowNode succ | + /* Exists a non-exception predecessor, successor pair */ + pred.getASuccessor() = succ and + not pred.getAnExceptionalSuccessor() = succ + | + /* Successor is either a normal flow node or a fall-through exit */ + not exists(Scope s | s.getReturnNode() = succ) and + /* Predecessor is in try body and successor is not */ + pred.getNode().getParentNode*() = try.getAStmt() and + not succ.getNode().getParentNode*() = try.getAStmt() + ) } predicate attribute_access(Stmt s) { - s.(ExprStmt).getValue() instanceof Attribute - or - exists(string name | s.(ExprStmt).getValue().(Call).getFunc().(Name).getId() = name | - name = "getattr" or name = "setattr" or name = "delattr" - ) - or - s.(Delete).getATarget() instanceof Attribute + s.(ExprStmt).getValue() instanceof Attribute + or + exists(string name | s.(ExprStmt).getValue().(Call).getFunc().(Name).getId() = name | + name = "getattr" or name = "setattr" or name = "delattr" + ) + or + s.(Delete).getATarget() instanceof Attribute } predicate subscript(Stmt s) { - s.(ExprStmt).getValue() instanceof Subscript - or - s.(Delete).getATarget() instanceof Subscript + s.(ExprStmt).getValue() instanceof Subscript + or + s.(Delete).getATarget() instanceof Subscript } predicate encode_decode(Call ex, ClassValue type) { - exists(string name | ex.getFunc().(Attribute).getName() = name | - name = "encode" and type = ClassValue::unicodeEncodeError() - or - name = "decode" and type = ClassValue::unicodeDecodeError() - ) + exists(string name | ex.getFunc().(Attribute).getName() = name | + name = "encode" and type = ClassValue::unicodeEncodeError() + or + name = "decode" and type = ClassValue::unicodeDecodeError() + ) } predicate small_handler(ExceptStmt ex, Stmt s, ClassValue type) { - not exists(ex.getTry().getStmt(1)) and - s = ex.getTry().getStmt(0) and - ex.getType().pointsTo(type) + not exists(ex.getTry().getStmt(1)) and + s = ex.getTry().getStmt(0) and + ex.getType().pointsTo(type) } predicate focussed_handler(ExceptStmt ex) { - exists(Stmt s, ClassValue type | small_handler(ex, s, type) | - subscript(s) and type.getASuperType() = ClassValue::lookupError() - or - attribute_access(s) and type = ClassValue::attributeError() - or - s.(ExprStmt).getValue() instanceof Name and type = ClassValue::nameError() - or - encode_decode(s.(ExprStmt).getValue(), type) - ) + exists(Stmt s, ClassValue type | small_handler(ex, s, type) | + subscript(s) and type.getASuperType() = ClassValue::lookupError() + or + attribute_access(s) and type = ClassValue::attributeError() + or + s.(ExprStmt).getValue() instanceof Name and type = ClassValue::nameError() + or + encode_decode(s.(ExprStmt).getValue(), type) + ) } Try try_return() { not exists(result.getStmt(1)) and result.getStmt(0) instanceof Return } from ExceptStmt ex where - empty_except(ex) and - no_else(ex) and - no_comment(ex) and - not non_local_control_flow(ex) and - not ex.getTry() = try_return() and - try_has_normal_exit(ex.getTry()) and - not focussed_handler(ex) + empty_except(ex) and + no_else(ex) and + no_comment(ex) and + not non_local_control_flow(ex) and + not ex.getTry() = try_return() and + try_has_normal_exit(ex.getTry()) and + not focussed_handler(ex) select ex, "'except' clause does nothing but pass and there is no explanatory comment." diff --git a/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql b/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql index c7e014eab4f..61850b2c0d1 100644 --- a/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql +++ b/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql @@ -15,16 +15,16 @@ import python from ExceptFlowNode ex, Value t, ClassValue c, ControlFlowNode origin, string what where - ex.handledException(t, c, origin) and - ( - exists(ClassValue x | x = t | - not x.isLegalExceptionType() and - not x.failedInference(_) and - what = "class '" + x.getName() + "'" - ) - or - not t instanceof ClassValue and - what = "instance of '" + c.getName() + "'" + ex.handledException(t, c, origin) and + ( + exists(ClassValue x | x = t | + not x.isLegalExceptionType() and + not x.failedInference(_) and + what = "class '" + x.getName() + "'" ) + or + not t instanceof ClassValue and + what = "instance of '" + c.getName() + "'" + ) select ex.getNode(), - "Non-exception $@ in exception handler which will never match raised exception.", origin, what + "Non-exception $@ in exception handler which will never match raised exception.", origin, what diff --git a/python/ql/src/Exceptions/IllegalRaise.ql b/python/ql/src/Exceptions/IllegalRaise.ql index f05f5437db2..7d8e8987410 100644 --- a/python/ql/src/Exceptions/IllegalRaise.ql +++ b/python/ql/src/Exceptions/IllegalRaise.ql @@ -17,9 +17,9 @@ import Exceptions.NotImplemented from Raise r, ClassValue t where - type_or_typeof(r, t, _) and - not t.isLegalExceptionType() and - not t.failedInference(_) and - not use_of_not_implemented_in_raise(r, _) + type_or_typeof(r, t, _) and + not t.isLegalExceptionType() and + not t.failedInference(_) and + not use_of_not_implemented_in_raise(r, _) select r, - "Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead." + "Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead." diff --git a/python/ql/src/Exceptions/IncorrectExceptOrder.ql b/python/ql/src/Exceptions/IncorrectExceptOrder.ql index be756c5efef..0b57dd4659d 100644 --- a/python/ql/src/Exceptions/IncorrectExceptOrder.ql +++ b/python/ql/src/Exceptions/IncorrectExceptOrder.ql @@ -15,14 +15,14 @@ import python predicate incorrect_except_order(ExceptStmt ex1, ClassValue cls1, ExceptStmt ex2, ClassValue cls2) { - exists(int i, int j, Try t | - ex1 = t.getHandler(i) and - ex2 = t.getHandler(j) and - i < j and - cls1 = except_class(ex1) and - cls2 = except_class(ex2) and - cls1 = cls2.getASuperType() - ) + exists(int i, int j, Try t | + ex1 = t.getHandler(i) and + ex2 = t.getHandler(j) and + i < j and + cls1 = except_class(ex1) and + cls2 = except_class(ex2) and + cls1 = cls2.getASuperType() + ) } ClassValue except_class(ExceptStmt ex) { ex.getType().pointsTo(result) } @@ -30,5 +30,5 @@ ClassValue except_class(ExceptStmt ex) { ex.getType().pointsTo(result) } from ExceptStmt ex1, ClassValue cls1, ExceptStmt ex2, ClassValue cls2 where incorrect_except_order(ex1, cls1, ex2, cls2) select ex2, - "Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference.", - cls2, cls2.getName(), ex1, "except block", cls1, cls1.getName() + "Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference.", + cls2, cls2.getName(), ex1, "except block", cls1, cls1.getName() diff --git a/python/ql/src/Exceptions/NotImplemented.qll b/python/ql/src/Exceptions/NotImplemented.qll index b319311290e..2186a7b5f30 100644 --- a/python/ql/src/Exceptions/NotImplemented.qll +++ b/python/ql/src/Exceptions/NotImplemented.qll @@ -2,9 +2,9 @@ import python /** Holds if `notimpl` refers to `NotImplemented` or `NotImplemented()` in the `raise` statement */ predicate use_of_not_implemented_in_raise(Raise raise, Expr notimpl) { - notimpl.pointsTo(Value::named("NotImplemented")) and - ( - notimpl = raise.getException() or - notimpl = raise.getException().(Call).getFunc() - ) + notimpl.pointsTo(Value::named("NotImplemented")) and + ( + notimpl = raise.getException() or + notimpl = raise.getException().(Call).getFunc() + ) } diff --git a/python/ql/src/Exceptions/Raising.qll b/python/ql/src/Exceptions/Raising.qll index ad98c93d0c4..c8149481187 100644 --- a/python/ql/src/Exceptions/Raising.qll +++ b/python/ql/src/Exceptions/Raising.qll @@ -2,11 +2,11 @@ import python /** Whether the raise statement 'r' raises 'type' from origin 'orig' */ predicate type_or_typeof(Raise r, ClassValue type, AstNode orig) { - exists(Expr exception | exception = r.getRaised() | - exception.pointsTo(type, orig) - or - not exists(ClassValue exc_type | exception.pointsTo(exc_type)) and - not type = ClassValue::type() and // First value is an unknown exception type - exists(Value val | exception.pointsTo(val, orig) | val.getClass() = type) - ) + exists(Expr exception | exception = r.getRaised() | + exception.pointsTo(type, orig) + or + not exists(ClassValue exc_type | exception.pointsTo(exc_type)) and + not type = ClassValue::type() and // First value is an unknown exception type + exists(Value val | exception.pointsTo(val, orig) | val.getClass() = type) + ) } diff --git a/python/ql/src/Exceptions/RaisingTuple.ql b/python/ql/src/Exceptions/RaisingTuple.ql index dc4b295a90d..abfe785e9cb 100644 --- a/python/ql/src/Exceptions/RaisingTuple.ql +++ b/python/ql/src/Exceptions/RaisingTuple.ql @@ -13,10 +13,10 @@ import python from Raise r, Value v, AstNode origin where - r.getException().pointsTo(v, origin) and - v.getClass() = ClassValue::tuple() and - major_version() = 2 + r.getException().pointsTo(v, origin) and + v.getClass() = ClassValue::tuple() and + major_version() = 2 /* Raising a tuple is a type error in Python 3, so is handled by the IllegalRaise query. */ select r, - "Raising $@ will result in the first element (recursively) being raised and all other elements being discarded.", - origin, "a tuple" + "Raising $@ will result in the first element (recursively) being raised and all other elements being discarded.", + origin, "a tuple" diff --git a/python/ql/src/Exceptions/UnguardedNextInGenerator.ql b/python/ql/src/Exceptions/UnguardedNextInGenerator.ql index c2435d41b3e..c4d7cc956a0 100755 --- a/python/ql/src/Exceptions/UnguardedNextInGenerator.ql +++ b/python/ql/src/Exceptions/UnguardedNextInGenerator.ql @@ -17,45 +17,45 @@ FunctionValue iter() { result = Value::named("iter") } BuiltinFunctionValue next() { result = Value::named("next") } predicate call_to_iter(CallNode call, EssaVariable sequence) { - sequence.getAUse() = iter().getArgumentForCall(call, 0) + sequence.getAUse() = iter().getArgumentForCall(call, 0) } predicate call_to_next(CallNode call, ControlFlowNode iter) { - iter = next().getArgumentForCall(call, 0) + iter = next().getArgumentForCall(call, 0) } predicate call_to_next_has_default(CallNode call) { - exists(call.getArg(1)) or exists(call.getArgByName("default")) + exists(call.getArg(1)) or exists(call.getArgByName("default")) } predicate guarded_not_empty_sequence(EssaVariable sequence) { - sequence.getDefinition() instanceof EssaEdgeRefinement + sequence.getDefinition() instanceof EssaEdgeRefinement } /** The pattern `next(iter(x))` is often used where `x` is known not be empty. Check for that. */ predicate iter_not_exhausted(EssaVariable iterator) { - exists(EssaVariable sequence | - call_to_iter(iterator.getDefinition().(AssignmentDefinition).getValue(), sequence) and - guarded_not_empty_sequence(sequence) - ) + exists(EssaVariable sequence | + call_to_iter(iterator.getDefinition().(AssignmentDefinition).getValue(), sequence) and + guarded_not_empty_sequence(sequence) + ) } predicate stop_iteration_handled(CallNode call) { - exists(Try t | - t.containsInScope(call.getNode()) and - t.getAHandler().getType().pointsTo(ClassValue::stopIteration()) - ) + exists(Try t | + t.containsInScope(call.getNode()) and + t.getAHandler().getType().pointsTo(ClassValue::stopIteration()) + ) } from CallNode call where - call_to_next(call, _) and - not call_to_next_has_default(call) and - not exists(EssaVariable iterator | - call_to_next(call, iterator.getAUse()) and - iter_not_exhausted(iterator) - ) and - call.getNode().getScope().(Function).isGenerator() and - not exists(Comp comp | comp.contains(call.getNode())) and - not stop_iteration_handled(call) + call_to_next(call, _) and + not call_to_next_has_default(call) and + not exists(EssaVariable iterator | + call_to_next(call, iterator.getAUse()) and + iter_not_exhausted(iterator) + ) and + call.getNode().getScope().(Function).isGenerator() and + not exists(Comp comp | comp.contains(call.getNode())) and + not stop_iteration_handled(call) select call, "Call to next() in a generator" diff --git a/python/ql/src/Expressions/CallArgs.qll b/python/ql/src/Expressions/CallArgs.qll index d5820fff91d..5e7e56d99d8 100644 --- a/python/ql/src/Expressions/CallArgs.qll +++ b/python/ql/src/Expressions/CallArgs.qll @@ -1,36 +1,36 @@ -/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */ +/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */ import python import Testing.Mox private int varargs_length_objectapi(Call call) { - not exists(call.getStarargs()) and result = 0 - or - exists(TupleObject t | call.getStarargs().refersTo(t) | result = t.getLength()) - or - result = count(call.getStarargs().(List).getAnElt()) + not exists(call.getStarargs()) and result = 0 + or + exists(TupleObject t | call.getStarargs().refersTo(t) | result = t.getLength()) + or + result = count(call.getStarargs().(List).getAnElt()) } private int varargs_length(Call call) { - not exists(call.getStarargs()) and result = 0 - or - exists(TupleValue t | call.getStarargs().pointsTo(t) | result = t.length()) - or - result = count(call.getStarargs().(List).getAnElt()) + not exists(call.getStarargs()) and result = 0 + or + exists(TupleValue t | call.getStarargs().pointsTo(t) | result = t.length()) + or + result = count(call.getStarargs().(List).getAnElt()) } /** Gets a keyword argument that is not a keyword-only parameter. */ private Keyword not_keyword_only_arg_objectapi(Call call, FunctionObject func) { - func.getACall().getNode() = call and - result = call.getAKeyword() and - not func.getFunction().getAKeywordOnlyArg().getId() = result.getArg() + func.getACall().getNode() = call and + result = call.getAKeyword() and + not func.getFunction().getAKeywordOnlyArg().getId() = result.getArg() } /** Gets a keyword argument that is not a keyword-only parameter. */ private Keyword not_keyword_only_arg(Call call, FunctionValue func) { - func.getACall().getNode() = call and - result = call.getAKeyword() and - not func.getScope().getAKeywordOnlyArg().getId() = result.getArg() + func.getACall().getNode() = call and + result = call.getAKeyword() and + not func.getScope().getAKeywordOnlyArg().getId() = result.getArg() } /** @@ -40,17 +40,17 @@ private Keyword not_keyword_only_arg(Call call, FunctionValue func) { * plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs). */ private int positional_arg_count_for_call_objectapi(Call call, Object callable) { - call = get_a_call_objectapi(callable).getNode() and - exists(int positional_keywords | - exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | - not func.getFunction().hasKwArg() and - positional_keywords = count(not_keyword_only_arg_objectapi(call, func)) - or - func.getFunction().hasKwArg() and positional_keywords = 0 - ) - | - result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + call = get_a_call_objectapi(callable).getNode() and + exists(int positional_keywords | + exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | + not func.getFunction().hasKwArg() and + positional_keywords = count(not_keyword_only_arg_objectapi(call, func)) + or + func.getFunction().hasKwArg() and positional_keywords = 0 ) + | + result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + ) } /** @@ -60,174 +60,174 @@ private int positional_arg_count_for_call_objectapi(Call call, Object callable) * plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs). */ private int positional_arg_count_for_call(Call call, Value callable) { - call = get_a_call(callable).getNode() and - exists(int positional_keywords | - exists(FunctionValue func | func = get_function_or_initializer(callable) | - not func.getScope().hasKwArg() and - positional_keywords = count(not_keyword_only_arg(call, func)) - or - func.getScope().hasKwArg() and positional_keywords = 0 - ) - | - result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + call = get_a_call(callable).getNode() and + exists(int positional_keywords | + exists(FunctionValue func | func = get_function_or_initializer(callable) | + not func.getScope().hasKwArg() and + positional_keywords = count(not_keyword_only_arg(call, func)) + or + func.getScope().hasKwArg() and positional_keywords = 0 ) + | + result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords + ) } /** Gets the number of arguments in `call`. */ int arg_count_objectapi(Call call) { - result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword()) + result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword()) } /** Gets the number of arguments in `call`. */ int arg_count(Call call) { - result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword()) + result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword()) } /** Gets a call corresponding to the given class or function. */ private ControlFlowNode get_a_call_objectapi(Object callable) { - result = callable.(ClassObject).getACall() - or - result = callable.(FunctionObject).getACall() + result = callable.(ClassObject).getACall() + or + result = callable.(FunctionObject).getACall() } /** Gets a call corresponding to the given class or function. */ private ControlFlowNode get_a_call(Value callable) { - result = callable.(ClassValue).getACall() - or - result = callable.(FunctionValue).getACall() + result = callable.(ClassValue).getACall() + or + result = callable.(FunctionValue).getACall() } /** Gets the function object corresponding to the given class or function. */ FunctionObject get_function_or_initializer_objectapi(Object func_or_cls) { - result = func_or_cls.(FunctionObject) - or - result = func_or_cls.(ClassObject).declaredAttribute("__init__") + result = func_or_cls.(FunctionObject) + or + result = func_or_cls.(ClassObject).declaredAttribute("__init__") } /** Gets the function object corresponding to the given class or function. */ FunctionValue get_function_or_initializer(Value func_or_cls) { - result = func_or_cls.(FunctionValue) - or - result = func_or_cls.(ClassValue).declaredAttribute("__init__") + result = func_or_cls.(FunctionValue) + or + result = func_or_cls.(ClassValue).declaredAttribute("__init__") } /**Whether there is an illegally named parameter called `name` in the `call` to `func` */ predicate illegally_named_parameter_objectapi(Call call, Object func, string name) { - not func.isC() and - name = call.getANamedArgumentName() and - call.getAFlowNode() = get_a_call_objectapi(func) and - not get_function_or_initializer_objectapi(func).isLegalArgumentName(name) + not func.isC() and + name = call.getANamedArgumentName() and + call.getAFlowNode() = get_a_call_objectapi(func) and + not get_function_or_initializer_objectapi(func).isLegalArgumentName(name) } /**Whether there is an illegally named parameter called `name` in the `call` to `func` */ predicate illegally_named_parameter(Call call, Value func, string name) { - not func.isBuiltin() and - name = call.getANamedArgumentName() and - call.getAFlowNode() = get_a_call(func) and - not get_function_or_initializer(func).isLegalArgumentName(name) + not func.isBuiltin() and + name = call.getANamedArgumentName() and + call.getAFlowNode() = get_a_call(func) and + not get_function_or_initializer(func).isLegalArgumentName(name) } /**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */ predicate too_few_args_objectapi(Call call, Object callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter_objectapi(call, callable, _) and - not exists(call.getStarargs()) and - not exists(call.getKwargs()) and - arg_count_objectapi(call) < limit and - exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | - call = func.getAFunctionCall().getNode() and - limit = func.minParameters() and - // The combination of misuse of `mox.Mox().StubOutWithMock()` - // and a bug in mox's implementation of methods results in having to - // pass 1 too few arguments to the mocked function. - not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) - or - call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 - or - callable instanceof ClassObject and - call.getAFlowNode() = get_a_call_objectapi(callable) and - limit = func.minParameters() - 1 - ) + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter_objectapi(call, callable, _) and + not exists(call.getStarargs()) and + not exists(call.getKwargs()) and + arg_count_objectapi(call) < limit and + exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) | + call = func.getAFunctionCall().getNode() and + limit = func.minParameters() and + // The combination of misuse of `mox.Mox().StubOutWithMock()` + // and a bug in mox's implementation of methods results in having to + // pass 1 too few arguments to the mocked function. + not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) + or + call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 + or + callable instanceof ClassObject and + call.getAFlowNode() = get_a_call_objectapi(callable) and + limit = func.minParameters() - 1 + ) } /**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */ predicate too_few_args(Call call, Value callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter(call, callable, _) and - not exists(call.getStarargs()) and - not exists(call.getKwargs()) and - arg_count(call) < limit and - exists(FunctionValue func | func = get_function_or_initializer(callable) | - call = func.getAFunctionCall().getNode() and - limit = func.minParameters() and - /* - * The combination of misuse of `mox.Mox().StubOutWithMock()` - * and a bug in mox's implementation of methods results in having to - * pass 1 too few arguments to the mocked function. - */ + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter(call, callable, _) and + not exists(call.getStarargs()) and + not exists(call.getKwargs()) and + arg_count(call) < limit and + exists(FunctionValue func | func = get_function_or_initializer(callable) | + call = func.getAFunctionCall().getNode() and + limit = func.minParameters() and + /* + * The combination of misuse of `mox.Mox().StubOutWithMock()` + * and a bug in mox's implementation of methods results in having to + * pass 1 too few arguments to the mocked function. + */ - not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) - or - call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 - or - callable instanceof ClassValue and - call.getAFlowNode() = get_a_call(callable) and - limit = func.minParameters() - 1 - ) + not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) + or + call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 + or + callable instanceof ClassValue and + call.getAFlowNode() = get_a_call(callable) and + limit = func.minParameters() - 1 + ) } /**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */ predicate too_many_args_objectapi(Call call, Object callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter_objectapi(call, callable, _) and - exists(FunctionObject func | - func = get_function_or_initializer_objectapi(callable) and - not func.getFunction().hasVarArg() and - limit >= 0 - | - call = func.getAFunctionCall().getNode() and limit = func.maxParameters() - or - call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 - or - callable instanceof ClassObject and - call.getAFlowNode() = get_a_call_objectapi(callable) and - limit = func.maxParameters() - 1 - ) and - positional_arg_count_for_call_objectapi(call, callable) > limit + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter_objectapi(call, callable, _) and + exists(FunctionObject func | + func = get_function_or_initializer_objectapi(callable) and + not func.getFunction().hasVarArg() and + limit >= 0 + | + call = func.getAFunctionCall().getNode() and limit = func.maxParameters() + or + call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 + or + callable instanceof ClassObject and + call.getAFlowNode() = get_a_call_objectapi(callable) and + limit = func.maxParameters() - 1 + ) and + positional_arg_count_for_call_objectapi(call, callable) > limit } /**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */ predicate too_many_args(Call call, Value callable, int limit) { - // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' - not illegally_named_parameter(call, callable, _) and - exists(FunctionValue func | - func = get_function_or_initializer(callable) and - not func.getScope().hasVarArg() and - limit >= 0 - | - call = func.getAFunctionCall().getNode() and limit = func.maxParameters() - or - call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 - or - callable instanceof ClassValue and - call.getAFlowNode() = get_a_call(callable) and - limit = func.maxParameters() - 1 - ) and - positional_arg_count_for_call(call, callable) > limit + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter(call, callable, _) and + exists(FunctionValue func | + func = get_function_or_initializer(callable) and + not func.getScope().hasVarArg() and + limit >= 0 + | + call = func.getAFunctionCall().getNode() and limit = func.maxParameters() + or + call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 + or + callable instanceof ClassValue and + call.getAFlowNode() = get_a_call(callable) and + limit = func.maxParameters() - 1 + ) and + positional_arg_count_for_call(call, callable) > limit } /** Holds if `call` has too many or too few arguments for `func` */ predicate wrong_args_objectapi(Call call, FunctionObject func, int limit, string too) { - too_few_args_objectapi(call, func, limit) and too = "too few" - or - too_many_args_objectapi(call, func, limit) and too = "too many" + too_few_args_objectapi(call, func, limit) and too = "too few" + or + too_many_args_objectapi(call, func, limit) and too = "too many" } /** Holds if `call` has too many or too few arguments for `func` */ predicate wrong_args(Call call, FunctionValue func, int limit, string too) { - too_few_args(call, func, limit) and too = "too few" - or - too_many_args(call, func, limit) and too = "too many" + too_few_args(call, func, limit) and too = "too few" + or + too_many_args(call, func, limit) and too = "too many" } /** @@ -236,8 +236,8 @@ predicate wrong_args(Call call, FunctionValue func, int limit, string too) { */ bindingset[call, func] predicate correct_args_if_called_as_method_objectapi(Call call, FunctionObject func) { - arg_count_objectapi(call) + 1 >= func.minParameters() and - arg_count_objectapi(call) < func.maxParameters() + arg_count_objectapi(call) + 1 >= func.minParameters() and + arg_count_objectapi(call) < func.maxParameters() } /** @@ -246,23 +246,23 @@ predicate correct_args_if_called_as_method_objectapi(Call call, FunctionObject f */ bindingset[call, func] predicate correct_args_if_called_as_method(Call call, FunctionValue func) { - arg_count(call) + 1 >= func.minParameters() and - arg_count(call) < func.maxParameters() + arg_count(call) + 1 >= func.minParameters() and + arg_count(call) < func.maxParameters() } /** Holds if `call` is a call to `overriding`, which overrides `func`. */ predicate overridden_call_objectapi(FunctionObject func, FunctionObject overriding, Call call) { - overriding.overrides(func) and - overriding.getACall().getNode() = call + overriding.overrides(func) and + overriding.getACall().getNode() = call } /** Holds if `call` is a call to `overriding`, which overrides `func`. */ predicate overridden_call(FunctionValue func, FunctionValue overriding, Call call) { - overriding.overrides(func) and - overriding.getACall().getNode() = call + overriding.overrides(func) and + overriding.getACall().getNode() = call } /** Holds if `func` will raise a `NotImplemented` error. */ predicate isAbstract(FunctionValue func) { - func.getARaisedType() = ClassValue::notImplementedError() + func.getARaisedType() = ClassValue::notImplementedError() } diff --git a/python/ql/src/Expressions/CallToSuperWrongClass.ql b/python/ql/src/Expressions/CallToSuperWrongClass.ql index 4f218ab5a2c..4098653d7f8 100644 --- a/python/ql/src/Expressions/CallToSuperWrongClass.ql +++ b/python/ql/src/Expressions/CallToSuperWrongClass.ql @@ -16,14 +16,14 @@ import python from CallNode call_to_super, string name where - exists(GlobalVariable gv, ControlFlowNode cn | - call_to_super = ClassValue::super_().getACall() and - gv.getId() = "super" and - cn = call_to_super.getArg(0) and - name = call_to_super.getScope().getScope().(Class).getName() and - exists(ClassValue other | - cn.pointsTo(other) and - not other.getScope().getName() = name - ) + exists(GlobalVariable gv, ControlFlowNode cn | + call_to_super = ClassValue::super_().getACall() and + gv.getId() = "super" and + cn = call_to_super.getArg(0) and + name = call_to_super.getScope().getScope().(Class).getName() and + exists(ClassValue other | + cn.pointsTo(other) and + not other.getScope().getName() = name ) + ) select call_to_super.getNode(), "First argument to super() should be " + name + "." diff --git a/python/ql/src/Expressions/CompareConstants.ql b/python/ql/src/Expressions/CompareConstants.ql index 5b04302db31..d2d8f827dc0 100644 --- a/python/ql/src/Expressions/CompareConstants.ql +++ b/python/ql/src/Expressions/CompareConstants.ql @@ -16,8 +16,8 @@ import python from Compare comparison, Expr left, Expr right where - comparison.compares(left, _, right) and - left.isConstant() and - right.isConstant() and - not exists(Assert a | a.getTest() = comparison) + comparison.compares(left, _, right) and + left.isConstant() and + right.isConstant() and + not exists(Assert a | a.getTest() = comparison) select comparison, "Comparison of constants; use 'True' or 'False' instead." diff --git a/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql index 29f21e7beb2..8bab127ec9d 100644 --- a/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql +++ b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql @@ -21,9 +21,9 @@ import semmle.python.Comparisons */ private predicate is_complex(Expr comp) { - exists(comp.(Compare).getOp(1)) - or - is_complex(comp.(UnaryExpr).getOperand()) + exists(comp.(Compare).getOp(1)) + or + is_complex(comp.(UnaryExpr).getOperand()) } /** @@ -31,21 +31,21 @@ private predicate is_complex(Expr comp) { * strict and also controls that block. */ private predicate useless_test(Comparison comp, ComparisonControlBlock controls, boolean isTrue) { - controls.impliesThat(comp.getBasicBlock(), comp, isTrue) and - /* Exclude complex comparisons of form `a < x < y`, as we do not (yet) have perfect flow control for those */ - not is_complex(controls.getTest().getNode()) + controls.impliesThat(comp.getBasicBlock(), comp, isTrue) and + /* Exclude complex comparisons of form `a < x < y`, as we do not (yet) have perfect flow control for those */ + not is_complex(controls.getTest().getNode()) } private predicate useless_test_ast(AstNode comp, AstNode previous, boolean isTrue) { - forex(Comparison compnode, ConditionBlock block | - compnode.getNode() = comp and - block.getLastNode().getNode() = previous - | - useless_test(compnode, block, isTrue) - ) + forex(Comparison compnode, ConditionBlock block | + compnode.getNode() = comp and + block.getLastNode().getNode() = previous + | + useless_test(compnode, block, isTrue) + ) } from Expr test, Expr other, boolean isTrue where - useless_test_ast(test, other, isTrue) and not useless_test_ast(test.getAChildNode+(), other, _) + useless_test_ast(test, other, isTrue) and not useless_test_ast(test.getAChildNode+(), other, _) select test, "Test is always " + isTrue + ", because of $@", other, "this condition" diff --git a/python/ql/src/Expressions/ContainsNonContainer.ql b/python/ql/src/Expressions/ContainsNonContainer.ql index 69f3b1c68b3..87a3866085c 100644 --- a/python/ql/src/Expressions/ContainsNonContainer.ql +++ b/python/ql/src/Expressions/ContainsNonContainer.ql @@ -14,21 +14,21 @@ import python import semmle.python.pointsto.PointsTo predicate rhs_in_expr(ControlFlowNode rhs, Compare cmp) { - exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs.getNode() | - op instanceof In or op instanceof NotIn - ) + exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs.getNode() | + op instanceof In or op instanceof NotIn + ) } from ControlFlowNode non_seq, Compare cmp, Value v, ClassValue cls, ControlFlowNode origin where - rhs_in_expr(non_seq, cmp) and - non_seq.pointsTo(_, v, origin) and - v.getClass() = cls and - not Types::failedInference(cls, _) and - not cls.hasAttribute("__contains__") and - not cls.hasAttribute("__iter__") and - not cls.hasAttribute("__getitem__") and - not cls = ClassValue::nonetype() and - not cls = Value::named("types.MappingProxyType") + rhs_in_expr(non_seq, cmp) and + non_seq.pointsTo(_, v, origin) and + v.getClass() = cls and + not Types::failedInference(cls, _) and + not cls.hasAttribute("__contains__") and + not cls.hasAttribute("__iter__") and + not cls.hasAttribute("__getitem__") and + not cls = ClassValue::nonetype() and + not cls = Value::named("types.MappingProxyType") select cmp, "This test may raise an Exception as the $@ may be of non-container class $@.", origin, - "target", cls, cls.getName() + "target", cls, cls.getName() diff --git a/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql index 99a1a0e44e1..61046863718 100644 --- a/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql +++ b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql @@ -15,31 +15,31 @@ import python import semmle.python.strings predicate dict_key(Dict d, Expr k, string s) { - k = d.getAKey() and - ( - s = k.(Num).getN() - or - // We use � to mark unrepresentable characters - // so two instances of � may represent different strings in the source code - not "�" = s.charAt(_) and - exists(StrConst c | c = k | - s = "u\"" + c.getText() + "\"" and c.isUnicode() - or - s = "b\"" + c.getText() + "\"" and not c.isUnicode() - ) + k = d.getAKey() and + ( + s = k.(Num).getN() + or + // We use � to mark unrepresentable characters + // so two instances of � may represent different strings in the source code + not "�" = s.charAt(_) and + exists(StrConst c | c = k | + s = "u\"" + c.getText() + "\"" and c.isUnicode() + or + s = "b\"" + c.getText() + "\"" and not c.isUnicode() ) + ) } from Dict d, Expr k1, Expr k2 where - exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and - ( - exists(BasicBlock b, int i1, int i2 | - k1.getAFlowNode() = b.getNode(i1) and - k2.getAFlowNode() = b.getNode(i2) and - i1 < i2 - ) - or - k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock()) + exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and + ( + exists(BasicBlock b, int i1, int i2 | + k1.getAFlowNode() = b.getNode(i1) and + k2.getAFlowNode() = b.getNode(i2) and + i1 < i2 ) + or + k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock()) + ) select k1, "Dictionary key " + repr(k1) + " is subsequently $@.", k2, "overwritten" diff --git a/python/ql/src/Expressions/ExpectedMappingForFormatString.ql b/python/ql/src/Expressions/ExpectedMappingForFormatString.ql index 96025cc444a..76d2f874779 100644 --- a/python/ql/src/Expressions/ExpectedMappingForFormatString.ql +++ b/python/ql/src/Expressions/ExpectedMappingForFormatString.ql @@ -15,12 +15,12 @@ import semmle.python.strings from Expr e, ClassValue t where - exists(BinaryExpr b | - b.getOp() instanceof Mod and - format_string(b.getLeft()) and - e = b.getRight() and - mapping_format(b.getLeft()) and - e.pointsTo().getClass() = t and - not t.isMapping() - ) + exists(BinaryExpr b | + b.getOp() instanceof Mod and + format_string(b.getLeft()) and + e = b.getRight() and + mapping_format(b.getLeft()) and + e.pointsTo().getClass() = t and + not t.isMapping() + ) select e, "Right hand side of a % operator must be a mapping, not class $@.", t, t.getName() diff --git a/python/ql/src/Expressions/ExplicitCallToDel.ql b/python/ql/src/Expressions/ExplicitCallToDel.ql index 81e8fc97b43..cb441ce0267 100644 --- a/python/ql/src/Expressions/ExplicitCallToDel.ql +++ b/python/ql/src/Expressions/ExplicitCallToDel.ql @@ -13,20 +13,20 @@ import python class DelCall extends Call { - DelCall() { this.getFunc().(Attribute).getName() = "__del__" } + DelCall() { this.getFunc().(Attribute).getName() = "__del__" } - predicate isSuperCall() { - exists(Function f | f = this.getScope() and f.getName() = "__del__" | - // We pass in `self` as the first argument... - f.getArg(0).asName().getVariable() = this.getArg(0).(Name).getVariable() - or - // ... or the call is of the form `super(Type, self).__del__()`, or the equivalent - // Python 3: `super().__del__()`. - exists(Call superCall | superCall = this.getFunc().(Attribute).getObject() | - superCall.getFunc().(Name).getId() = "super" - ) - ) - } + predicate isSuperCall() { + exists(Function f | f = this.getScope() and f.getName() = "__del__" | + // We pass in `self` as the first argument... + f.getArg(0).asName().getVariable() = this.getArg(0).(Name).getVariable() + or + // ... or the call is of the form `super(Type, self).__del__()`, or the equivalent + // Python 3: `super().__del__()`. + exists(Call superCall | superCall = this.getFunc().(Attribute).getObject() | + superCall.getFunc().(Name).getId() = "super" + ) + ) + } } from DelCall del diff --git a/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll index 8c3917e15c3..4941a5f4f1f 100644 --- a/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll +++ b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll @@ -2,125 +2,125 @@ import python /** A string constant that looks like it may be used in string formatting operations. */ library class PossibleAdvancedFormatString extends StrConst { - PossibleAdvancedFormatString() { this.getText().matches("%{%}%") } + PossibleAdvancedFormatString() { this.getText().matches("%{%}%") } - private predicate field(int start, int end) { - brace_pair(this, start, end) and - this.getText().substring(start, end) != "{{}}" - } + private predicate field(int start, int end) { + brace_pair(this, start, end) and + this.getText().substring(start, end) != "{{}}" + } - /** Gets the number of the formatting field at [start, end) */ - int getFieldNumber(int start, int end) { - result = this.fieldId(start, end).toInt() - or - this.implicitlyNumberedField(start, end) and - result = count(int s | this.implicitlyNumberedField(s, _) and s < start) - } + /** Gets the number of the formatting field at [start, end) */ + int getFieldNumber(int start, int end) { + result = this.fieldId(start, end).toInt() + or + this.implicitlyNumberedField(start, end) and + result = count(int s | this.implicitlyNumberedField(s, _) and s < start) + } - /** Gets the text of the formatting field at [start, end) */ - string getField(int start, int end) { - this.field(start, end) and - result = this.getText().substring(start, end) - } + /** Gets the text of the formatting field at [start, end) */ + string getField(int start, int end) { + this.field(start, end) and + result = this.getText().substring(start, end) + } - private string fieldId(int start, int end) { - this.field(start, end) and - ( - result = this.getText().substring(start, end).regexpCapture("\\{([^!:.\\[]+)[!:.\\[].*", 1) - or - result = this.getText().substring(start + 1, end - 1) and result.regexpMatch("[^!:.\\[]+") - ) - } + private string fieldId(int start, int end) { + this.field(start, end) and + ( + result = this.getText().substring(start, end).regexpCapture("\\{([^!:.\\[]+)[!:.\\[].*", 1) + or + result = this.getText().substring(start + 1, end - 1) and result.regexpMatch("[^!:.\\[]+") + ) + } - /** Gets the name of the formatting field at [start, end) */ - string getFieldName(int start, int end) { - result = this.fieldId(start, end) and - not exists(this.getFieldNumber(start, end)) - } + /** Gets the name of the formatting field at [start, end) */ + string getFieldName(int start, int end) { + result = this.fieldId(start, end) and + not exists(this.getFieldNumber(start, end)) + } - private predicate implicitlyNumberedField(int start, int end) { - this.field(start, end) and - exists(string c | start + 1 = this.getText().indexOf(c) | - c = "}" or c = ":" or c = "!" or c = "." - ) - } + private predicate implicitlyNumberedField(int start, int end) { + this.field(start, end) and + exists(string c | start + 1 = this.getText().indexOf(c) | + c = "}" or c = ":" or c = "!" or c = "." + ) + } - /** Whether this format string has implicitly numbered fields */ - predicate isImplicitlyNumbered() { this.implicitlyNumberedField(_, _) } + /** Whether this format string has implicitly numbered fields */ + predicate isImplicitlyNumbered() { this.implicitlyNumberedField(_, _) } - /** Whether this format string has explicitly numbered fields */ - predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) } + /** Whether this format string has explicitly numbered fields */ + predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) } } /** Holds if the formatting string `fmt` contains a sequence of braces `{` of length `len`, beginning at index `index`. */ predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) { - exists(string text | text = fmt.getText() | - text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1 - or - text.charAt(index) = "{" and - text.charAt(index - 1) = "{" and - brace_sequence(fmt, index - 1, len - 1) - ) + exists(string text | text = fmt.getText() | + text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1 + or + text.charAt(index) = "{" and + text.charAt(index - 1) = "{" and + brace_sequence(fmt, index - 1, len - 1) + ) } /** Holds if index `index` in the format string `fmt` contains an escaped brace `{`. */ predicate escaped_brace(PossibleAdvancedFormatString fmt, int index) { - exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0) + exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0) } /** Holds if index `index` in the format string `fmt` contains a left brace `{` that acts as an escape character. */ predicate escaping_brace(PossibleAdvancedFormatString fmt, int index) { - escaped_brace(fmt, index + 1) + escaped_brace(fmt, index + 1) } private predicate inner_brace_pair(PossibleAdvancedFormatString fmt, int start, int end) { - not escaping_brace(fmt, start) and - not escaped_brace(fmt, start) and - fmt.getText().charAt(start) = "{" and - exists(string pair | - pair = fmt.getText().suffix(start).regexpCapture("(?s)(\\{([^{}]|\\{\\{)*+\\}).*", 1) - | - end = start + pair.length() - ) + not escaping_brace(fmt, start) and + not escaped_brace(fmt, start) and + fmt.getText().charAt(start) = "{" and + exists(string pair | + pair = fmt.getText().suffix(start).regexpCapture("(?s)(\\{([^{}]|\\{\\{)*+\\}).*", 1) + | + end = start + pair.length() + ) } private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int end) { - inner_brace_pair(fmt, start, end) - or - not escaping_brace(fmt, start) and - not escaped_brace(fmt, start) and - exists(string prefix, string postfix, int innerstart, int innerend | - brace_pair(fmt, innerstart, innerend) and - prefix = fmt.getText().regexpFind("\\{([^{}]|\\{\\{)+\\{", _, start) and - innerstart = start + prefix.length() - 1 and - postfix = fmt.getText().regexpFind("\\}([^{}]|\\}\\})*\\}", _, innerend - 1) and - end = innerend + postfix.length() - 1 - ) + inner_brace_pair(fmt, start, end) + or + not escaping_brace(fmt, start) and + not escaped_brace(fmt, start) and + exists(string prefix, string postfix, int innerstart, int innerend | + brace_pair(fmt, innerstart, innerend) and + prefix = fmt.getText().regexpFind("\\{([^{}]|\\{\\{)+\\{", _, start) and + innerstart = start + prefix.length() - 1 and + postfix = fmt.getText().regexpFind("\\}([^{}]|\\}\\})*\\}", _, innerend - 1) and + end = innerend + postfix.length() - 1 + ) } private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) { - exists(CallNode call | call = format_expr.getAFlowNode() | - call.getFunction().pointsTo(Value::named("format")) and - call.getArg(0).pointsTo(_, fmt.getAFlowNode()) and - args = count(format_expr.getAnArg()) - 1 - or - call.getFunction().(AttrNode).getObject("format").pointsTo(_, fmt.getAFlowNode()) and - args = count(format_expr.getAnArg()) - ) + exists(CallNode call | call = format_expr.getAFlowNode() | + call.getFunction().pointsTo(Value::named("format")) and + call.getArg(0).pointsTo(_, fmt.getAFlowNode()) and + args = count(format_expr.getAnArg()) - 1 + or + call.getFunction().(AttrNode).getObject("format").pointsTo(_, fmt.getAFlowNode()) and + args = count(format_expr.getAnArg()) + ) } /** A string constant that has the `format` method applied to it. */ class AdvancedFormatString extends PossibleAdvancedFormatString { - AdvancedFormatString() { advanced_format_call(_, this, _) } + AdvancedFormatString() { advanced_format_call(_, this, _) } } /** A string formatting operation that uses the `format` method. */ class AdvancedFormattingCall extends Call { - AdvancedFormattingCall() { advanced_format_call(this, _, _) } + AdvancedFormattingCall() { advanced_format_call(this, _, _) } - /** Count of the arguments actually provided */ - int providedArgCount() { advanced_format_call(this, _, result) } + /** Count of the arguments actually provided */ + int providedArgCount() { advanced_format_call(this, _, result) } - /** Gets a formatting string for this call. */ - AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) } + /** Gets a formatting string for this call. */ + AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) } } diff --git a/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql index 89af180099d..43c0348a8bd 100644 --- a/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql +++ b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql @@ -18,11 +18,11 @@ int field_count(AdvancedFormatString fmt) { result = max(fmt.getFieldNumber(_, _ from AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field where - arg_count = call.providedArgCount() and - max_field = field_count(fmt) and - call.getAFormat() = fmt and - not exists(call.getStarargs()) and - forall(AdvancedFormatString other | other = call.getAFormat() | field_count(other) < arg_count) + arg_count = call.providedArgCount() and + max_field = field_count(fmt) and + call.getAFormat() = fmt and + not exists(call.getStarargs()) and + forall(AdvancedFormatString other | other = call.getAFormat() | field_count(other) < arg_count) select call, - "Too many arguments for string format. Format $@ requires only " + max_field + ", but " + - arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\"" + "Too many arguments for string format. Format $@ requires only " + max_field + ", but " + + arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\"" diff --git a/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql index 62c598a397e..d5aac3aaab2 100644 --- a/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql +++ b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql @@ -15,17 +15,17 @@ import AdvancedFormatting from AdvancedFormattingCall call, AdvancedFormatString fmt, string name, string fmt_repr where - call.getAFormat() = fmt and - name = call.getAKeyword().getArg() and - forall(AdvancedFormatString format | format = call.getAFormat() | - not format.getFieldName(_, _) = name - ) and - not exists(call.getKwargs()) and - ( - strictcount(call.getAFormat()) = 1 and fmt_repr = "format \"" + fmt.getText() + "\"" - or - strictcount(call.getAFormat()) != 1 and fmt_repr = "any format used." - ) + call.getAFormat() = fmt and + name = call.getAKeyword().getArg() and + forall(AdvancedFormatString format | format = call.getAFormat() | + not format.getFieldName(_, _) = name + ) and + not exists(call.getKwargs()) and + ( + strictcount(call.getAFormat()) = 1 and fmt_repr = "format \"" + fmt.getText() + "\"" + or + strictcount(call.getAFormat()) != 1 and fmt_repr = "any format used." + ) select call, - "Surplus named argument for string format. An argument named '" + name + - "' is provided, but it is not required by $@.", fmt, fmt_repr + "Surplus named argument for string format. An argument named '" + name + + "' is provided, but it is not required by $@.", fmt, fmt_repr diff --git a/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql index 384d9b9d58e..1cc1e4a9455 100644 --- a/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql +++ b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql @@ -16,10 +16,10 @@ import AdvancedFormatting from AdvancedFormattingCall call, AdvancedFormatString fmt, string name where - call.getAFormat() = fmt and - not name = call.getAKeyword().getArg() and - fmt.getFieldName(_, _) = name and - not exists(call.getKwargs()) + call.getAFormat() = fmt and + not name = call.getAKeyword().getArg() and + fmt.getFieldName(_, _) = name and + not exists(call.getKwargs()) select call, - "Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.", - fmt, "\"" + fmt.getText() + "\"" + "Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.", + fmt, "\"" + fmt.getText() + "\"" diff --git a/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql index 8f3479c5be5..e120cd6b5bb 100644 --- a/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql +++ b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql @@ -15,15 +15,15 @@ import python import AdvancedFormatting from - AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field, - string provided + AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field, + string provided where - arg_count = call.providedArgCount() and - max_field = max(fmt.getFieldNumber(_, _)) and - call.getAFormat() = fmt and - not exists(call.getStarargs()) and - arg_count <= max_field and - (if arg_count = 1 then provided = " is provided." else provided = " are provided.") + arg_count = call.providedArgCount() and + max_field = max(fmt.getFieldNumber(_, _)) and + call.getAFormat() = fmt and + not exists(call.getStarargs()) and + arg_count <= max_field and + (if arg_count = 1 then provided = " is provided." else provided = " are provided.") select call, - "Too few arguments for string format. Format $@ requires at least " + (max_field + 1) + ", but " + - arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\"" + "Too few arguments for string format. Format $@ requires at least " + (max_field + 1) + ", but " + + arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\"" diff --git a/python/ql/src/Expressions/HashedButNoHash.ql b/python/ql/src/Expressions/HashedButNoHash.ql index 7fbb723fc54..336c344fa37 100644 --- a/python/ql/src/Expressions/HashedButNoHash.ql +++ b/python/ql/src/Expressions/HashedButNoHash.ql @@ -19,39 +19,39 @@ import python */ predicate numpy_array_type(ClassValue na) { - exists(ModuleValue np | np.getName() = "numpy" or np.getName() = "numpy.core" | - na.getASuperType() = np.attr("ndarray") - ) + exists(ModuleValue np | np.getName() = "numpy" or np.getName() = "numpy.core" | + na.getASuperType() = np.attr("ndarray") + ) } predicate has_custom_getitem(Value v) { - v.getClass().lookup("__getitem__") instanceof PythonFunctionValue - or - numpy_array_type(v.getClass()) + v.getClass().lookup("__getitem__") instanceof PythonFunctionValue + or + numpy_array_type(v.getClass()) } predicate explicitly_hashed(ControlFlowNode f) { - exists(CallNode c, GlobalVariable hash | - c.getArg(0) = f and c.getFunction().(NameNode).uses(hash) and hash.getId() = "hash" - ) + exists(CallNode c, GlobalVariable hash | + c.getArg(0) = f and c.getFunction().(NameNode).uses(hash) and hash.getId() = "hash" + ) } predicate unhashable_subscript(ControlFlowNode f, ClassValue c, ControlFlowNode origin) { - is_unhashable(f, c, origin) and - exists(SubscriptNode sub | sub.getIndex() = f | - exists(Value custom_getitem | - sub.getObject().pointsTo(custom_getitem) and - not has_custom_getitem(custom_getitem) - ) + is_unhashable(f, c, origin) and + exists(SubscriptNode sub | sub.getIndex() = f | + exists(Value custom_getitem | + sub.getObject().pointsTo(custom_getitem) and + not has_custom_getitem(custom_getitem) ) + ) } predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origin) { - exists(Value v | f.pointsTo(v, origin) and v.getClass() = cls | - not cls.hasAttribute("__hash__") and not cls.failedInference(_) and cls.isNewStyle() - or - cls.lookup("__hash__") = Value::named("None") - ) + exists(Value v | f.pointsTo(v, origin) and v.getClass() = cls | + not cls.hasAttribute("__hash__") and not cls.failedInference(_) and cls.isNewStyle() + or + cls.lookup("__hash__") = Value::named("None") + ) } /** @@ -68,18 +68,18 @@ predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origi * it. */ predicate typeerror_is_caught(ControlFlowNode f) { - exists(Try try | - try.getBody().contains(f.getNode()) and - try.getAHandler().getType().pointsTo(ClassValue::typeError()) - ) + exists(Try try | + try.getBody().contains(f.getNode()) and + try.getAHandler().getType().pointsTo(ClassValue::typeError()) + ) } from ControlFlowNode f, ClassValue c, ControlFlowNode origin where - not typeerror_is_caught(f) and - ( - explicitly_hashed(f) and is_unhashable(f, c, origin) - or - unhashable_subscript(f, c, origin) - ) + not typeerror_is_caught(f) and + ( + explicitly_hashed(f) and is_unhashable(f, c, origin) + or + unhashable_subscript(f, c, origin) + ) select f.getNode(), "This $@ of $@ is unhashable.", origin, "instance", c, c.getQualifiedName() diff --git a/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql b/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql index 5dda5b857f9..ab45d6c15d9 100644 --- a/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql +++ b/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql @@ -15,13 +15,13 @@ import IsComparisons from Compare comp, Cmpop op, ClassValue c, string alt where - invalid_portable_is_comparison(comp, op, c) and - not cpython_interned_constant(comp.getASubExpression()) and - ( - op instanceof Is and alt = "==" - or - op instanceof IsNot and alt = "!=" - ) + invalid_portable_is_comparison(comp, op, c) and + not cpython_interned_constant(comp.getASubExpression()) and + ( + op instanceof Is and alt = "==" + or + op instanceof IsNot and alt = "!=" + ) select comp, - "Values compared using '" + op.getSymbol() + - "' when equivalence is not the same as identity. Use '" + alt + "' instead." + "Values compared using '" + op.getSymbol() + + "' when equivalence is not the same as identity. Use '" + alt + "' instead." diff --git a/python/ql/src/Expressions/IsComparisons.qll b/python/ql/src/Expressions/IsComparisons.qll index a8ce9982859..b6f06b30108 100644 --- a/python/ql/src/Expressions/IsComparisons.qll +++ b/python/ql/src/Expressions/IsComparisons.qll @@ -4,60 +4,60 @@ import python /** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */ predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) { - exists(CompareNode fcomp | fcomp = comp.getAFlowNode() | - fcomp.operands(left, op, right) and - (op instanceof Is or op instanceof IsNot) - ) + exists(CompareNode fcomp | fcomp = comp.getAFlowNode() | + fcomp.operands(left, op, right) and + (op instanceof Is or op instanceof IsNot) + ) } /** Holds if the class `c` overrides the default notion of equality or comparison. */ predicate overrides_eq_or_cmp(ClassValue c) { - major_version() = 2 and c.hasAttribute("__eq__") - or - c.declaresAttribute("__eq__") and not c = Value::named("object") - or - exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | - sup.declaresAttribute("__eq__") - ) - or - major_version() = 2 and c.hasAttribute("__cmp__") + major_version() = 2 and c.hasAttribute("__eq__") + or + c.declaresAttribute("__eq__") and not c = Value::named("object") + or + exists(ClassValue sup | sup = c.getASuperType() and not sup = Value::named("object") | + sup.declaresAttribute("__eq__") + ) + or + major_version() = 2 and c.hasAttribute("__cmp__") } /** Holds if the class `cls` is likely to only have a single instance throughout the program. */ predicate probablySingleton(ClassValue cls) { - strictcount(Value inst | inst.getClass() = cls) = 1 - or - cls = Value::named("None").getClass() + strictcount(Value inst | inst.getClass() = cls) = 1 + or + cls = Value::named("None").getClass() } /** Holds if using `is` to compare instances of the class `c` is likely to cause unexpected behavior. */ predicate invalid_to_use_is_portably(ClassValue c) { - overrides_eq_or_cmp(c) and - // Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__ - not c = Value::named("type") and - not c = ClassValue::builtinFunction() and - not c = Value::named("bool") and - // OK to compare with 'is' if a singleton - not probablySingleton(c) + overrides_eq_or_cmp(c) and + // Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__ + not c = Value::named("type") and + not c = ClassValue::builtinFunction() and + not c = Value::named("bool") and + // OK to compare with 'is' if a singleton + not probablySingleton(c) } /** Holds if the control flow node `f` points to either `True`, `False`, or `None`. */ predicate simple_constant(ControlFlowNode f) { - exists(Value val | f.pointsTo(val) | - val = Value::named("True") or val = Value::named("False") or val = Value::named("None") - ) + exists(Value val | f.pointsTo(val) | + val = Value::named("True") or val = Value::named("False") or val = Value::named("None") + ) } private predicate cpython_interned_value(Expr e) { - exists(string text | text = e.(StrConst).getText() | - text.length() = 0 - or - text.length() = 1 and text.regexpMatch("[U+0000-U+00ff]") - ) + exists(string text | text = e.(StrConst).getText() | + text.length() = 0 or - exists(int i | i = e.(IntegerLiteral).getN().toInt() | -5 <= i and i <= 256) - or - exists(Tuple t | t = e and not exists(t.getAnElt())) + text.length() = 1 and text.regexpMatch("[U+0000-U+00ff]") + ) + or + exists(int i | i = e.(IntegerLiteral).getN().toInt() | -5 <= i and i <= 256) + or + exists(Tuple t | t = e and not exists(t.getAnElt())) } /** @@ -66,83 +66,83 @@ private predicate cpython_interned_value(Expr e) { * follow CPython, but it varies, so this is a best guess. */ private predicate universally_interned_value(Expr e) { - e.(IntegerLiteral).getN().toInt() = 0 - or - exists(Tuple t | t = e and not exists(t.getAnElt())) - or - e.(StrConst).getText() = "" + e.(IntegerLiteral).getN().toInt() = 0 + or + exists(Tuple t | t = e and not exists(t.getAnElt())) + or + e.(StrConst).getText() = "" } /** Holds if the expression `e` points to an interned constant in CPython. */ predicate cpython_interned_constant(Expr e) { - exists(Expr const | e.pointsTo(_, const) | cpython_interned_value(const)) + exists(Expr const | e.pointsTo(_, const) | cpython_interned_value(const)) } /** Holds if the expression `e` points to a value that can be reasonably expected to be interned across all implementations of Python. */ predicate universally_interned_constant(Expr e) { - exists(Expr const | e.pointsTo(_, const) | universally_interned_value(const)) + exists(Expr const | e.pointsTo(_, const) | universally_interned_value(const)) } private predicate comparison_both_types(Compare comp, Cmpop op, ClassValue cls1, ClassValue cls2) { - exists(ControlFlowNode op1, ControlFlowNode op2 | - comparison_using_is(comp, op1, op, op2) or comparison_using_is(comp, op2, op, op1) - | - op1.inferredValue().getClass() = cls1 and - op2.inferredValue().getClass() = cls2 - ) + exists(ControlFlowNode op1, ControlFlowNode op2 | + comparison_using_is(comp, op1, op, op2) or comparison_using_is(comp, op2, op, op1) + | + op1.inferredValue().getClass() = cls1 and + op2.inferredValue().getClass() = cls2 + ) } private predicate comparison_one_type(Compare comp, Cmpop op, ClassValue cls) { - not comparison_both_types(comp, _, _, _) and - exists(ControlFlowNode operand | - comparison_using_is(comp, operand, op, _) or comparison_using_is(comp, _, op, operand) - | - operand.inferredValue().getClass() = cls - ) + not comparison_both_types(comp, _, _, _) and + exists(ControlFlowNode operand | + comparison_using_is(comp, operand, op, _) or comparison_using_is(comp, _, op, operand) + | + operand.inferredValue().getClass() = cls + ) } /** -* Holds if using `is` or `is not` as the operator `op` in the comparison `comp` would be invalid when applied to the class `cls`. + * Holds if using `is` or `is not` as the operator `op` in the comparison `comp` would be invalid when applied to the class `cls`. */ predicate invalid_portable_is_comparison(Compare comp, Cmpop op, ClassValue cls) { - // OK to use 'is' when defining '__eq__' - not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" | - eq = comp.getScope().getScope*() - ) and - ( - comparison_one_type(comp, op, cls) and invalid_to_use_is_portably(cls) - or - exists(ClassValue other | comparison_both_types(comp, op, cls, other) | - invalid_to_use_is_portably(cls) and - invalid_to_use_is_portably(other) - ) - ) and - // OK to use 'is' when comparing items from a known set of objects - not exists(Expr left, Expr right, Value val | - comp.compares(left, op, right) and - exists(ImmutableLiteral il | il.getLiteralValue() = val) - | - left.pointsTo(val) and right.pointsTo(val) - or - // Simple constant in module, probably some sort of sentinel - exists(AstNode origin | - not left.pointsTo(_) and - right.pointsTo(val, origin) and - origin.getScope().getEnclosingModule() = comp.getScope().getEnclosingModule() - ) - ) and - // OK to use 'is' when comparing with a member of an enum - not exists(Expr left, Expr right, AstNode origin | - comp.compares(left, op, right) and - enum_member(origin) - | - left.pointsTo(_, origin) or right.pointsTo(_, origin) + // OK to use 'is' when defining '__eq__' + not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" | + eq = comp.getScope().getScope*() + ) and + ( + comparison_one_type(comp, op, cls) and invalid_to_use_is_portably(cls) + or + exists(ClassValue other | comparison_both_types(comp, op, cls, other) | + invalid_to_use_is_portably(cls) and + invalid_to_use_is_portably(other) ) + ) and + // OK to use 'is' when comparing items from a known set of objects + not exists(Expr left, Expr right, Value val | + comp.compares(left, op, right) and + exists(ImmutableLiteral il | il.getLiteralValue() = val) + | + left.pointsTo(val) and right.pointsTo(val) + or + // Simple constant in module, probably some sort of sentinel + exists(AstNode origin | + not left.pointsTo(_) and + right.pointsTo(val, origin) and + origin.getScope().getEnclosingModule() = comp.getScope().getEnclosingModule() + ) + ) and + // OK to use 'is' when comparing with a member of an enum + not exists(Expr left, Expr right, AstNode origin | + comp.compares(left, op, right) and + enum_member(origin) + | + left.pointsTo(_, origin) or right.pointsTo(_, origin) + ) } private predicate enum_member(AstNode obj) { - exists(ClassValue cls, AssignStmt asgn | cls.getASuperType().getName() = "Enum" | - cls.getScope() = asgn.getScope() and - asgn.getValue() = obj - ) + exists(ClassValue cls, AssignStmt asgn | cls.getASuperType().getName() = "Enum" | + cls.getScope() = asgn.getScope() and + asgn.getValue() = obj + ) } diff --git a/python/ql/src/Expressions/NonCallableCalled.ql b/python/ql/src/Expressions/NonCallableCalled.ql index fdd0bbd13c3..aed13af8f63 100644 --- a/python/ql/src/Expressions/NonCallableCalled.ql +++ b/python/ql/src/Expressions/NonCallableCalled.ql @@ -16,12 +16,12 @@ import Exceptions.NotImplemented from Call c, Value v, ClassValue t, Expr f, AstNode origin where - f = c.getFunc() and - f.pointsTo(v, origin) and - t = v.getClass() and - not t.isCallable() and - not t.failedInference(_) and - not t.hasAttribute("__get__") and - not v = Value::named("None") and - not use_of_not_implemented_in_raise(_, f) + f = c.getFunc() and + f.pointsTo(v, origin) and + t = v.getClass() and + not t.isCallable() and + not t.failedInference(_) and + not t.hasAttribute("__get__") and + not v = Value::named("None") and + not use_of_not_implemented_in_raise(_, f) select c, "Call to a $@ of $@.", origin, "non-callable", t, t.toString() diff --git a/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql b/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql index 3e01ccdacf7..db266020aeb 100644 --- a/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql +++ b/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql @@ -15,11 +15,11 @@ import IsComparisons from Compare comp, Cmpop op, ClassValue c where - invalid_portable_is_comparison(comp, op, c) and - exists(Expr sub | sub = comp.getASubExpression() | - cpython_interned_constant(sub) and - not universally_interned_constant(sub) - ) + invalid_portable_is_comparison(comp, op, c) and + exists(Expr sub | sub = comp.getASubExpression() | + cpython_interned_constant(sub) and + not universally_interned_constant(sub) + ) select comp, - "The result of this comparison with '" + op.getSymbol() + - "' may differ between implementations of Python." + "The result of this comparison with '" + op.getSymbol() + + "' may differ between implementations of Python." diff --git a/python/ql/src/Expressions/RedundantComparison.qll b/python/ql/src/Expressions/RedundantComparison.qll index 6157173020b..a0d4f906501 100644 --- a/python/ql/src/Expressions/RedundantComparison.qll +++ b/python/ql/src/Expressions/RedundantComparison.qll @@ -4,53 +4,54 @@ import python /** A comparison where the left and right hand sides appear to be identical. */ class RedundantComparison extends Compare { - RedundantComparison() { - exists(Expr left, Expr right | - this.compares(left, _, right) and - same_variable(left, right) - ) - } + RedundantComparison() { + exists(Expr left, Expr right | + this.compares(left, _, right) and + same_variable(left, right) + ) + } - /** Holds if this comparison could be redundant due to a missing `self.`, for example - * ```python - * foo == foo - * ``` - * instead of - * ```python - * self.foo == foo - * ``` - */ - predicate maybeMissingSelf() { - exists(Name left | - this.compares(left, _, _) and - not this.isConstant() and - exists(Class cls | left.getScope().getScope() = cls | - exists(SelfAttribute sa | sa.getName() = left.getId() | sa.getClass() = cls) - ) - ) - } + /** + * Holds if this comparison could be redundant due to a missing `self.`, for example + * ```python + * foo == foo + * ``` + * instead of + * ```python + * self.foo == foo + * ``` + */ + predicate maybeMissingSelf() { + exists(Name left | + this.compares(left, _, _) and + not this.isConstant() and + exists(Class cls | left.getScope().getScope() = cls | + exists(SelfAttribute sa | sa.getName() = left.getId() | sa.getClass() = cls) + ) + ) + } } private predicate same_variable(Expr left, Expr right) { - same_name(left, right) - or - same_attribute(left, right) + same_name(left, right) + or + same_attribute(left, right) } private predicate name_in_comparison(Compare comp, Name n, Variable v) { - comp.contains(n) and v = n.getVariable() + comp.contains(n) and v = n.getVariable() } private predicate same_name(Name n1, Name n2) { - n1 != n2 and - exists(Compare comp, Variable v | - name_in_comparison(comp, n1, v) and name_in_comparison(comp, n2, v) - ) + n1 != n2 and + exists(Compare comp, Variable v | + name_in_comparison(comp, n1, v) and name_in_comparison(comp, n2, v) + ) } private predicate same_attribute(Attribute a1, Attribute a2) { - a1 != a2 and - exists(Compare comp | comp.contains(a1) and comp.contains(a2)) and - a1.getName() = a2.getName() and - same_name(a1.getObject(), a2.getObject()) + a1 != a2 and + exists(Compare comp | comp.contains(a1) and comp.contains(a2)) and + a1.getName() = a2.getName() and + same_name(a1.getObject(), a2.getObject()) } diff --git a/python/ql/src/Expressions/Regex/BackspaceEscape.ql b/python/ql/src/Expressions/Regex/BackspaceEscape.ql index b18d581257a..ce69dabec44 100644 --- a/python/ql/src/Expressions/Regex/BackspaceEscape.ql +++ b/python/ql/src/Expressions/Regex/BackspaceEscape.ql @@ -15,7 +15,7 @@ import semmle.python.regex from Regex r, int offset where - r.escapingChar(offset) and - r.getChar(offset + 1) = "b" and - exists(int start, int end | start < offset and end > offset | r.charSet(start, end)) + r.escapingChar(offset) and + r.getChar(offset + 1) = "b" and + exists(int start, int end | start < offset and end > offset | r.charSet(start, end)) select r, "Backspace escape in regular expression at offset " + offset + "." diff --git a/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql index 42a745affb8..895c8714ddf 100644 --- a/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql +++ b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql @@ -14,29 +14,29 @@ import python import semmle.python.regex predicate duplicate_char_in_class(Regex r, string char) { - exists(int i, int j, int x, int y, int start, int end | - i != x and - j != y and - start < i and - j < end and - start < x and - y < end and - r.character(i, j) and - char = r.getText().substring(i, j) and - r.character(x, y) and - char = r.getText().substring(x, y) and - r.charSet(start, end) - ) and - /* Exclude � as we use it for any unencodable character */ - char != "�" and - //Ignore whitespace in verbose mode - not ( - r.getAMode() = "VERBOSE" and - (char = " " or char = "\t" or char = "\r" or char = "\n") - ) + exists(int i, int j, int x, int y, int start, int end | + i != x and + j != y and + start < i and + j < end and + start < x and + y < end and + r.character(i, j) and + char = r.getText().substring(i, j) and + r.character(x, y) and + char = r.getText().substring(x, y) and + r.charSet(start, end) + ) and + /* Exclude � as we use it for any unencodable character */ + char != "�" and + //Ignore whitespace in verbose mode + not ( + r.getAMode() = "VERBOSE" and + (char = " " or char = "\t" or char = "\r" or char = "\n") + ) } from Regex r, string char where duplicate_char_in_class(r, char) select r, - "This regular expression includes duplicate character '" + char + "' in a set of characters." + "This regular expression includes duplicate character '" + char + "' in a set of characters." diff --git a/python/ql/src/Expressions/Regex/UnmatchableCaret.ql b/python/ql/src/Expressions/Regex/UnmatchableCaret.ql index 7a5c087ec02..f954169ae02 100644 --- a/python/ql/src/Expressions/Regex/UnmatchableCaret.ql +++ b/python/ql/src/Expressions/Regex/UnmatchableCaret.ql @@ -14,13 +14,13 @@ import python import semmle.python.regex predicate unmatchable_caret(Regex r, int start) { - not r.getAMode() = "MULTILINE" and - not r.getAMode() = "VERBOSE" and - r.specialCharacter(start, start + 1, "^") and - not r.firstItem(start, start + 1) + not r.getAMode() = "MULTILINE" and + not r.getAMode() = "VERBOSE" and + r.specialCharacter(start, start + 1, "^") and + not r.firstItem(start, start + 1) } from Regex r, int offset where unmatchable_caret(r, offset) select r, - "This regular expression includes an unmatchable caret at offset " + offset.toString() + "." + "This regular expression includes an unmatchable caret at offset " + offset.toString() + "." diff --git a/python/ql/src/Expressions/Regex/UnmatchableDollar.ql b/python/ql/src/Expressions/Regex/UnmatchableDollar.ql index dfd2bfcf893..3f9457f5bd2 100644 --- a/python/ql/src/Expressions/Regex/UnmatchableDollar.ql +++ b/python/ql/src/Expressions/Regex/UnmatchableDollar.ql @@ -14,13 +14,13 @@ import python import semmle.python.regex predicate unmatchable_dollar(Regex r, int start) { - not r.getAMode() = "MULTILINE" and - not r.getAMode() = "VERBOSE" and - r.specialCharacter(start, start + 1, "$") and - not r.lastItem(start, start + 1) + not r.getAMode() = "MULTILINE" and + not r.getAMode() = "VERBOSE" and + r.specialCharacter(start, start + 1, "$") and + not r.lastItem(start, start + 1) } from Regex r, int offset where unmatchable_dollar(r, offset) select r, - "This regular expression includes an unmatchable dollar at offset " + offset.toString() + "." + "This regular expression includes an unmatchable dollar at offset " + offset.toString() + "." diff --git a/python/ql/src/Expressions/TruncatedDivision.ql b/python/ql/src/Expressions/TruncatedDivision.ql index 399435dbabf..0904081f5ff 100644 --- a/python/ql/src/Expressions/TruncatedDivision.ql +++ b/python/ql/src/Expressions/TruncatedDivision.ql @@ -15,23 +15,23 @@ import python from BinaryExpr div, ControlFlowNode left, ControlFlowNode right where - // Only relevant for Python 2, as all later versions implement true division - major_version() = 2 and - exists(BinaryExprNode bin, Value lval, Value rval | - bin = div.getAFlowNode() and - bin.getNode().getOp() instanceof Div and - bin.getLeft().pointsTo(lval, left) and - lval.getClass() = ClassValue::int_() and - bin.getRight().pointsTo(rval, right) and - rval.getClass() = ClassValue::int_() and - // Ignore instances where integer division leaves no remainder - not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0 and - not bin.getNode().getEnclosingModule().hasFromFuture("division") and - // Filter out results wrapped in `int(...)` - not exists(CallNode c | - c = ClassValue::int_().getACall() and - c.getAnArg() = bin - ) + // Only relevant for Python 2, as all later versions implement true division + major_version() = 2 and + exists(BinaryExprNode bin, Value lval, Value rval | + bin = div.getAFlowNode() and + bin.getNode().getOp() instanceof Div and + bin.getLeft().pointsTo(lval, left) and + lval.getClass() = ClassValue::int_() and + bin.getRight().pointsTo(rval, right) and + rval.getClass() = ClassValue::int_() and + // Ignore instances where integer division leaves no remainder + not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0 and + not bin.getNode().getEnclosingModule().hasFromFuture("division") and + // Filter out results wrapped in `int(...)` + not exists(CallNode c | + c = ClassValue::int_().getACall() and + c.getAnArg() = bin ) + ) select div, "Result of division may be truncated as its $@ and $@ arguments may both be integers.", - left.getLocation(), "left", right.getLocation(), "right" + left.getLocation(), "left", right.getLocation(), "right" diff --git a/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql index 8199be8a051..9547d0045ca 100644 --- a/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql +++ b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql @@ -15,20 +15,20 @@ import python predicate string_const(Expr s) { - s instanceof StrConst - or - string_const(s.(BinaryExpr).getLeft()) and string_const(s.(BinaryExpr).getRight()) + s instanceof StrConst + or + string_const(s.(BinaryExpr).getLeft()) and string_const(s.(BinaryExpr).getRight()) } from StrConst s where - // Implicitly concatenated string is in a list and that list contains at least one other string. - exists(List l, Expr other | - not s = other and - l.getAnElt() = s and - l.getAnElt() = other and - string_const(other) - ) and - exists(s.getAnImplicitlyConcatenatedPart()) and - not s.isParenthesized() + // Implicitly concatenated string is in a list and that list contains at least one other string. + exists(List l, Expr other | + not s = other and + l.getAnElt() = s and + l.getAnElt() = other and + string_const(other) + ) and + exists(s.getAnImplicitlyConcatenatedPart()) and + not s.isParenthesized() select s, "Implicit string concatenation. Maybe missing a comma?" diff --git a/python/ql/src/Expressions/UnnecessaryLambda.ql b/python/ql/src/Expressions/UnnecessaryLambda.ql index 2b927973015..7486e27d695 100644 --- a/python/ql/src/Expressions/UnnecessaryLambda.ql +++ b/python/ql/src/Expressions/UnnecessaryLambda.ql @@ -14,48 +14,48 @@ import python /* f consists of a single return statement, whose value is a call. The arguments of the call are exactly the parameters of f */ predicate simple_wrapper(Lambda l, Expr wrapped) { - exists(Function f, Call c | f = l.getInnerScope() and c = l.getExpression() | - wrapped = c.getFunc() and - count(f.getAnArg()) = count(c.getAnArg()) and - forall(int arg | exists(f.getArg(arg)) | f.getArgName(arg) = c.getArg(arg).(Name).getId()) and - /* Either no **kwargs or they must match */ - ( - not exists(f.getKwarg()) and not exists(c.getKwargs()) - or - f.getKwarg().(Name).getId() = c.getKwargs().(Name).getId() - ) and - /* Either no *args or they must match */ - ( - not exists(f.getVararg()) and not exists(c.getStarargs()) - or - f.getVararg().(Name).getId() = c.getStarargs().(Name).getId() - ) and - /* No named parameters in call */ - not exists(c.getAKeyword()) + exists(Function f, Call c | f = l.getInnerScope() and c = l.getExpression() | + wrapped = c.getFunc() and + count(f.getAnArg()) = count(c.getAnArg()) and + forall(int arg | exists(f.getArg(arg)) | f.getArgName(arg) = c.getArg(arg).(Name).getId()) and + /* Either no **kwargs or they must match */ + ( + not exists(f.getKwarg()) and not exists(c.getKwargs()) + or + f.getKwarg().(Name).getId() = c.getKwargs().(Name).getId() ) and - // f is not necessarily a drop-in replacement for the lambda if there are default argument values - not exists(l.getArgs().getADefault()) + /* Either no *args or they must match */ + ( + not exists(f.getVararg()) and not exists(c.getStarargs()) + or + f.getVararg().(Name).getId() = c.getStarargs().(Name).getId() + ) and + /* No named parameters in call */ + not exists(c.getAKeyword()) + ) and + // f is not necessarily a drop-in replacement for the lambda if there are default argument values + not exists(l.getArgs().getADefault()) } /* The expression called will refer to the same object if evaluated when the lambda is created or when the lambda is executed. */ predicate unnecessary_lambda(Lambda l, Expr e) { - simple_wrapper(l, e) and - ( - /* plain class */ - exists(ClassValue c | e.pointsTo(c)) - or - /* plain function */ - exists(FunctionValue f | e.pointsTo(f)) - or - /* bound-method of enclosing instance */ - exists(ClassValue cls, Attribute a | cls.getScope() = l.getScope().getScope() and a = e | - a.getObject().(Name).getId() = "self" and - cls.hasAttribute(a.getName()) - ) + simple_wrapper(l, e) and + ( + /* plain class */ + exists(ClassValue c | e.pointsTo(c)) + or + /* plain function */ + exists(FunctionValue f | e.pointsTo(f)) + or + /* bound-method of enclosing instance */ + exists(ClassValue cls, Attribute a | cls.getScope() = l.getScope().getScope() and a = e | + a.getObject().(Name).getId() = "self" and + cls.hasAttribute(a.getName()) ) + ) } from Lambda l, Expr e where unnecessary_lambda(l, e) select l, - "This 'lambda' is just a simple wrapper around a callable object. Use that object directly." + "This 'lambda' is just a simple wrapper around a callable object. Use that object directly." diff --git a/python/ql/src/Expressions/UseofInput.ql b/python/ql/src/Expressions/UseofInput.ql index dc67458a083..2b11eecfa2b 100644 --- a/python/ql/src/Expressions/UseofInput.ql +++ b/python/ql/src/Expressions/UseofInput.ql @@ -14,8 +14,8 @@ import python from CallNode call, Context context, ControlFlowNode func where - context.getAVersion().includes(2, _) and - call.getFunction() = func and - func.pointsTo(context, Value::named("input"), _) and - not func.pointsTo(context, Value::named("raw_input"), _) + context.getAVersion().includes(2, _) and + call.getFunction() = func and + func.pointsTo(context, Value::named("input"), _) and + not func.pointsTo(context, Value::named("raw_input"), _) select call, "The unsafe built-in function 'input' is used in Python 2." diff --git a/python/ql/src/Expressions/WrongNameForArgumentInCall.ql b/python/ql/src/Expressions/WrongNameForArgumentInCall.ql index 4800f898c54..053b0ef2ad2 100644 --- a/python/ql/src/Expressions/WrongNameForArgumentInCall.ql +++ b/python/ql/src/Expressions/WrongNameForArgumentInCall.ql @@ -18,10 +18,10 @@ import Expressions.CallArgs from Call call, FunctionObject func, string name where - illegally_named_parameter_objectapi(call, func, name) and - not func.isAbstract() and - not exists(FunctionObject overridden | - func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name - ) + illegally_named_parameter_objectapi(call, func, name) and + not func.isAbstract() and + not exists(FunctionObject overridden | + func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name + ) select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", func, - func.descriptiveString() + func.descriptiveString() diff --git a/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql index 39d265fe290..c9e751d58a0 100644 --- a/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql +++ b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql @@ -16,32 +16,32 @@ import python import semmle.python.strings predicate string_format(BinaryExpr operation, StrConst str, Value args, AstNode origin) { - operation.getOp() instanceof Mod and - exists(Value fmt, Context ctx | - operation.getLeft().pointsTo(ctx, fmt, str) and - operation.getRight().pointsTo(ctx, args, origin) - ) + operation.getOp() instanceof Mod and + exists(Value fmt, Context ctx | + operation.getLeft().pointsTo(ctx, fmt, str) and + operation.getRight().pointsTo(ctx, args, origin) + ) } int sequence_length(Value args) { - /* Guess length of sequence */ - exists(Tuple seq, AstNode origin | seq.pointsTo(args, origin) | - result = strictcount(seq.getAnElt()) and - not seq.getAnElt() instanceof Starred - ) - or - exists(ImmutableLiteral i | i.getLiteralValue() = args | result = 1) + /* Guess length of sequence */ + exists(Tuple seq, AstNode origin | seq.pointsTo(args, origin) | + result = strictcount(seq.getAnElt()) and + not seq.getAnElt() instanceof Starred + ) + or + exists(ImmutableLiteral i | i.getLiteralValue() = args | result = 1) } from - BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin, - string provided + BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin, + string provided where - string_format(operation, fmt, args, origin) and - slen = sequence_length(args) and - alen = format_items(fmt) and - slen != alen and - (if slen = 1 then provided = " is provided." else provided = " are provided.") + string_format(operation, fmt, args, origin) and + slen = sequence_length(args) and + alen = format_items(fmt) and + slen != alen and + (if slen = 1 then provided = " is provided." else provided = " are provided.") select operation, - "Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " + - slen.toString() + provided, origin, "arguments", fmt, fmt.getText() + "Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " + + slen.toString() + provided, origin, "arguments", fmt, fmt.getText() diff --git a/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql index 02bc685c096..ffebb000034 100644 --- a/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql +++ b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql @@ -16,15 +16,16 @@ import CallArgs from Call call, FunctionValue func, string too, string should, int limit where -( + ( too_many_args(call, func, limit) and too = "too many arguments" and should = "no more than " or too_few_args(call, func, limit) and too = "too few arguments" and should = "no fewer than " -) and -not isAbstract(func) and -not exists(FunctionValue overridden | func.overrides(overridden) and correct_args_if_called_as_method(call, overridden)) -/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */ -and not func.getName() = "__new__" - -select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func, func.descriptiveString() - + ) and + not isAbstract(func) and + not exists(FunctionValue overridden | + func.overrides(overridden) and correct_args_if_called_as_method(call, overridden) + ) and + /* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */ + not func.getName() = "__new__" +select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func, + func.descriptiveString() diff --git a/python/ql/src/Filters/ClassifyFiles.ql b/python/ql/src/Filters/ClassifyFiles.ql index 20062f0451f..4c9db8a8462 100644 --- a/python/ql/src/Filters/ClassifyFiles.ql +++ b/python/ql/src/Filters/ClassifyFiles.ql @@ -11,9 +11,9 @@ import semmle.python.filters.GeneratedCode import semmle.python.filters.Tests predicate classify(File f, string tag) { - f instanceof GeneratedFile and tag = "generated" - or - exists(TestScope t | t.getLocation().getFile() = f) and tag = "test" + f instanceof GeneratedFile and tag = "generated" + or + exists(TestScope t | t.getLocation().getFile() = f) and tag = "test" } from File f, string tag diff --git a/python/ql/src/Functions/ConsistentReturns.ql b/python/ql/src/Functions/ConsistentReturns.ql index 9e28dee36a3..f9d81c63936 100644 --- a/python/ql/src/Functions/ConsistentReturns.ql +++ b/python/ql/src/Functions/ConsistentReturns.ql @@ -13,21 +13,21 @@ import python predicate explicitly_returns_non_none(Function func) { - exists(Return return | - return.getScope() = func and - exists(Expr val | val = return.getValue() | not val instanceof None) - ) + exists(Return return | + return.getScope() = func and + exists(Expr val | val = return.getValue() | not val instanceof None) + ) } predicate has_implicit_return(Function func) { - exists(ControlFlowNode fallthru | - fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable() - ) - or - exists(Return return | return.getScope() = func and not exists(return.getValue())) + exists(ControlFlowNode fallthru | + fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable() + ) + or + exists(Return return | return.getScope() = func and not exists(return.getValue())) } from Function func where explicitly_returns_non_none(func) and has_implicit_return(func) select func, - "Mixing implicit and explicit returns may indicate an error as implicit returns always return None." + "Mixing implicit and explicit returns may indicate an error as implicit returns always return None." diff --git a/python/ql/src/Functions/DeprecatedSliceMethod.ql b/python/ql/src/Functions/DeprecatedSliceMethod.ql index c37f2195b54..2f3e8373b0b 100644 --- a/python/ql/src/Functions/DeprecatedSliceMethod.ql +++ b/python/ql/src/Functions/DeprecatedSliceMethod.ql @@ -12,13 +12,13 @@ import python predicate slice_method_name(string name) { - name = "__getslice__" or name = "__setslice__" or name = "__delslice__" + name = "__getslice__" or name = "__setslice__" or name = "__delslice__" } from PythonFunctionValue f, string meth where - f.getScope().isMethod() and - not f.isOverridingMethod() and - slice_method_name(meth) and - f.getName() = meth + f.getScope().isMethod() and + not f.isOverridingMethod() and + slice_method_name(meth) and + f.getName() = meth select f, meth + " method has been deprecated since Python 2.0" diff --git a/python/ql/src/Functions/ExplicitReturnInInit.ql b/python/ql/src/Functions/ExplicitReturnInInit.ql index b839c3bc9b7..0ce20249119 100644 --- a/python/ql/src/Functions/ExplicitReturnInInit.ql +++ b/python/ql/src/Functions/ExplicitReturnInInit.ql @@ -14,10 +14,10 @@ import python from Return r, Expr rv where - exists(Function init | init.isInitMethod() and r.getScope() = init) and - r.getValue() = rv and - not rv.pointsTo(Value::none_()) and - not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and - // to avoid double reporting, don't trigger if returning result from other __init__ function - not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__") + exists(Function init | init.isInitMethod() and r.getScope() = init) and + r.getValue() = rv and + not rv.pointsTo(Value::none_()) and + not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and + // to avoid double reporting, don't trigger if returning result from other __init__ function + not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__") select r, "Explicit return in __init__ method." diff --git a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql index 14af8ad9058..b3c97e967f6 100644 --- a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql +++ b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql @@ -14,142 +14,142 @@ import python private predicate attribute_method(string name) { - name = "__getattribute__" or name = "__getattr__" or name = "__setattr__" + name = "__getattribute__" or name = "__getattr__" or name = "__setattr__" } private predicate indexing_method(string name) { - name = "__getitem__" or name = "__setitem__" or name = "__delitem__" + name = "__getitem__" or name = "__setitem__" or name = "__delitem__" } private predicate arithmetic_method(string name) { - name = "__add__" or - name = "__sub__" or - name = "__div__" or - name = "__pos__" or - name = "__abs__" or - name = "__floordiv__" or - name = "__div__" or - name = "__divmod__" or - name = "__lshift__" or - name = "__and__" or - name = "__or__" or - name = "__xor__" or - name = "__rshift__" or - name = "__pow__" or - name = "__mul__" or - name = "__neg__" or - name = "__radd__" or - name = "__rsub__" or - name = "__rdiv__" or - name = "__rfloordiv__" or - name = "__rdiv__" or - name = "__rlshift__" or - name = "__rand__" or - name = "__ror__" or - name = "__rxor__" or - name = "__rrshift__" or - name = "__rpow__" or - name = "__rmul__" or - name = "__truediv__" or - name = "__rtruediv__" or - name = "__iadd__" or - name = "__isub__" or - name = "__idiv__" or - name = "__ifloordiv__" or - name = "__idiv__" or - name = "__ilshift__" or - name = "__iand__" or - name = "__ior__" or - name = "__ixor__" or - name = "__irshift__" or - name = "__ipow__" or - name = "__imul__" or - name = "__itruediv__" + name = "__add__" or + name = "__sub__" or + name = "__div__" or + name = "__pos__" or + name = "__abs__" or + name = "__floordiv__" or + name = "__div__" or + name = "__divmod__" or + name = "__lshift__" or + name = "__and__" or + name = "__or__" or + name = "__xor__" or + name = "__rshift__" or + name = "__pow__" or + name = "__mul__" or + name = "__neg__" or + name = "__radd__" or + name = "__rsub__" or + name = "__rdiv__" or + name = "__rfloordiv__" or + name = "__rdiv__" or + name = "__rlshift__" or + name = "__rand__" or + name = "__ror__" or + name = "__rxor__" or + name = "__rrshift__" or + name = "__rpow__" or + name = "__rmul__" or + name = "__truediv__" or + name = "__rtruediv__" or + name = "__iadd__" or + name = "__isub__" or + name = "__idiv__" or + name = "__ifloordiv__" or + name = "__idiv__" or + name = "__ilshift__" or + name = "__iand__" or + name = "__ior__" or + name = "__ixor__" or + name = "__irshift__" or + name = "__ipow__" or + name = "__imul__" or + name = "__itruediv__" } private predicate ordering_method(string name) { - name = "__lt__" - or - name = "__le__" - or - name = "__gt__" - or - name = "__ge__" - or - name = "__cmp__" and major_version() = 2 + name = "__lt__" + or + name = "__le__" + or + name = "__gt__" + or + name = "__ge__" + or + name = "__cmp__" and major_version() = 2 } private predicate cast_method(string name) { - name = "__nonzero__" and major_version() = 2 - or - name = "__int__" - or - name = "__float__" - or - name = "__long__" - or - name = "__trunc__" - or - name = "__complex__" + name = "__nonzero__" and major_version() = 2 + or + name = "__int__" + or + name = "__float__" + or + name = "__long__" + or + name = "__trunc__" + or + name = "__complex__" } predicate correct_raise(string name, ClassObject ex) { - ex.getAnImproperSuperType() = theTypeErrorType() and - ( - name = "__copy__" or - name = "__deepcopy__" or - name = "__call__" or - indexing_method(name) or - attribute_method(name) - ) - or - preferred_raise(name, ex) - or - preferred_raise(name, ex.getASuperType()) + ex.getAnImproperSuperType() = theTypeErrorType() and + ( + name = "__copy__" or + name = "__deepcopy__" or + name = "__call__" or + indexing_method(name) or + attribute_method(name) + ) + or + preferred_raise(name, ex) + or + preferred_raise(name, ex.getASuperType()) } predicate preferred_raise(string name, ClassObject ex) { - attribute_method(name) and ex = theAttributeErrorType() - or - indexing_method(name) and ex = Object::builtin("LookupError") - or - ordering_method(name) and ex = theTypeErrorType() - or - arithmetic_method(name) and ex = Object::builtin("ArithmeticError") - or - name = "__bool__" and ex = theTypeErrorType() + attribute_method(name) and ex = theAttributeErrorType() + or + indexing_method(name) and ex = Object::builtin("LookupError") + or + ordering_method(name) and ex = theTypeErrorType() + or + arithmetic_method(name) and ex = Object::builtin("ArithmeticError") + or + name = "__bool__" and ex = theTypeErrorType() } predicate no_need_to_raise(string name, string message) { - name = "__hash__" and message = "use __hash__ = None instead" - or - cast_method(name) and message = "there is no need to implement the method at all." + name = "__hash__" and message = "use __hash__ = None instead" + or + cast_method(name) and message = "there is no need to implement the method at all." } predicate is_abstract(FunctionObject func) { - func.getFunction().getADecorator().(Name).getId().matches("%abstract%") + func.getFunction().getADecorator().(Name).getId().matches("%abstract%") } predicate always_raises(FunctionObject f, ClassObject ex) { - ex = f.getARaisedType() and - strictcount(f.getARaisedType()) = 1 and - not exists(f.getFunction().getANormalExit()) and - /* raising StopIteration is equivalent to a return in a generator */ - not ex = theStopIterationType() + ex = f.getARaisedType() and + strictcount(f.getARaisedType()) = 1 and + not exists(f.getFunction().getANormalExit()) and + /* raising StopIteration is equivalent to a return in a generator */ + not ex = theStopIterationType() } from FunctionObject f, ClassObject cls, string message where - f.getFunction().isSpecialMethod() and - not is_abstract(f) and - always_raises(f, cls) and - ( - no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError" - or - not correct_raise(f.getName(), cls) and - not cls.getName() = "NotImplementedError" and - exists(ClassObject preferred | preferred_raise(f.getName(), preferred) | - message = "raise " + preferred.getName() + " instead" - ) + f.getFunction().isSpecialMethod() and + not is_abstract(f) and + always_raises(f, cls) and + ( + no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError" + or + not correct_raise(f.getName(), cls) and + not cls.getName() = "NotImplementedError" and + exists(ClassObject preferred | preferred_raise(f.getName(), preferred) | + message = "raise " + preferred.getName() + " instead" ) + ) select f, "Function always raises $@; " + message, cls, cls.toString() diff --git a/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql b/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql index 953641a6c6a..e607245f97f 100644 --- a/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql +++ b/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql @@ -14,18 +14,18 @@ import Expressions.CallArgs from Call call, FunctionValue func, FunctionValue overridden, string problem where - func.overrides(overridden) and - ( - wrong_args(call, func, _, problem) and - correct_args_if_called_as_method(call, overridden) - or - exists(string name | - illegally_named_parameter(call, func, name) and - problem = "an argument named '" + name + "'" and - overridden.getScope().getAnArg().(Name).getId() = name - ) + func.overrides(overridden) and + ( + wrong_args(call, func, _, problem) and + correct_args_if_called_as_method(call, overridden) + or + exists(string name | + illegally_named_parameter(call, func, name) and + problem = "an argument named '" + name + "'" and + overridden.getScope().getAnArg().(Name).getId() = name ) + ) select func, - "Overriding method signature does not match $@, where it is passed " + problem + - ". Overridden method $@ is correctly specified.", call, "here", overridden, - overridden.descriptiveString() + "Overriding method signature does not match $@, where it is passed " + problem + + ". Overridden method $@ is correctly specified.", call, "here", overridden, + overridden.descriptiveString() diff --git a/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql index 56f4abe29e8..0d68d0b506e 100644 --- a/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql +++ b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql @@ -15,23 +15,23 @@ import Expressions.CallArgs from Call call, FunctionValue func, FunctionValue overriding, string problem where - not func.getName() = "__init__" and - overriding.overrides(func) and - call = overriding.getAMethodCall().getNode() and - correct_args_if_called_as_method(call, overriding) and - ( - arg_count(call) + 1 < func.minParameters() and problem = "too few arguments" - or - arg_count(call) >= func.maxParameters() and problem = "too many arguments" - or - exists(string name | - call.getAKeyword().getArg() = name and - overriding.getScope().getAnArg().(Name).getId() = name and - not func.getScope().getAnArg().(Name).getId() = name and - problem = "an argument named '" + name + "'" - ) + not func.getName() = "__init__" and + overriding.overrides(func) and + call = overriding.getAMethodCall().getNode() and + correct_args_if_called_as_method(call, overriding) and + ( + arg_count(call) + 1 < func.minParameters() and problem = "too few arguments" + or + arg_count(call) >= func.maxParameters() and problem = "too many arguments" + or + exists(string name | + call.getAKeyword().getArg() = name and + overriding.getScope().getAnArg().(Name).getId() = name and + not func.getScope().getAnArg().(Name).getId() = name and + problem = "an argument named '" + name + "'" ) + ) select func, - "Overridden method signature does not match $@, where it is passed " + problem + - ". Overriding method $@ matches the call.", call, "call", overriding, - overriding.descriptiveString() + "Overridden method signature does not match $@, where it is passed " + problem + + ". Overriding method $@ matches the call.", call, "call", overriding, + overriding.descriptiveString() diff --git a/python/ql/src/Functions/InitIsGenerator.ql b/python/ql/src/Functions/InitIsGenerator.ql index bb02f103ea3..5e3f1ff574b 100644 --- a/python/ql/src/Functions/InitIsGenerator.ql +++ b/python/ql/src/Functions/InitIsGenerator.ql @@ -14,6 +14,6 @@ import python from Function f where - f.isInitMethod() and - (exists(Yield y | y.getScope() = f) or exists(YieldFrom y | y.getScope() = f)) + f.isInitMethod() and + (exists(Yield y | y.getScope() = f) or exists(YieldFrom y | y.getScope() = f)) select f, "__init__ method is a generator." diff --git a/python/ql/src/Functions/IterReturnsNonIterator.ql b/python/ql/src/Functions/IterReturnsNonIterator.ql index 405bff78c04..aba772c9ab7 100644 --- a/python/ql/src/Functions/IterReturnsNonIterator.ql +++ b/python/ql/src/Functions/IterReturnsNonIterator.ql @@ -14,10 +14,10 @@ import python from ClassValue iterable, FunctionValue iter, ClassValue iterator where - iter = iterable.lookup("__iter__") and - iterator = iter.getAnInferredReturnType() and - not iterator.isIterator() + iter = iterable.lookup("__iter__") and + iterator = iter.getAnInferredReturnType() and + not iterator.isIterator() select iterator, - "Class " + iterator.getName() + - " is returned as an iterator (by $@) but does not fully implement the iterator interface.", - iter, iter.getName() + "Class " + iterator.getName() + + " is returned as an iterator (by $@) but does not fully implement the iterator interface.", + iter, iter.getName() diff --git a/python/ql/src/Functions/IterReturnsNonSelf.ql b/python/ql/src/Functions/IterReturnsNonSelf.ql index 095685b749a..3d6c8c7da35 100644 --- a/python/ql/src/Functions/IterReturnsNonSelf.ql +++ b/python/ql/src/Functions/IterReturnsNonSelf.ql @@ -17,14 +17,14 @@ Function iter_method(ClassValue t) { result = t.lookup("__iter__").(FunctionValu predicate is_self(Name value, Function f) { value.getVariable() = f.getArg(0).(Name).getVariable() } predicate returns_non_self(Function f) { - exists(f.getFallthroughNode()) - or - exists(Return r | r.getScope() = f and not is_self(r.getValue(), f)) - or - exists(Return r | r.getScope() = f and not exists(r.getValue())) + exists(f.getFallthroughNode()) + or + exists(Return r | r.getScope() = f and not is_self(r.getValue(), f)) + or + exists(Return r | r.getScope() = f and not exists(r.getValue())) } from ClassValue t, Function iter where t.isIterator() and iter = iter_method(t) and returns_non_self(iter) select t, "Class " + t.getName() + " is an iterator but its $@ method does not return 'self'.", - iter, iter.getName() + iter, iter.getName() diff --git a/python/ql/src/Functions/ModificationOfParameterWithDefault.ql b/python/ql/src/Functions/ModificationOfParameterWithDefault.ql index aa7c90e4a6b..03edc35fa17 100644 --- a/python/ql/src/Functions/ModificationOfParameterWithDefault.ql +++ b/python/ql/src/Functions/ModificationOfParameterWithDefault.ql @@ -15,85 +15,85 @@ import python import semmle.python.security.Paths predicate safe_method(string name) { - name = "count" or - name = "index" or - name = "copy" or - name = "get" or - name = "has_key" or - name = "items" or - name = "keys" or - name = "values" or - name = "iteritems" or - name = "iterkeys" or - name = "itervalues" or - name = "__contains__" or - name = "__getitem__" or - name = "__getattribute__" + name = "count" or + name = "index" or + name = "copy" or + name = "get" or + name = "has_key" or + name = "items" or + name = "keys" or + name = "values" or + name = "iteritems" or + name = "iterkeys" or + name = "itervalues" or + name = "__contains__" or + name = "__getitem__" or + name = "__getattribute__" } /** Gets the truthiness (non emptyness) of the default of `p` if that value is mutable */ private boolean mutableDefaultValue(Parameter p) { - exists(Dict d | p.getDefault() = d | - exists(d.getAKey()) and result = true - or - not exists(d.getAKey()) and result = false - ) + exists(Dict d | p.getDefault() = d | + exists(d.getAKey()) and result = true or - exists(List l | p.getDefault() = l | - exists(l.getAnElt()) and result = true - or - not exists(l.getAnElt()) and result = false - ) + not exists(d.getAKey()) and result = false + ) + or + exists(List l | p.getDefault() = l | + exists(l.getAnElt()) and result = true + or + not exists(l.getAnElt()) and result = false + ) } class NonEmptyMutableValue extends TaintKind { - NonEmptyMutableValue() { this = "non-empty mutable value" } + NonEmptyMutableValue() { this = "non-empty mutable value" } } class EmptyMutableValue extends TaintKind { - EmptyMutableValue() { this = "empty mutable value" } + EmptyMutableValue() { this = "empty mutable value" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } class MutableDefaultValue extends TaintSource { - boolean nonEmpty; + boolean nonEmpty; - MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.(NameNode).getNode()) } + MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.(NameNode).getNode()) } - override string toString() { result = "mutable default value" } + override string toString() { result = "mutable default value" } - override predicate isSourceOf(TaintKind kind) { - nonEmpty = false and kind instanceof EmptyMutableValue - or - nonEmpty = true and kind instanceof NonEmptyMutableValue - } + override predicate isSourceOf(TaintKind kind) { + nonEmpty = false and kind instanceof EmptyMutableValue + or + nonEmpty = true and kind instanceof NonEmptyMutableValue + } } private ClassValue mutable_class() { - result = Value::named("list") or - result = Value::named("dict") + result = Value::named("list") or + result = Value::named("dict") } class Mutation extends TaintSink { - Mutation() { - exists(AugAssign a | a.getTarget().getAFlowNode() = this) - or - exists(Call c, Attribute a | c.getFunc() = a | - a.getObject().getAFlowNode() = this and - not safe_method(a.getName()) and - this.(ControlFlowNode).pointsTo().getClass() = mutable_class() - ) - } + Mutation() { + exists(AugAssign a | a.getTarget().getAFlowNode() = this) + or + exists(Call c, Attribute a | c.getFunc() = a | + a.getObject().getAFlowNode() = this and + not safe_method(a.getName()) and + this.(ControlFlowNode).pointsTo().getClass() = mutable_class() + ) + } - override predicate sinks(TaintKind kind) { - kind instanceof EmptyMutableValue - or - kind instanceof NonEmptyMutableValue - } + override predicate sinks(TaintKind kind) { + kind instanceof EmptyMutableValue + or + kind instanceof NonEmptyMutableValue + } } from TaintedPathSource src, TaintedPathSink sink where src.flowsTo(sink) select sink.getSink(), src, sink, "$@ flows to here and is mutated.", src.getSource(), - "Default value" + "Default value" diff --git a/python/ql/src/Functions/NonCls.ql b/python/ql/src/Functions/NonCls.ql index 10ca06af12c..5cb9fafab89 100644 --- a/python/ql/src/Functions/NonCls.ql +++ b/python/ql/src/Functions/NonCls.ql @@ -15,36 +15,36 @@ import python predicate first_arg_cls(Function f) { - exists(string argname | argname = f.getArgName(0) | - argname = "cls" - or - /* Not PEP8, but relatively common */ - argname = "mcls" - ) + exists(string argname | argname = f.getArgName(0) | + argname = "cls" + or + /* Not PEP8, but relatively common */ + argname = "mcls" + ) } predicate is_type_method(Function f) { - exists(ClassValue c | c.getScope() = f.getScope() and c.getASuperType() = ClassValue::type()) + exists(ClassValue c | c.getScope() = f.getScope() and c.getASuperType() = ClassValue::type()) } predicate classmethod_decorators_only(Function f) { - forall(Expr decorator | decorator = f.getADecorator() | decorator.(Name).getId() = "classmethod") + forall(Expr decorator | decorator = f.getADecorator() | decorator.(Name).getId() = "classmethod") } from Function f, string message where - (f.getADecorator().(Name).getId() = "classmethod" or is_type_method(f)) and - not first_arg_cls(f) and - classmethod_decorators_only(f) and - not f.getName() = "__new__" and - ( - if exists(f.getArgName(0)) - then - message = - "Class methods or methods of a type deriving from type should have 'cls', rather than '" + - f.getArgName(0) + "', as their first parameter." - else - message = - "Class methods or methods of a type deriving from type should have 'cls' as their first parameter." - ) + (f.getADecorator().(Name).getId() = "classmethod" or is_type_method(f)) and + not first_arg_cls(f) and + classmethod_decorators_only(f) and + not f.getName() = "__new__" and + ( + if exists(f.getArgName(0)) + then + message = + "Class methods or methods of a type deriving from type should have 'cls', rather than '" + + f.getArgName(0) + "', as their first parameter." + else + message = + "Class methods or methods of a type deriving from type should have 'cls' as their first parameter." + ) select f, message diff --git a/python/ql/src/Functions/NonSelf.ql b/python/ql/src/Functions/NonSelf.ql index a3102eee2aa..cb8924c071a 100644 --- a/python/ql/src/Functions/NonSelf.ql +++ b/python/ql/src/Functions/NonSelf.ql @@ -17,42 +17,42 @@ import python import semmle.python.libraries.Zope predicate is_type_method(FunctionValue fv) { - exists(ClassValue c | c.declaredAttribute(_) = fv and c.getASuperType() = ClassValue::type()) + exists(ClassValue c | c.declaredAttribute(_) = fv and c.getASuperType() = ClassValue::type()) } predicate used_in_defining_scope(FunctionValue fv) { - exists(Call c | c.getScope() = fv.getScope().getScope() and c.getFunc().pointsTo(fv)) + exists(Call c | c.getScope() = fv.getScope().getScope() and c.getFunc().pointsTo(fv)) } from Function f, FunctionValue fv, string message where - exists(ClassValue cls, string name | - cls.declaredAttribute(name) = fv and - cls.isNewStyle() and - not name = "__new__" and - not name = "__metaclass__" and - not name = "__init_subclass__" and - not name = "__class_getitem__" and - /* declared in scope */ - f.getScope() = cls.getScope() - ) and - not f.getArgName(0) = "self" and - not is_type_method(fv) and - fv.getScope() = f and - not f.getName() = "lambda" and - not used_in_defining_scope(fv) and + exists(ClassValue cls, string name | + cls.declaredAttribute(name) = fv and + cls.isNewStyle() and + not name = "__new__" and + not name = "__metaclass__" and + not name = "__init_subclass__" and + not name = "__class_getitem__" and + /* declared in scope */ + f.getScope() = cls.getScope() + ) and + not f.getArgName(0) = "self" and + not is_type_method(fv) and + fv.getScope() = f and + not f.getName() = "lambda" and + not used_in_defining_scope(fv) and + ( ( - ( - if exists(f.getArgName(0)) - then - message = - "Normal methods should have 'self', rather than '" + f.getArgName(0) + - "', as their first parameter." - else - message = - "Normal methods should have at least one parameter (the first of which should be 'self')." - ) and - not f.hasVarArg() + if exists(f.getArgName(0)) + then + message = + "Normal methods should have 'self', rather than '" + f.getArgName(0) + + "', as their first parameter." + else + message = + "Normal methods should have at least one parameter (the first of which should be 'self')." ) and - not fv instanceof ZopeInterfaceMethodValue + not f.hasVarArg() + ) and + not fv instanceof ZopeInterfaceMethodValue select f, message diff --git a/python/ql/src/Functions/OverlyComplexDelMethod.ql b/python/ql/src/Functions/OverlyComplexDelMethod.ql index b709af7fb11..2fc8789da34 100644 --- a/python/ql/src/Functions/OverlyComplexDelMethod.ql +++ b/python/ql/src/Functions/OverlyComplexDelMethod.ql @@ -17,8 +17,8 @@ import python from FunctionValue method where - exists(ClassValue c | - c.declaredAttribute("__del__") = method and - method.getScope().getMetrics().getCyclomaticComplexity() > 3 - ) + exists(ClassValue c | + c.declaredAttribute("__del__") = method and + method.getScope().getMetrics().getCyclomaticComplexity() > 3 + ) select method, "Overly complex '__del__' method." diff --git a/python/ql/src/Functions/ReturnConsistentTupleSizes.ql b/python/ql/src/Functions/ReturnConsistentTupleSizes.ql index 02965c2a3a5..9046f52cecb 100644 --- a/python/ql/src/Functions/ReturnConsistentTupleSizes.ql +++ b/python/ql/src/Functions/ReturnConsistentTupleSizes.ql @@ -13,18 +13,18 @@ import python predicate returns_tuple_of_size(Function func, int size, AstNode origin) { - exists(Return return, TupleValue val | - return.getScope() = func and - return.getValue().pointsTo(val, origin) - | - size = val.length() - ) + exists(Return return, TupleValue val | + return.getScope() = func and + return.getValue().pointsTo(val, origin) + | + size = val.length() + ) } from Function func, int s1, int s2, AstNode t1, AstNode t2 where - returns_tuple_of_size(func, s1, t1) and - returns_tuple_of_size(func, s2, t2) and - s1 < s2 + returns_tuple_of_size(func, s1, t1) and + returns_tuple_of_size(func, s2, t2) and + s1 < s2 select func, func.getQualifiedName() + " returns $@ and $@.", t1, "tuple of size " + s1, t2, - "tuple of size " + s2 + "tuple of size " + s2 diff --git a/python/ql/src/Functions/ReturnValueIgnored.ql b/python/ql/src/Functions/ReturnValueIgnored.ql index e6d962e594f..b7f272dcc2d 100644 --- a/python/ql/src/Functions/ReturnValueIgnored.ql +++ b/python/ql/src/Functions/ReturnValueIgnored.ql @@ -18,66 +18,66 @@ import python import semmle.python.objects.Callables predicate meaningful_return_value(Expr val) { - val instanceof Name - or - val instanceof BooleanLiteral - or - exists(FunctionValue callee | - val = callee.getACall().getNode() and returns_meaningful_value(callee) - ) - or - not exists(FunctionValue callee | val = callee.getACall().getNode()) and not val instanceof Name + val instanceof Name + or + val instanceof BooleanLiteral + or + exists(FunctionValue callee | + val = callee.getACall().getNode() and returns_meaningful_value(callee) + ) + or + not exists(FunctionValue callee | val = callee.getACall().getNode()) and not val instanceof Name } /* Value is used before returning, and thus its value is not lost if ignored */ predicate used_value(Expr val) { - exists(LocalVariable var, Expr other | - var.getAnAccess() = val and other = var.getAnAccess() and not other = val - ) + exists(LocalVariable var, Expr other | + var.getAnAccess() = val and other = var.getAnAccess() and not other = val + ) } predicate returns_meaningful_value(FunctionValue f) { - not exists(f.getScope().getFallthroughNode()) and - ( - exists(Return ret, Expr val | ret.getScope() = f.getScope() and val = ret.getValue() | - meaningful_return_value(val) and - not used_value(val) - ) - or - /* - * Is f a builtin function that returns something other than None? - * Ignore __import__ as it is often called purely for side effects - */ - - f.isBuiltin() and - f.getAnInferredReturnType() != ClassValue::nonetype() and - not f.getName() = "__import__" + not exists(f.getScope().getFallthroughNode()) and + ( + exists(Return ret, Expr val | ret.getScope() = f.getScope() and val = ret.getValue() | + meaningful_return_value(val) and + not used_value(val) ) + or + /* + * Is f a builtin function that returns something other than None? + * Ignore __import__ as it is often called purely for side effects + */ + + f.isBuiltin() and + f.getAnInferredReturnType() != ClassValue::nonetype() and + not f.getName() = "__import__" + ) } /* If a call is wrapped tightly in a try-except then we assume it is being executed for the exception. */ predicate wrapped_in_try_except(ExprStmt call) { - exists(Try t | - exists(t.getAHandler()) and - strictcount(Call c | t.getBody().contains(c)) = 1 and - call = t.getAStmt() - ) + exists(Try t | + exists(t.getAHandler()) and + strictcount(Call c | t.getBody().contains(c)) = 1 and + call = t.getAStmt() + ) } from ExprStmt call, FunctionValue callee, float percentage_used, int total where - call.getValue() = callee.getACall().getNode() and - returns_meaningful_value(callee) and - not wrapped_in_try_except(call) and - exists(int unused | - unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and - total = count(callee.getACall()) - | - percentage_used = (100.0 * (total - unused) / total).floor() - ) and - /* Report an alert if we see at least 5 calls and the return value is used in at least 3/4 of those calls. */ - percentage_used >= 75 and - total >= 5 + call.getValue() = callee.getACall().getNode() and + returns_meaningful_value(callee) and + not wrapped_in_try_except(call) and + exists(int unused | + unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and + total = count(callee.getACall()) + | + percentage_used = (100.0 * (total - unused) / total).floor() + ) and + /* Report an alert if we see at least 5 calls and the return value is used in at least 3/4 of those calls. */ + percentage_used >= 75 and + total >= 5 select call, - "Call discards return value of function $@. The result is used in " + percentage_used.toString() + - "% of calls.", callee, callee.getName() + "Call discards return value of function $@. The result is used in " + percentage_used.toString() + + "% of calls.", callee, callee.getName() diff --git a/python/ql/src/Functions/SignatureOverriddenMethod.ql b/python/ql/src/Functions/SignatureOverriddenMethod.ql index f24cea3811e..e695f2385ea 100644 --- a/python/ql/src/Functions/SignatureOverriddenMethod.ql +++ b/python/ql/src/Functions/SignatureOverriddenMethod.ql @@ -16,20 +16,20 @@ import Expressions.CallArgs from FunctionValue base, PythonFunctionValue derived where - not exists(base.getACall()) and - not exists(FunctionValue a_derived | - a_derived.overrides(base) and - exists(a_derived.getACall()) - ) and - not derived.getScope().isSpecialMethod() and - derived.getName() != "__init__" and - derived.isNormalMethod() and - not derived.getScope().isSpecialMethod() and - // call to overrides distributed for efficiency - ( - derived.overrides(base) and derived.minParameters() > base.maxParameters() - or - derived.overrides(base) and derived.maxParameters() < base.minParameters() - ) + not exists(base.getACall()) and + not exists(FunctionValue a_derived | + a_derived.overrides(base) and + exists(a_derived.getACall()) + ) and + not derived.getScope().isSpecialMethod() and + derived.getName() != "__init__" and + derived.isNormalMethod() and + not derived.getScope().isSpecialMethod() and + // call to overrides distributed for efficiency + ( + derived.overrides(base) and derived.minParameters() > base.maxParameters() + or + derived.overrides(base) and derived.maxParameters() < base.minParameters() + ) select derived, "Overriding method '" + derived.getName() + "' has signature mismatch with $@.", - base, "overridden method" + base, "overridden method" diff --git a/python/ql/src/Functions/SignatureSpecialMethods.ql b/python/ql/src/Functions/SignatureSpecialMethods.ql index bd5587ec903..87aeeae51ff 100644 --- a/python/ql/src/Functions/SignatureSpecialMethods.ql +++ b/python/ql/src/Functions/SignatureSpecialMethods.ql @@ -13,200 +13,200 @@ import python predicate is_unary_op(string name) { - name = "__del__" or - name = "__repr__" or - name = "__str__" or - name = "__hash__" or - name = "__bool__" or - name = "__nonzero__" or - name = "__unicode__" or - name = "__len__" or - name = "__iter__" or - name = "__reversed__" or - name = "__neg__" or - name = "__pos__" or - name = "__abs__" or - name = "__invert__" or - name = "__complex__" or - name = "__int__" or - name = "__float__" or - name = "__long__" or - name = "__oct__" or - name = "__hex__" or - name = "__index__" or - name = "__enter__" + name = "__del__" or + name = "__repr__" or + name = "__str__" or + name = "__hash__" or + name = "__bool__" or + name = "__nonzero__" or + name = "__unicode__" or + name = "__len__" or + name = "__iter__" or + name = "__reversed__" or + name = "__neg__" or + name = "__pos__" or + name = "__abs__" or + name = "__invert__" or + name = "__complex__" or + name = "__int__" or + name = "__float__" or + name = "__long__" or + name = "__oct__" or + name = "__hex__" or + name = "__index__" or + name = "__enter__" } predicate is_binary_op(string name) { - name = "__lt__" or - name = "__le__" or - name = "__eq__" or - name = "__ne__" or - name = "__gt__" or - name = "__ge__" or - name = "__cmp__" or - name = "__rcmp__" or - name = "__getattr___" or - name = "__getattribute___" or - name = "__delattr__" or - name = "__delete__" or - name = "__instancecheck__" or - name = "__subclasscheck__" or - name = "__getitem__" or - name = "__delitem__" or - name = "__contains__" or - name = "__add__" or - name = "__sub__" or - name = "__mul__" or - name = "__floordiv__" or - name = "__div__" or - name = "__truediv__" or - name = "__mod__" or - name = "__divmod__" or - name = "__lshift__" or - name = "__rshift__" or - name = "__and__" or - name = "__xor__" or - name = "__or__" or - name = "__radd__" or - name = "__rsub__" or - name = "__rmul__" or - name = "__rfloordiv__" or - name = "__rdiv__" or - name = "__rtruediv__" or - name = "__rmod__" or - name = "__rdivmod__" or - name = "__rpow__" or - name = "__rlshift__" or - name = "__rrshift__" or - name = "__rand__" or - name = "__rxor__" or - name = "__ror__" or - name = "__iadd__" or - name = "__isub__" or - name = "__imul__" or - name = "__ifloordiv__" or - name = "__idiv__" or - name = "__itruediv__" or - name = "__imod__" or - name = "__idivmod__" or - name = "__ipow__" or - name = "__ilshift__" or - name = "__irshift__" or - name = "__iand__" or - name = "__ixor__" or - name = "__ior__" or - name = "__coerce__" + name = "__lt__" or + name = "__le__" or + name = "__eq__" or + name = "__ne__" or + name = "__gt__" or + name = "__ge__" or + name = "__cmp__" or + name = "__rcmp__" or + name = "__getattr___" or + name = "__getattribute___" or + name = "__delattr__" or + name = "__delete__" or + name = "__instancecheck__" or + name = "__subclasscheck__" or + name = "__getitem__" or + name = "__delitem__" or + name = "__contains__" or + name = "__add__" or + name = "__sub__" or + name = "__mul__" or + name = "__floordiv__" or + name = "__div__" or + name = "__truediv__" or + name = "__mod__" or + name = "__divmod__" or + name = "__lshift__" or + name = "__rshift__" or + name = "__and__" or + name = "__xor__" or + name = "__or__" or + name = "__radd__" or + name = "__rsub__" or + name = "__rmul__" or + name = "__rfloordiv__" or + name = "__rdiv__" or + name = "__rtruediv__" or + name = "__rmod__" or + name = "__rdivmod__" or + name = "__rpow__" or + name = "__rlshift__" or + name = "__rrshift__" or + name = "__rand__" or + name = "__rxor__" or + name = "__ror__" or + name = "__iadd__" or + name = "__isub__" or + name = "__imul__" or + name = "__ifloordiv__" or + name = "__idiv__" or + name = "__itruediv__" or + name = "__imod__" or + name = "__idivmod__" or + name = "__ipow__" or + name = "__ilshift__" or + name = "__irshift__" or + name = "__iand__" or + name = "__ixor__" or + name = "__ior__" or + name = "__coerce__" } predicate is_ternary_op(string name) { - name = "__setattr__" or - name = "__set__" or - name = "__setitem__" or - name = "__getslice__" or - name = "__delslice__" + name = "__setattr__" or + name = "__set__" or + name = "__setitem__" or + name = "__getslice__" or + name = "__delslice__" } predicate is_quad_op(string name) { name = "__setslice__" or name = "__exit__" } int argument_count(PythonFunctionValue f, string name, ClassValue cls) { - cls.declaredAttribute(name) = f and - ( - is_unary_op(name) and result = 1 - or - is_binary_op(name) and result = 2 - or - is_ternary_op(name) and result = 3 - or - is_quad_op(name) and result = 4 - ) + cls.declaredAttribute(name) = f and + ( + is_unary_op(name) and result = 1 + or + is_binary_op(name) and result = 2 + or + is_ternary_op(name) and result = 3 + or + is_quad_op(name) and result = 4 + ) } predicate incorrect_special_method_defn( - PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner + PythonFunctionValue func, string message, boolean show_counts, string name, ClassValue owner ) { - exists(int required | required = argument_count(func, name, owner) | - /* actual_non_default <= actual */ - if required > func.maxParameters() - then message = "Too few parameters" and show_counts = true - else - if required < func.minParameters() - then message = "Too many parameters" and show_counts = true - else - if func.minParameters() < required and not func.getScope().hasVarArg() - then - message = (required - func.minParameters()) + " default values(s) will never be used" and - show_counts = false - else none() - ) + exists(int required | required = argument_count(func, name, owner) | + /* actual_non_default <= actual */ + if required > func.maxParameters() + then message = "Too few parameters" and show_counts = true + else + if required < func.minParameters() + then message = "Too many parameters" and show_counts = true + else + if func.minParameters() < required and not func.getScope().hasVarArg() + then + message = (required - func.minParameters()) + " default values(s) will never be used" and + show_counts = false + else none() + ) } predicate incorrect_pow(FunctionValue func, string message, boolean show_counts, ClassValue owner) { - owner.declaredAttribute("__pow__") = func and - ( - func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true - or - func.minParameters() > 3 and message = "Too many parameters" and show_counts = true - or - func.minParameters() < 2 and - message = (2 - func.minParameters()) + " default value(s) will never be used" and - show_counts = false - or - func.minParameters() = 3 and - message = "Third parameter to __pow__ should have a default value" and - show_counts = false - ) + owner.declaredAttribute("__pow__") = func and + ( + func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true + or + func.minParameters() > 3 and message = "Too many parameters" and show_counts = true + or + func.minParameters() < 2 and + message = (2 - func.minParameters()) + " default value(s) will never be used" and + show_counts = false + or + func.minParameters() = 3 and + message = "Third parameter to __pow__ should have a default value" and + show_counts = false + ) } predicate incorrect_get(FunctionValue func, string message, boolean show_counts, ClassValue owner) { - owner.declaredAttribute("__get__") = func and - ( - func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true - or - func.minParameters() > 3 and message = "Too many parameters" and show_counts = true - or - func.minParameters() < 2 and - not func.getScope().hasVarArg() and - message = (2 - func.minParameters()) + " default value(s) will never be used" and - show_counts = false - ) + owner.declaredAttribute("__get__") = func and + ( + func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true + or + func.minParameters() > 3 and message = "Too many parameters" and show_counts = true + or + func.minParameters() < 2 and + not func.getScope().hasVarArg() and + message = (2 - func.minParameters()) + " default value(s) will never be used" and + show_counts = false + ) } string should_have_parameters(PythonFunctionValue f, string name, ClassValue owner) { - exists(int i | i = argument_count(f, name, owner) | result = i.toString()) - or - owner.declaredAttribute(name) = f and - (name = "__get__" or name = "__pow__") and - result = "2 or 3" + exists(int i | i = argument_count(f, name, owner) | result = i.toString()) + or + owner.declaredAttribute(name) = f and + (name = "__get__" or name = "__pow__") and + result = "2 or 3" } string has_parameters(PythonFunctionValue f) { - exists(int i | i = f.minParameters() | - i = 0 and result = "no parameters" - or - i = 1 and result = "1 parameter" - or - i > 1 and result = i.toString() + " parameters" - ) + exists(int i | i = f.minParameters() | + i = 0 and result = "no parameters" + or + i = 1 and result = "1 parameter" + or + i > 1 and result = i.toString() + " parameters" + ) } from - PythonFunctionValue f, string message, string sizes, boolean show_counts, string name, - ClassValue owner + PythonFunctionValue f, string message, string sizes, boolean show_counts, string name, + ClassValue owner where - ( - incorrect_special_method_defn(f, message, show_counts, name, owner) - or - incorrect_pow(f, message, show_counts, owner) and name = "__pow__" - or - incorrect_get(f, message, show_counts, owner) and name = "__get__" - ) and - ( - show_counts = false and sizes = "" - or - show_counts = true and - sizes = - ", which has " + has_parameters(f) + ", but should have " + - should_have_parameters(f, name, owner) - ) + ( + incorrect_special_method_defn(f, message, show_counts, name, owner) + or + incorrect_pow(f, message, show_counts, owner) and name = "__pow__" + or + incorrect_get(f, message, show_counts, owner) and name = "__get__" + ) and + ( + show_counts = false and sizes = "" + or + show_counts = true and + sizes = + ", which has " + has_parameters(f) + ", but should have " + + should_have_parameters(f, name, owner) + ) select f, message + " for special method " + name + sizes + ", in class $@.", owner, owner.getName() diff --git a/python/ql/src/Functions/UseImplicitNoneReturnValue.ql b/python/ql/src/Functions/UseImplicitNoneReturnValue.ql index 38632358c08..606f1e6da51 100644 --- a/python/ql/src/Functions/UseImplicitNoneReturnValue.ql +++ b/python/ql/src/Functions/UseImplicitNoneReturnValue.ql @@ -13,26 +13,26 @@ import python import Testing.Mox predicate is_used(Call c) { - exists(Expr outer | outer != c and outer.containsInScope(c) | - outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript - ) - or - exists(Stmt s | - c = s.getASubExpression() and - not s instanceof ExprStmt and - /* Ignore if a single return, as def f(): return g() is quite common. Covers implicit return in a lambda. */ - not (s instanceof Return and strictcount(Return r | r.getScope() = s.getScope()) = 1) - ) + exists(Expr outer | outer != c and outer.containsInScope(c) | + outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript + ) + or + exists(Stmt s | + c = s.getASubExpression() and + not s instanceof ExprStmt and + /* Ignore if a single return, as def f(): return g() is quite common. Covers implicit return in a lambda. */ + not (s instanceof Return and strictcount(Return r | r.getScope() = s.getScope()) = 1) + ) } from Call c, FunctionValue func where - /* Call result is used, but callee is a procedure */ - is_used(c) and - c.getFunc().pointsTo(func) and - func.getScope().isProcedure() and - /* All callees are procedures */ - forall(FunctionValue callee | c.getFunc().pointsTo(callee) | callee.getScope().isProcedure()) and - /* Mox return objects have an `AndReturn` method */ - not useOfMoxInModule(c.getEnclosingModule()) + /* Call result is used, but callee is a procedure */ + is_used(c) and + c.getFunc().pointsTo(func) and + func.getScope().isProcedure() and + /* All callees are procedures */ + forall(FunctionValue callee | c.getFunc().pointsTo(callee) | callee.getScope().isProcedure()) and + /* Mox return objects have an `AndReturn` method */ + not useOfMoxInModule(c.getEnclosingModule()) select c, "The result of '$@' is used even though it is always None.", func, func.getQualifiedName() diff --git a/python/ql/src/Imports/Cyclic.qll b/python/ql/src/Imports/Cyclic.qll index 29c05b08209..dd25f06d0e5 100644 --- a/python/ql/src/Imports/Cyclic.qll +++ b/python/ql/src/Imports/Cyclic.qll @@ -3,84 +3,84 @@ import python predicate is_import_time(Stmt s) { not s.getScope+() instanceof Function } ModuleValue module_imported_by(ModuleValue m) { - exists(Stmt imp | - result = stmt_imports(imp) and - imp.getEnclosingModule() = m.getScope() and - // Import must reach exit to be part of a cycle - imp.getAnEntryNode().getBasicBlock().reachesExit() - ) + exists(Stmt imp | + result = stmt_imports(imp) and + imp.getEnclosingModule() = m.getScope() and + // Import must reach exit to be part of a cycle + imp.getAnEntryNode().getBasicBlock().reachesExit() + ) } /** Is there a circular import of 'm1' beginning with 'm2'? */ predicate circular_import(ModuleValue m1, ModuleValue m2) { - m1 != m2 and - m2 = module_imported_by(m1) and - m1 = module_imported_by+(m2) + m1 != m2 and + m2 = module_imported_by(m1) and + m1 = module_imported_by+(m2) } ModuleValue stmt_imports(ImportingStmt s) { - exists(string name | result.importedAs(name) and not name = "__main__" | - name = s.getAnImportedModuleName() and - s.getASubExpression().pointsTo(result) and - not result.isPackage() - ) + exists(string name | result.importedAs(name) and not name = "__main__" | + name = s.getAnImportedModuleName() and + s.getASubExpression().pointsTo(result) and + not result.isPackage() + ) } predicate import_time_imported_module(ModuleValue m1, ModuleValue m2, Stmt imp) { - imp.getEnclosingModule() = m1.getScope() and - is_import_time(imp) and - m2 = stmt_imports(imp) + imp.getEnclosingModule() = m1.getScope() and + is_import_time(imp) and + m2 = stmt_imports(imp) } /** Is there a cyclic import of 'm1' beginning with an import 'm2' at 'imp' where all the imports are top-level? */ predicate import_time_circular_import(ModuleValue m1, ModuleValue m2, Stmt imp) { - m1 != m2 and - import_time_imported_module(m1, m2, imp) and - import_time_transitive_import(m2, _, m1) + m1 != m2 and + import_time_imported_module(m1, m2, imp) and + import_time_transitive_import(m2, _, m1) } predicate import_time_transitive_import(ModuleValue base, Stmt imp, ModuleValue last) { - last != base and - ( - import_time_imported_module(base, last, imp) - or - exists(ModuleValue mid | - import_time_transitive_import(base, imp, mid) and - import_time_imported_module(mid, last, _) - ) - ) and - // Import must reach exit to be part of a cycle - imp.getAnEntryNode().getBasicBlock().reachesExit() + last != base and + ( + import_time_imported_module(base, last, imp) + or + exists(ModuleValue mid | + import_time_transitive_import(base, imp, mid) and + import_time_imported_module(mid, last, _) + ) + ) and + // Import must reach exit to be part of a cycle + imp.getAnEntryNode().getBasicBlock().reachesExit() } /** * Returns import-time usages of module 'm' in module 'enclosing' */ predicate import_time_module_use(ModuleValue m, ModuleValue enclosing, Expr use, string attr) { - exists(Expr mod | - use.getEnclosingModule() = enclosing.getScope() and - not use.getScope+() instanceof Function and - mod.pointsTo(m) and - not is_annotation_with_from_future_import_annotations(use) - | - // either 'M.foo' - use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr - or - // or 'from M import foo' - use.(ImportMember).getModule() = mod and use.(ImportMember).getName() = attr - ) + exists(Expr mod | + use.getEnclosingModule() = enclosing.getScope() and + not use.getScope+() instanceof Function and + mod.pointsTo(m) and + not is_annotation_with_from_future_import_annotations(use) + | + // either 'M.foo' + use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr + or + // or 'from M import foo' + use.(ImportMember).getModule() = mod and use.(ImportMember).getName() = attr + ) } /** * Holds if `use` appears inside an annotation. */ predicate is_used_in_annotation(Expr use) { - exists(FunctionExpr f | - f.getReturns().getASubExpression*() = use or - f.getArgs().getAnAnnotation().getASubExpression*() = use - ) - or - exists(AnnAssign a | a.getAnnotation().getASubExpression*() = use) + exists(FunctionExpr f | + f.getReturns().getASubExpression*() = use or + f.getArgs().getAnAnnotation().getASubExpression*() = use + ) + or + exists(AnnAssign a | a.getAnnotation().getASubExpression*() = use) } /** @@ -89,10 +89,10 @@ predicate is_used_in_annotation(Expr use) { * See https://www.python.org/dev/peps/pep-0563/ */ predicate is_annotation_with_from_future_import_annotations(Expr use) { - exists(ImportMember i | i.getScope() = use.getEnclosingModule() | - i.getModule().pointsTo().getName() = "__future__" and i.getName() = "annotations" - ) and - is_used_in_annotation(use) + exists(ImportMember i | i.getScope() = use.getEnclosingModule() | + i.getModule().pointsTo().getName() = "__future__" and i.getName() = "annotations" + ) and + is_used_in_annotation(use) } /** @@ -101,18 +101,18 @@ predicate is_annotation_with_from_future_import_annotations(Expr use) { * occur after the import 'other' in 'first'. */ predicate failing_import_due_to_cycle( - ModuleValue first, ModuleValue other, Stmt imp, ControlFlowNode defn, Expr use, string attr + ModuleValue first, ModuleValue other, Stmt imp, ControlFlowNode defn, Expr use, string attr ) { - import_time_imported_module(other, first, _) and - import_time_transitive_import(first, imp, other) and - import_time_module_use(first, other, use, attr) and - exists(ImportTimeScope n, SsaVariable v | - defn = v.getDefinition() and - n = first.getScope() and - v.getVariable().getScope() = n and - v.getId() = attr - | - not defn.strictlyDominates(imp.getAnEntryNode()) - ) and - not exists(If i | i.isNameEqMain() and i.contains(use)) + import_time_imported_module(other, first, _) and + import_time_transitive_import(first, imp, other) and + import_time_module_use(first, other, use, attr) and + exists(ImportTimeScope n, SsaVariable v | + defn = v.getDefinition() and + n = first.getScope() and + v.getVariable().getScope() = n and + v.getId() = attr + | + not defn.strictlyDominates(imp.getAnEntryNode()) + ) and + not exists(If i | i.isNameEqMain() and i.contains(use)) } diff --git a/python/ql/src/Imports/CyclicImport.ql b/python/ql/src/Imports/CyclicImport.ql index e14b41acb8e..9e4a153a110 100644 --- a/python/ql/src/Imports/CyclicImport.ql +++ b/python/ql/src/Imports/CyclicImport.ql @@ -16,11 +16,11 @@ import Cyclic from ModuleValue m1, ModuleValue m2, Stmt imp where - imp.getEnclosingModule() = m1.getScope() and - stmt_imports(imp) = m2 and - circular_import(m1, m2) and - m1 != m2 and - // this query finds all cyclic imports that are *not* flagged by ModuleLevelCyclicImport - not failing_import_due_to_cycle(m2, m1, _, _, _, _) and - not exists(If i | i.isNameEqMain() and i.contains(imp)) + imp.getEnclosingModule() = m1.getScope() and + stmt_imports(imp) = m2 and + circular_import(m1, m2) and + m1 != m2 and + // this query finds all cyclic imports that are *not* flagged by ModuleLevelCyclicImport + not failing_import_due_to_cycle(m2, m1, _, _, _, _) and + not exists(If i | i.isNameEqMain() and i.contains(imp)) select imp, "Import of module $@ begins an import cycle.", m2, m2.getName() diff --git a/python/ql/src/Imports/DeprecatedModule.ql b/python/ql/src/Imports/DeprecatedModule.ql index 359f3dad10d..62d17bd5e22 100644 --- a/python/ql/src/Imports/DeprecatedModule.ql +++ b/python/ql/src/Imports/DeprecatedModule.ql @@ -17,69 +17,69 @@ import python * and module `instead` should be used instead (or `instead = "no replacement"`) */ predicate deprecated_module(string name, string instead, int major, int minor) { - name = "posixfile" and instead = "fcntl" and major = 1 and minor = 5 - or - name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5 - or - name = "rgbimgmodule" and instead = "no replacement" and major = 2 and minor = 5 - or - name = "pre" and instead = "re" and major = 1 and minor = 5 - or - name = "whrandom" and instead = "random" and major = 2 and minor = 1 - or - name = "rfc822" and instead = "email" and major = 2 and minor = 3 - or - name = "mimetools" and instead = "email" and major = 2 and minor = 3 - or - name = "MimeWriter" and instead = "email" and major = 2 and minor = 3 - or - name = "mimify" and instead = "email" and major = 2 and minor = 3 - or - name = "rotor" and instead = "no replacement" and major = 2 and minor = 4 - or - name = "statcache" and instead = "no replacement" and major = 2 and minor = 2 - or - name = "mpz" and instead = "a third party" and major = 2 and minor = 2 - or - name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3 - or - name = "multifile" and instead = "email" and major = 2 and minor = 5 - or - name = "sets" and instead = "builtins" and major = 2 and minor = 6 - or - name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3 - or - name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4 - or - name = "macfs" and instead = "no replacement" and major = 2 and minor = 3 - or - name = "md5" and instead = "hashlib" and major = 2 and minor = 5 - or - name = "sha" and instead = "hashlib" and major = 2 and minor = 5 + name = "posixfile" and instead = "fcntl" and major = 1 and minor = 5 + or + name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5 + or + name = "rgbimgmodule" and instead = "no replacement" and major = 2 and minor = 5 + or + name = "pre" and instead = "re" and major = 1 and minor = 5 + or + name = "whrandom" and instead = "random" and major = 2 and minor = 1 + or + name = "rfc822" and instead = "email" and major = 2 and minor = 3 + or + name = "mimetools" and instead = "email" and major = 2 and minor = 3 + or + name = "MimeWriter" and instead = "email" and major = 2 and minor = 3 + or + name = "mimify" and instead = "email" and major = 2 and minor = 3 + or + name = "rotor" and instead = "no replacement" and major = 2 and minor = 4 + or + name = "statcache" and instead = "no replacement" and major = 2 and minor = 2 + or + name = "mpz" and instead = "a third party" and major = 2 and minor = 2 + or + name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "multifile" and instead = "email" and major = 2 and minor = 5 + or + name = "sets" and instead = "builtins" and major = 2 and minor = 6 + or + name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4 + or + name = "macfs" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "md5" and instead = "hashlib" and major = 2 and minor = 5 + or + name = "sha" and instead = "hashlib" and major = 2 and minor = 5 } string deprecation_message(string mod) { - exists(int major, int minor | deprecated_module(mod, _, major, minor) | - result = - "The " + mod + " module was deprecated in version " + major.toString() + "." + - minor.toString() + "." - ) + exists(int major, int minor | deprecated_module(mod, _, major, minor) | + result = + "The " + mod + " module was deprecated in version " + major.toString() + "." + + minor.toString() + "." + ) } string replacement_message(string mod) { - exists(string instead | deprecated_module(mod, instead, _, _) | - result = " Use " + instead + " module instead." and not instead = "no replacement" - or - result = "" and instead = "no replacement" - ) + exists(string instead | deprecated_module(mod, instead, _, _) | + result = " Use " + instead + " module instead." and not instead = "no replacement" + or + result = "" and instead = "no replacement" + ) } from ImportExpr imp, string name, string instead where - name = imp.getName() and - deprecated_module(name, instead, _, _) and - not exists(Try try, ExceptStmt except | except = try.getAHandler() | - except.getType().pointsTo(ClassValue::importError()) and - except.containsInScope(imp) - ) + name = imp.getName() and + deprecated_module(name, instead, _, _) and + not exists(Try try, ExceptStmt except | except = try.getAHandler() | + except.getType().pointsTo(ClassValue::importError()) and + except.containsInScope(imp) + ) select imp, deprecation_message(name) + replacement_message(name) diff --git a/python/ql/src/Imports/FromImportOfMutableAttribute.ql b/python/ql/src/Imports/FromImportOfMutableAttribute.ql index aa66fd6d9b2..cbb74977a03 100644 --- a/python/ql/src/Imports/FromImportOfMutableAttribute.ql +++ b/python/ql/src/Imports/FromImportOfMutableAttribute.ql @@ -16,19 +16,19 @@ import semmle.python.filters.Tests from ImportMember im, ModuleValue m, AttrNode store_attr, string name where - m.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and - im.getName() = name and - /* Modification must be in a function, so it can occur during lifetime of the import value */ - store_attr.getScope() instanceof Function and - /* variable resulting from import must have a long lifetime */ - not im.getScope() instanceof Function and - store_attr.isStore() and - store_attr.getObject(name).pointsTo(m) and - /* Import not in same module as modification. */ - not im.getEnclosingModule() = store_attr.getScope().getEnclosingModule() and - /* Modification is not in a test */ - not store_attr.getScope().getScope*() instanceof TestScope + m.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and + im.getName() = name and + /* Modification must be in a function, so it can occur during lifetime of the import value */ + store_attr.getScope() instanceof Function and + /* variable resulting from import must have a long lifetime */ + not im.getScope() instanceof Function and + store_attr.isStore() and + store_attr.getObject(name).pointsTo(m) and + /* Import not in same module as modification. */ + not im.getEnclosingModule() = store_attr.getScope().getEnclosingModule() and + /* Modification is not in a test */ + not store_attr.getScope().getScope*() instanceof TestScope select im, - "Importing the value of '" + name + - "' from $@ means that any change made to $@ will be not be observed locally.", m, - "module " + m.getName(), store_attr, m.getName() + "." + store_attr.getName() + "Importing the value of '" + name + + "' from $@ means that any change made to $@ will be not be observed locally.", m, + "module " + m.getName(), store_attr, m.getName() + "." + store_attr.getName() diff --git a/python/ql/src/Imports/ImportShadowedByLoopVar.ql b/python/ql/src/Imports/ImportShadowedByLoopVar.ql index f3817a1bcde..035f1640d71 100644 --- a/python/ql/src/Imports/ImportShadowedByLoopVar.ql +++ b/python/ql/src/Imports/ImportShadowedByLoopVar.ql @@ -13,11 +13,11 @@ import python predicate shadowsImport(Variable l) { - exists(Import i, Name shadow | - shadow = i.getAName().getAsname() and - shadow.getId() = l.getId() and - i.getScope() = l.getScope().getScope*() - ) + exists(Import i, Name shadow | + shadow = i.getAName().getAsname() and + shadow.getId() = l.getId() and + i.getScope() = l.getScope().getScope*() + ) } from Variable l, Name defn diff --git a/python/ql/src/Imports/ImportandImportFrom.ql b/python/ql/src/Imports/ImportandImportFrom.ql index f04e6d896ba..e57cac8aed4 100644 --- a/python/ql/src/Imports/ImportandImportFrom.ql +++ b/python/ql/src/Imports/ImportandImportFrom.ql @@ -12,12 +12,12 @@ import python predicate import_and_import_from(Import i1, Import i2, Module m) { - i1.getEnclosingModule() = i2.getEnclosingModule() and - exists(ImportExpr e1, ImportExpr e2, ImportMember im | - e1 = i1.getAName().getValue() and im = i2.getAName().getValue() and e2 = im.getModule() - | - e1.getName() = m.getName() and e2.getName() = m.getName() - ) + i1.getEnclosingModule() = i2.getEnclosingModule() and + exists(ImportExpr e1, ImportExpr e2, ImportMember im | + e1 = i1.getAName().getValue() and im = i2.getAName().getValue() and e2 = im.getModule() + | + e1.getName() = m.getName() and e2.getName() = m.getName() + ) } from Stmt i1, Stmt i2, Module m diff --git a/python/ql/src/Imports/ModuleImportsItself.ql b/python/ql/src/Imports/ModuleImportsItself.ql index 3a4ad487687..c876853fff5 100644 --- a/python/ql/src/Imports/ModuleImportsItself.ql +++ b/python/ql/src/Imports/ModuleImportsItself.ql @@ -13,14 +13,14 @@ import python predicate modules_imports_itself(ImportingStmt i, ModuleValue m) { - i.getEnclosingModule() = m.getScope() and - m = - max(string s, ModuleValue m_ | - s = i.getAnImportedModuleName() and - m_.importedAs(s) - | - m_ order by s.length() - ) + i.getEnclosingModule() = m.getScope() and + m = + max(string s, ModuleValue m_ | + s = i.getAnImportedModuleName() and + m_.importedAs(s) + | + m_ order by s.length() + ) } from ImportingStmt i, ModuleValue m diff --git a/python/ql/src/Imports/ModuleLevelCyclicImport.ql b/python/ql/src/Imports/ModuleLevelCyclicImport.ql index 8cdb25c4ca5..7d9b0cc31c7 100644 --- a/python/ql/src/Imports/ModuleLevelCyclicImport.ql +++ b/python/ql/src/Imports/ModuleLevelCyclicImport.ql @@ -24,7 +24,7 @@ import Cyclic from ModuleValue m1, Stmt imp, ModuleValue m2, string attr, Expr use, ControlFlowNode defn where failing_import_due_to_cycle(m1, m2, imp, defn, use, attr) select use, - "'" + attr + "' may not be defined if module $@ is imported before module $@, as the $@ of " + - attr + " occurs after the cyclic $@ of " + m2.getName() + ".", - // Arguments for the placeholders in the above message: - m1, m1.getName(), m2, m2.getName(), defn, "definition", imp, "import" + "'" + attr + "' may not be defined if module $@ is imported before module $@, as the $@ of " + + attr + " occurs after the cyclic $@ of " + m2.getName() + ".", + // Arguments for the placeholders in the above message: + m1, m1.getName(), m2, m2.getName(), defn, "definition", imp, "import" diff --git a/python/ql/src/Imports/MultipleImports.ql b/python/ql/src/Imports/MultipleImports.ql index 09638457423..fdff082e0c5 100644 --- a/python/ql/src/Imports/MultipleImports.ql +++ b/python/ql/src/Imports/MultipleImports.ql @@ -15,32 +15,32 @@ import python predicate is_simple_import(Import imp) { not exists(Attribute a | imp.contains(a)) } predicate double_import(Import original, Import duplicate, Module m) { - original != duplicate and - is_simple_import(original) and - is_simple_import(duplicate) and - /* Imports import the same thing */ - exists(ImportExpr e1, ImportExpr e2 | - e1.getName() = m.getName() and - e2.getName() = m.getName() and - e1 = original.getAName().getValue() and - e2 = duplicate.getAName().getValue() - ) and - original.getAName().getAsname().(Name).getId() = duplicate.getAName().getAsname().(Name).getId() and - exists(Module enclosing | - original.getScope() = enclosing and - duplicate.getEnclosingModule() = enclosing and - ( - /* Duplicate is not at top level scope */ - duplicate.getScope() != enclosing - or - /* Original dominates duplicate */ - original.getAnEntryNode().dominates(duplicate.getAnEntryNode()) - ) + original != duplicate and + is_simple_import(original) and + is_simple_import(duplicate) and + /* Imports import the same thing */ + exists(ImportExpr e1, ImportExpr e2 | + e1.getName() = m.getName() and + e2.getName() = m.getName() and + e1 = original.getAName().getValue() and + e2 = duplicate.getAName().getValue() + ) and + original.getAName().getAsname().(Name).getId() = duplicate.getAName().getAsname().(Name).getId() and + exists(Module enclosing | + original.getScope() = enclosing and + duplicate.getEnclosingModule() = enclosing and + ( + /* Duplicate is not at top level scope */ + duplicate.getScope() != enclosing + or + /* Original dominates duplicate */ + original.getAnEntryNode().dominates(duplicate.getAnEntryNode()) ) + ) } from Import original, Import duplicate, Module m where double_import(original, duplicate, m) select duplicate, - "This import of module " + m.getName() + " is redundant, as it was previously imported $@.", - original, "on line " + original.getLocation().getStartLine().toString() + "This import of module " + m.getName() + " is redundant, as it was previously imported $@.", + original, "on line " + original.getLocation().getStartLine().toString() diff --git a/python/ql/src/Imports/UnintentionalImport.ql b/python/ql/src/Imports/UnintentionalImport.ql index 47ae2c999a5..dfd751fd527 100644 --- a/python/ql/src/Imports/UnintentionalImport.ql +++ b/python/ql/src/Imports/UnintentionalImport.ql @@ -14,19 +14,19 @@ import python predicate import_star(ImportStar imp, ModuleValue exporter) { - exporter.importedAs(imp.getImportedModuleName()) + exporter.importedAs(imp.getImportedModuleName()) } predicate all_defined(ModuleValue exporter) { - exporter.isBuiltin() - or - exporter.getScope().(ImportTimeScope).definesName("__all__") - or - exporter.getScope().getInitModule().(ImportTimeScope).definesName("__all__") + exporter.isBuiltin() + or + exporter.getScope().(ImportTimeScope).definesName("__all__") + or + exporter.getScope().getInitModule().(ImportTimeScope).definesName("__all__") } from ImportStar imp, ModuleValue exporter where import_star(imp, exporter) and not all_defined(exporter) select imp, - "Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'.", - exporter, exporter.getName() + "Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'.", + exporter, exporter.getName() diff --git a/python/ql/src/Imports/UnusedImport.ql b/python/ql/src/Imports/UnusedImport.ql index b8e4903b743..e9c2dbe839d 100644 --- a/python/ql/src/Imports/UnusedImport.ql +++ b/python/ql/src/Imports/UnusedImport.ql @@ -14,112 +14,112 @@ import python import Variables.Definition predicate global_name_used(Module m, string name) { - exists(Name u, GlobalVariable v | - u.uses(v) and - v.getId() = name and - u.getEnclosingModule() = m - ) - or - // A use of an undefined class local variable, will use the global variable - exists(Name u, LocalVariable v | - u.uses(v) and - v.getId() = name and - u.getEnclosingModule() = m and - not v.getScope().getEnclosingScope*() instanceof Function - ) + exists(Name u, GlobalVariable v | + u.uses(v) and + v.getId() = name and + u.getEnclosingModule() = m + ) + or + // A use of an undefined class local variable, will use the global variable + exists(Name u, LocalVariable v | + u.uses(v) and + v.getId() = name and + u.getEnclosingModule() = m and + not v.getScope().getEnclosingScope*() instanceof Function + ) } /** Holds if a module has `__all__` but we don't understand it */ predicate all_not_understood(Module m) { - exists(GlobalVariable a | a.getId() = "__all__" and a.getScope() = m | - // `__all__` is not defined as a simple list - not m.declaredInAll(_) - or - // `__all__` is modified - exists(Call c | c.getFunc().(Attribute).getObject() = a.getALoad()) - ) + exists(GlobalVariable a | a.getId() = "__all__" and a.getScope() = m | + // `__all__` is not defined as a simple list + not m.declaredInAll(_) + or + // `__all__` is modified + exists(Call c | c.getFunc().(Attribute).getObject() = a.getALoad()) + ) } predicate imported_module_used_in_doctest(Import imp) { - exists(string modname, string docstring | - imp.getAName().getAsname().(Name).getId() = modname and - // Look for doctests containing the patterns: - // >>> …name… - // ... …name… - docstring = doctest_in_scope(imp.getScope()) and - docstring.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.).*" + modname + "[\\s\\S]*") - ) + exists(string modname, string docstring | + imp.getAName().getAsname().(Name).getId() = modname and + // Look for doctests containing the patterns: + // >>> …name… + // ... …name… + docstring = doctest_in_scope(imp.getScope()) and + docstring.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.).*" + modname + "[\\s\\S]*") + ) } pragma[noinline] private string doctest_in_scope(Scope scope) { - exists(StrConst doc | - doc.getEnclosingModule() = scope and - doc.isDocString() and - result = doc.getText() and - result.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.)[\\s\\S]*") - ) + exists(StrConst doc | + doc.getEnclosingModule() = scope and + doc.isDocString() and + result = doc.getText() and + result.regexpMatch("[\\s\\S]*(>>>|\\.\\.\\.)[\\s\\S]*") + ) } pragma[noinline] private string typehint_annotation_in_module(Module module_scope) { - exists(StrConst annotation | - annotation = any(Arguments a).getAnAnnotation().getASubExpression*() - or - annotation = any(AnnAssign a).getAnnotation().getASubExpression*() - or - annotation = any(FunctionExpr f).getReturns().getASubExpression*() - | - annotation.pointsTo(Value::forString(result)) and - annotation.getEnclosingModule() = module_scope - ) + exists(StrConst annotation | + annotation = any(Arguments a).getAnAnnotation().getASubExpression*() + or + annotation = any(AnnAssign a).getAnnotation().getASubExpression*() + or + annotation = any(FunctionExpr f).getReturns().getASubExpression*() + | + annotation.pointsTo(Value::forString(result)) and + annotation.getEnclosingModule() = module_scope + ) } pragma[noinline] private string typehint_comment_in_file(File file) { - exists(Comment typehint | - file = typehint.getLocation().getFile() and - result = typehint.getText() and - result.matches("# type:%") - ) + exists(Comment typehint | + file = typehint.getLocation().getFile() and + result = typehint.getText() and + result.matches("# type:%") + ) } /** Holds if the imported alias `name` from `imp` is used in a typehint (in the same file as `imp`) */ predicate imported_alias_used_in_typehint(Import imp, Variable name) { - imp.getAName().getAsname().(Name).getVariable() = name and - exists(File file, Module module_scope | - module_scope = imp.getEnclosingModule() and - file = module_scope.getFile() - | - // Look for type hints containing the patterns: - // # type: …name… - typehint_comment_in_file(file).regexpMatch("# type:.*" + name.getId() + ".*") - or - // Type hint is inside a string annotation, as needed for forward references - typehint_annotation_in_module(module_scope).regexpMatch(".*\\b" + name.getId() + "\\b.*") - ) + imp.getAName().getAsname().(Name).getVariable() = name and + exists(File file, Module module_scope | + module_scope = imp.getEnclosingModule() and + file = module_scope.getFile() + | + // Look for type hints containing the patterns: + // # type: …name… + typehint_comment_in_file(file).regexpMatch("# type:.*" + name.getId() + ".*") + or + // Type hint is inside a string annotation, as needed for forward references + typehint_annotation_in_module(module_scope).regexpMatch(".*\\b" + name.getId() + "\\b.*") + ) } predicate unused_import(Import imp, Variable name) { - imp.getAName().getAsname().(Name).getVariable() = name and - not imp.getAnImportedModuleName() = "__future__" and - not imp.getEnclosingModule().declaredInAll(name.getId()) and - imp.getScope() = imp.getEnclosingModule() and - not global_name_used(imp.getScope(), name.getId()) and - // Imports in `__init__.py` are used to force module loading - not imp.getEnclosingModule().isPackageInit() and - // Name may be imported for use in epytext documentation - not exists(Comment cmt | cmt.getText().matches("%L{" + name.getId() + "}%") | - cmt.getLocation().getFile() = imp.getLocation().getFile() - ) and - not name_acceptable_for_unused_variable(name) and - // Assume that opaque `__all__` includes imported module - not all_not_understood(imp.getEnclosingModule()) and - not imported_module_used_in_doctest(imp) and - not imported_alias_used_in_typehint(imp, name) and - // Only consider import statements that actually point-to something (possibly an unknown module). - // If this is not the case, it's likely that the import statement never gets executed. - imp.getAName().getValue().pointsTo(_) + imp.getAName().getAsname().(Name).getVariable() = name and + not imp.getAnImportedModuleName() = "__future__" and + not imp.getEnclosingModule().declaredInAll(name.getId()) and + imp.getScope() = imp.getEnclosingModule() and + not global_name_used(imp.getScope(), name.getId()) and + // Imports in `__init__.py` are used to force module loading + not imp.getEnclosingModule().isPackageInit() and + // Name may be imported for use in epytext documentation + not exists(Comment cmt | cmt.getText().matches("%L{" + name.getId() + "}%") | + cmt.getLocation().getFile() = imp.getLocation().getFile() + ) and + not name_acceptable_for_unused_variable(name) and + // Assume that opaque `__all__` includes imported module + not all_not_understood(imp.getEnclosingModule()) and + not imported_module_used_in_doctest(imp) and + not imported_alias_used_in_typehint(imp, name) and + // Only consider import statements that actually point-to something (possibly an unknown module). + // If this is not the case, it's likely that the import statement never gets executed. + imp.getAName().getValue().pointsTo(_) } from Stmt s, Variable name diff --git a/python/ql/src/Lexical/CommentedOutCode.qll b/python/ql/src/Lexical/CommentedOutCode.qll index f352bcfac17..97315321a79 100644 --- a/python/ql/src/Lexical/CommentedOutCode.qll +++ b/python/ql/src/Lexical/CommentedOutCode.qll @@ -1,234 +1,234 @@ import python private predicate def_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?def\\s.*\\(.*\\).*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?def\\s.*\\(.*\\).*:\\s*(#.*)?") } private predicate if_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?(el)?if\\s.*:\\s*(#.*)?") - or - c.getText().regexpMatch("#(\\S*\\s+)?else:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?(el)?if\\s.*:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?else:\\s*(#.*)?") } private predicate for_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?for\\s.*\\sin\\s.*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?for\\s.*\\sin\\s.*:\\s*(#.*)?") } private predicate with_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?with\\s+.*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?with\\s+.*:\\s*(#.*)?") } private predicate try_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?try:\\s*(#.*)?") - or - c.getText().regexpMatch("#(\\S*\\s+)?except\\s*(\\w+\\s*(\\sas\\s+\\w+\\s*)?)?:\\s*(#.*)?") - or - c.getText().regexpMatch("#(\\S*\\s+)?finally:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?try:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?except\\s*(\\w+\\s*(\\sas\\s+\\w+\\s*)?)?:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?finally:\\s*(#.*)?") } private int indentation(Comment c) { - exists(int offset | - maybe_code(c) and - exists(c.getText().regexpFind("[^\\s#]", 1, offset)) and - result = offset + c.getLocation().getStartColumn() - ) + exists(int offset | + maybe_code(c) and + exists(c.getText().regexpFind("[^\\s#]", 1, offset)) and + result = offset + c.getLocation().getStartColumn() + ) } private predicate class_statement(Comment c) { - c.getText().regexpMatch("#(\\S*\\s+)?class\\s+\\w+.*:\\s*(#.*)?") + c.getText().regexpMatch("#(\\S*\\s+)?class\\s+\\w+.*:\\s*(#.*)?") } private predicate triple_quote(Comment c) { c.getText().regexpMatch("#.*(\"\"\"|''').*") } private predicate triple_quoted_string_part(Comment start, Comment end) { - triple_quote(start) and end = start - or - exists(Comment mid | - triple_quoted_string_part(start, mid) and - end = non_empty_following(mid) and - not triple_quote(end) - ) + triple_quote(start) and end = start + or + exists(Comment mid | + triple_quoted_string_part(start, mid) and + end = non_empty_following(mid) and + not triple_quote(end) + ) } private predicate maybe_code(Comment c) { - not non_code(c) and not filler(c) and not endline_comment(c) and not file_or_url(c) - or - commented_out_comment(c) + not non_code(c) and not filler(c) and not endline_comment(c) and not file_or_url(c) + or + commented_out_comment(c) } private predicate commented_out_comment(Comment c) { c.getText().regexpMatch("#+\\s+#.*") } private int scope_start(Comment start) { - ( - def_statement(start) or - class_statement(start) - ) and - result = indentation(start) and - not non_code(start) + ( + def_statement(start) or + class_statement(start) + ) and + result = indentation(start) and + not non_code(start) } private int block_start(Comment start) { - ( - if_statement(start) or - for_statement(start) or - try_statement(start) or - with_statement(start) - ) and - result = indentation(start) and - not non_code(start) + ( + if_statement(start) or + for_statement(start) or + try_statement(start) or + with_statement(start) + ) and + result = indentation(start) and + not non_code(start) } private int scope_doc_string_part(Comment start, Comment end) { - result = scope_start(start) and - triple_quote(end) and - end = non_empty_following(start) - or - exists(Comment mid | - result = scope_doc_string_part(start, mid) and - end = non_empty_following(mid) - | - not triple_quote(end) - ) + result = scope_start(start) and + triple_quote(end) and + end = non_empty_following(start) + or + exists(Comment mid | + result = scope_doc_string_part(start, mid) and + end = non_empty_following(mid) + | + not triple_quote(end) + ) } private int scope_part(Comment start, Comment end) { - result = scope_start(start) and end = start - or - exists(Comment mid | - result = scope_doc_string_part(start, mid) and - end = non_empty_following(mid) and - triple_quote(end) - ) - or - exists(Comment mid | - result = scope_part(start, mid) and - end = non_empty_following(mid) - | - indentation(end) > result - ) + result = scope_start(start) and end = start + or + exists(Comment mid | + result = scope_doc_string_part(start, mid) and + end = non_empty_following(mid) and + triple_quote(end) + ) + or + exists(Comment mid | + result = scope_part(start, mid) and + end = non_empty_following(mid) + | + indentation(end) > result + ) } private int block_part(Comment start, Comment end) { - result = block_start(start) and - end = non_empty_following(start) and + result = block_start(start) and + end = non_empty_following(start) and + indentation(end) > result + or + exists(Comment mid | + result = block_part(start, mid) and + end = non_empty_following(mid) + | indentation(end) > result or - exists(Comment mid | - result = block_part(start, mid) and - end = non_empty_following(mid) - | - indentation(end) > result - or - result = block_start(end) - ) + result = block_start(end) + ) } private predicate commented_out_scope_part(Comment start, Comment end) { - exists(scope_doc_string_part(start, end)) - or - exists(scope_part(start, end)) + exists(scope_doc_string_part(start, end)) + or + exists(scope_part(start, end)) } private predicate commented_out_code(Comment c) { - commented_out_scope_part(c, _) - or - commented_out_scope_part(_, c) - or - exists(block_part(c, _)) - or - exists(block_part(_, c)) + commented_out_scope_part(c, _) + or + commented_out_scope_part(_, c) + or + exists(block_part(c, _)) + or + exists(block_part(_, c)) } private predicate commented_out_code_part(Comment start, Comment end) { - commented_out_code(start) and - end = start and - not exists(Comment prev | non_empty_following(prev) = start | commented_out_code(prev)) - or - exists(Comment mid | - commented_out_code_part(start, mid) and - non_empty_following(mid) = end and - commented_out_code(end) - ) + commented_out_code(start) and + end = start and + not exists(Comment prev | non_empty_following(prev) = start | commented_out_code(prev)) + or + exists(Comment mid | + commented_out_code_part(start, mid) and + non_empty_following(mid) = end and + commented_out_code(end) + ) } private predicate commented_out_code_block(Comment start, Comment end) { - /* A block must be at least 2 comments long. */ - start != end and - commented_out_code_part(start, end) and - not commented_out_code(non_empty_following(end)) + /* A block must be at least 2 comments long. */ + start != end and + commented_out_code_part(start, end) and + not commented_out_code(non_empty_following(end)) } /* A single line comment that appears to be commented out code */ class CommentedOutCodeLine extends Comment { - CommentedOutCodeLine() { exists(CommentedOutCodeBlock b | b.contains(this)) } + CommentedOutCodeLine() { exists(CommentedOutCodeBlock b | b.contains(this)) } - /* Whether this commented-out code line is likely to be example code embedded in a larger comment. */ - predicate maybeExampleCode() { - exists(CommentedOutCodeBlock block | - block.contains(this) and - block.maybeExampleCode() - ) - } + /* Whether this commented-out code line is likely to be example code embedded in a larger comment. */ + predicate maybeExampleCode() { + exists(CommentedOutCodeBlock block | + block.contains(this) and + block.maybeExampleCode() + ) + } } /** A block of comments that appears to be commented out code */ class CommentedOutCodeBlock extends @py_comment { - CommentedOutCodeBlock() { commented_out_code_block(this, _) } + CommentedOutCodeBlock() { commented_out_code_block(this, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Commented out code" } + /** Gets a textual representation of this element. */ + string toString() { result = "Commented out code" } - /** Whether this commented-out code block contains the comment c */ - predicate contains(Comment c) { - this = c - or - exists(Comment prev | - non_empty_following(prev) = c and - not commented_out_code_block(this, prev) and - this.contains(prev) - ) - } + /** Whether this commented-out code block contains the comment c */ + predicate contains(Comment c) { + this = c + or + exists(Comment prev | + non_empty_following(prev) = c and + not commented_out_code_block(this, prev) and + this.contains(prev) + ) + } - /** The length of this comment block (in comments) */ - int length() { result = count(Comment c | this.contains(c)) } + /** The length of this comment block (in comments) */ + int length() { result = count(Comment c | this.contains(c)) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - exists(Comment end | commented_out_code_block(this, end) | - end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + exists(Comment end | commented_out_code_block(this, end) | + end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn) + ) + } - /** Whether this commented-out code block is likely to be example code embedded in a larger comment. */ - predicate maybeExampleCode() { - exists(CommentBlock block | block.contains(this.(Comment)) | - exists(int all_code | - all_code = sum(CommentedOutCodeBlock code | block.contains(code.(Comment)) | code.length()) and - /* This ratio may need fine tuning */ - block.length() > all_code * 2 - ) - ) - } + /** Whether this commented-out code block is likely to be example code embedded in a larger comment. */ + predicate maybeExampleCode() { + exists(CommentBlock block | block.contains(this.(Comment)) | + exists(int all_code | + all_code = sum(CommentedOutCodeBlock code | block.contains(code.(Comment)) | code.length()) and + /* This ratio may need fine tuning */ + block.length() > all_code * 2 + ) + ) + } } /** Does c contain the pair of words "s1 s2" with only whitespace between them */ private predicate word_pair(Comment c, string s1, string s2) { - exists(int i1, int i2, int o1, int o2 | - s1 = c.getText().regexpFind("\\w+", i1, o1) and - s2 = c.getText().regexpFind("\\w+", i2, o2) and - i2 = i1 + 1 and - c.getText().prefix(o1).regexpMatch("[^'\"]*") and - c.getText().substring(o1 + s1.length(), o2).regexpMatch("\\s+") - ) + exists(int i1, int i2, int o1, int o2 | + s1 = c.getText().regexpFind("\\w+", i1, o1) and + s2 = c.getText().regexpFind("\\w+", i2, o2) and + i2 = i1 + 1 and + c.getText().prefix(o1).regexpMatch("[^'\"]*") and + c.getText().substring(o1 + s1.length(), o2).regexpMatch("\\s+") + ) } /** @@ -241,99 +241,99 @@ private predicate word_pair(Comment c, string s1, string s2) { * "with spam" can only be code if the comment contains a colon. */ private predicate non_code(Comment c) { - exists(string word1, string word2 | - word_pair(c, word1, word2) and - not word2 = operator_keyword() - | - not word1 = a_keyword() - or - word1 = keyword_requiring_colon() and not c.getText().matches("%:%") - ) and - /* Except comments of the form: # (maybe code) # some comment */ - not c.getText().regexpMatch("#\\S+\\s.*#.*") + exists(string word1, string word2 | + word_pair(c, word1, word2) and + not word2 = operator_keyword() + | + not word1 = a_keyword() or - /* Don't count doctests as code */ - c.getText().matches("%>>>%") - or - c.getText().matches("%...%") + word1 = keyword_requiring_colon() and not c.getText().matches("%:%") + ) and + /* Except comments of the form: # (maybe code) # some comment */ + not c.getText().regexpMatch("#\\S+\\s.*#.*") + or + /* Don't count doctests as code */ + c.getText().matches("%>>>%") + or + c.getText().matches("%...%") } private predicate filler(Comment c) { c.getText().regexpMatch("#+[\\s*#-_=+]*") } /** Gets the first non empty comment following c */ private Comment non_empty_following(Comment c) { - not empty(result) and - ( - result = empty_following(c).getFollowing() - or - not empty(c) and result = c.getFollowing() - ) + not empty(result) and + ( + result = empty_following(c).getFollowing() + or + not empty(c) and result = c.getFollowing() + ) } /* Helper for non_empty_following() */ private Comment empty_following(Comment c) { - not empty(c) and - empty(result) and - exists(Comment prev | result = prev.getFollowing() | - prev = c - or - prev = empty_following(c) - ) + not empty(c) and + empty(result) and + exists(Comment prev | result = prev.getFollowing() | + prev = c + or + prev = empty_following(c) + ) } private predicate empty(Comment c) { c.getText().regexpMatch("#+\\s*") } /* A comment following code on the same line */ private predicate endline_comment(Comment c) { - exists(Expr e, string f, int line | - e.getLocation().hasLocationInfo(f, line, _, _, _) and - c.getLocation().hasLocationInfo(f, line, _, _, _) - ) + exists(Expr e, string f, int line | + e.getLocation().hasLocationInfo(f, line, _, _, _) and + c.getLocation().hasLocationInfo(f, line, _, _, _) + ) } private predicate file_or_url(Comment c) { - c.getText().regexpMatch("#[^'\"]+(https?|file)://.*") or - c.getText().regexpMatch("#[^'\"]+(/[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") or - c.getText().regexpMatch("#[^'\"]+(\\[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") + c.getText().regexpMatch("#[^'\"]+(https?|file)://.*") or + c.getText().regexpMatch("#[^'\"]+(/[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") or + c.getText().regexpMatch("#[^'\"]+(\\[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") } private string operator_keyword() { - result = "import" or - result = "and" or - result = "is" or - result = "or" or - result = "in" or - result = "not" or - result = "as" + result = "import" or + result = "and" or + result = "is" or + result = "or" or + result = "in" or + result = "not" or + result = "as" } private string keyword_requiring_colon() { - result = "try" or - result = "while" or - result = "elif" or - result = "else" or - result = "if" or - result = "except" or - result = "def" or - result = "class" + result = "try" or + result = "while" or + result = "elif" or + result = "else" or + result = "if" or + result = "except" or + result = "def" or + result = "class" } private string other_keyword() { - result = "del" or - result = "lambda" or - result = "from" or - result = "global" or - result = "with" or - result = "assert" or - result = "yield" or - result = "finally" or - result = "print" or - result = "exec" or - result = "raise" or - result = "return" or - result = "for" + result = "del" or + result = "lambda" or + result = "from" or + result = "global" or + result = "with" or + result = "assert" or + result = "yield" or + result = "finally" or + result = "print" or + result = "exec" or + result = "raise" or + result = "return" or + result = "for" } private string a_keyword() { - result = keyword_requiring_colon() or result = other_keyword() or result = operator_keyword() + result = keyword_requiring_colon() or result = other_keyword() or result = operator_keyword() } diff --git a/python/ql/src/Lexical/OldOctalLiteral.ql b/python/ql/src/Lexical/OldOctalLiteral.ql index 28791d8903d..81b66375ff8 100644 --- a/python/ql/src/Lexical/OldOctalLiteral.ql +++ b/python/ql/src/Lexical/OldOctalLiteral.ql @@ -12,17 +12,17 @@ import python predicate is_old_octal(IntegerLiteral i) { - exists(string text | text = i.getText() | - text.charAt(0) = "0" and - not text = "00" and - exists(text.charAt(1).toInt()) and - /* Do not flag file permission masks */ - exists(int len | len = text.length() | - len != 4 and - len != 5 and - len != 7 - ) + exists(string text | text = i.getText() | + text.charAt(0) = "0" and + not text = "00" and + exists(text.charAt(1).toInt()) and + /* Do not flag file permission masks */ + exists(int len | len = text.length() | + len != 4 and + len != 5 and + len != 7 ) + ) } from IntegerLiteral i diff --git a/python/ql/src/Metrics/CommentRatio.ql b/python/ql/src/Metrics/CommentRatio.ql index 76a185321ac..8ebb27cf304 100644 --- a/python/ql/src/Metrics/CommentRatio.ql +++ b/python/ql/src/Metrics/CommentRatio.ql @@ -16,4 +16,4 @@ import python from Module m, ModuleMetrics mm where mm = m.getMetrics() and mm.getNumberOfLines() > 0 select m, 100.0 * (mm.getNumberOfLinesOfComments().(float) / mm.getNumberOfLines().(float)) as ratio - order by ratio desc + order by ratio desc diff --git a/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql b/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql index b2c319070ea..6eaf7422b18 100644 --- a/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql +++ b/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql @@ -30,15 +30,15 @@ import semmle.python.dependencies.TechInventory */ predicate src_package_count(File sourceFile, ExternalPackage package, int total) { - total = - strictcount(AstNode src | - dependency(src, package) and - src.getLocation().getFile() = sourceFile - ) + total = + strictcount(AstNode src | + dependency(src, package) and + src.getLocation().getFile() = sourceFile + ) } from File sourceFile, int total, string entity, ExternalPackage package where - src_package_count(sourceFile, package, total) and - entity = munge(sourceFile, package) + src_package_count(sourceFile, package, total) and + entity = munge(sourceFile, package) select entity, total order by total desc diff --git a/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql b/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql index 2424d82abeb..c752ec8bc5e 100644 --- a/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql +++ b/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql @@ -19,9 +19,9 @@ import semmle.python.dependencies.TechInventory from File sourceFile, string entity where - exists(PackageObject package, AstNode src | - dependency(src, package) and - src.getLocation().getFile() = sourceFile and - entity = munge(sourceFile, package) - ) + exists(PackageObject package, AstNode src | + dependency(src, package) and + src.getLocation().getFile() = sourceFile and + entity = munge(sourceFile, package) + ) select entity, sourceFile diff --git a/python/ql/src/Metrics/DocStringRatio.ql b/python/ql/src/Metrics/DocStringRatio.ql index 46859560c16..a8cd8b8dc4e 100644 --- a/python/ql/src/Metrics/DocStringRatio.ql +++ b/python/ql/src/Metrics/DocStringRatio.ql @@ -15,5 +15,5 @@ import python from Module m, ModuleMetrics mm where mm = m.getMetrics() and mm.getNumberOfLines() > 0 select m, - 100.0 * (mm.getNumberOfLinesOfDocStrings().(float) / mm.getNumberOfLines().(float)) as ratio - order by ratio desc + 100.0 * (mm.getNumberOfLinesOfDocStrings().(float) / mm.getNumberOfLines().(float)) as ratio + order by ratio desc diff --git a/python/ql/src/Metrics/FLinesOfComments.ql b/python/ql/src/Metrics/FLinesOfComments.ql index bd52f8d5caa..b426a0b25f3 100644 --- a/python/ql/src/Metrics/FLinesOfComments.ql +++ b/python/ql/src/Metrics/FLinesOfComments.ql @@ -14,5 +14,5 @@ import python from Module m, int n where - n = m.getMetrics().getNumberOfLinesOfComments() + m.getMetrics().getNumberOfLinesOfDocStrings() + n = m.getMetrics().getNumberOfLinesOfComments() + m.getMetrics().getNumberOfLinesOfDocStrings() select m, n order by n desc diff --git a/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql b/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql index 28673a258c1..36602310dd5 100644 --- a/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql +++ b/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql @@ -16,11 +16,11 @@ import external.CodeDuplication from File f, int n where - n = - count(int line | - exists(DuplicateBlock d | d.sourceFile() = f | - line in [d.sourceStartLine() .. d.sourceEndLine()] and - not allowlistedLineForDuplication(f, line) - ) - ) + n = + count(int line | + exists(DuplicateBlock d | d.sourceFile() = f | + line in [d.sourceStartLine() .. d.sourceEndLine()] and + not allowlistedLineForDuplication(f, line) + ) + ) select f, n order by n desc diff --git a/python/ql/src/Metrics/FLinesOfSimilarCode.ql b/python/ql/src/Metrics/FLinesOfSimilarCode.ql index 169c4c8f1b5..b9eb3ddfaa1 100644 --- a/python/ql/src/Metrics/FLinesOfSimilarCode.ql +++ b/python/ql/src/Metrics/FLinesOfSimilarCode.ql @@ -16,11 +16,11 @@ import external.CodeDuplication from File f, int n where - n = - count(int line | - exists(SimilarBlock d | d.sourceFile() = f | - line in [d.sourceStartLine() .. d.sourceEndLine()] and - not allowlistedLineForDuplication(f, line) - ) - ) + n = + count(int line | + exists(SimilarBlock d | d.sourceFile() = f | + line in [d.sourceStartLine() .. d.sourceEndLine()] and + not allowlistedLineForDuplication(f, line) + ) + ) select f, n order by n desc diff --git a/python/ql/src/Metrics/History/HChurn.ql b/python/ql/src/Metrics/History/HChurn.ql index e18b8dd528a..d6a8722e6e2 100644 --- a/python/ql/src/Metrics/History/HChurn.ql +++ b/python/ql/src/Metrics/History/HChurn.ql @@ -13,11 +13,11 @@ import external.VCS from Module m, int n where - n = - sum(Commit entry, int churn | - churn = entry.getRecentChurnForFile(m.getFile()) and not artificialChange(entry) - | - churn - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + n = + sum(Commit entry, int churn | + churn = entry.getRecentChurnForFile(m.getFile()) and not artificialChange(entry) + | + churn + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, n order by n desc diff --git a/python/ql/src/Metrics/History/HLinesAdded.ql b/python/ql/src/Metrics/History/HLinesAdded.ql index 239d227f365..3ecec917ab1 100644 --- a/python/ql/src/Metrics/History/HLinesAdded.ql +++ b/python/ql/src/Metrics/History/HLinesAdded.ql @@ -13,11 +13,11 @@ import external.VCS from Module m, int n where - n = - sum(Commit entry, int churn | - churn = entry.getRecentAdditionsForFile(m.getFile()) and not artificialChange(entry) - | - churn - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + n = + sum(Commit entry, int churn | + churn = entry.getRecentAdditionsForFile(m.getFile()) and not artificialChange(entry) + | + churn + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, n order by n desc diff --git a/python/ql/src/Metrics/History/HLinesDeleted.ql b/python/ql/src/Metrics/History/HLinesDeleted.ql index 7f02c17cc2c..ded74756d55 100644 --- a/python/ql/src/Metrics/History/HLinesDeleted.ql +++ b/python/ql/src/Metrics/History/HLinesDeleted.ql @@ -13,11 +13,11 @@ import external.VCS from Module m, int n where - n = - sum(Commit entry, int churn | - churn = entry.getRecentDeletionsForFile(m.getFile()) and not artificialChange(entry) - | - churn - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + n = + sum(Commit entry, int churn | + churn = entry.getRecentDeletionsForFile(m.getFile()) and not artificialChange(entry) + | + churn + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, n order by n desc diff --git a/python/ql/src/Metrics/History/HNumberOfCoCommits.ql b/python/ql/src/Metrics/History/HNumberOfCoCommits.ql index 4f48641e394..afb62353a03 100644 --- a/python/ql/src/Metrics/History/HNumberOfCoCommits.ql +++ b/python/ql/src/Metrics/History/HNumberOfCoCommits.ql @@ -16,8 +16,8 @@ int committedFiles(Commit commit) { result = count(commit.getAnAffectedFile()) } from Module m where exists(m.getMetrics().getNumberOfLinesOfCode()) select m, - avg(Commit commit, int toAvg | - commit.getAnAffectedFile() = m.getFile() and toAvg = committedFiles(commit) - 1 - | - toAvg - ) + avg(Commit commit, int toAvg | + commit.getAnAffectedFile() = m.getFile() and toAvg = committedFiles(commit) - 1 + | + toAvg + ) diff --git a/python/ql/src/Metrics/History/HNumberOfReCommits.ql b/python/ql/src/Metrics/History/HNumberOfReCommits.ql index c1863e934c9..5df9194ee34 100644 --- a/python/ql/src/Metrics/History/HNumberOfReCommits.ql +++ b/python/ql/src/Metrics/History/HNumberOfReCommits.ql @@ -12,21 +12,21 @@ import python import external.VCS predicate inRange(Commit first, Commit second) { - first.getAnAffectedFile() = second.getAnAffectedFile() and - first != second and - exists(int n | - n = first.getDate().daysTo(second.getDate()) and - n >= 0 and - n < 5 - ) + first.getAnAffectedFile() = second.getAnAffectedFile() and + first != second and + exists(int n | + n = first.getDate().daysTo(second.getDate()) and + n >= 0 and + n < 5 + ) } int recommitsForFile(File f) { - result = - count(Commit recommit | - f = recommit.getAnAffectedFile() and - exists(Commit prev | inRange(prev, recommit)) - ) + result = + count(Commit recommit | + f = recommit.getAnAffectedFile() and + exists(Commit prev | inRange(prev, recommit)) + ) } from Module m diff --git a/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql b/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql index 75832cc82bd..e04baa491f4 100644 --- a/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql +++ b/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql @@ -14,11 +14,11 @@ import external.VCS from Module m where exists(m.getMetrics().getNumberOfLinesOfCode()) select m, - count(Author author | - exists(Commit e | - e = author.getACommit() and - m.getFile() = e.getAnAffectedFile() and - e.daysToNow() <= 180 and - not artificialChange(e) - ) + count(Author author | + exists(Commit e | + e = author.getACommit() and + m.getFile() = e.getAnAffectedFile() and + e.daysToNow() <= 180 and + not artificialChange(e) ) + ) diff --git a/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql b/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql index 9b90a73294f..f0d8473b302 100644 --- a/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql +++ b/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql @@ -13,8 +13,8 @@ import external.VCS from Module m where - exists(Commit e | - e.getAnAffectedFile() = m.getFile() and e.daysToNow() <= 180 and not artificialChange(e) - ) and - exists(m.getMetrics().getNumberOfLinesOfCode()) + exists(Commit e | + e.getAnAffectedFile() = m.getFile() and e.daysToNow() <= 180 and not artificialChange(e) + ) and + exists(m.getMetrics().getNumberOfLinesOfCode()) select m, 1 diff --git a/python/ql/src/Metrics/Internal/Extents.qll b/python/ql/src/Metrics/Internal/Extents.qll index 024d0e26f4c..1e38a4d544d 100644 --- a/python/ql/src/Metrics/Internal/Extents.qll +++ b/python/ql/src/Metrics/Internal/Extents.qll @@ -15,18 +15,19 @@ import python * including the body (if any), as opposed to the location of its name only. */ class RangeFunction extends Function { - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) + } } /** @@ -34,16 +35,17 @@ class RangeFunction extends Function { * including the body (if any), as opposed to the location of its name only. */ class RangeClass extends Class { - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) + } } diff --git a/python/ql/src/Numerics/Pythagorean.ql b/python/ql/src/Numerics/Pythagorean.ql index 6522da8a2b2..bda74405318 100644 --- a/python/ql/src/Numerics/Pythagorean.ql +++ b/python/ql/src/Numerics/Pythagorean.ql @@ -12,34 +12,34 @@ import python predicate squareOp(BinaryExpr e) { - e.getOp() instanceof Pow and e.getRight().(IntegerLiteral).getN() = "2" + e.getOp() instanceof Pow and e.getRight().(IntegerLiteral).getN() = "2" } predicate squareMul(BinaryExpr e) { - e.getOp() instanceof Mult and e.getRight().(Name).getId() = e.getLeft().(Name).getId() + e.getOp() instanceof Mult and e.getRight().(Name).getId() = e.getLeft().(Name).getId() } predicate squareRef(Name e) { - e.isUse() and - exists(SsaVariable v, Expr s | v.getVariable() = e.getVariable() | - s = v.getDefinition().getNode().getParentNode().(AssignStmt).getValue() and - square(s) - ) + e.isUse() and + exists(SsaVariable v, Expr s | v.getVariable() = e.getVariable() | + s = v.getDefinition().getNode().getParentNode().(AssignStmt).getValue() and + square(s) + ) } predicate square(Expr e) { - squareOp(e) - or - squareMul(e) - or - squareRef(e) + squareOp(e) + or + squareMul(e) + or + squareRef(e) } from Call c, BinaryExpr s where - c.getFunc().toString() = "sqrt" and - c.getArg(0) = s and - s.getOp() instanceof Add and - square(s.getLeft()) and - square(s.getRight()) + c.getFunc().toString() = "sqrt" and + c.getArg(0) = s and + s.getOp() instanceof Add and + square(s.getLeft()) and + square(s.getRight()) select c, "Pythagorean calculation with sub-optimal numerics" diff --git a/python/ql/src/Resources/FileNotAlwaysClosed.ql b/python/ql/src/Resources/FileNotAlwaysClosed.ql index 6a2e6201a63..5b5a869e62a 100755 --- a/python/ql/src/Resources/FileNotAlwaysClosed.ql +++ b/python/ql/src/Resources/FileNotAlwaysClosed.ql @@ -20,55 +20,55 @@ import FileOpen * either `__enter__` and `__exit__` or `__init__` and `__del__` */ predicate opened_in_enter_closed_in_exit(ControlFlowNode open) { - file_not_closed_at_scope_exit(open) and - exists(FunctionValue entry, FunctionValue exit | - open.getScope() = entry.getScope() and - exists(ClassValue cls | - cls.declaredAttribute("__enter__") = entry and cls.declaredAttribute("__exit__") = exit - or - cls.declaredAttribute("__init__") = entry and cls.declaredAttribute("__del__") = exit - ) and - exists(AttrNode attr_open, AttrNode attrclose | - attr_open.getScope() = entry.getScope() and - attrclose.getScope() = exit.getScope() and - expr_is_open(attr_open.(DefinitionNode).getValue(), open) and - attr_open.getName() = attrclose.getName() and - close_method_call(_, attrclose) - ) + file_not_closed_at_scope_exit(open) and + exists(FunctionValue entry, FunctionValue exit | + open.getScope() = entry.getScope() and + exists(ClassValue cls | + cls.declaredAttribute("__enter__") = entry and cls.declaredAttribute("__exit__") = exit + or + cls.declaredAttribute("__init__") = entry and cls.declaredAttribute("__del__") = exit + ) and + exists(AttrNode attr_open, AttrNode attrclose | + attr_open.getScope() = entry.getScope() and + attrclose.getScope() = exit.getScope() and + expr_is_open(attr_open.(DefinitionNode).getValue(), open) and + attr_open.getName() = attrclose.getName() and + close_method_call(_, attrclose) ) + ) } predicate file_not_closed_at_scope_exit(ControlFlowNode open) { - exists(EssaVariable v | - BaseFlow::reaches_exit(v) and - var_is_open(v, open) and - not file_is_returned(v, open) - ) - or - call_to_open(open) and - not exists(AssignmentDefinition def | def.getValue() = open) and - not exists(Return r | r.getValue() = open.getNode()) + exists(EssaVariable v | + BaseFlow::reaches_exit(v) and + var_is_open(v, open) and + not file_is_returned(v, open) + ) + or + call_to_open(open) and + not exists(AssignmentDefinition def | def.getValue() = open) and + not exists(Return r | r.getValue() = open.getNode()) } predicate file_not_closed_at_exception_exit(ControlFlowNode open, ControlFlowNode exit) { - exists(EssaVariable v | - exit.(RaisingNode).viableExceptionalExit(_, _) and - not closes_arg(exit, v.getSourceVariable()) and - not close_method_call(exit, v.getAUse().(NameNode)) and - var_is_open(v, open) and - v.getAUse() = exit.getAChild*() - ) + exists(EssaVariable v | + exit.(RaisingNode).viableExceptionalExit(_, _) and + not closes_arg(exit, v.getSourceVariable()) and + not close_method_call(exit, v.getAUse().(NameNode)) and + var_is_open(v, open) and + v.getAUse() = exit.getAChild*() + ) } /* Check to see if a file is opened but not closed or returned */ from ControlFlowNode defn, string message where - not opened_in_enter_closed_in_exit(defn) and - ( - file_not_closed_at_scope_exit(defn) and message = "File is opened but is not closed." - or - not file_not_closed_at_scope_exit(defn) and - file_not_closed_at_exception_exit(defn, _) and - message = "File may not be closed if an exception is raised." - ) + not opened_in_enter_closed_in_exit(defn) and + ( + file_not_closed_at_scope_exit(defn) and message = "File is opened but is not closed." + or + not file_not_closed_at_scope_exit(defn) and + file_not_closed_at_exception_exit(defn, _) and + message = "File may not be closed if an exception is raised." + ) select defn.getNode(), message diff --git a/python/ql/src/Resources/FileOpen.qll b/python/ql/src/Resources/FileOpen.qll index 5a7aae0595b..c2df0d6263a 100644 --- a/python/ql/src/Resources/FileOpen.qll +++ b/python/ql/src/Resources/FileOpen.qll @@ -6,155 +6,155 @@ import semmle.python.pointsto.Filters /** Holds if `open` is a call that returns a newly opened file */ predicate call_to_open(ControlFlowNode open) { - exists(FunctionValue f | - function_opens_file(f) and - f.getACall() = open - ) and - /* If in `with` statement, then it will be automatically closed. So just treat as not opened */ - not exists(With w | w.getContextExpr() = open.getNode()) + exists(FunctionValue f | + function_opens_file(f) and + f.getACall() = open + ) and + /* If in `with` statement, then it will be automatically closed. So just treat as not opened */ + not exists(With w | w.getContextExpr() = open.getNode()) } /** Holds if `n` refers to a file opened at `open` */ predicate expr_is_open(ControlFlowNode n, ControlFlowNode open) { - call_to_open(open) and open = n + call_to_open(open) and open = n + or + exists(EssaVariable v | + n instanceof NameNode and + var_is_open(v, open) + | + n = v.getAUse() or - exists(EssaVariable v | - n instanceof NameNode and - var_is_open(v, open) - | - n = v.getAUse() - or - wraps_file(n, v) - ) + wraps_file(n, v) + ) } /** Holds if `call` wraps the object referred to by `v` and returns it */ private predicate wraps_file(CallNode call, EssaVariable v) { - exists(ClassValue cls | - call = cls.getACall() and - call.getAnArg() = v.getAUse() - ) + exists(ClassValue cls | + call = cls.getACall() and + call.getAnArg() = v.getAUse() + ) } /** Holds if `var` refers to a file opened at `open` */ predicate var_is_open(EssaVariable v, ControlFlowNode open) { - def_is_open(v.getDefinition(), open) and - /* If use in context expression in `with` statement, then it will be automatically closed. */ - not exists(With w | w.getContextExpr() = v.getAUse().getNode()) + def_is_open(v.getDefinition(), open) and + /* If use in context expression in `with` statement, then it will be automatically closed. */ + not exists(With w | w.getContextExpr() = v.getAUse().getNode()) } /** Holds if `test` will pass through an open file in variable `v` for the `sense` successor */ predicate passes_open_files(Variable v, ControlFlowNode test, boolean sense) { - // `if fd.closed:` - exists(AttrNode closed | - closed = test and - closed.getObject("closed") = v.getAUse() - ) and - sense = false - or - // `if fd ==/is ...:` most commonly `if fd is None:` - equality_test(test, v.getAUse(), sense.booleanNot(), _) - or - // `if fd:` - test = v.getAUse() and sense = true - or - exists(UnaryExprNode n | - n = test and - n.getNode().getOp() instanceof Not - | - passes_open_files(v, n.getOperand(), sense.booleanNot()) - ) + // `if fd.closed:` + exists(AttrNode closed | + closed = test and + closed.getObject("closed") = v.getAUse() + ) and + sense = false + or + // `if fd ==/is ...:` most commonly `if fd is None:` + equality_test(test, v.getAUse(), sense.booleanNot(), _) + or + // `if fd:` + test = v.getAUse() and sense = true + or + exists(UnaryExprNode n | + n = test and + n.getNode().getOp() instanceof Not + | + passes_open_files(v, n.getOperand(), sense.booleanNot()) + ) } /* Helper for `def_is_open` to give better join order */ private predicate passes_open_files(PyEdgeRefinement refinement) { - passes_open_files(refinement.getSourceVariable(), refinement.getPredecessor().getLastNode(), - refinement.getSense()) + passes_open_files(refinement.getSourceVariable(), refinement.getPredecessor().getLastNode(), + refinement.getSense()) } /** Holds if `def` refers to a file opened at `open` */ predicate def_is_open(EssaDefinition def, ControlFlowNode open) { - expr_is_open(def.(AssignmentDefinition).getValue(), open) - or - exists(PyEdgeRefinement refinement | refinement = def | - var_is_open(refinement.getInput(), open) and - passes_open_files(refinement) - ) - or - exists(EssaNodeRefinement refinement | refinement = def | - not closes_file(def) and - not wraps_file(refinement.getDefiningNode(), refinement.getInput()) and - var_is_open(refinement.getInput(), open) - ) - or - var_is_open(def.(PhiFunction).getAnInput(), open) + expr_is_open(def.(AssignmentDefinition).getValue(), open) + or + exists(PyEdgeRefinement refinement | refinement = def | + var_is_open(refinement.getInput(), open) and + passes_open_files(refinement) + ) + or + exists(EssaNodeRefinement refinement | refinement = def | + not closes_file(def) and + not wraps_file(refinement.getDefiningNode(), refinement.getInput()) and + var_is_open(refinement.getInput(), open) + ) + or + var_is_open(def.(PhiFunction).getAnInput(), open) } /** Holds if `call` closes a file */ predicate closes_file(EssaNodeRefinement call) { - closes_arg(call.(ArgumentRefinement).getDefiningNode(), call.getSourceVariable()) or - close_method_call(call.(MethodCallsiteRefinement).getCall(), - call.getSourceVariable().(Variable).getAUse()) + closes_arg(call.(ArgumentRefinement).getDefiningNode(), call.getSourceVariable()) or + close_method_call(call.(MethodCallsiteRefinement).getCall(), + call.getSourceVariable().(Variable).getAUse()) } /** Holds if `call` closes its argument, which is an open file referred to by `v` */ predicate closes_arg(CallNode call, Variable v) { - call.getAnArg() = v.getAUse() and - ( - exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) - or - call.getFunction().(NameNode).getId() = "close" - ) + call.getAnArg() = v.getAUse() and + ( + exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) + or + call.getFunction().(NameNode).getId() = "close" + ) } /** Holds if `call` closes its 'self' argument, which is an open file referred to by `v` */ predicate close_method_call(CallNode call, ControlFlowNode self) { - call.getFunction().(AttrNode).getObject() = self and - exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) - or - call.getFunction().(AttrNode).getObject("close") = self + call.getFunction().(AttrNode).getObject() = self and + exists(FunctionValue close | call = close.getACall() and function_closes_file(close)) + or + call.getFunction().(AttrNode).getObject("close") = self } /** Holds if `close` is a function that appears to close files that are passed to it as an argument. */ predicate function_closes_file(FunctionValue close) { - close = Value::named("os.close") - or - function_should_close_parameter(close.getScope()) + close = Value::named("os.close") + or + function_should_close_parameter(close.getScope()) } /** INTERNAL - Helper predicate for `function_closes_file` */ predicate function_should_close_parameter(Function func) { - exists(EssaDefinition def | - closes_file(def) and - def.getSourceVariable().(Variable).getScope() = func - ) + exists(EssaDefinition def | + closes_file(def) and + def.getSourceVariable().(Variable).getScope() = func + ) } /** Holds if the function `f` opens a file, either directly or indirectly. */ predicate function_opens_file(FunctionValue f) { - f = Value::named("open") - or - exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() | - ret.getValue().getAFlowNode() = v.getAUse() and - var_is_open(v, _) - ) - or - exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() | - ret.getValue().getAFlowNode() = callee.getACall() and - function_opens_file(callee) - ) + f = Value::named("open") + or + exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() | + ret.getValue().getAFlowNode() = v.getAUse() and + var_is_open(v, _) + ) + or + exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() | + ret.getValue().getAFlowNode() = callee.getACall() and + function_opens_file(callee) + ) } /** Holds if the variable `v` refers to a file opened at `open` which is subsequently returned from a function. */ predicate file_is_returned(EssaVariable v, ControlFlowNode open) { - exists(NameNode n, Return ret | - var_is_open(v, open) and - v.getAUse() = n - | - ret.getValue() = n.getNode() - or - ret.getValue().(Tuple).getAnElt() = n.getNode() - or - ret.getValue().(List).getAnElt() = n.getNode() - ) + exists(NameNode n, Return ret | + var_is_open(v, open) and + v.getAUse() = n + | + ret.getValue() = n.getNode() + or + ret.getValue().(Tuple).getAnElt() = n.getNode() + or + ret.getValue().(List).getAnElt() = n.getNode() + ) } diff --git a/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql b/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql index b787c9094d2..0c160111fba 100644 --- a/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql +++ b/python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql @@ -15,24 +15,24 @@ import python Value aSocket() { result.getClass() = Value::named("socket.socket") } CallNode socketBindCall() { - result = aSocket().attr("bind").(CallableValue).getACall() and major_version() = 3 - or - result.getFunction().(AttrNode).getObject("bind").pointsTo(aSocket()) and - major_version() = 2 + result = aSocket().attr("bind").(CallableValue).getACall() and major_version() = 3 + or + result.getFunction().(AttrNode).getObject("bind").pointsTo(aSocket()) and + major_version() = 2 } string allInterfaces() { result = "0.0.0.0" or result = "" } Value getTextValue(string address) { - result = Value::forUnicode(address) and major_version() = 3 - or - result = Value::forString(address) and major_version() = 2 + result = Value::forUnicode(address) and major_version() = 3 + or + result = Value::forString(address) and major_version() = 2 } from CallNode call, TupleValue args, string address where - call = socketBindCall() and - call.getArg(0).pointsTo(args) and - args.getItem(0) = getTextValue(address) and - address = allInterfaces() + call = socketBindCall() and + call.getArg(0).pointsTo(args) and + args.getItem(0) = getTextValue(address) and + address = allInterfaces() select call.getNode(), "'" + address + "' binds a socket to all interfaces." diff --git a/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql b/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql index 3fb7046f8cc..cebaa4fdd2e 100644 --- a/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql +++ b/python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql @@ -21,21 +21,21 @@ private string commonTopLevelDomainRegex() { result = "com|org|edu|gov|uk|net|io */ bindingset[pattern] predicate isIncompleteHostNameRegExpPattern(string pattern, string hostPart) { - hostPart = - pattern - .regexpCapture("(?i).*" + - // an unescaped single `.` - "(?:` clears taint on its `false` edge. */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof TarFileInfo and - clears_taint_on_false_edge(test.getTest(), test.getSense()) - } + /** The test `if :` clears taint on its `false` edge. */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof TarFileInfo and + clears_taint_on_false_edge(test.getTest(), test.getSense()) + } - private predicate clears_taint_on_false_edge(ControlFlowNode test, boolean sense) { - path_sanitizing_test(test) and - sense = false - or - // handle `not` (also nested) - test.(UnaryExprNode).getNode().getOp() instanceof Not and - clears_taint_on_false_edge(test.(UnaryExprNode).getOperand(), sense.booleanNot()) - } + private predicate clears_taint_on_false_edge(ControlFlowNode test, boolean sense) { + path_sanitizing_test(test) and + sense = false + or + // handle `not` (also nested) + test.(UnaryExprNode).getNode().getOp() instanceof Not and + clears_taint_on_false_edge(test.(UnaryExprNode).getOperand(), sense.booleanNot()) + } } private predicate path_sanitizing_test(ControlFlowNode test) { - /* Assume that any test with "path" in it is a sanitizer */ - test.getAChild+().(AttrNode).getName().matches("%path") - or - test.getAChild+().(NameNode).getId().matches("%path") + /* Assume that any test with "path" in it is a sanitizer */ + test.getAChild+().(AttrNode).getName().matches("%path") + or + test.getAChild+().(NameNode).getId().matches("%path") } class TarSlipConfiguration extends TaintTracking::Configuration { - TarSlipConfiguration() { this = "TarSlip configuration" } + TarSlipConfiguration() { this = "TarSlip configuration" } - override predicate isSource(TaintTracking::Source source) { source instanceof TarfileOpen } + override predicate isSource(TaintTracking::Source source) { source instanceof TarfileOpen } - override predicate isSink(TaintTracking::Sink sink) { - sink instanceof ExtractSink or - sink instanceof ExtractAllSink or - sink instanceof ExtractMembersSink - } + override predicate isSink(TaintTracking::Sink sink) { + sink instanceof ExtractSink or + sink instanceof ExtractAllSink or + sink instanceof ExtractMembersSink + } - override predicate isSanitizer(Sanitizer sanitizer) { - sanitizer instanceof TarFileInfoSanitizer - or - sanitizer instanceof ExcludeTarFilePy - } + override predicate isSanitizer(Sanitizer sanitizer) { + sanitizer instanceof TarFileInfoSanitizer + or + sanitizer instanceof ExcludeTarFilePy + } - override predicate isBarrier(DataFlow::Node node) { - // Avoid flow into the tarfile module - exists(ParameterDefinition def | - node.asVariable().getDefinition() = def - or - node.asCfgNode() = def.getDefiningNode() - | - def.getScope() = Value::named("tarfile.open").(CallableValue).getScope() - or - def.isSelf() and def.getScope().getEnclosingModule().getName() = "tarfile" - ) - } + override predicate isBarrier(DataFlow::Node node) { + // Avoid flow into the tarfile module + exists(ParameterDefinition def | + node.asVariable().getDefinition() = def + or + node.asCfgNode() = def.getDefiningNode() + | + def.getScope() = Value::named("tarfile.open").(CallableValue).getScope() + or + def.isSelf() and def.getScope().getEnclosingModule().getName() = "tarfile" + ) + } } from TarSlipConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "Extraction of tarfile from $@", src.getSource(), - "a potentially untrusted source" + "a potentially untrusted source" diff --git a/python/ql/src/Security/CWE-078/CommandInjection.ql b/python/ql/src/Security/CWE-078/CommandInjection.ql index 61ae6db00cd..51b46d72e5d 100755 --- a/python/ql/src/Security/CWE-078/CommandInjection.ql +++ b/python/ql/src/Security/CWE-078/CommandInjection.ql @@ -22,22 +22,22 @@ import semmle.python.web.HttpRequest import semmle.python.security.injection.Command class CommandInjectionConfiguration extends TaintTracking::Configuration { - CommandInjectionConfiguration() { this = "Command injection configuration" } + CommandInjectionConfiguration() { this = "Command injection configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } - override predicate isExtension(TaintTracking::Extension extension) { - extension instanceof FirstElementFlow - or - extension instanceof FabricExecuteExtension - } + override predicate isExtension(TaintTracking::Extension extension) { + extension instanceof FirstElementFlow + or + extension instanceof FabricExecuteExtension + } } from CommandInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "This command depends on $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql index f04e442c8ce..ff3870678d8 100644 --- a/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql +++ b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql @@ -25,24 +25,24 @@ import python */ ClassValue jinja2EnvironmentOrTemplate() { - result = Value::named("jinja2.Environment") - or - result = Value::named("jinja2.Template") + result = Value::named("jinja2.Environment") + or + result = Value::named("jinja2.Template") } ControlFlowNode getAutoEscapeParameter(CallNode call) { result = call.getArgByName("autoescape") } from CallNode call where - call.getFunction().pointsTo(jinja2EnvironmentOrTemplate()) and - not exists(call.getNode().getStarargs()) and - not exists(call.getNode().getKwargs()) and - ( - not exists(getAutoEscapeParameter(call)) - or - exists(Value isFalse | - getAutoEscapeParameter(call).pointsTo(isFalse) and - isFalse.getDefiniteBooleanValue() = false - ) + call.getFunction().pointsTo(jinja2EnvironmentOrTemplate()) and + not exists(call.getNode().getStarargs()) and + not exists(call.getNode().getKwargs()) and + ( + not exists(getAutoEscapeParameter(call)) + or + exists(Value isFalse | + getAutoEscapeParameter(call).pointsTo(isFalse) and + isFalse.getDefiniteBooleanValue() = false ) + ) select call, "Using jinja2 templates with autoescape=False can potentially allow XSS attacks." diff --git a/python/ql/src/Security/CWE-079/ReflectedXss.ql b/python/ql/src/Security/CWE-079/ReflectedXss.ql index cea41442c5b..9ee38e2ed24 100644 --- a/python/ql/src/Security/CWE-079/ReflectedXss.ql +++ b/python/ql/src/Security/CWE-079/ReflectedXss.ql @@ -22,21 +22,21 @@ import semmle.python.web.HttpResponse import semmle.python.security.strings.Untrusted class ReflectedXssConfiguration extends TaintTracking::Configuration { - ReflectedXssConfiguration() { this = "Reflected XSS configuration" } + ReflectedXssConfiguration() { this = "Reflected XSS configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { - sink instanceof HttpResponseTaintSink and - not sink instanceof DjangoResponseContent - or - sink instanceof DjangoResponseContentXSSVulnerable - } + override predicate isSink(TaintTracking::Sink sink) { + sink instanceof HttpResponseTaintSink and + not sink instanceof DjangoResponseContent + or + sink instanceof DjangoResponseContentXSSVulnerable + } } from ReflectedXssConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "Cross-site scripting vulnerability due to $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-089/SqlInjection.ql b/python/ql/src/Security/CWE-089/SqlInjection.ql index 86695fdf2ca..b4bff49d2cc 100755 --- a/python/ql/src/Security/CWE-089/SqlInjection.ql +++ b/python/ql/src/Security/CWE-089/SqlInjection.ql @@ -21,13 +21,13 @@ import semmle.python.web.django.Db import semmle.python.web.django.Model class SQLInjectionConfiguration extends TaintTracking::Configuration { - SQLInjectionConfiguration() { this = "SQL injection configuration" } + SQLInjectionConfiguration() { this = "SQL injection configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof SqlInjectionSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof SqlInjectionSink } } /* @@ -37,15 +37,15 @@ class SQLInjectionConfiguration extends TaintTracking::Configuration { */ class DbConfiguration extends TaintTracking::Configuration { - DbConfiguration() { this = "DB configuration" } + DbConfiguration() { this = "DB configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof DjangoModelObjects or - source instanceof DbConnectionSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof DjangoModelObjects or + source instanceof DbConnectionSource + } } from SQLInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "This SQL query depends on $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-094/CodeInjection.ql b/python/ql/src/Security/CWE-094/CodeInjection.ql index 5aa5aa4c5c4..ed8b5d4e3e3 100644 --- a/python/ql/src/Security/CWE-094/CodeInjection.ql +++ b/python/ql/src/Security/CWE-094/CodeInjection.ql @@ -22,16 +22,16 @@ import semmle.python.web.HttpRequest import semmle.python.security.injection.Exec class CodeInjectionConfiguration extends TaintTracking::Configuration { - CodeInjectionConfiguration() { this = "Code injection configuration" } + CodeInjectionConfiguration() { this = "Code injection configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof StringEvaluationNode } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof StringEvaluationNode } } from CodeInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ flows to here and is interpreted as code.", src.getSource(), - "A user-provided value" + "A user-provided value" diff --git a/python/ql/src/Security/CWE-209/StackTraceExposure.ql b/python/ql/src/Security/CWE-209/StackTraceExposure.ql index 928cd44600a..27d89607a29 100644 --- a/python/ql/src/Security/CWE-209/StackTraceExposure.ql +++ b/python/ql/src/Security/CWE-209/StackTraceExposure.ql @@ -18,14 +18,14 @@ import semmle.python.security.Exceptions import semmle.python.web.HttpResponse class StackTraceExposureConfiguration extends TaintTracking::Configuration { - StackTraceExposureConfiguration() { this = "Stack trace exposure configuration" } + StackTraceExposureConfiguration() { this = "Stack trace exposure configuration" } - override predicate isSource(TaintTracking::Source source) { source instanceof ErrorInfoSource } + override predicate isSource(TaintTracking::Source source) { source instanceof ErrorInfoSource } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpResponseTaintSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpResponseTaintSink } } from StackTraceExposureConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ may be exposed to an external user", src.getSource(), - "Error information" + "Error information" diff --git a/python/ql/src/Security/CWE-215/FlaskDebug.ql b/python/ql/src/Security/CWE-215/FlaskDebug.ql index f7869a9829d..a33d6fd788f 100644 --- a/python/ql/src/Security/CWE-215/FlaskDebug.ql +++ b/python/ql/src/Security/CWE-215/FlaskDebug.ql @@ -15,8 +15,8 @@ import semmle.python.web.flask.General from CallNode call, Value isTrue where - call = theFlaskClass().declaredAttribute("run").(FunctionValue).getACall() and - call.getArgByName("debug").pointsTo(isTrue) and - isTrue.getDefiniteBooleanValue() = true + call = theFlaskClass().declaredAttribute("run").(FunctionValue).getACall() and + call.getArgByName("debug").pointsTo(isTrue) and + isTrue.getDefiniteBooleanValue() = true select call, - "A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger." + "A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger." diff --git a/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql b/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql index 751e609b460..2241a212690 100644 --- a/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql +++ b/python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql @@ -14,21 +14,21 @@ import python private ModuleValue theParamikoClientModule() { result = Value::named("paramiko.client") } private ClassValue theParamikoSSHClientClass() { - result = theParamikoClientModule().attr("SSHClient") + result = theParamikoClientModule().attr("SSHClient") } private ClassValue unsafe_paramiko_policy(string name) { - (name = "AutoAddPolicy" or name = "WarningPolicy") and - result = theParamikoClientModule().attr(name) + (name = "AutoAddPolicy" or name = "WarningPolicy") and + result = theParamikoClientModule().attr(name) } from CallNode call, ControlFlowNode arg, string name where - call = - theParamikoSSHClientClass().lookup("set_missing_host_key_policy").(FunctionValue).getACall() and - arg = call.getAnArg() and - ( - arg.pointsTo(unsafe_paramiko_policy(name)) or - arg.pointsTo().getClass() = unsafe_paramiko_policy(name) - ) + call = + theParamikoSSHClientClass().lookup("set_missing_host_key_policy").(FunctionValue).getACall() and + arg = call.getAnArg() and + ( + arg.pointsTo(unsafe_paramiko_policy(name)) or + arg.pointsTo().getClass() = unsafe_paramiko_policy(name) + ) select call, "Setting missing host key policy to " + name + " may be unsafe." diff --git a/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql b/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql index b6b22a3f255..173ffbe7671 100644 --- a/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql +++ b/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql @@ -19,8 +19,8 @@ predicate falseNotNone(Value v) { v.getDefiniteBooleanValue() = false and not v from CallNode call, FunctionValue func, Value falsey, ControlFlowNode origin where - func = requestFunction() and - func.getACall() = call and - falseNotNone(falsey) and - call.getArgByName("verify").pointsTo(falsey, origin) + func = requestFunction() and + func.getACall() = call and + falseNotNone(falsey) and + call.getArgByName("verify").pointsTo(falsey, origin) select call, "Call to $@ with verify=$@", func, "requests." + func.getName(), origin, "False" diff --git a/python/ql/src/Security/CWE-312/CleartextLogging.ql b/python/ql/src/Security/CWE-312/CleartextLogging.ql index d1c6ac94d4b..071ab9db141 100644 --- a/python/ql/src/Security/CWE-312/CleartextLogging.ql +++ b/python/ql/src/Security/CWE-312/CleartextLogging.ql @@ -19,19 +19,19 @@ import semmle.python.security.SensitiveData import semmle.python.security.ClearText class CleartextLoggingConfiguration extends TaintTracking::Configuration { - CleartextLoggingConfiguration() { this = "ClearTextLogging" } + CleartextLoggingConfiguration() { this = "ClearTextLogging" } - override predicate isSource(DataFlow::Node src, TaintKind kind) { - src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) - } + override predicate isSource(DataFlow::Node src, TaintKind kind) { + src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) + } - override predicate isSink(DataFlow::Node sink, TaintKind kind) { - sink.asCfgNode() instanceof ClearTextLogging::Sink and - kind instanceof SensitiveData - } + override predicate isSink(DataFlow::Node sink, TaintKind kind) { + sink.asCfgNode() instanceof ClearTextLogging::Sink and + kind instanceof SensitiveData + } } from CleartextLoggingConfiguration config, TaintedPathSource source, TaintedPathSink sink where config.hasFlowPath(source, sink) select sink.getSink(), source, sink, "Sensitive data returned by $@ is logged here.", - source.getSource(), source.getCfgNode().(SensitiveData::Source).repr() + source.getSource(), source.getCfgNode().(SensitiveData::Source).repr() diff --git a/python/ql/src/Security/CWE-312/CleartextStorage.ql b/python/ql/src/Security/CWE-312/CleartextStorage.ql index f1f898b00dd..2c33837b464 100644 --- a/python/ql/src/Security/CWE-312/CleartextStorage.ql +++ b/python/ql/src/Security/CWE-312/CleartextStorage.ql @@ -19,19 +19,19 @@ import semmle.python.security.SensitiveData import semmle.python.security.ClearText class CleartextStorageConfiguration extends TaintTracking::Configuration { - CleartextStorageConfiguration() { this = "ClearTextStorage" } + CleartextStorageConfiguration() { this = "ClearTextStorage" } - override predicate isSource(DataFlow::Node src, TaintKind kind) { - src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) - } + override predicate isSource(DataFlow::Node src, TaintKind kind) { + src.asCfgNode().(SensitiveData::Source).isSourceOf(kind) + } - override predicate isSink(DataFlow::Node sink, TaintKind kind) { - sink.asCfgNode() instanceof ClearTextStorage::Sink and - kind instanceof SensitiveData - } + override predicate isSink(DataFlow::Node sink, TaintKind kind) { + sink.asCfgNode() instanceof ClearTextStorage::Sink and + kind instanceof SensitiveData + } } from CleartextStorageConfiguration config, TaintedPathSource source, TaintedPathSink sink where config.hasFlowPath(source, sink) select sink.getSink(), source, sink, "Sensitive data from $@ is stored here.", source.getSource(), - source.getCfgNode().(SensitiveData::Source).repr() + source.getCfgNode().(SensitiveData::Source).repr() diff --git a/python/ql/src/Security/CWE-326/WeakCrypto.ql b/python/ql/src/Security/CWE-326/WeakCrypto.ql index 1d1637f19a5..27c1fcce429 100644 --- a/python/ql/src/Security/CWE-326/WeakCrypto.ql +++ b/python/ql/src/Security/CWE-326/WeakCrypto.ql @@ -12,70 +12,70 @@ import python int minimumSecureKeySize(string algo) { - algo = "DSA" and result = 2048 - or - algo = "RSA" and result = 2048 - or - algo = "ECC" and result = 224 + algo = "DSA" and result = 2048 + or + algo = "RSA" and result = 2048 + or + algo = "ECC" and result = 224 } predicate dsaRsaKeySizeArg(FunctionValue func, string algorithm, string arg) { - exists(ModuleValue mod | func = mod.attr(_) | - algorithm = "DSA" and - ( - mod = Module::named("cryptography.hazmat.primitives.asymmetric.dsa") and arg = "key_size" - or - mod = Module::named("Crypto.PublicKey.DSA") and arg = "bits" - or - mod = Module::named("Cryptodome.PublicKey.DSA") and arg = "bits" - ) - or - algorithm = "RSA" and - ( - mod = Module::named("cryptography.hazmat.primitives.asymmetric.rsa") and arg = "key_size" - or - mod = Module::named("Crypto.PublicKey.RSA") and arg = "bits" - or - mod = Module::named("Cryptodome.PublicKey.RSA") and arg = "bits" - ) + exists(ModuleValue mod | func = mod.attr(_) | + algorithm = "DSA" and + ( + mod = Module::named("cryptography.hazmat.primitives.asymmetric.dsa") and arg = "key_size" + or + mod = Module::named("Crypto.PublicKey.DSA") and arg = "bits" + or + mod = Module::named("Cryptodome.PublicKey.DSA") and arg = "bits" ) + or + algorithm = "RSA" and + ( + mod = Module::named("cryptography.hazmat.primitives.asymmetric.rsa") and arg = "key_size" + or + mod = Module::named("Crypto.PublicKey.RSA") and arg = "bits" + or + mod = Module::named("Cryptodome.PublicKey.RSA") and arg = "bits" + ) + ) } predicate ecKeySizeArg(FunctionValue func, string arg) { - exists(ModuleValue mod | func = mod.attr(_) | - mod = Module::named("cryptography.hazmat.primitives.asymmetric.ec") and arg = "curve" - ) + exists(ModuleValue mod | func = mod.attr(_) | + mod = Module::named("cryptography.hazmat.primitives.asymmetric.ec") and arg = "curve" + ) } int keySizeFromCurve(ClassValue curveClass) { - result = curveClass.declaredAttribute("key_size").(NumericValue).getIntValue() + result = curveClass.declaredAttribute("key_size").(NumericValue).getIntValue() } predicate algorithmAndKeysizeForCall( - CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin + CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin ) { - exists(FunctionValue func, string argname, ControlFlowNode arg | - arg = func.getNamedArgumentForCall(call, argname) - | - exists(NumericValue key | - arg.pointsTo(key, keyOrigin) and - dsaRsaKeySizeArg(func, algorithm, argname) and - keySize = key.getIntValue() - ) - or - exists(Value curveClassInstance | - algorithm = "ECC" and - ecKeySizeArg(func, argname) and - arg.pointsTo(_, curveClassInstance, keyOrigin) and - keySize = keySizeFromCurve(curveClassInstance.getClass()) - ) + exists(FunctionValue func, string argname, ControlFlowNode arg | + arg = func.getNamedArgumentForCall(call, argname) + | + exists(NumericValue key | + arg.pointsTo(key, keyOrigin) and + dsaRsaKeySizeArg(func, algorithm, argname) and + keySize = key.getIntValue() ) + or + exists(Value curveClassInstance | + algorithm = "ECC" and + ecKeySizeArg(func, argname) and + arg.pointsTo(_, curveClassInstance, keyOrigin) and + keySize = keySizeFromCurve(curveClassInstance.getClass()) + ) + ) } from CallNode call, string algo, int keySize, ControlFlowNode origin where - algorithmAndKeysizeForCall(call, algo, keySize, origin) and - keySize < minimumSecureKeySize(algo) + algorithmAndKeysizeForCall(call, algo, keySize, origin) and + keySize < minimumSecureKeySize(algo) select call, - "Creation of an " + algo + " key uses $@ bits, which is below " + minimumSecureKeySize(algo) + - " and considered breakable.", origin, keySize.toString() + "Creation of an " + algo + " key uses $@ bits, which is below " + minimumSecureKeySize(algo) + + " and considered breakable.", origin, keySize.toString() diff --git a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql index 55d0f79f791..36064dc0386 100644 --- a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql +++ b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql @@ -15,16 +15,16 @@ import semmle.python.security.SensitiveData import semmle.python.security.Crypto class BrokenCryptoConfiguration extends TaintTracking::Configuration { - BrokenCryptoConfiguration() { this = "Broken crypto configuration" } + BrokenCryptoConfiguration() { this = "Broken crypto configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof SensitiveDataSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof SensitiveDataSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof WeakCryptoSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof WeakCryptoSink } } from BrokenCryptoConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ is used in a broken or weak cryptographic algorithm.", - src.getSource(), "Sensitive data" + src.getSource(), "Sensitive data" diff --git a/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql b/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql index 7e6e3194b2c..30e0c6c0e55 100644 --- a/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql +++ b/python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql @@ -17,18 +17,18 @@ FunctionValue ssl_wrap_socket() { result = Value::named("ssl.wrap_socket") } ClassValue ssl_Context_class() { result = Value::named("ssl.SSLContext") } CallNode unsafe_call(string method_name) { - result = ssl_wrap_socket().getACall() and - not exists(result.getArgByName("ssl_version")) and - method_name = "deprecated method ssl.wrap_socket" - or - result = ssl_Context_class().getACall() and - not exists(result.getArgByName("protocol")) and - not exists(result.getArg(0)) and - method_name = "ssl.SSLContext" + result = ssl_wrap_socket().getACall() and + not exists(result.getArgByName("ssl_version")) and + method_name = "deprecated method ssl.wrap_socket" + or + result = ssl_Context_class().getACall() and + not exists(result.getArgByName("protocol")) and + not exists(result.getArg(0)) and + method_name = "ssl.SSLContext" } from CallNode call, string method_name where call = unsafe_call(method_name) select call, - "Call to " + method_name + - " does not specify a protocol, which may result in an insecure default being used." + "Call to " + method_name + + " does not specify a protocol, which may result in an insecure default being used." diff --git a/python/ql/src/Security/CWE-327/InsecureProtocol.ql b/python/ql/src/Security/CWE-327/InsecureProtocol.ql index 21eff3b38f5..d1ae714b6be 100644 --- a/python/ql/src/Security/CWE-327/InsecureProtocol.ql +++ b/python/ql/src/Security/CWE-327/InsecureProtocol.ql @@ -22,17 +22,17 @@ private ModuleValue the_pyOpenSSL_module() { result = Value::named("pyOpenSSL.SS ClassValue the_pyOpenSSL_Context_class() { result = Value::named("pyOpenSSL.SSL.Context") } string insecure_version_name() { - // For `pyOpenSSL.SSL` - result = "SSLv2_METHOD" or - result = "SSLv23_METHOD" or - result = "SSLv3_METHOD" or - result = "TLSv1_METHOD" or - // For the `ssl` module - result = "PROTOCOL_SSLv2" or - result = "PROTOCOL_SSLv3" or - result = "PROTOCOL_SSLv23" or - result = "PROTOCOL_TLS" or - result = "PROTOCOL_TLSv1" + // For `pyOpenSSL.SSL` + result = "SSLv2_METHOD" or + result = "SSLv23_METHOD" or + result = "SSLv3_METHOD" or + result = "TLSv1_METHOD" or + // For the `ssl` module + result = "PROTOCOL_SSLv2" or + result = "PROTOCOL_SSLv3" or + result = "PROTOCOL_SSLv23" or + result = "PROTOCOL_TLS" or + result = "PROTOCOL_TLSv1" } /* @@ -43,53 +43,53 @@ string insecure_version_name() { bindingset[named_argument] predicate probable_insecure_ssl_constant( - CallNode call, string insecure_version, string named_argument + CallNode call, string insecure_version, string named_argument ) { - exists(ControlFlowNode arg | - arg = call.getArgByName(named_argument) or - arg = call.getArg(0) - | - arg.(AttrNode).getObject(insecure_version).pointsTo(the_ssl_module()) - or - arg.(NameNode).getId() = insecure_version and - exists(Import imp | - imp.getAnImportedModuleName() = "ssl" and - imp.getAName().getAsname().(Name).getId() = insecure_version - ) + exists(ControlFlowNode arg | + arg = call.getArgByName(named_argument) or + arg = call.getArg(0) + | + arg.(AttrNode).getObject(insecure_version).pointsTo(the_ssl_module()) + or + arg.(NameNode).getId() = insecure_version and + exists(Import imp | + imp.getAnImportedModuleName() = "ssl" and + imp.getAName().getAsname().(Name).getId() = insecure_version ) + ) } predicate unsafe_ssl_wrap_socket_call( - CallNode call, string method_name, string insecure_version, string named_argument + CallNode call, string method_name, string insecure_version, string named_argument ) { - ( - call = ssl_wrap_socket().getACall() and - method_name = "deprecated method ssl.wrap_socket" and - named_argument = "ssl_version" - or - call = ssl_Context_class().getACall() and - named_argument = "protocol" and - method_name = "ssl.SSLContext" - ) and - insecure_version = insecure_version_name() and - ( - call.getArgByName(named_argument).pointsTo(the_ssl_module().attr(insecure_version)) - or - probable_insecure_ssl_constant(call, insecure_version, named_argument) - ) + ( + call = ssl_wrap_socket().getACall() and + method_name = "deprecated method ssl.wrap_socket" and + named_argument = "ssl_version" + or + call = ssl_Context_class().getACall() and + named_argument = "protocol" and + method_name = "ssl.SSLContext" + ) and + insecure_version = insecure_version_name() and + ( + call.getArgByName(named_argument).pointsTo(the_ssl_module().attr(insecure_version)) + or + probable_insecure_ssl_constant(call, insecure_version, named_argument) + ) } predicate unsafe_pyOpenSSL_Context_call(CallNode call, string insecure_version) { - call = the_pyOpenSSL_Context_class().getACall() and - insecure_version = insecure_version_name() and - call.getArg(0).pointsTo(the_pyOpenSSL_module().attr(insecure_version)) + call = the_pyOpenSSL_Context_class().getACall() and + insecure_version = insecure_version_name() and + call.getArg(0).pointsTo(the_pyOpenSSL_module().attr(insecure_version)) } from CallNode call, string method_name, string insecure_version where - unsafe_ssl_wrap_socket_call(call, method_name, insecure_version, _) - or - unsafe_pyOpenSSL_Context_call(call, insecure_version) and method_name = "pyOpenSSL.SSL.Context" + unsafe_ssl_wrap_socket_call(call, method_name, insecure_version, _) + or + unsafe_pyOpenSSL_Context_call(call, insecure_version) and method_name = "pyOpenSSL.SSL.Context" select call, - "Insecure SSL/TLS protocol version " + insecure_version + " specified in call to " + method_name + - "." + "Insecure SSL/TLS protocol version " + insecure_version + " specified in call to " + method_name + + "." diff --git a/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql b/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql index 41e7e5185ad..df694e8303d 100644 --- a/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql +++ b/python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql @@ -13,17 +13,17 @@ import python FunctionValue temporary_name_function(string mod, string function) { + ( + mod = "tempfile" and function = "mktemp" + or + mod = "os" and ( - mod = "tempfile" and function = "mktemp" - or - mod = "os" and - ( - function = "tmpnam" - or - function = "tempnam" - ) - ) and - result = Module::named(mod).attr(function) + function = "tmpnam" + or + function = "tempnam" + ) + ) and + result = Module::named(mod).attr(function) } from Call c, string mod, string function diff --git a/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql b/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql index 22f803690b5..0f365240cd5 100644 --- a/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql +++ b/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql @@ -23,13 +23,13 @@ import semmle.python.security.injection.Marshal import semmle.python.security.injection.Yaml class UnsafeDeserializationConfiguration extends TaintTracking::Configuration { - UnsafeDeserializationConfiguration() { this = "Unsafe deserialization configuration" } + UnsafeDeserializationConfiguration() { this = "Unsafe deserialization configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof DeserializationSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof DeserializationSink } } from UnsafeDeserializationConfiguration config, TaintedPathSource src, TaintedPathSink sink diff --git a/python/ql/src/Security/CWE-601/UrlRedirect.ql b/python/ql/src/Security/CWE-601/UrlRedirect.ql index 546395477fa..cb517043a36 100644 --- a/python/ql/src/Security/CWE-601/UrlRedirect.ql +++ b/python/ql/src/Security/CWE-601/UrlRedirect.ql @@ -19,23 +19,23 @@ import semmle.python.security.strings.Untrusted /** Url redirection is a problem only if the user controls the prefix of the URL */ class UntrustedPrefixStringKind extends UntrustedStringKind { - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = UntrustedStringKind.super.getTaintForFlowStep(fromnode, tonode) and - not tonode.(BinaryExprNode).getRight() = fromnode - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = UntrustedStringKind.super.getTaintForFlowStep(fromnode, tonode) and + not tonode.(BinaryExprNode).getRight() = fromnode + } } class UrlRedirectConfiguration extends TaintTracking::Configuration { - UrlRedirectConfiguration() { this = "URL redirect configuration" } + UrlRedirectConfiguration() { this = "URL redirect configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HttpRequestTaintSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HttpRequestTaintSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpRedirectTaintSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpRedirectTaintSink } } from UrlRedirectConfiguration config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "Untrusted URL redirection due to $@.", src.getSource(), - "a user-provided value" + "a user-provided value" diff --git a/python/ql/src/Security/CWE-732/WeakFilePermissions.ql b/python/ql/src/Security/CWE-732/WeakFilePermissions.ql index a3bd852fb31..7163d02b530 100644 --- a/python/ql/src/Security/CWE-732/WeakFilePermissions.ql +++ b/python/ql/src/Security/CWE-732/WeakFilePermissions.ql @@ -20,32 +20,32 @@ int group_permission(int p) { result = (p / 8) % 8 } bindingset[p] string access(int p) { - p % 4 >= 2 and result = "writable" - or - p % 4 < 2 and p != 0 and result = "readable" + p % 4 >= 2 and result = "writable" + or + p % 4 < 2 and p != 0 and result = "readable" } bindingset[p] string permissive_permission(int p) { - result = "world " + access(world_permission(p)) - or - world_permission(p) = 0 and result = "group " + access(group_permission(p)) + result = "world " + access(world_permission(p)) + or + world_permission(p) = 0 and result = "group " + access(group_permission(p)) } predicate chmod_call(CallNode call, FunctionValue chmod, NumericValue num) { - Value::named("os.chmod") = chmod and - chmod.getACall() = call and - call.getArg(1).pointsTo(num) + Value::named("os.chmod") = chmod and + chmod.getACall() = call and + call.getArg(1).pointsTo(num) } predicate open_call(CallNode call, FunctionValue open, NumericValue num) { - Value::named("os.open") = open and - open.getACall() = call and - call.getArg(2).pointsTo(num) + Value::named("os.open") = open and + open.getACall() = call and + call.getArg(2).pointsTo(num) } from CallNode call, FunctionValue func, NumericValue num, string permission where - (chmod_call(call, func, num) or open_call(call, func, num)) and - permission = permissive_permission(num.getIntValue()) + (chmod_call(call, func, num) or open_call(call, func, num)) and + permission = permissive_permission(num.getIntValue()) select call, "Overly permissive mask in " + func.getName() + " sets file to " + permission + "." diff --git a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql index edc03fb5f36..f62e89abcf7 100644 --- a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql +++ b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql @@ -17,31 +17,31 @@ import semmle.python.dataflow.TaintTracking import semmle.python.filters.Tests class HardcodedValue extends TaintKind { - HardcodedValue() { this = "hard coded value" } + HardcodedValue() { this = "hard coded value" } } bindingset[char, fraction] predicate fewer_characters_than(StrConst str, string char, float fraction) { - exists(string text, int chars | - text = str.getText() and - chars = count(int i | text.charAt(i) = char) - | - /* Allow one character */ - chars = 1 or - chars < text.length() * fraction - ) + exists(string text, int chars | + text = str.getText() and + chars = count(int i | text.charAt(i) = char) + | + /* Allow one character */ + chars = 1 or + chars < text.length() * fraction + ) } predicate possible_reflective_name(string name) { - exists(any(ModuleValue m).attr(name)) - or - exists(any(ClassValue c).lookup(name)) - or - any(ClassValue c).getName() = name - or - exists(Module::named(name)) - or - exists(Value::named(name)) + exists(any(ModuleValue m).attr(name)) + or + exists(any(ClassValue c).lookup(name)) + or + any(ClassValue c).getName() = name + or + exists(Module::named(name)) + or + exists(Value::named(name)) } int char_count(StrConst str) { result = count(string c | c = str.getText().charAt(_)) } @@ -51,57 +51,57 @@ predicate capitalized_word(StrConst str) { str.getText().regexpMatch("[A-Z][a-z] predicate format_string(StrConst str) { str.getText().matches("%{%}%") } predicate maybeCredential(ControlFlowNode f) { - /* A string that is not too short and unlikely to be text or an identifier. */ - exists(StrConst str | str = f.getNode() | - /* At least 10 characters */ - str.getText().length() > 9 and - /* Not too much whitespace */ - fewer_characters_than(str, " ", 0.05) and - /* or underscores */ - fewer_characters_than(str, "_", 0.2) and - /* Not too repetitive */ - exists(int chars | chars = char_count(str) | - chars > 15 or - chars * 3 > str.getText().length() * 2 - ) and - not possible_reflective_name(str.getText()) and - not capitalized_word(str) and - not format_string(str) - ) - or - /* Or, an integer with over 32 bits */ - exists(IntegerLiteral lit | f.getNode() = lit | - not exists(lit.getValue()) and - /* Not a set of flags or round number */ - not lit.getN().matches("%00%") - ) + /* A string that is not too short and unlikely to be text or an identifier. */ + exists(StrConst str | str = f.getNode() | + /* At least 10 characters */ + str.getText().length() > 9 and + /* Not too much whitespace */ + fewer_characters_than(str, " ", 0.05) and + /* or underscores */ + fewer_characters_than(str, "_", 0.2) and + /* Not too repetitive */ + exists(int chars | chars = char_count(str) | + chars > 15 or + chars * 3 > str.getText().length() * 2 + ) and + not possible_reflective_name(str.getText()) and + not capitalized_word(str) and + not format_string(str) + ) + or + /* Or, an integer with over 32 bits */ + exists(IntegerLiteral lit | f.getNode() = lit | + not exists(lit.getValue()) and + /* Not a set of flags or round number */ + not lit.getN().matches("%00%") + ) } class HardcodedValueSource extends TaintSource { - HardcodedValueSource() { maybeCredential(this) } + HardcodedValueSource() { maybeCredential(this) } - override predicate isSourceOf(TaintKind kind) { kind instanceof HardcodedValue } + override predicate isSourceOf(TaintKind kind) { kind instanceof HardcodedValue } } class CredentialSink extends TaintSink { - CredentialSink() { - exists(string name | - name.regexpMatch(getACredentialRegex()) and - not name.suffix(name.length() - 4) = "file" - | - any(FunctionValue func).getNamedArgumentForCall(_, name) = this - or - exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this) - or - exists(CompareNode cmp, NameNode n | n.getId() = name | - cmp.operands(this, any(Eq eq), n) - or - cmp.operands(n, any(Eq eq), this) - ) - ) - } + CredentialSink() { + exists(string name | + name.regexpMatch(getACredentialRegex()) and + not name.suffix(name.length() - 4) = "file" + | + any(FunctionValue func).getNamedArgumentForCall(_, name) = this + or + exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this) + or + exists(CompareNode cmp, NameNode n | n.getId() = name | + cmp.operands(this, any(Eq eq), n) + or + cmp.operands(n, any(Eq eq), this) + ) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof HardcodedValue } + override predicate sinks(TaintKind kind) { kind instanceof HardcodedValue } } /** @@ -109,23 +109,23 @@ class CredentialSink extends TaintSink { * indicate the value being held is a credential. */ private string getACredentialRegex() { - result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or - result = "(?i).*(puid|username|userid).*" or - result = "(?i).*(cert)(?!.*(format|name)).*" + result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or + result = "(?i).*(puid|username|userid).*" or + result = "(?i).*(cert)(?!.*(format|name)).*" } class HardcodedCredentialsConfiguration extends TaintTracking::Configuration { - HardcodedCredentialsConfiguration() { this = "Hardcoded coredentials configuration" } + HardcodedCredentialsConfiguration() { this = "Hardcoded coredentials configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HardcodedValueSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof HardcodedValueSource + } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof CredentialSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof CredentialSink } } from HardcodedCredentialsConfiguration config, TaintedPathSource src, TaintedPathSink sink where - config.hasFlowPath(src, sink) and - not any(TestScope test).contains(src.getAstNode()) + config.hasFlowPath(src, sink) and + not any(TestScope test).contains(src.getAstNode()) select sink.getSink(), src, sink, "Use of $@.", src.getSource(), "hardcoded credentials" diff --git a/python/ql/src/Statements/AssertLiteralConstant.ql b/python/ql/src/Statements/AssertLiteralConstant.ql index cea2d7302f2..372b25fd10d 100644 --- a/python/ql/src/Statements/AssertLiteralConstant.ql +++ b/python/ql/src/Statements/AssertLiteralConstant.ql @@ -16,15 +16,15 @@ import semmle.python.filters.Tests from Assert a, string value where - /* Exclude asserts inside test cases */ - not a.getScope().getScope*() instanceof TestScope and - exists(Expr test | test = a.getTest() | - value = test.(IntegerLiteral).getN() - or - value = "\"" + test.(StrConst).getS() + "\"" - or - value = test.(NameConstant).toString() - ) and - /* Exclude asserts appearing at the end of a chain of `elif`s */ - not exists(If i | i.getElif().getAnOrelse() = a) + /* Exclude asserts inside test cases */ + not a.getScope().getScope*() instanceof TestScope and + exists(Expr test | test = a.getTest() | + value = test.(IntegerLiteral).getN() + or + value = "\"" + test.(StrConst).getS() + "\"" + or + value = test.(NameConstant).toString() + ) and + /* Exclude asserts appearing at the end of a chain of `elif`s */ + not exists(If i | i.getElif().getAnOrelse() = a) select a, "Assert of literal constant " + value + "." diff --git a/python/ql/src/Statements/AssertOnTuple.ql b/python/ql/src/Statements/AssertOnTuple.ql index 0a2c83f986c..e86e05483c3 100644 --- a/python/ql/src/Statements/AssertOnTuple.ql +++ b/python/ql/src/Statements/AssertOnTuple.ql @@ -15,13 +15,13 @@ import python from Assert a, string b, string non where - a.getTest() instanceof Tuple and - ( - if exists(a.getTest().(Tuple).getAnElt()) - then ( - b = "True" and non = "non-" - ) else ( - b = "False" and non = "" - ) + a.getTest() instanceof Tuple and + ( + if exists(a.getTest().(Tuple).getAnElt()) + then ( + b = "True" and non = "non-" + ) else ( + b = "False" and non = "" ) + ) select a, "Assertion of " + non + "empty tuple is always " + b + "." diff --git a/python/ql/src/Statements/BreakOrReturnInFinally.ql b/python/ql/src/Statements/BreakOrReturnInFinally.ql index 7dbc2fa8edd..02f501e0bfd 100644 --- a/python/ql/src/Statements/BreakOrReturnInFinally.ql +++ b/python/ql/src/Statements/BreakOrReturnInFinally.ql @@ -16,12 +16,12 @@ import python from Stmt s, string kind where - s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s)) - or - s instanceof Break and - kind = "break" and - exists(Try t | t.getFinalbody().contains(s) | - not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop)) and - not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop)) - ) + s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s)) + or + s instanceof Break and + kind = "break" and + exists(Try t | t.getFinalbody().contains(s) | + not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop)) and + not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop)) + ) select s, "'" + kind + "' in a finally block will swallow any exceptions raised." diff --git a/python/ql/src/Statements/C_StyleParentheses.ql b/python/ql/src/Statements/C_StyleParentheses.ql index 1c0f27bf8b6..d428f78f1b6 100644 --- a/python/ql/src/Statements/C_StyleParentheses.ql +++ b/python/ql/src/Statements/C_StyleParentheses.ql @@ -15,21 +15,21 @@ import python from Expr e, Location l, string kind, string what where - e.isParenthesized() and - not e instanceof Tuple and - ( - exists(If i | i.getTest() = e) and kind = "if" and what = "condition" - or - exists(While w | w.getTest() = e) and kind = "while" and what = "condition" - or - exists(Return r | r.getValue() = e) and kind = "return" and what = "value" - or - exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and - kind = "assert" and - what = "test" - ) and - // These require parentheses - (not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and - l = e.getLocation() and - l.getStartLine() = l.getEndLine() + e.isParenthesized() and + not e instanceof Tuple and + ( + exists(If i | i.getTest() = e) and kind = "if" and what = "condition" + or + exists(While w | w.getTest() = e) and kind = "while" and what = "condition" + or + exists(Return r | r.getValue() = e) and kind = "return" and what = "value" + or + exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and + kind = "assert" and + what = "test" + ) and + // These require parentheses + (not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and + l = e.getLocation() and + l.getStartLine() = l.getEndLine() select e, "Parenthesized " + what + " in '" + kind + "' statement." diff --git a/python/ql/src/Statements/ConstantInConditional.ql b/python/ql/src/Statements/ConstantInConditional.ql index e01e693467a..0b12d6efd98 100644 --- a/python/ql/src/Statements/ConstantInConditional.ql +++ b/python/ql/src/Statements/ConstantInConditional.ql @@ -16,27 +16,27 @@ import python predicate is_condition(Expr cond) { - exists(If i | i.getTest() = cond) or - exists(IfExp ie | ie.getTest() = cond) + exists(If i | i.getTest() = cond) or + exists(IfExp ie | ie.getTest() = cond) } /* Treat certain unmodified builtins as constants as well. */ predicate effective_constant(Name cond) { - exists(GlobalVariable var | var = cond.getVariable() and not exists(NameNode f | f.defines(var)) | - var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented" - ) + exists(GlobalVariable var | var = cond.getVariable() and not exists(NameNode f | f.defines(var)) | + var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented" + ) } predicate test_makes_code_unreachable(Expr cond) { - exists(If i | i.getTest() = cond | i.getStmt(0).isUnreachable() or i.getOrelse(0).isUnreachable()) - or - exists(While w | w.getTest() = cond and w.getStmt(0).isUnreachable()) + exists(If i | i.getTest() = cond | i.getStmt(0).isUnreachable() or i.getOrelse(0).isUnreachable()) + or + exists(While w | w.getTest() = cond and w.getStmt(0).isUnreachable()) } from Expr cond where - is_condition(cond) and - (cond.isConstant() or effective_constant(cond)) and - /* Ignore cases where test makes code unreachable, as that is handled in different query */ - not test_makes_code_unreachable(cond) + is_condition(cond) and + (cond.isConstant() or effective_constant(cond)) and + /* Ignore cases where test makes code unreachable, as that is handled in different query */ + not test_makes_code_unreachable(cond) select cond, "Testing a constant will always give the same result." diff --git a/python/ql/src/Statements/DocStrings.ql b/python/ql/src/Statements/DocStrings.ql index d6a1d812300..b20731f723b 100644 --- a/python/ql/src/Statements/DocStrings.ql +++ b/python/ql/src/Statements/DocStrings.ql @@ -18,32 +18,32 @@ import python predicate needs_docstring(Scope s) { - s.isPublic() and - ( - not s instanceof Function - or - function_needs_docstring(s) - ) + s.isPublic() and + ( + not s instanceof Function + or + function_needs_docstring(s) + ) } predicate function_needs_docstring(Function f) { - not exists(FunctionValue fo, FunctionValue base | fo.overrides(base) and fo.getScope() = f | - not function_needs_docstring(base.getScope()) - ) and - f.getName() != "lambda" and - (f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2 and - not exists(PythonPropertyObject p | - p.getGetter().getFunction() = f or - p.getSetter().getFunction() = f - ) + not exists(FunctionValue fo, FunctionValue base | fo.overrides(base) and fo.getScope() = f | + not function_needs_docstring(base.getScope()) + ) and + f.getName() != "lambda" and + (f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2 and + not exists(PythonPropertyObject p | + p.getGetter().getFunction() = f or + p.getSetter().getFunction() = f + ) } string scope_type(Scope s) { - result = "Module" and s instanceof Module and not s.(Module).isPackage() - or - result = "Class" and s instanceof Class - or - result = "Function" and s instanceof Function + result = "Module" and s instanceof Module and not s.(Module).isPackage() + or + result = "Class" and s instanceof Class + or + result = "Function" and s instanceof Function } from Scope s diff --git a/python/ql/src/Statements/ExecUsed.ql b/python/ql/src/Statements/ExecUsed.ql index 44d4da02e32..e1e4bb45c6b 100644 --- a/python/ql/src/Statements/ExecUsed.ql +++ b/python/ql/src/Statements/ExecUsed.ql @@ -13,13 +13,13 @@ import python string message() { - result = "The 'exec' statement is used." and major_version() = 2 - or - result = "The 'exec' function is used." and major_version() = 3 + result = "The 'exec' statement is used." and major_version() = 2 + or + result = "The 'exec' function is used." and major_version() = 3 } predicate exec_function_call(Call c) { - exists(GlobalVariable exec | exec = c.getFunc().(Name).getVariable() and exec.getId() = "exec") + exists(GlobalVariable exec | exec = c.getFunc().(Name).getVariable() and exec.getId() = "exec") } from AstNode exec diff --git a/python/ql/src/Statements/IterableStringOrSequence.ql b/python/ql/src/Statements/IterableStringOrSequence.ql index 7ab43f33b76..a92a1d79d5f 100644 --- a/python/ql/src/Statements/IterableStringOrSequence.ql +++ b/python/ql/src/Statements/IterableStringOrSequence.ql @@ -15,24 +15,24 @@ import python import semmle.python.filters.Tests predicate has_string_type(Value v) { - v.getClass() = ClassValue::str() - or - v.getClass() = ClassValue::unicode() and major_version() = 2 + v.getClass() = ClassValue::str() + or + v.getClass() = ClassValue::unicode() and major_version() = 2 } from - For loop, ControlFlowNode iter, Value str, Value seq, ControlFlowNode seq_origin, - ControlFlowNode str_origin + For loop, ControlFlowNode iter, Value str, Value seq, ControlFlowNode seq_origin, + ControlFlowNode str_origin where - loop.getIter().getAFlowNode() = iter and - iter.pointsTo(str, str_origin) and - iter.pointsTo(seq, seq_origin) and - has_string_type(str) and - seq.getClass().isIterable() and - not has_string_type(seq) and - // suppress occurrences from tests - not seq_origin.getScope().getScope*() instanceof TestScope and - not str_origin.getScope().getScope*() instanceof TestScope + loop.getIter().getAFlowNode() = iter and + iter.pointsTo(str, str_origin) and + iter.pointsTo(seq, seq_origin) and + has_string_type(str) and + seq.getClass().isIterable() and + not has_string_type(seq) and + // suppress occurrences from tests + not seq_origin.getScope().getScope*() instanceof TestScope and + not str_origin.getScope().getScope*() instanceof TestScope select loop, - "Iteration over $@, of class " + seq.getClass().getName() + ", may also iterate over $@.", - seq_origin, "sequence", str_origin, "string" + "Iteration over $@, of class " + seq.getClass().getName() + ", may also iterate over $@.", + seq_origin, "sequence", str_origin, "string" diff --git a/python/ql/src/Statements/MismatchInMultipleAssignment.ql b/python/ql/src/Statements/MismatchInMultipleAssignment.ql index 157ddf1270b..f4ba916cb50 100644 --- a/python/ql/src/Statements/MismatchInMultipleAssignment.ql +++ b/python/ql/src/Statements/MismatchInMultipleAssignment.ql @@ -17,45 +17,45 @@ import python private int len(ExprList el) { result = count(el.getAnItem()) } predicate mismatched(Assign a, int lcount, int rcount, Location loc, string sequenceType) { - exists(ExprList l, ExprList r | - ( - a.getATarget().(Tuple).getElts() = l or - a.getATarget().(List).getElts() = l - ) and - ( - a.getValue().(Tuple).getElts() = r and sequenceType = "tuple" - or - a.getValue().(List).getElts() = r and sequenceType = "list" - ) and - loc = a.getValue().getLocation() and - lcount = len(l) and - rcount = len(r) and - lcount != rcount and - not exists(Starred s | l.getAnItem() = s or r.getAnItem() = s) - ) + exists(ExprList l, ExprList r | + ( + a.getATarget().(Tuple).getElts() = l or + a.getATarget().(List).getElts() = l + ) and + ( + a.getValue().(Tuple).getElts() = r and sequenceType = "tuple" + or + a.getValue().(List).getElts() = r and sequenceType = "list" + ) and + loc = a.getValue().getLocation() and + lcount = len(l) and + rcount = len(r) and + lcount != rcount and + not exists(Starred s | l.getAnItem() = s or r.getAnItem() = s) + ) } predicate mismatched_tuple_rhs(Assign a, int lcount, int rcount, Location loc) { - exists(ExprList l, TupleValue r, AstNode origin | - ( - a.getATarget().(Tuple).getElts() = l or - a.getATarget().(List).getElts() = l - ) and - a.getValue().pointsTo(r, origin) and - loc = origin.getLocation() and - lcount = len(l) and - rcount = r.length() and - lcount != rcount and - not exists(Starred s | l.getAnItem() = s) - ) + exists(ExprList l, TupleValue r, AstNode origin | + ( + a.getATarget().(Tuple).getElts() = l or + a.getATarget().(List).getElts() = l + ) and + a.getValue().pointsTo(r, origin) and + loc = origin.getLocation() and + lcount = len(l) and + rcount = r.length() and + lcount != rcount and + not exists(Starred s | l.getAnItem() = s) + ) } from Assign a, int lcount, int rcount, Location loc, string sequenceType where - mismatched(a, lcount, rcount, loc, sequenceType) - or - mismatched_tuple_rhs(a, lcount, rcount, loc) and - sequenceType = "tuple" + mismatched(a, lcount, rcount, loc, sequenceType) + or + mismatched_tuple_rhs(a, lcount, rcount, loc) and + sequenceType = "tuple" select a, - "Left hand side of assignment contains " + lcount + - " variables, but right hand side is a $@ of length " + rcount + ".", loc, sequenceType + "Left hand side of assignment contains " + lcount + + " variables, but right hand side is a $@ of length " + rcount + ".", loc, sequenceType diff --git a/python/ql/src/Statements/ModificationOfLocals.ql b/python/ql/src/Statements/ModificationOfLocals.ql index 8253d51426a..1a76c38c52e 100644 --- a/python/ql/src/Statements/ModificationOfLocals.ql +++ b/python/ql/src/Statements/ModificationOfLocals.ql @@ -15,18 +15,18 @@ import python predicate originIsLocals(ControlFlowNode n) { n.pointsTo(_, _, Value::named("locals").getACall()) } predicate modification_of_locals(ControlFlowNode f) { - originIsLocals(f.(SubscriptNode).getObject()) and - (f.isStore() or f.isDelete()) - or - exists(string mname, AttrNode attr | - attr = f.(CallNode).getFunction() and - originIsLocals(attr.getObject(mname)) - | - mname = "pop" or - mname = "popitem" or - mname = "update" or - mname = "clear" - ) + originIsLocals(f.(SubscriptNode).getObject()) and + (f.isStore() or f.isDelete()) + or + exists(string mname, AttrNode attr | + attr = f.(CallNode).getFunction() and + originIsLocals(attr.getObject(mname)) + | + mname = "pop" or + mname = "popitem" or + mname = "update" or + mname = "clear" + ) } from AstNode a, ControlFlowNode f diff --git a/python/ql/src/Statements/NestedLoopsSameVariable.ql b/python/ql/src/Statements/NestedLoopsSameVariable.ql index 8e966077c16..f57fa9b361a 100644 --- a/python/ql/src/Statements/NestedLoopsSameVariable.ql +++ b/python/ql/src/Statements/NestedLoopsSameVariable.ql @@ -16,15 +16,15 @@ import python predicate loop_variable(For f, Variable v) { f.getTarget().defines(v) } predicate variableUsedInNestedLoops(For inner, For outer, Variable v) { - /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ - outer.getBody().contains(inner) and - loop_variable(inner, v) and - loop_variable(outer, v) and - /* Ignore cases where there is no use of the variable or the only use is in the inner loop */ - exists(Name n | n.uses(v) and outer.contains(n) and not inner.contains(n)) + /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ + outer.getBody().contains(inner) and + loop_variable(inner, v) and + loop_variable(outer, v) and + /* Ignore cases where there is no use of the variable or the only use is in the inner loop */ + exists(Name n | n.uses(v) and outer.contains(n) and not inner.contains(n)) } from For inner, For outer, Variable v where variableUsedInNestedLoops(inner, outer, v) select inner, "Nested for statement uses loop variable '" + v.getId() + "' of enclosing $@.", outer, - "for statement" + "for statement" diff --git a/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql index 400c43d1d94..de293a7aeea 100644 --- a/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql +++ b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql @@ -14,23 +14,23 @@ import python predicate loop_variable_ssa(For f, Variable v, SsaVariable s) { - f.getTarget().getAFlowNode() = s.getDefinition() and v = s.getVariable() + f.getTarget().getAFlowNode() = s.getDefinition() and v = s.getVariable() } predicate variableUsedInNestedLoops(For inner, For outer, Variable v, Name n) { - /* Ignore cases where there is no use of the variable or the only use is in the inner loop. */ - outer.contains(n) and - not inner.contains(n) and - /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ - outer.getBody().contains(inner) and - exists(SsaVariable s | - loop_variable_ssa(inner, v, s.getAnUltimateDefinition()) and - loop_variable_ssa(outer, v, _) and - s.getAUse().getNode() = n - ) + /* Ignore cases where there is no use of the variable or the only use is in the inner loop. */ + outer.contains(n) and + not inner.contains(n) and + /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ + outer.getBody().contains(inner) and + exists(SsaVariable s | + loop_variable_ssa(inner, v, s.getAnUltimateDefinition()) and + loop_variable_ssa(outer, v, _) and + s.getAUse().getNode() = n + ) } from For inner, For outer, Variable v, Name n where variableUsedInNestedLoops(inner, outer, v, n) select inner, "Nested for statement $@ loop variable '" + v.getId() + "' of enclosing $@.", n, - "uses", outer, "for statement" + "uses", outer, "for statement" diff --git a/python/ql/src/Statements/NonIteratorInForLoop.ql b/python/ql/src/Statements/NonIteratorInForLoop.ql index 85982ccc030..0df5c30a77d 100644 --- a/python/ql/src/Statements/NonIteratorInForLoop.ql +++ b/python/ql/src/Statements/NonIteratorInForLoop.ql @@ -15,11 +15,11 @@ import python from For loop, ControlFlowNode iter, Value v, ClassValue t, ControlFlowNode origin where - loop.getIter().getAFlowNode() = iter and - iter.pointsTo(_, v, origin) and - v.getClass() = t and - not t.isIterable() and - not t.failedInference(_) and - not v = Value::named("None") and - not t.isDescriptorType() + loop.getIter().getAFlowNode() = iter and + iter.pointsTo(_, v, origin) and + v.getClass() = t and + not t.isIterable() and + not t.failedInference(_) and + not v = Value::named("None") and + not t.isDescriptorType() select loop, "$@ of class '$@' may be used in for-loop.", origin, "Non-iterable", t, t.getName() diff --git a/python/ql/src/Statements/RedundantAssignment.ql b/python/ql/src/Statements/RedundantAssignment.ql index 87b03cd5989..b56dea3eecb 100644 --- a/python/ql/src/Statements/RedundantAssignment.ql +++ b/python/ql/src/Statements/RedundantAssignment.ql @@ -14,27 +14,27 @@ import python predicate assignment(AssignStmt a, Expr left, Expr right) { - a.getATarget() = left and a.getValue() = right + a.getATarget() = left and a.getValue() = right } predicate corresponding(Expr left, Expr right) { - assignment(_, left, right) - or - exists(Attribute la, Attribute ra | - corresponding(la, ra) and - left = la.getObject() and - right = ra.getObject() - ) + assignment(_, left, right) + or + exists(Attribute la, Attribute ra | + corresponding(la, ra) and + left = la.getObject() and + right = ra.getObject() + ) } predicate same_value(Expr left, Expr right) { - same_name(left, right) - or - same_attribute(left, right) + same_name(left, right) + or + same_attribute(left, right) } predicate maybe_defined_in_outer_scope(Name n) { - exists(SsaVariable v | v.getAUse().getNode() = n | v.maybeUndefined()) + exists(SsaVariable v | v.getAUse().getNode() = n | v.maybeUndefined()) } /* @@ -50,54 +50,54 @@ predicate maybe_defined_in_outer_scope(Name n) { predicate isBuiltin(string name) { exists(Value v | v = Value::named(name) and v.isBuiltin()) } predicate same_name(Name n1, Name n2) { - corresponding(n1, n2) and - n1.getVariable() = n2.getVariable() and - not isBuiltin(n1.getId()) and - not maybe_defined_in_outer_scope(n2) + corresponding(n1, n2) and + n1.getVariable() = n2.getVariable() and + not isBuiltin(n1.getId()) and + not maybe_defined_in_outer_scope(n2) } ClassValue value_type(Attribute a) { a.getObject().pointsTo().getClass() = result } predicate is_property_access(Attribute a) { - value_type(a).lookup(a.getName()) instanceof PropertyValue + value_type(a).lookup(a.getName()) instanceof PropertyValue } predicate same_attribute(Attribute a1, Attribute a2) { - corresponding(a1, a2) and - a1.getName() = a2.getName() and - same_value(a1.getObject(), a2.getObject()) and - exists(value_type(a1)) and - not is_property_access(a1) + corresponding(a1, a2) and + a1.getName() = a2.getName() and + same_value(a1.getObject(), a2.getObject()) and + exists(value_type(a1)) and + not is_property_access(a1) } int pyflakes_commented_line(File file) { - exists(Comment c | c.getText().toLowerCase().matches("%pyflakes%") | - c.getLocation().hasLocationInfo(file.getAbsolutePath(), result, _, _, _) - ) + exists(Comment c | c.getText().toLowerCase().matches("%pyflakes%") | + c.getLocation().hasLocationInfo(file.getAbsolutePath(), result, _, _, _) + ) } predicate pyflakes_commented(AssignStmt assignment) { - exists(Location loc | - assignment.getLocation() = loc and - loc.getStartLine() = pyflakes_commented_line(loc.getFile()) - ) + exists(Location loc | + assignment.getLocation() = loc and + loc.getStartLine() = pyflakes_commented_line(loc.getFile()) + ) } predicate side_effecting_lhs(Attribute lhs) { - exists(ClassValue cls, ClassValue decl | - lhs.getObject().pointsTo().getClass() = cls and - decl = cls.getASuperType() and - not decl.isBuiltin() - | - decl.declaresAttribute("__setattr__") - ) + exists(ClassValue cls, ClassValue decl | + lhs.getObject().pointsTo().getClass() = cls and + decl = cls.getASuperType() and + not decl.isBuiltin() + | + decl.declaresAttribute("__setattr__") + ) } from AssignStmt a, Expr left, Expr right where - assignment(a, left, right) and - same_value(left, right) and - // some people use self-assignment to shut Pyflakes up, such as `ok = ok # Pyflakes` - not pyflakes_commented(a) and - not side_effecting_lhs(left) + assignment(a, left, right) and + same_value(left, right) and + // some people use self-assignment to shut Pyflakes up, such as `ok = ok # Pyflakes` + not pyflakes_commented(a) and + not side_effecting_lhs(left) select a, "This assignment assigns a variable to itself." diff --git a/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql b/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql index ff6b7f379d3..a940dc60123 100644 --- a/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql +++ b/python/ql/src/Statements/ReturnOrYieldOutsideFunction.ql @@ -14,12 +14,12 @@ import python from AstNode node, string kind where - not node.getScope() instanceof Function and - ( - node instanceof Return and kind = "return" - or - node instanceof Yield and kind = "yield" - or - node instanceof YieldFrom and kind = "yield from" - ) + not node.getScope() instanceof Function and + ( + node instanceof Return and kind = "return" + or + node instanceof Yield and kind = "yield" + or + node instanceof YieldFrom and kind = "yield from" + ) select node, "'" + kind + "' is used outside a function." diff --git a/python/ql/src/Statements/ShouldUseWithStatement.ql b/python/ql/src/Statements/ShouldUseWithStatement.ql index 9be28d5ceec..b453f971e86 100644 --- a/python/ql/src/Statements/ShouldUseWithStatement.ql +++ b/python/ql/src/Statements/ShouldUseWithStatement.ql @@ -17,23 +17,23 @@ import python predicate calls_close(Call c) { exists(Attribute a | c.getFunc() = a and a.getName() = "close") } predicate only_stmt_in_finally(Try t, Call c) { - exists(ExprStmt s | - t.getAFinalstmt() = s and s.getValue() = c and strictcount(t.getAFinalstmt()) = 1 - ) + exists(ExprStmt s | + t.getAFinalstmt() = s and s.getValue() = c and strictcount(t.getAFinalstmt()) = 1 + ) } predicate points_to_context_manager(ControlFlowNode f, ClassValue cls) { - forex(Value v | f.pointsTo(v) | v.getClass() = cls) and - cls.isContextManager() + forex(Value v | f.pointsTo(v) | v.getClass() = cls) and + cls.isContextManager() } from Call close, Try t, ClassValue cls where - only_stmt_in_finally(t, close) and - calls_close(close) and - exists(ControlFlowNode f | f = close.getFunc().getAFlowNode().(AttrNode).getObject() | - points_to_context_manager(f, cls) - ) + only_stmt_in_finally(t, close) and + calls_close(close) and + exists(ControlFlowNode f | f = close.getFunc().getAFlowNode().(AttrNode).getObject() | + points_to_context_manager(f, cls) + ) select close, - "Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement.", - cls, cls.getName() + "Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement.", + cls, cls.getName() diff --git a/python/ql/src/Statements/SideEffectInAssert.ql b/python/ql/src/Statements/SideEffectInAssert.ql index a8ed146b16e..f96e04243af 100644 --- a/python/ql/src/Statements/SideEffectInAssert.ql +++ b/python/ql/src/Statements/SideEffectInAssert.ql @@ -14,37 +14,37 @@ import python predicate func_with_side_effects(Expr e) { - exists(string name | name = e.(Attribute).getName() or name = e.(Name).getId() | - name = "print" or - name = "write" or - name = "append" or - name = "pop" or - name = "remove" or - name = "discard" or - name = "delete" or - name = "close" or - name = "open" or - name = "exit" - ) + exists(string name | name = e.(Attribute).getName() or name = e.(Name).getId() | + name = "print" or + name = "write" or + name = "append" or + name = "pop" or + name = "remove" or + name = "discard" or + name = "delete" or + name = "close" or + name = "open" or + name = "exit" + ) } predicate call_with_side_effect(Call e) { - e.getAFlowNode() = Value::named("subprocess.call").getACall() - or - e.getAFlowNode() = Value::named("subprocess.check_call").getACall() - or - e.getAFlowNode() = Value::named("subprocess.check_output").getACall() + e.getAFlowNode() = Value::named("subprocess.call").getACall() + or + e.getAFlowNode() = Value::named("subprocess.check_call").getACall() + or + e.getAFlowNode() = Value::named("subprocess.check_output").getACall() } predicate probable_side_effect(Expr e) { - // Only consider explicit yields, not artificial ones in comprehensions - e instanceof Yield and not exists(Comp c | c.contains(e)) - or - e instanceof YieldFrom - or - e instanceof Call and func_with_side_effects(e.(Call).getFunc()) - or - e instanceof Call and call_with_side_effect(e) + // Only consider explicit yields, not artificial ones in comprehensions + e instanceof Yield and not exists(Comp c | c.contains(e)) + or + e instanceof YieldFrom + or + e instanceof Call and func_with_side_effects(e.(Call).getFunc()) + or + e instanceof Call and call_with_side_effect(e) } from Assert a, Expr e diff --git a/python/ql/src/Statements/StatementNoEffect.ql b/python/ql/src/Statements/StatementNoEffect.ql index dde0b5d7cde..48129bc38d5 100644 --- a/python/ql/src/Statements/StatementNoEffect.ql +++ b/python/ql/src/Statements/StatementNoEffect.ql @@ -14,34 +14,34 @@ import python predicate understood_attribute(Attribute attr, ClassValue cls, ClassValue attr_cls) { - exists(string name | attr.getName() = name | - attr.getObject().pointsTo().getClass() = cls and - cls.attr(name).getClass() = attr_cls - ) + exists(string name | attr.getName() = name | + attr.getObject().pointsTo().getClass() = cls and + cls.attr(name).getClass() = attr_cls + ) } /* Conservative estimate of whether attribute lookup has a side effect */ predicate side_effecting_attribute(Attribute attr) { - exists(ClassValue cls, ClassValue attr_cls | - understood_attribute(attr, cls, attr_cls) and - side_effecting_descriptor_type(attr_cls) - ) + exists(ClassValue cls, ClassValue attr_cls | + understood_attribute(attr, cls, attr_cls) and + side_effecting_descriptor_type(attr_cls) + ) } predicate maybe_side_effecting_attribute(Attribute attr) { - not understood_attribute(attr, _, _) and not attr.pointsTo(_) - or - side_effecting_attribute(attr) + not understood_attribute(attr, _, _) and not attr.pointsTo(_) + or + side_effecting_attribute(attr) } predicate side_effecting_descriptor_type(ClassValue descriptor) { - descriptor.isDescriptorType() and - // Technically all descriptor gets have side effects, - // but some are indicative of a missing call and - // we want to treat them as having no effect. - not descriptor = ClassValue::functionType() and - not descriptor = ClassValue::staticmethod() and - not descriptor = ClassValue::classmethod() + descriptor.isDescriptorType() and + // Technically all descriptor gets have side effects, + // but some are indicative of a missing call and + // we want to treat them as having no effect. + not descriptor = ClassValue::functionType() and + not descriptor = ClassValue::staticmethod() and + not descriptor = ClassValue::classmethod() } /** @@ -49,87 +49,87 @@ predicate side_effecting_descriptor_type(ClassValue descriptor) { * side-effecting unless we know otherwise. */ predicate side_effecting_binary(Expr b) { - exists(Expr sub, ClassValue cls, string method_name | - binary_operator_special_method(b, sub, cls, method_name) - or - comparison_special_method(b, sub, cls, method_name) - | - method_name = special_method() and - cls.hasAttribute(method_name) and - not exists(ClassValue declaring | - declaring.declaresAttribute(method_name) and - declaring = cls.getASuperType() and - declaring.isBuiltin() and - not declaring = ClassValue::object() - ) + exists(Expr sub, ClassValue cls, string method_name | + binary_operator_special_method(b, sub, cls, method_name) + or + comparison_special_method(b, sub, cls, method_name) + | + method_name = special_method() and + cls.hasAttribute(method_name) and + not exists(ClassValue declaring | + declaring.declaresAttribute(method_name) and + declaring = cls.getASuperType() and + declaring.isBuiltin() and + not declaring = ClassValue::object() ) + ) } pragma[nomagic] private predicate binary_operator_special_method( - BinaryExpr b, Expr sub, ClassValue cls, string method_name + BinaryExpr b, Expr sub, ClassValue cls, string method_name ) { - method_name = special_method() and - sub = b.getLeft() and - method_name = b.getOp().getSpecialMethodName() and - sub.pointsTo().getClass() = cls + method_name = special_method() and + sub = b.getLeft() and + method_name = b.getOp().getSpecialMethodName() and + sub.pointsTo().getClass() = cls } pragma[nomagic] private predicate comparison_special_method(Compare b, Expr sub, ClassValue cls, string method_name) { - exists(Cmpop op | - b.compares(sub, op, _) and - method_name = op.getSpecialMethodName() - ) and - sub.pointsTo().getClass() = cls + exists(Cmpop op | + b.compares(sub, op, _) and + method_name = op.getSpecialMethodName() + ) and + sub.pointsTo().getClass() = cls } private string special_method() { - result = any(Cmpop c).getSpecialMethodName() - or - result = any(BinaryExpr b).getOp().getSpecialMethodName() + result = any(Cmpop c).getSpecialMethodName() + or + result = any(BinaryExpr b).getOp().getSpecialMethodName() } predicate is_notebook(File f) { - exists(Comment c | c.getLocation().getFile() = f | - c.getText().regexpMatch("#\\s*.+\\s*") - ) + exists(Comment c | c.getLocation().getFile() = f | + c.getText().regexpMatch("#\\s*.+\\s*") + ) } /** Expression (statement) in a jupyter/ipython notebook */ predicate in_notebook(Expr e) { is_notebook(e.getScope().(Module).getFile()) } FunctionValue assertRaises() { - result = Value::named("unittest.TestCase").(ClassValue).lookup("assertRaises") + result = Value::named("unittest.TestCase").(ClassValue).lookup("assertRaises") } /** Holds if expression `e` is in a `with` block that tests for exceptions being raised. */ predicate in_raises_test(Expr e) { - exists(With w | - w.contains(e) and - w.getContextExpr() = assertRaises().getACall().getNode() - ) + exists(With w | + w.contains(e) and + w.getContextExpr() = assertRaises().getACall().getNode() + ) } /** Holds if expression has the form of a Python 2 `print >> out, ...` statement */ predicate python2_print(Expr e) { - e.(BinaryExpr).getLeft().(Name).getId() = "print" and - e.(BinaryExpr).getOp() instanceof RShift - or - python2_print(e.(Tuple).getElt(0)) + e.(BinaryExpr).getLeft().(Name).getId() = "print" and + e.(BinaryExpr).getOp() instanceof RShift + or + python2_print(e.(Tuple).getElt(0)) } predicate no_effect(Expr e) { - // strings can be used as comments - not e instanceof StrConst and - not e.hasSideEffects() and - forall(Expr sub | sub = e.getASubExpression*() | - not side_effecting_binary(sub) and - not maybe_side_effecting_attribute(sub) - ) and - not in_notebook(e) and - not in_raises_test(e) and - not python2_print(e) + // strings can be used as comments + not e instanceof StrConst and + not e.hasSideEffects() and + forall(Expr sub | sub = e.getASubExpression*() | + not side_effecting_binary(sub) and + not maybe_side_effecting_attribute(sub) + ) and + not in_notebook(e) and + not in_raises_test(e) and + not python2_print(e) } from ExprStmt stmt diff --git a/python/ql/src/Statements/StringConcatenationInLoop.ql b/python/ql/src/Statements/StringConcatenationInLoop.ql index f225e27cdcd..563a42e5462 100644 --- a/python/ql/src/Statements/StringConcatenationInLoop.ql +++ b/python/ql/src/Statements/StringConcatenationInLoop.ql @@ -13,14 +13,14 @@ import python predicate string_concat_in_loop(BinaryExpr b) { - b.getOp() instanceof Add and - exists(SsaVariable d, SsaVariable u, BinaryExprNode add | - add.getNode() = b and d = u.getAnUltimateDefinition() - | - d.getDefinition().(DefinitionNode).getValue() = add and - u.getAUse() = add.getAnOperand() and - add.getAnOperand().pointsTo().getClass() = ClassValue::str() - ) + b.getOp() instanceof Add and + exists(SsaVariable d, SsaVariable u, BinaryExprNode add | + add.getNode() = b and d = u.getAnUltimateDefinition() + | + d.getDefinition().(DefinitionNode).getValue() = add and + u.getAUse() = add.getAnOperand() and + add.getAnOperand().pointsTo().getClass() = ClassValue::str() + ) } from BinaryExpr b, Stmt s diff --git a/python/ql/src/Statements/TopLevelPrint.ql b/python/ql/src/Statements/TopLevelPrint.ql index d818d80a251..b2d111cce1f 100644 --- a/python/ql/src/Statements/TopLevelPrint.ql +++ b/python/ql/src/Statements/TopLevelPrint.ql @@ -14,27 +14,27 @@ import python predicate main_eq_name(If i) { - exists(Name n, StrConst m, Compare c | - i.getTest() = c and - c.getLeft() = n and - c.getAComparator() = m and - n.getId() = "__name__" and - m.getText() = "__main__" - ) + exists(Name n, StrConst m, Compare c | + i.getTest() = c and + c.getLeft() = n and + c.getAComparator() = m and + n.getId() = "__name__" and + m.getText() = "__main__" + ) } predicate is_print_stmt(Stmt s) { - s instanceof Print - or - exists(ExprStmt e, Call c, Name n | - e = s and c = e.getValue() and n = c.getFunc() and n.getId() = "print" - ) + s instanceof Print + or + exists(ExprStmt e, Call c, Name n | + e = s and c = e.getValue() and n = c.getFunc() and n.getId() = "print" + ) } from Stmt p where - is_print_stmt(p) and - // TODO: Need to discuss how we would like to handle ModuleObject.getKind in the glorious future - exists(ModuleValue m | m.getScope() = p.getScope() and m.isUsedAsModule()) and - not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p) + is_print_stmt(p) and + // TODO: Need to discuss how we would like to handle ModuleObject.getKind in the glorious future + exists(ModuleValue m | m.getScope() = p.getScope() and m.isUsedAsModule()) and + not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p) select p, "Print statement may execute during import." diff --git a/python/ql/src/Statements/UnnecessaryDelete.ql b/python/ql/src/Statements/UnnecessaryDelete.ql index d10bcc2ed20..c9e047c307d 100644 --- a/python/ql/src/Statements/UnnecessaryDelete.ql +++ b/python/ql/src/Statements/UnnecessaryDelete.ql @@ -16,17 +16,17 @@ import python from Delete del, Expr e, Function f where - f.getLastStatement() = del and - e = del.getATarget() and - f.containsInScope(e) and - not e instanceof Subscript and - not e instanceof Attribute and - not exists(Stmt s | s.(While).contains(del) or s.(For).contains(del)) and - // False positive: calling `sys.exc_info` within a function results in a - // reference cycle, and an explicit call to `del` helps break this cycle. - not exists(FunctionValue ex | - ex = Value::named("sys.exc_info") and - ex.getACall().getScope() = f - ) + f.getLastStatement() = del and + e = del.getATarget() and + f.containsInScope(e) and + not e instanceof Subscript and + not e instanceof Attribute and + not exists(Stmt s | s.(While).contains(del) or s.(For).contains(del)) and + // False positive: calling `sys.exc_info` within a function results in a + // reference cycle, and an explicit call to `del` helps break this cycle. + not exists(FunctionValue ex | + ex = Value::named("sys.exc_info") and + ex.getACall().getScope() = f + ) select del, "Unnecessary deletion of local variable $@ in function $@.", e.getLocation(), - e.toString(), f.getLocation(), f.getName() + e.toString(), f.getLocation(), f.getName() diff --git a/python/ql/src/Statements/UnnecessaryElseClause.ql b/python/ql/src/Statements/UnnecessaryElseClause.ql index 8884b06e740..35ac254b276 100644 --- a/python/ql/src/Statements/UnnecessaryElseClause.ql +++ b/python/ql/src/Statements/UnnecessaryElseClause.ql @@ -14,11 +14,11 @@ import python from Stmt loop, StmtList body, StmtList clause, string kind where - ( - exists(For f | f = loop | clause = f.getOrelse() and body = f.getBody() and kind = "for") - or - exists(While w | w = loop | clause = w.getOrelse() and body = w.getBody() and kind = "while") - ) and - not exists(Break b | body.contains(b)) + ( + exists(For f | f = loop | clause = f.getOrelse() and body = f.getBody() and kind = "for") + or + exists(While w | w = loop | clause = w.getOrelse() and body = w.getBody() and kind = "while") + ) and + not exists(Break b | body.contains(b)) select loop, - "This '" + kind + "' statement has a redundant 'else' as no 'break' is present in the body." + "This '" + kind + "' statement has a redundant 'else' as no 'break' is present in the body." diff --git a/python/ql/src/Statements/UnnecessaryPass.ql b/python/ql/src/Statements/UnnecessaryPass.ql index fe0e0171930..215fac5192e 100644 --- a/python/ql/src/Statements/UnnecessaryPass.ql +++ b/python/ql/src/Statements/UnnecessaryPass.ql @@ -13,20 +13,20 @@ import python predicate is_doc_string(ExprStmt s) { - s.getValue() instanceof Unicode or s.getValue() instanceof Bytes + s.getValue() instanceof Unicode or s.getValue() instanceof Bytes } predicate has_doc_string(StmtList stmts) { - stmts.getParent() instanceof Scope and - is_doc_string(stmts.getItem(0)) + stmts.getParent() instanceof Scope and + is_doc_string(stmts.getItem(0)) } from Pass p, StmtList list where - list.getAnItem() = p and - ( - strictcount(list.getAnItem()) = 2 and not has_doc_string(list) - or - strictcount(list.getAnItem()) > 2 - ) + list.getAnItem() = p and + ( + strictcount(list.getAnItem()) = 2 and not has_doc_string(list) + or + strictcount(list.getAnItem()) > 2 + ) select p, "Unnecessary 'pass' statement." diff --git a/python/ql/src/Statements/UnreachableCode.ql b/python/ql/src/Statements/UnreachableCode.ql index 88f8b9427a2..04e9f79c415 100644 --- a/python/ql/src/Statements/UnreachableCode.ql +++ b/python/ql/src/Statements/UnreachableCode.ql @@ -14,49 +14,49 @@ import python predicate typing_import(ImportingStmt is) { - exists(Module m | - is.getScope() = m and - exists(TypeHintComment tc | tc.getLocation().getFile() = m.getFile()) - ) + exists(Module m | + is.getScope() = m and + exists(TypeHintComment tc | tc.getLocation().getFile() = m.getFile()) + ) } /** Holds if `s` contains the only `yield` in scope */ predicate unique_yield(Stmt s) { - exists(Yield y | s.contains(y)) and - exists(Function f | - f = s.getScope() and - strictcount(Yield y | f.containsInScope(y)) = 1 - ) + exists(Yield y | s.contains(y)) and + exists(Function f | + f = s.getScope() and + strictcount(Yield y | f.containsInScope(y)) = 1 + ) } /** Holds if `contextlib.suppress` may be used in the same scope as `s` */ predicate suppression_in_scope(Stmt s) { - exists(With w | - w.getContextExpr().(Call).getFunc().pointsTo(Value::named("contextlib.suppress")) and - w.getScope() = s.getScope() - ) + exists(With w | + w.getContextExpr().(Call).getFunc().pointsTo(Value::named("contextlib.suppress")) and + w.getScope() = s.getScope() + ) } /** Holds if `s` is a statement that raises an exception at the end of an if-elif-else chain. */ predicate marks_an_impossible_else_branch(Stmt s) { - exists(If i | i.getOrelse().getItem(0) = s | - s.(Assert).getTest() instanceof False - or - s instanceof Raise - ) + exists(If i | i.getOrelse().getItem(0) = s | + s.(Assert).getTest() instanceof False + or + s instanceof Raise + ) } predicate reportable_unreachable(Stmt s) { - s.isUnreachable() and - not typing_import(s) and - not suppression_in_scope(s) and - not exists(Stmt other | other.isUnreachable() | - other.contains(s) - or - exists(StmtList l, int i, int j | l.getItem(i) = other and l.getItem(j) = s and i < j) - ) and - not unique_yield(s) and - not marks_an_impossible_else_branch(s) + s.isUnreachable() and + not typing_import(s) and + not suppression_in_scope(s) and + not exists(Stmt other | other.isUnreachable() | + other.contains(s) + or + exists(StmtList l, int i, int j | l.getItem(i) = other and l.getItem(j) = s and i < j) + ) and + not unique_yield(s) and + not marks_an_impossible_else_branch(s) } from Stmt s diff --git a/python/ql/src/Statements/UnusedExceptionObject.ql b/python/ql/src/Statements/UnusedExceptionObject.ql index 32b59113c5b..6c19f82d60f 100644 --- a/python/ql/src/Statements/UnusedExceptionObject.ql +++ b/python/ql/src/Statements/UnusedExceptionObject.ql @@ -14,7 +14,7 @@ import python from Call call, ClassValue ex where - call.getFunc().pointsTo(ex) and - ex.getASuperType() = ClassValue::exception() and - exists(ExprStmt s | s.getValue() = call) + call.getFunc().pointsTo(ex) and + ex.getASuperType() = ClassValue::exception() and + exists(ExprStmt s | s.getValue() = call) select call, "Instantiating an exception, but not raising it, has no effect" diff --git a/python/ql/src/Statements/UseOfExit.ql b/python/ql/src/Statements/UseOfExit.ql index 31e0e51ab39..4a2730b7753 100644 --- a/python/ql/src/Statements/UseOfExit.ql +++ b/python/ql/src/Statements/UseOfExit.ql @@ -14,5 +14,5 @@ import python from CallNode call, string name where call.getFunction().pointsTo(Value::siteQuitter(name)) select call, - "The '" + name + - "' site.Quitter object may not exist if the 'site' module is not loaded or is modified." + "The '" + name + + "' site.Quitter object may not exist if the 'site' module is not loaded or is modified." diff --git a/python/ql/src/Testing/ImpreciseAssert.ql b/python/ql/src/Testing/ImpreciseAssert.ql index a3d7e1ae7c3..121ec6024e8 100644 --- a/python/ql/src/Testing/ImpreciseAssert.ql +++ b/python/ql/src/Testing/ImpreciseAssert.ql @@ -14,79 +14,79 @@ import python /* Helper predicate for CallToAssertOnComparison class */ predicate callToAssertOnComparison(Call call, string assertName, Cmpop op) { - call.getFunc().(Attribute).getName() = assertName and - (assertName = "assertTrue" or assertName = "assertFalse") and - exists(Compare cmp | - cmp = call.getArg(0) and - /* Exclude complex comparisons like: a < b < c */ - not exists(cmp.getOp(1)) and - op = cmp.getOp(0) - ) + call.getFunc().(Attribute).getName() = assertName and + (assertName = "assertTrue" or assertName = "assertFalse") and + exists(Compare cmp | + cmp = call.getArg(0) and + /* Exclude complex comparisons like: a < b < c */ + not exists(cmp.getOp(1)) and + op = cmp.getOp(0) + ) } class CallToAssertOnComparison extends Call { - CallToAssertOnComparison() { callToAssertOnComparison(this, _, _) } + CallToAssertOnComparison() { callToAssertOnComparison(this, _, _) } - Cmpop getOperator() { callToAssertOnComparison(this, _, result) } + Cmpop getOperator() { callToAssertOnComparison(this, _, result) } - string getMethodName() { callToAssertOnComparison(this, result, _) } + string getMethodName() { callToAssertOnComparison(this, result, _) } - string getBetterName() { - exists(Cmpop op | - callToAssertOnComparison(this, "assertTrue", op) and - ( - op instanceof Eq and result = "assertEqual" - or - op instanceof NotEq and result = "assertNotEqual" - or - op instanceof Lt and result = "assertLess" - or - op instanceof LtE and result = "assertLessEqual" - or - op instanceof Gt and result = "assertGreater" - or - op instanceof GtE and result = "assertGreaterEqual" - or - op instanceof In and result = "assertIn" - or - op instanceof NotIn and result = "assertNotIn" - or - op instanceof Is and result = "assertIs" - or - op instanceof IsNot and result = "assertIsNot" - ) - or - callToAssertOnComparison(this, "assertFalse", op) and - ( - op instanceof NotEq and result = "assertEqual" - or - op instanceof Eq and result = "assertNotEqual" - or - op instanceof GtE and result = "assertLess" - or - op instanceof Gt and result = "assertLessEqual" - or - op instanceof LtE and result = "assertGreater" - or - op instanceof Lt and result = "assertGreaterEqual" - or - op instanceof NotIn and result = "assertIn" - or - op instanceof In and result = "assertNotIn" - or - op instanceof IsNot and result = "assertIs" - or - op instanceof Is and result = "assertIsNot" - ) - ) - } + string getBetterName() { + exists(Cmpop op | + callToAssertOnComparison(this, "assertTrue", op) and + ( + op instanceof Eq and result = "assertEqual" + or + op instanceof NotEq and result = "assertNotEqual" + or + op instanceof Lt and result = "assertLess" + or + op instanceof LtE and result = "assertLessEqual" + or + op instanceof Gt and result = "assertGreater" + or + op instanceof GtE and result = "assertGreaterEqual" + or + op instanceof In and result = "assertIn" + or + op instanceof NotIn and result = "assertNotIn" + or + op instanceof Is and result = "assertIs" + or + op instanceof IsNot and result = "assertIsNot" + ) + or + callToAssertOnComparison(this, "assertFalse", op) and + ( + op instanceof NotEq and result = "assertEqual" + or + op instanceof Eq and result = "assertNotEqual" + or + op instanceof GtE and result = "assertLess" + or + op instanceof Gt and result = "assertLessEqual" + or + op instanceof LtE and result = "assertGreater" + or + op instanceof Lt and result = "assertGreaterEqual" + or + op instanceof NotIn and result = "assertIn" + or + op instanceof In and result = "assertNotIn" + or + op instanceof IsNot and result = "assertIs" + or + op instanceof Is and result = "assertIsNot" + ) + ) + } } from CallToAssertOnComparison call where - /* Exclude cases where an explicit message is provided*/ - not exists(call.getArg(1)) + /* Exclude cases where an explicit message is provided*/ + not exists(call.getArg(1)) select call, - call.getMethodName() + "(a " + call.getOperator().getSymbol() + " b) " + - "cannot provide an informative message. Using " + call.getBetterName() + - "(a, b) instead will give more informative messages." + call.getMethodName() + "(a " + call.getOperator().getSymbol() + " b) " + + "cannot provide an informative message. Using " + call.getBetterName() + + "(a, b) instead will give more informative messages." diff --git a/python/ql/src/Testing/Mox.qll b/python/ql/src/Testing/Mox.qll index 0c26d2ef899..a131ca7eeca 100644 --- a/python/ql/src/Testing/Mox.qll +++ b/python/ql/src/Testing/Mox.qll @@ -2,15 +2,15 @@ import python /** Whether `mox` or `.StubOutWithMock()` is used in thin module `m`. */ predicate useOfMoxInModule(Module m) { - exists(ModuleObject mox | mox.getName() = "mox" or mox.getName() = "mox3.mox" | - exists(ControlFlowNode use | - use.refersTo(mox) and - use.getScope().getEnclosingModule() = m - ) - ) - or - exists(Call call | - call.getFunc().(Attribute).getName() = "StubOutWithMock" and - call.getEnclosingModule() = m + exists(ModuleObject mox | mox.getName() = "mox" or mox.getName() = "mox3.mox" | + exists(ControlFlowNode use | + use.refersTo(mox) and + use.getScope().getEnclosingModule() = m ) + ) + or + exists(Call call | + call.getFunc().(Attribute).getName() = "StubOutWithMock" and + call.getEnclosingModule() = m + ) } diff --git a/python/ql/src/Variables/Definition.qll b/python/ql/src/Variables/Definition.qll index e8bc95ef79c..76f3986d605 100644 --- a/python/ql/src/Variables/Definition.qll +++ b/python/ql/src/Variables/Definition.qll @@ -4,100 +4,100 @@ import python * A control-flow node that defines a variable */ class Definition extends NameNode, DefinitionNode { - /** - * The variable defined by this control-flow node. - */ - Variable getVariable() { this.defines(result) } + /** + * The variable defined by this control-flow node. + */ + Variable getVariable() { this.defines(result) } - /** - * The SSA variable corresponding to the current definition. Since SSA variables - * are only generated for definitions with at least one use, not all definitions - * will have an SSA variable. - */ - SsaVariable getSsaVariable() { result.getDefinition() = this } + /** + * The SSA variable corresponding to the current definition. Since SSA variables + * are only generated for definitions with at least one use, not all definitions + * will have an SSA variable. + */ + SsaVariable getSsaVariable() { result.getDefinition() = this } - /** - * The index of this definition in its basic block. - */ - private int indexInBB(BasicBlock bb, Variable v) { - v = this.getVariable() and - this = bb.getNode(result) - } + /** + * The index of this definition in its basic block. + */ + private int indexInBB(BasicBlock bb, Variable v) { + v = this.getVariable() and + this = bb.getNode(result) + } - /** - * The rank of this definition among other definitions of the same variable - * in its basic block. The first definition will have rank 1, and subsequent - * definitions will have sequentially increasing ranks. - */ - private int rankInBB(BasicBlock bb, Variable v) { - exists(int defIdx | defIdx = this.indexInBB(bb, v) | - defIdx = rank[result](int idx, Definition def | idx = def.indexInBB(bb, v) | idx) - ) - } + /** + * The rank of this definition among other definitions of the same variable + * in its basic block. The first definition will have rank 1, and subsequent + * definitions will have sequentially increasing ranks. + */ + private int rankInBB(BasicBlock bb, Variable v) { + exists(int defIdx | defIdx = this.indexInBB(bb, v) | + defIdx = rank[result](int idx, Definition def | idx = def.indexInBB(bb, v) | idx) + ) + } - /** Is this definition the first in its basic block for its variable? */ - predicate isFirst() { this.rankInBB(_, _) = 1 } + /** Is this definition the first in its basic block for its variable? */ + predicate isFirst() { this.rankInBB(_, _) = 1 } - /** Is this definition the last in its basic block for its variable? */ - predicate isLast() { - exists(BasicBlock b, Variable v | - this.rankInBB(b, v) = max(Definition other | any() | other.rankInBB(b, v)) - ) - } + /** Is this definition the last in its basic block for its variable? */ + predicate isLast() { + exists(BasicBlock b, Variable v | + this.rankInBB(b, v) = max(Definition other | any() | other.rankInBB(b, v)) + ) + } - /** - * Is this definition unused? A definition is unused if the value it provides - * is not read anywhere. - */ - predicate isUnused() { - // SSA variables only exist for definitions that have at least one use. - not exists(this.getSsaVariable()) and - // If a variable is used in a foreign scope, all bets are off. - not this.getVariable().escapes() and - // Global variables don't have SSA variables unless the scope is global. - this.getVariable().getScope() = this.getScope() and - // A call to locals() or vars() in the variable scope counts as a use - not exists(Function f, Call c, string locals_or_vars | - c.getScope() = f and - this.getScope() = f and - c.getFunc().(Name).getId() = locals_or_vars - | - locals_or_vars = "locals" or locals_or_vars = "vars" - ) - } + /** + * Is this definition unused? A definition is unused if the value it provides + * is not read anywhere. + */ + predicate isUnused() { + // SSA variables only exist for definitions that have at least one use. + not exists(this.getSsaVariable()) and + // If a variable is used in a foreign scope, all bets are off. + not this.getVariable().escapes() and + // Global variables don't have SSA variables unless the scope is global. + this.getVariable().getScope() = this.getScope() and + // A call to locals() or vars() in the variable scope counts as a use + not exists(Function f, Call c, string locals_or_vars | + c.getScope() = f and + this.getScope() = f and + c.getFunc().(Name).getId() = locals_or_vars + | + locals_or_vars = "locals" or locals_or_vars = "vars" + ) + } - /** - * An immediate re-definition of this definition's variable. - */ - Definition getARedef() { - result != this and - exists(Variable var | var = this.getVariable() and var = result.getVariable() | - // Definitions in different basic blocks. - this.isLast() and - reaches_without_redef(var, this.getBasicBlock(), result.getBasicBlock()) and - result.isFirst() - ) - or - // Definitions in the same basic block. - exists(BasicBlock common, Variable var | - this.rankInBB(common, var) + 1 = result.rankInBB(common, var) - ) - } + /** + * An immediate re-definition of this definition's variable. + */ + Definition getARedef() { + result != this and + exists(Variable var | var = this.getVariable() and var = result.getVariable() | + // Definitions in different basic blocks. + this.isLast() and + reaches_without_redef(var, this.getBasicBlock(), result.getBasicBlock()) and + result.isFirst() + ) + or + // Definitions in the same basic block. + exists(BasicBlock common, Variable var | + this.rankInBB(common, var) + 1 = result.rankInBB(common, var) + ) + } - /** - * We only consider assignments as potential alert targets, not parameters - * and imports and other name-defining constructs. - * We also ignore anything named "_", "empty", "unused" or "dummy" - */ - predicate isRelevant() { - exists(AstNode p | p = this.getNode().getParentNode() | - p instanceof Assign or p instanceof AugAssign or p instanceof Tuple - ) and - not name_acceptable_for_unused_variable(this.getVariable()) and - /* Decorated classes and functions are used */ - not exists(this.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and - not exists(this.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) - } + /** + * We only consider assignments as potential alert targets, not parameters + * and imports and other name-defining constructs. + * We also ignore anything named "_", "empty", "unused" or "dummy" + */ + predicate isRelevant() { + exists(AstNode p | p = this.getNode().getParentNode() | + p instanceof Assign or p instanceof AugAssign or p instanceof Tuple + ) and + not name_acceptable_for_unused_variable(this.getVariable()) and + /* Decorated classes and functions are used */ + not exists(this.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and + not exists(this.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) + } } /** @@ -106,36 +106,36 @@ class Definition extends NameNode, DefinitionNode { * observed transitivity will be caused by loops in the control-flow graph. */ private predicate reaches_without_redef(Variable v, BasicBlock a, BasicBlock b) { - exists(Definition def | a.getASuccessor() = b | - def.getBasicBlock() = a and def.getVariable() = v and maybe_redefined(v) - ) - or - exists(BasicBlock mid | reaches_without_redef(v, a, mid) | - not exists(NameNode cfn | cfn.defines(v) | cfn.getBasicBlock() = mid) and - mid.getASuccessor() = b - ) + exists(Definition def | a.getASuccessor() = b | + def.getBasicBlock() = a and def.getVariable() = v and maybe_redefined(v) + ) + or + exists(BasicBlock mid | reaches_without_redef(v, a, mid) | + not exists(NameNode cfn | cfn.defines(v) | cfn.getBasicBlock() = mid) and + mid.getASuccessor() = b + ) } private predicate maybe_redefined(Variable v) { strictcount(Definition d | d.defines(v)) > 1 } predicate name_acceptable_for_unused_variable(Variable var) { - exists(string name | var.getId() = name | - name.regexpMatch("_+") or - name = "empty" or - name.matches("%unused%") or - name = "dummy" or - name.regexpMatch("__.*") - ) + exists(string name | var.getId() = name | + name.regexpMatch("_+") or + name = "empty" or + name.matches("%unused%") or + name = "dummy" or + name.regexpMatch("__.*") + ) } class ListComprehensionDeclaration extends ListComp { - Name getALeakedVariableUse() { - major_version() = 2 and - this.getIterationVariable(_).getId() = result.getId() and - result.getScope() = this.getScope() and - this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and - result.isUse() - } + Name getALeakedVariableUse() { + major_version() = 2 and + this.getIterationVariable(_).getId() = result.getId() and + result.getScope() = this.getScope() and + this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and + result.isUse() + } - Name getDefinition() { result = this.getIterationVariable(0).getAStore() } + Name getDefinition() { result = this.getIterationVariable(0).getAStore() } } diff --git a/python/ql/src/Variables/LeakingListComprehension.ql b/python/ql/src/Variables/LeakingListComprehension.ql index 32a338d31c1..9b98fb43a31 100644 --- a/python/ql/src/Variables/LeakingListComprehension.ql +++ b/python/ql/src/Variables/LeakingListComprehension.ql @@ -15,17 +15,17 @@ import Definition from ListComprehensionDeclaration l, Name use, Name defn where - use = l.getALeakedVariableUse() and - defn = l.getDefinition() and - l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and - /* Make sure we aren't in a loop, as the variable may be redefined */ - not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and - not l.contains(use) and - not use.deletes(_) and - not exists(SsaVariable v | - v.getAUse() = use.getAFlowNode() and - not v.getDefinition().strictlyDominates(l.getAFlowNode()) - ) + use = l.getALeakedVariableUse() and + defn = l.getDefinition() and + l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and + /* Make sure we aren't in a loop, as the variable may be redefined */ + not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and + not l.contains(use) and + not use.deletes(_) and + not exists(SsaVariable v | + v.getAUse() = use.getAFlowNode() and + not v.getDefinition().strictlyDominates(l.getAFlowNode()) + ) select use, - use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn, - "list comprehension variable" + use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn, + "list comprehension variable" diff --git a/python/ql/src/Variables/Loop.qll b/python/ql/src/Variables/Loop.qll index cc998f70e94..be19d4077c4 100644 --- a/python/ql/src/Variables/Loop.qll +++ b/python/ql/src/Variables/Loop.qll @@ -1,15 +1,15 @@ import python private predicate empty_sequence(Expr e) { - exists(SsaVariable var | var.getAUse().getNode() = e | - empty_sequence(var.getDefinition().getNode()) - ) - or - e instanceof List and not exists(e.(List).getAnElt()) - or - e instanceof Tuple and not exists(e.(Tuple).getAnElt()) - or - e.(StrConst).getText().length() = 0 + exists(SsaVariable var | var.getAUse().getNode() = e | + empty_sequence(var.getDefinition().getNode()) + ) + or + e instanceof List and not exists(e.(List).getAnElt()) + or + e instanceof Tuple and not exists(e.(Tuple).getAnElt()) + or + e.(StrConst).getText().length() = 0 } /* This has the potential for refinement, but we err on the side of fewer false positives for now. */ @@ -17,23 +17,23 @@ private predicate probably_non_empty_sequence(Expr e) { not empty_sequence(e) } /** A loop which probably defines v */ private Stmt loop_probably_defines(Variable v) { - exists(Name defn | defn.defines(v) and result.contains(defn) | - probably_non_empty_sequence(result.(For).getIter()) - or - probably_non_empty_sequence(result.(While).getTest()) - ) + exists(Name defn | defn.defines(v) and result.contains(defn) | + probably_non_empty_sequence(result.(For).getIter()) + or + probably_non_empty_sequence(result.(While).getTest()) + ) } /** Holds if the variable used by `use` is probably defined in a loop */ predicate probably_defined_in_loop(Name use) { - exists(Stmt loop | loop = loop_probably_defines(use.getVariable()) | - loop.getAFlowNode().strictlyReaches(use.getAFlowNode()) - ) + exists(Stmt loop | loop = loop_probably_defines(use.getVariable()) | + loop.getAFlowNode().strictlyReaches(use.getAFlowNode()) + ) } /** Holds if `s` is a loop that probably executes at least once */ predicate loop_probably_executes_at_least_once(Stmt s) { - probably_non_empty_sequence(s.(For).getIter()) - or - probably_non_empty_sequence(s.(While).getTest()) + probably_non_empty_sequence(s.(For).getIter()) + or + probably_non_empty_sequence(s.(While).getTest()) } diff --git a/python/ql/src/Variables/LoopVariableCapture.ql b/python/ql/src/Variables/LoopVariableCapture.ql index a88abb8b5f5..74cd20b1d6c 100644 --- a/python/ql/src/Variables/LoopVariableCapture.ql +++ b/python/ql/src/Variables/LoopVariableCapture.ql @@ -13,33 +13,33 @@ import python // Gets the scope of the iteration variable of the looping scope Scope iteration_variable_scope(AstNode loop) { - result = loop.(For).getScope() - or - result = loop.(Comp).getFunction() + result = loop.(For).getScope() + or + result = loop.(Comp).getFunction() } predicate capturing_looping_construct(CallableExpr capturing, AstNode loop, Variable var) { - var.getScope() = iteration_variable_scope(loop) and - var.getAnAccess().getScope() = capturing.getInnerScope() and - capturing.getParentNode+() = loop and - ( - loop.(For).getTarget() = var.getAnAccess() - or - var = loop.(Comp).getAnIterationVariable() - ) + var.getScope() = iteration_variable_scope(loop) and + var.getAnAccess().getScope() = capturing.getInnerScope() and + capturing.getParentNode+() = loop and + ( + loop.(For).getTarget() = var.getAnAccess() + or + var = loop.(Comp).getAnIterationVariable() + ) } predicate escaping_capturing_looping_construct(CallableExpr capturing, AstNode loop, Variable var) { - capturing_looping_construct(capturing, loop, var) and - // Escapes if used out side of for loop or is a lambda in a comprehension - ( - loop instanceof For and - exists(Expr e | e.pointsTo(_, _, capturing) | not loop.contains(e)) - or - loop.(Comp).getElt() = capturing - or - loop.(Comp).getElt().(Tuple).getAnElt() = capturing - ) + capturing_looping_construct(capturing, loop, var) and + // Escapes if used out side of for loop or is a lambda in a comprehension + ( + loop instanceof For and + exists(Expr e | e.pointsTo(_, _, capturing) | not loop.contains(e)) + or + loop.(Comp).getElt() = capturing + or + loop.(Comp).getElt().(Tuple).getAnElt() = capturing + ) } from CallableExpr capturing, AstNode loop, Variable var diff --git a/python/ql/src/Variables/MonkeyPatched.qll b/python/ql/src/Variables/MonkeyPatched.qll index 2a846e3deb1..d06731f5223 100644 --- a/python/ql/src/Variables/MonkeyPatched.qll +++ b/python/ql/src/Variables/MonkeyPatched.qll @@ -1,24 +1,24 @@ import python predicate monkey_patched_builtin(string name) { - exists(AttrNode attr, SubscriptNode subscr, StrConst s | - subscr.isStore() and - subscr.getIndex().getNode() = s and - s.getText() = name and - subscr.getObject() = attr and - attr.getObject("__dict__").pointsTo(Module::builtinModule()) - ) - or - exists(CallNode call, ControlFlowNode bltn, StrConst s | - call.getArg(0) = bltn and - bltn.pointsTo(Module::builtinModule()) and - call.getArg(1).getNode() = s and - s.getText() = name and - call.getFunction().pointsTo(Value::named("setattr")) - ) - or - exists(AttrNode attr | - attr.isStore() and - attr.getObject(name).pointsTo(Module::builtinModule()) - ) + exists(AttrNode attr, SubscriptNode subscr, StrConst s | + subscr.isStore() and + subscr.getIndex().getNode() = s and + s.getText() = name and + subscr.getObject() = attr and + attr.getObject("__dict__").pointsTo(Module::builtinModule()) + ) + or + exists(CallNode call, ControlFlowNode bltn, StrConst s | + call.getArg(0) = bltn and + bltn.pointsTo(Module::builtinModule()) and + call.getArg(1).getNode() = s and + s.getText() = name and + call.getFunction().pointsTo(Value::named("setattr")) + ) + or + exists(AttrNode attr | + attr.isStore() and + attr.getObject(name).pointsTo(Module::builtinModule()) + ) } diff --git a/python/ql/src/Variables/MultiplyDefined.ql b/python/ql/src/Variables/MultiplyDefined.ql index cae39729b9b..c7f09987ff1 100644 --- a/python/ql/src/Variables/MultiplyDefined.ql +++ b/python/ql/src/Variables/MultiplyDefined.ql @@ -15,35 +15,35 @@ import python import Definition predicate multiply_defined(AstNode asgn1, AstNode asgn2, Variable v) { - /* - * Must be redefined on all possible paths in the CFG corresponding to the original source. - * For example, splitting may create a path where `def` is unconditionally redefined, even though - * it is not in the original source. - */ + /* + * Must be redefined on all possible paths in the CFG corresponding to the original source. + * For example, splitting may create a path where `def` is unconditionally redefined, even though + * it is not in the original source. + */ - forex(Definition def, Definition redef | - def.getVariable() = v and - def = asgn1.getAFlowNode() and - redef = asgn2.getAFlowNode() - | - def.isUnused() and - def.getARedef() = redef and - def.isRelevant() - ) + forex(Definition def, Definition redef | + def.getVariable() = v and + def = asgn1.getAFlowNode() and + redef = asgn2.getAFlowNode() + | + def.isUnused() and + def.getARedef() = redef and + def.isRelevant() + ) } predicate simple_literal(Expr e) { - e.(Num).getN() = "0" - or - e instanceof NameConstant - or - e instanceof List and not exists(e.(List).getAnElt()) - or - e instanceof Tuple and not exists(e.(Tuple).getAnElt()) - or - e instanceof Dict and not exists(e.(Dict).getAKey()) - or - e.(StrConst).getText() = "" + e.(Num).getN() = "0" + or + e instanceof NameConstant + or + e instanceof List and not exists(e.(List).getAnElt()) + or + e instanceof Tuple and not exists(e.(Tuple).getAnElt()) + or + e instanceof Dict and not exists(e.(Dict).getAKey()) + or + e.(StrConst).getText() = "" } /** @@ -56,14 +56,14 @@ predicate simple_literal(Expr e) { * x = value2 */ predicate uninteresting_definition(AstNode asgn1) { - exists(AssignStmt a | a.getATarget() = asgn1 | simple_literal(a.getValue())) + exists(AssignStmt a | a.getATarget() = asgn1 | simple_literal(a.getValue())) } from AstNode asgn1, AstNode asgn2, Variable v where - multiply_defined(asgn1, asgn2, v) and - forall(Name el | el = asgn1.getParentNode().(Tuple).getAnElt() | multiply_defined(el, _, _)) and - not uninteresting_definition(asgn1) + multiply_defined(asgn1, asgn2, v) and + forall(Name el | el = asgn1.getParentNode().(Tuple).getAnElt() | multiply_defined(el, _, _)) and + not uninteresting_definition(asgn1) select asgn1, - "This assignment to '" + v.getId() + - "' is unnecessary as it is redefined $@ before this value is used.", asgn2 as t, "here" + "This assignment to '" + v.getId() + + "' is unnecessary as it is redefined $@ before this value is used.", asgn2 as t, "here" diff --git a/python/ql/src/Variables/ShadowBuiltin.ql b/python/ql/src/Variables/ShadowBuiltin.ql index 7073c429ec2..7e3b305380d 100644 --- a/python/ql/src/Variables/ShadowBuiltin.ql +++ b/python/ql/src/Variables/ShadowBuiltin.ql @@ -17,49 +17,49 @@ import Shadowing import semmle.python.types.Builtins predicate allow_list(string name) { - /* These are rarely used and thus unlikely to be confusing */ - name = "iter" or - name = "next" or - name = "input" or - name = "file" or - name = "apply" or - name = "slice" or - name = "buffer" or - name = "coerce" or - name = "intern" or - name = "exit" or - name = "quit" or - name = "license" or - /* These are short and/or hard to avoid */ - name = "dir" or - name = "id" or - name = "max" or - name = "min" or - name = "sum" or - name = "cmp" or - name = "chr" or - name = "ord" or - name = "bytes" or - name = "_" + /* These are rarely used and thus unlikely to be confusing */ + name = "iter" or + name = "next" or + name = "input" or + name = "file" or + name = "apply" or + name = "slice" or + name = "buffer" or + name = "coerce" or + name = "intern" or + name = "exit" or + name = "quit" or + name = "license" or + /* These are short and/or hard to avoid */ + name = "dir" or + name = "id" or + name = "max" or + name = "min" or + name = "sum" or + name = "cmp" or + name = "chr" or + name = "ord" or + name = "bytes" or + name = "_" } predicate shadows(Name d, string name, Function scope, int line) { - exists(LocalVariable l | - d.defines(l) and - l.getId() = name and - exists(Builtin::builtin(l.getId())) - ) and - d.getScope() = scope and - d.getLocation().getStartLine() = line and - not allow_list(name) and - not optimizing_parameter(d) + exists(LocalVariable l | + d.defines(l) and + l.getId() = name and + exists(Builtin::builtin(l.getId())) + ) and + d.getScope() = scope and + d.getLocation().getStartLine() = line and + not allow_list(name) and + not optimizing_parameter(d) } predicate first_shadowing_definition(Name d, string name) { - exists(int first, Scope scope | - shadows(d, name, scope, first) and - first = min(int line | shadows(_, name, scope, line)) - ) + exists(int first, Scope scope | + shadows(d, name, scope, first) and + first = min(int line | shadows(_, name, scope, line)) + ) } from Name d, string name diff --git a/python/ql/src/Variables/ShadowGlobal.ql b/python/ql/src/Variables/ShadowGlobal.ql index c7140c75d83..065abf42fe4 100644 --- a/python/ql/src/Variables/ShadowGlobal.ql +++ b/python/ql/src/Variables/ShadowGlobal.ql @@ -17,54 +17,54 @@ import Shadowing import semmle.python.types.Builtins predicate shadows(Name d, GlobalVariable g, Function scope, int line) { - g.getScope() = scope.getScope() and - d.getScope() = scope and - exists(LocalVariable l | - d.defines(l) and - l.getId() = g.getId() - ) and - not exists(Import il, Import ig, Name gd | il.contains(d) and gd.defines(g) and ig.contains(gd)) and - not exists(Assign a | a.getATarget() = d and a.getValue() = g.getAnAccess()) and - not exists(Builtin::builtin(g.getId())) and - d.getLocation().getStartLine() = line and - exists(Name defn | defn.defines(g) | not exists(If i | i.isNameEqMain() | i.contains(defn))) and - not optimizing_parameter(d) + g.getScope() = scope.getScope() and + d.getScope() = scope and + exists(LocalVariable l | + d.defines(l) and + l.getId() = g.getId() + ) and + not exists(Import il, Import ig, Name gd | il.contains(d) and gd.defines(g) and ig.contains(gd)) and + not exists(Assign a | a.getATarget() = d and a.getValue() = g.getAnAccess()) and + not exists(Builtin::builtin(g.getId())) and + d.getLocation().getStartLine() = line and + exists(Name defn | defn.defines(g) | not exists(If i | i.isNameEqMain() | i.contains(defn))) and + not optimizing_parameter(d) } /* pytest dynamically populates its namespace so, we cannot look directly for the pytest.fixture function */ AttrNode pytest_fixture_attr() { - exists(ModuleValue pytest | result.getObject("fixture").pointsTo(pytest)) + exists(ModuleValue pytest | result.getObject("fixture").pointsTo(pytest)) } Value pytest_fixture() { - exists(CallNode call | - call.getFunction() = pytest_fixture_attr() - or - call.getFunction().(CallNode).getFunction() = pytest_fixture_attr() - | - call.pointsTo(result) - ) + exists(CallNode call | + call.getFunction() = pytest_fixture_attr() + or + call.getFunction().(CallNode).getFunction() = pytest_fixture_attr() + | + call.pointsTo(result) + ) } /* pytest fixtures require that the parameter name is also a global */ predicate assigned_pytest_fixture(GlobalVariable v) { - exists(NameNode def | - def.defines(v) and def.(DefinitionNode).getValue().pointsTo(pytest_fixture()) - ) + exists(NameNode def | + def.defines(v) and def.(DefinitionNode).getValue().pointsTo(pytest_fixture()) + ) } predicate first_shadowing_definition(Name d, GlobalVariable g) { - exists(int first, Scope scope | - shadows(d, g, scope, first) and - first = min(int line | shadows(_, g, scope, line)) - ) + exists(int first, Scope scope | + shadows(d, g, scope, first) and + first = min(int line | shadows(_, g, scope, line)) + ) } from Name d, GlobalVariable g, Name def where - first_shadowing_definition(d, g) and - not exists(Name n | n.deletes(g)) and - def.defines(g) and - not assigned_pytest_fixture(g) and - not g.getId() = "_" + first_shadowing_definition(d, g) and + not exists(Name n | n.deletes(g)) and + def.defines(g) and + not assigned_pytest_fixture(g) and + not g.getId() = "_" select d, "Local variable '" + g.getId() + "' shadows a global variable defined $@.", def, "here" diff --git a/python/ql/src/Variables/Shadowing.qll b/python/ql/src/Variables/Shadowing.qll index a3dabb0a4e6..e8e47943a64 100644 --- a/python/ql/src/Variables/Shadowing.qll +++ b/python/ql/src/Variables/Shadowing.qll @@ -7,8 +7,8 @@ import python */ predicate optimizing_parameter(Parameter p) { - exists(string name, Name glob | p.getDefault() = glob | - glob.getId() = name and - p.asName().getId() = name - ) + exists(string name, Name glob | p.getDefault() = glob | + glob.getId() = name and + p.asName().getId() = name + ) } diff --git a/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql index 77d1e5ca67c..169b686a22a 100644 --- a/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql +++ b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql @@ -14,16 +14,16 @@ import python import Definition predicate is_increment(Stmt s) { - /* x += n */ - s.(AugAssign).getValue() instanceof IntegerLiteral - or - /* x = x + n */ - exists(Name t, BinaryExpr add | - t = s.(AssignStmt).getTarget(0) and - add = s.(AssignStmt).getValue() and - add.getLeft().(Name).getVariable() = t.getVariable() and - add.getRight() instanceof IntegerLiteral - ) + /* x += n */ + s.(AugAssign).getValue() instanceof IntegerLiteral + or + /* x = x + n */ + exists(Name t, BinaryExpr add | + t = s.(AssignStmt).getTarget(0) and + add = s.(AssignStmt).getValue() and + add.getLeft().(Name).getVariable() = t.getVariable() and + add.getRight() instanceof IntegerLiteral + ) } predicate counting_loop(For f) { is_increment(f.getAStmt()) } @@ -31,49 +31,49 @@ predicate counting_loop(For f) { is_increment(f.getAStmt()) } predicate empty_loop(For f) { not exists(f.getStmt(1)) and f.getStmt(0) instanceof Pass } predicate one_item_only(For f) { - not exists(Continue c | f.contains(c)) and - exists(Stmt s | s = f.getBody().getLastItem() | - s instanceof Return - or - s instanceof Break - ) + not exists(Continue c | f.contains(c)) and + exists(Stmt s | s = f.getBody().getLastItem() | + s instanceof Return + or + s instanceof Break + ) } predicate points_to_call_to_range(ControlFlowNode f) { - /* (x)range is a function in Py2 and a class in Py3, so we must treat it as a plain object */ - exists(Value range | - range = Value::named("range") or - range = Value::named("xrange") - | - f = range.getACall() - ) - or - /* In case points-to fails due to 'from six.moves import range' or similar. */ - exists(string range | f.getNode().(Call).getFunc().(Name).getId() = range | - range = "range" or range = "xrange" - ) - or - /* Handle list(range(...)) and list(list(range(...))) */ - f.(CallNode).pointsTo().getClass() = ClassValue::list() and - points_to_call_to_range(f.(CallNode).getArg(0)) + /* (x)range is a function in Py2 and a class in Py3, so we must treat it as a plain object */ + exists(Value range | + range = Value::named("range") or + range = Value::named("xrange") + | + f = range.getACall() + ) + or + /* In case points-to fails due to 'from six.moves import range' or similar. */ + exists(string range | f.getNode().(Call).getFunc().(Name).getId() = range | + range = "range" or range = "xrange" + ) + or + /* Handle list(range(...)) and list(list(range(...))) */ + f.(CallNode).pointsTo().getClass() = ClassValue::list() and + points_to_call_to_range(f.(CallNode).getArg(0)) } /** Whether n is a use of a variable that is a not effectively a constant. */ predicate use_of_non_constant(Name n) { - exists(Variable var | - n.uses(var) and - /* use is local */ - not n.getScope() instanceof Module and - /* variable is not global */ - not var.getScope() instanceof Module - | - /* Defined more than once (dynamically) */ - strictcount(Name def | def.defines(var)) > 1 - or - exists(For f, Name def | f.contains(def) and def.defines(var)) - or - exists(While w, Name def | w.contains(def) and def.defines(var)) - ) + exists(Variable var | + n.uses(var) and + /* use is local */ + not n.getScope() instanceof Module and + /* variable is not global */ + not var.getScope() instanceof Module + | + /* Defined more than once (dynamically) */ + strictcount(Name def | def.defines(var)) > 1 + or + exists(For f, Name def | f.contains(def) and def.defines(var)) + or + exists(While w, Name def | w.contains(def) and def.defines(var)) + ) } /** @@ -81,9 +81,9 @@ predicate use_of_non_constant(Name n) { * E.g. queue.add(None) */ predicate implicit_repeat(For f) { - not exists(f.getStmt(1)) and - exists(ImmutableLiteral imm | f.getStmt(0).contains(imm)) and - not exists(Name n | f.getBody().contains(n) and use_of_non_constant(n)) + not exists(f.getStmt(1)) and + exists(ImmutableLiteral imm | f.getStmt(0).contains(imm)) and + not exists(Name n | f.getBody().contains(n) and use_of_non_constant(n)) } /** @@ -93,22 +93,22 @@ predicate implicit_repeat(For f) { * E.g. gets `x` from `{ y for y in x }`. */ ControlFlowNode get_comp_iterable(For f) { - exists(Comp c | c.getFunction().getStmt(0) = f | c.getAFlowNode().getAPredecessor() = result) + exists(Comp c | c.getFunction().getStmt(0) = f | c.getAFlowNode().getAPredecessor() = result) } from For f, Variable v, string msg where - f.getTarget() = v.getAnAccess() and - not f.getAStmt().contains(v.getAnAccess()) and - not points_to_call_to_range(f.getIter().getAFlowNode()) and - not points_to_call_to_range(get_comp_iterable(f)) and - not name_acceptable_for_unused_variable(v) and - not f.getScope().getName() = "genexpr" and - not empty_loop(f) and - not one_item_only(f) and - not counting_loop(f) and - not implicit_repeat(f) and - if exists(Name del | del.deletes(v) and f.getAStmt().contains(del)) - then msg = "' is deleted, but not used, in the loop body." - else msg = "' is not used in the loop body." + f.getTarget() = v.getAnAccess() and + not f.getAStmt().contains(v.getAnAccess()) and + not points_to_call_to_range(f.getIter().getAFlowNode()) and + not points_to_call_to_range(get_comp_iterable(f)) and + not name_acceptable_for_unused_variable(v) and + not f.getScope().getName() = "genexpr" and + not empty_loop(f) and + not one_item_only(f) and + not counting_loop(f) and + not implicit_repeat(f) and + if exists(Name del | del.deletes(v) and f.getAStmt().contains(del)) + then msg = "' is deleted, but not used, in the loop body." + else msg = "' is not used in the loop body." select f, "For loop variable '" + v.getId() + msg diff --git a/python/ql/src/Variables/Undefined.qll b/python/ql/src/Variables/Undefined.qll index 2c757733af4..ae401a83aaf 100644 --- a/python/ql/src/Variables/Undefined.qll +++ b/python/ql/src/Variables/Undefined.qll @@ -4,24 +4,24 @@ import semmle.python.dataflow.TaintTracking /** Marker for "uninitialized". */ class Uninitialized extends TaintKind { - Uninitialized() { this = "undefined" } + Uninitialized() { this = "undefined" } } private predicate loop_entry_variables(EssaVariable pred, EssaVariable succ) { - exists(PhiFunction phi, BasicBlock pb | - loop_entry_edge(pb, phi.getBasicBlock()) and - succ = phi.getVariable() and - pred = phi.getInput(pb) - ) + exists(PhiFunction phi, BasicBlock pb | + loop_entry_edge(pb, phi.getBasicBlock()) and + succ = phi.getVariable() and + pred = phi.getInput(pb) + ) } private predicate loop_entry_edge(BasicBlock pred, BasicBlock loop) { - pred = loop.getAPredecessor() and - pred = loop.getImmediateDominator() and - exists(Stmt s | - loop_probably_executes_at_least_once(s) and - s.getAFlowNode().getBasicBlock() = loop - ) + pred = loop.getAPredecessor() and + pred = loop.getImmediateDominator() and + exists(Stmt s | + loop_probably_executes_at_least_once(s) and + s.getAFlowNode().getBasicBlock() = loop + ) } /** @@ -29,11 +29,11 @@ private predicate loop_entry_edge(BasicBlock pred, BasicBlock loop) { * any use dominated by another use of the same variable must be defined, or is unreachable. */ private predicate first_use(NameNode u, EssaVariable v) { - v.getASourceUse() = u and - not exists(NameNode other | - v.getASourceUse() = other and - other.strictlyDominates(u) - ) + v.getASourceUse() = u and + not exists(NameNode other | + v.getASourceUse() = other and + other.strictlyDominates(u) + ) } /** @@ -41,77 +41,77 @@ private predicate first_use(NameNode u, EssaVariable v) { * there is a function called `method_name` that can exit the program. */ private predicate maybe_call_to_exiting_function(CallNode call) { - exists(FunctionValue exits, string name | exits.neverReturns() and exits.getName() = name | - call.getFunction().(NameNode).getId() = name or - call.getFunction().(AttrNode).getName() = name - ) + exists(FunctionValue exits, string name | exits.neverReturns() and exits.getName() = name | + call.getFunction().(NameNode).getId() = name or + call.getFunction().(AttrNode).getName() = name + ) } predicate exitFunctionGuardedEdge(EssaVariable pred, EssaVariable succ) { - exists(CallNode exit_call | - succ.(PhiFunction).getInput(exit_call.getBasicBlock()) = pred and - maybe_call_to_exiting_function(exit_call) - ) + exists(CallNode exit_call | + succ.(PhiFunction).getInput(exit_call.getBasicBlock()) = pred and + maybe_call_to_exiting_function(exit_call) + ) } class UninitializedConfig extends TaintTracking::Configuration { - UninitializedConfig() { this = "Unitialized local config" } + UninitializedConfig() { this = "Unitialized local config" } - override predicate isSource(DataFlow::Node source, TaintKind kind) { - kind instanceof Uninitialized and - exists(EssaVariable var | - source.asVariable() = var and - var.getSourceVariable() instanceof FastLocalVariable and - not var.getSourceVariable().(Variable).escapes() - | - var instanceof ScopeEntryDefinition - or - var instanceof DeletionDefinition - ) - } + override predicate isSource(DataFlow::Node source, TaintKind kind) { + kind instanceof Uninitialized and + exists(EssaVariable var | + source.asVariable() = var and + var.getSourceVariable() instanceof FastLocalVariable and + not var.getSourceVariable().(Variable).escapes() + | + var instanceof ScopeEntryDefinition + or + var instanceof DeletionDefinition + ) + } - override predicate isBarrier(DataFlow::Node node, TaintKind kind) { - kind instanceof Uninitialized and - ( - definition(node.asVariable()) - or - use(node.asVariable()) - or - sanitizingNode(node.asCfgNode()) - ) - } + override predicate isBarrier(DataFlow::Node node, TaintKind kind) { + kind instanceof Uninitialized and + ( + definition(node.asVariable()) + or + use(node.asVariable()) + or + sanitizingNode(node.asCfgNode()) + ) + } - private predicate definition(EssaDefinition def) { - def instanceof AssignmentDefinition - or - def instanceof ExceptionCapture - or - def instanceof ParameterDefinition - } + private predicate definition(EssaDefinition def) { + def instanceof AssignmentDefinition + or + def instanceof ExceptionCapture + or + def instanceof ParameterDefinition + } - private predicate use(EssaDefinition def) { - exists(def.(EssaNodeRefinement).getInput().getASourceUse()) - or - exists(def.(PhiFunction).getAnInput().getASourceUse()) - or - exists(def.(EssaEdgeRefinement).getInput().getASourceUse()) - } + private predicate use(EssaDefinition def) { + exists(def.(EssaNodeRefinement).getInput().getASourceUse()) + or + exists(def.(PhiFunction).getAnInput().getASourceUse()) + or + exists(def.(EssaEdgeRefinement).getInput().getASourceUse()) + } - private predicate sanitizingNode(ControlFlowNode node) { - exists(EssaVariable v | - v.getASourceUse() = node and - not first_use(node, v) - ) - } + private predicate sanitizingNode(ControlFlowNode node) { + exists(EssaVariable v | + v.getASourceUse() = node and + not first_use(node, v) + ) + } - override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { - /* - * If we are guaranteed to iterate over a loop at least once, then we can prune any edges that - * don't pass through the body. - */ + override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { + /* + * If we are guaranteed to iterate over a loop at least once, then we can prune any edges that + * don't pass through the body. + */ - loop_entry_variables(src.asVariable(), dest.asVariable()) - or - exitFunctionGuardedEdge(src.asVariable(), dest.asVariable()) - } + loop_entry_variables(src.asVariable(), dest.asVariable()) + or + exitFunctionGuardedEdge(src.asVariable(), dest.asVariable()) + } } diff --git a/python/ql/src/Variables/UndefinedExport.ql b/python/ql/src/Variables/UndefinedExport.ql index 61e7baab2e2..52d51ce4f72 100644 --- a/python/ql/src/Variables/UndefinedExport.ql +++ b/python/ql/src/Variables/UndefinedExport.ql @@ -15,68 +15,67 @@ import python /** Whether name is declared in the __all__ list of this module */ predicate declaredInAll(Module m, StrConst name) { - exists(Assign a, GlobalVariable all | - a.defines(all) and - a.getScope() = m and - all.getId() = "__all__" and - a.getValue().(List).getAnElt() = name - ) + exists(Assign a, GlobalVariable all | + a.defines(all) and + a.getScope() = m and + all.getId() = "__all__" and + a.getValue().(List).getAnElt() = name + ) } predicate mutates_globals(ModuleValue m) { - exists(CallNode globals | - globals = Value::named("globals").(FunctionValue).getACall() and - globals.getScope() = m.getScope() - | - exists(AttrNode attr | attr.getObject() = globals) - or - exists(SubscriptNode sub | sub.getObject() = globals and sub.isStore()) - ) + exists(CallNode globals | + globals = Value::named("globals").(FunctionValue).getACall() and + globals.getScope() = m.getScope() + | + exists(AttrNode attr | attr.getObject() = globals) or - // Enum (added in 3.4) has method `_convert_` that alters globals - // This was called `_convert` until 3.8, but that name will be removed in 3.9 - exists(ClassValue enum_class | - enum_class.getASuperType() = Value::named("enum.Enum") and - ( - // In Python < 3.8, Enum._convert can be found with points-to - exists(Value enum_convert | - enum_convert = enum_class.attr("_convert") and - exists(CallNode call | call.getScope() = m.getScope() | - enum_convert.getACall() = call or - call.getFunction().pointsTo(enum_convert) - ) - ) - or - // In Python 3.8, Enum._convert_ is implemented using a metaclass, and our points-to - // analysis doesn't handle that well enough. So we need a special case for this - not exists(Value enum_convert | enum_convert = enum_class.attr("_convert")) and - exists(CallNode call | call.getScope() = m.getScope() | - call.getFunction().(AttrNode).getObject(["_convert", "_convert_"]).pointsTo() = - enum_class - ) + exists(SubscriptNode sub | sub.getObject() = globals and sub.isStore()) + ) + or + // Enum (added in 3.4) has method `_convert_` that alters globals + // This was called `_convert` until 3.8, but that name will be removed in 3.9 + exists(ClassValue enum_class | + enum_class.getASuperType() = Value::named("enum.Enum") and + ( + // In Python < 3.8, Enum._convert can be found with points-to + exists(Value enum_convert | + enum_convert = enum_class.attr("_convert") and + exists(CallNode call | call.getScope() = m.getScope() | + enum_convert.getACall() = call or + call.getFunction().pointsTo(enum_convert) ) + ) + or + // In Python 3.8, Enum._convert_ is implemented using a metaclass, and our points-to + // analysis doesn't handle that well enough. So we need a special case for this + not exists(Value enum_convert | enum_convert = enum_class.attr("_convert")) and + exists(CallNode call | call.getScope() = m.getScope() | + call.getFunction().(AttrNode).getObject(["_convert", "_convert_"]).pointsTo() = enum_class + ) ) + ) } predicate is_exported_submodule_name(ModuleValue m, string exported_name) { - m.getScope().getShortName() = "__init__" and - exists(m.getScope().getPackage().getSubModule(exported_name)) + m.getScope().getShortName() = "__init__" and + exists(m.getScope().getPackage().getSubModule(exported_name)) } predicate contains_unknown_import_star(ModuleValue m) { - exists(ImportStarNode imp | imp.getEnclosingModule() = m.getScope() | - imp.getModule().pointsTo().isAbsent() - or - not exists(imp.getModule().pointsTo()) - ) + exists(ImportStarNode imp | imp.getEnclosingModule() = m.getScope() | + imp.getModule().pointsTo().isAbsent() + or + not exists(imp.getModule().pointsTo()) + ) } from ModuleValue m, StrConst name, string exported_name where - declaredInAll(m.getScope(), name) and - exported_name = name.getText() and - not m.hasAttribute(exported_name) and - not is_exported_submodule_name(m, exported_name) and - not contains_unknown_import_star(m) and - not mutates_globals(m) + declaredInAll(m.getScope(), name) and + exported_name = name.getText() and + not m.hasAttribute(exported_name) and + not is_exported_submodule_name(m, exported_name) and + not contains_unknown_import_star(m) and + not mutates_globals(m) select name, "The name '" + exported_name + "' is exported by __all__ but is not defined." diff --git a/python/ql/src/Variables/UndefinedGlobal.ql b/python/ql/src/Variables/UndefinedGlobal.ql index c21c048a3e1..bbb48db8fb1 100644 --- a/python/ql/src/Variables/UndefinedGlobal.ql +++ b/python/ql/src/Variables/UndefinedGlobal.ql @@ -16,104 +16,104 @@ import Loop import semmle.python.pointsto.PointsTo predicate guarded_against_name_error(Name u) { - exists(Try t | t.getBody().getAnItem().contains(u) | - t.getAHandler().getType().(Name).getId() = "NameError" - ) - or - exists(ConditionBlock guard, BasicBlock controlled, Call globals | - guard.getLastNode().getNode().contains(globals) or - guard.getLastNode().getNode() = globals - | - globals.getFunc().(Name).getId() = "globals" and - guard.controls(controlled, _) and - controlled.contains(u.getAFlowNode()) - ) + exists(Try t | t.getBody().getAnItem().contains(u) | + t.getAHandler().getType().(Name).getId() = "NameError" + ) + or + exists(ConditionBlock guard, BasicBlock controlled, Call globals | + guard.getLastNode().getNode().contains(globals) or + guard.getLastNode().getNode() = globals + | + globals.getFunc().(Name).getId() = "globals" and + guard.controls(controlled, _) and + controlled.contains(u.getAFlowNode()) + ) } predicate contains_unknown_import_star(Module m) { - exists(ImportStar imp | imp.getScope() = m | - exists(ModuleValue imported | imported.importedAs(imp.getImportedModuleName()) | - not imported.hasCompleteExportInfo() - ) + exists(ImportStar imp | imp.getScope() = m | + exists(ModuleValue imported | imported.importedAs(imp.getImportedModuleName()) | + not imported.hasCompleteExportInfo() ) + ) } predicate undefined_use_in_function(Name u) { - exists(Function f | - u.getScope().getScope*() = f and - // Either function is a method or inner function or it is live at the end of the module scope - ( - not f.getScope() = u.getEnclosingModule() or - u.getEnclosingModule().(ImportTimeScope).definesName(f.getName()) - ) and - // There is a use, but not a definition of this global variable in the function or enclosing scope - exists(GlobalVariable v | u.uses(v) | - not exists(Assign a, Scope defnScope | - a.getATarget() = v.getAnAccess() and a.getScope() = defnScope - | - defnScope = f - or - // Exclude modules as that case is handled more precisely below. - defnScope = f.getScope().getScope*() and not defnScope instanceof Module - ) - ) + exists(Function f | + u.getScope().getScope*() = f and + // Either function is a method or inner function or it is live at the end of the module scope + ( + not f.getScope() = u.getEnclosingModule() or + u.getEnclosingModule().(ImportTimeScope).definesName(f.getName()) ) and - not u.getEnclosingModule().(ImportTimeScope).definesName(u.getId()) and - not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and - not globallyDefinedName(u.getId()) and - not exists(SsaVariable var | var.getAUse().getNode() = u and not var.maybeUndefined()) and - not guarded_against_name_error(u) and - not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") + // There is a use, but not a definition of this global variable in the function or enclosing scope + exists(GlobalVariable v | u.uses(v) | + not exists(Assign a, Scope defnScope | + a.getATarget() = v.getAnAccess() and a.getScope() = defnScope + | + defnScope = f + or + // Exclude modules as that case is handled more precisely below. + defnScope = f.getScope().getScope*() and not defnScope instanceof Module + ) + ) + ) and + not u.getEnclosingModule().(ImportTimeScope).definesName(u.getId()) and + not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and + not globallyDefinedName(u.getId()) and + not exists(SsaVariable var | var.getAUse().getNode() = u and not var.maybeUndefined()) and + not guarded_against_name_error(u) and + not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") } predicate undefined_use_in_class_or_module(Name u) { - exists(GlobalVariable v | u.uses(v)) and - not exists(Function f | u.getScope().getScope*() = f) and - exists(SsaVariable var | var.getAUse().getNode() = u | var.maybeUndefined()) and - not guarded_against_name_error(u) and - not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and - not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") and - not globallyDefinedName(u.getId()) + exists(GlobalVariable v | u.uses(v)) and + not exists(Function f | u.getScope().getScope*() = f) and + exists(SsaVariable var | var.getAUse().getNode() = u | var.maybeUndefined()) and + not guarded_against_name_error(u) and + not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and + not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") and + not globallyDefinedName(u.getId()) } predicate use_of_exec(Module m) { - exists(Exec exec | exec.getScope() = m) - or - exists(CallNode call, FunctionValue exec | exec.getACall() = call and call.getScope() = m | - exec = Value::named("exec") or - exec = Value::named("execfile") - ) + exists(Exec exec | exec.getScope() = m) + or + exists(CallNode call, FunctionValue exec | exec.getACall() = call and call.getScope() = m | + exec = Value::named("exec") or + exec = Value::named("execfile") + ) } predicate undefined_use(Name u) { - ( - undefined_use_in_class_or_module(u) - or - undefined_use_in_function(u) - ) and - not monkey_patched_builtin(u.getId()) and - not contains_unknown_import_star(u.getEnclosingModule()) and - not use_of_exec(u.getEnclosingModule()) and - not exists(u.getVariable().getAStore()) and - not u.pointsTo(_) and - not probably_defined_in_loop(u) + ( + undefined_use_in_class_or_module(u) + or + undefined_use_in_function(u) + ) and + not monkey_patched_builtin(u.getId()) and + not contains_unknown_import_star(u.getEnclosingModule()) and + not use_of_exec(u.getEnclosingModule()) and + not exists(u.getVariable().getAStore()) and + not u.pointsTo(_) and + not probably_defined_in_loop(u) } private predicate first_use_in_a_block(Name use) { - exists(GlobalVariable v, BasicBlock b, int i | - i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = use.getAFlowNode() - ) + exists(GlobalVariable v, BasicBlock b, int i | + i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = use.getAFlowNode() + ) } predicate first_undefined_use(Name use) { - undefined_use(use) and - exists(GlobalVariable v | v.getALoad() = use | - first_use_in_a_block(use) and - not exists(ControlFlowNode other | - other.getNode() = v.getALoad() and - other.getBasicBlock().strictlyDominates(use.getAFlowNode().getBasicBlock()) - ) + undefined_use(use) and + exists(GlobalVariable v | v.getALoad() = use | + first_use_in_a_block(use) and + not exists(ControlFlowNode other | + other.getNode() = v.getALoad() and + other.getBasicBlock().strictlyDominates(use.getAFlowNode().getBasicBlock()) ) + ) } from Name u diff --git a/python/ql/src/Variables/UndefinedPlaceHolder.ql b/python/ql/src/Variables/UndefinedPlaceHolder.ql index 1ec4f85749f..29004a6123f 100644 --- a/python/ql/src/Variables/UndefinedPlaceHolder.ql +++ b/python/ql/src/Variables/UndefinedPlaceHolder.ql @@ -15,32 +15,32 @@ import Variables.MonkeyPatched /* Local variable part */ predicate initialized_as_local(PlaceHolder use) { - exists(SsaVariable l, Function f | f = use.getScope() and l.getAUse() = use.getAFlowNode() | - l.getVariable() instanceof LocalVariable and - not l.maybeUndefined() - ) + exists(SsaVariable l, Function f | f = use.getScope() and l.getAUse() = use.getAFlowNode() | + l.getVariable() instanceof LocalVariable and + not l.maybeUndefined() + ) } /* Not a template member */ Class enclosing_class(PlaceHolder use) { result.getAMethod() = use.getScope() } predicate template_attribute(PlaceHolder use) { - exists(ImportTimeScope cls | cls = enclosing_class(use) | cls.definesName(use.getId())) + exists(ImportTimeScope cls | cls = enclosing_class(use) | cls.definesName(use.getId())) } /* Global Stuff */ predicate not_a_global(PlaceHolder use) { - not exists(PythonModuleObject mo | - mo.hasAttribute(use.getId()) and mo.getModule() = use.getEnclosingModule() - ) and - not globallyDefinedName(use.getId()) and - not monkey_patched_builtin(use.getId()) and - not globallyDefinedName(use.getId()) + not exists(PythonModuleObject mo | + mo.hasAttribute(use.getId()) and mo.getModule() = use.getEnclosingModule() + ) and + not globallyDefinedName(use.getId()) and + not monkey_patched_builtin(use.getId()) and + not globallyDefinedName(use.getId()) } from PlaceHolder p where - not initialized_as_local(p) and - not template_attribute(p) and - not_a_global(p) + not initialized_as_local(p) and + not template_attribute(p) and + not_a_global(p) select p, "This use of place-holder variable '" + p.getId() + "' may be undefined" diff --git a/python/ql/src/Variables/UninitializedLocal.ql b/python/ql/src/Variables/UninitializedLocal.ql index 343036be152..23a063be5ab 100644 --- a/python/ql/src/Variables/UninitializedLocal.ql +++ b/python/ql/src/Variables/UninitializedLocal.ql @@ -15,20 +15,20 @@ import Undefined import semmle.python.pointsto.PointsTo predicate uninitialized_local(NameNode use) { - exists(FastLocalVariable local | use.uses(local) or use.deletes(local) | not local.escapes()) and - ( - any(Uninitialized uninit).taints(use) and - PointsToInternal::reachableBlock(use.getBasicBlock(), _) - or - not exists(EssaVariable var | var.getASourceUse() = use) - ) + exists(FastLocalVariable local | use.uses(local) or use.deletes(local) | not local.escapes()) and + ( + any(Uninitialized uninit).taints(use) and + PointsToInternal::reachableBlock(use.getBasicBlock(), _) + or + not exists(EssaVariable var | var.getASourceUse() = use) + ) } predicate explicitly_guarded(NameNode u) { - exists(Try t | - t.getBody().contains(u.getNode()) and - t.getAHandler().getType().pointsTo(ClassValue::nameError()) - ) + exists(Try t | + t.getBody().contains(u.getNode()) and + t.getAHandler().getType().pointsTo(ClassValue::nameError()) + ) } from NameNode u diff --git a/python/ql/src/Variables/UnusedLocalVariable.ql b/python/ql/src/Variables/UnusedLocalVariable.ql index ab280151bf3..de83345f62d 100644 --- a/python/ql/src/Variables/UnusedLocalVariable.ql +++ b/python/ql/src/Variables/UnusedLocalVariable.ql @@ -15,20 +15,20 @@ import python import Definition predicate unused_local(Name unused, LocalVariable v) { - forex(Definition def | def.getNode() = unused | - def.getVariable() = v and - def.isUnused() and - not exists(def.getARedef()) and - def.isRelevant() and - not v = any(Nonlocal n).getAVariable() and - not exists(def.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and - not exists(def.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) - ) + forex(Definition def | def.getNode() = unused | + def.getVariable() = v and + def.isUnused() and + not exists(def.getARedef()) and + def.isRelevant() and + not v = any(Nonlocal n).getAVariable() and + not exists(def.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and + not exists(def.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) + ) } from Name unused, LocalVariable v where - unused_local(unused, v) and - // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. - forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_local(el, _)) + unused_local(unused, v) and + // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. + forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_local(el, _)) select unused, "The value assigned to local variable '" + v.getId() + "' is never used." diff --git a/python/ql/src/Variables/UnusedModuleVariable.ql b/python/ql/src/Variables/UnusedModuleVariable.ql index 2d873908ba5..543f17f6f35 100644 --- a/python/ql/src/Variables/UnusedModuleVariable.ql +++ b/python/ql/src/Variables/UnusedModuleVariable.ql @@ -19,48 +19,48 @@ import Definition * but it is more complex than a simple list of strings */ predicate complex_all(Module m) { - exists(Assign a, GlobalVariable all | - a.defines(all) and a.getScope() = m and all.getId() = "__all__" - | - not a.getValue() instanceof List - or - exists(Expr e | e = a.getValue().(List).getAnElt() | not e instanceof StrConst) - ) + exists(Assign a, GlobalVariable all | + a.defines(all) and a.getScope() = m and all.getId() = "__all__" + | + not a.getValue() instanceof List or - exists(Call c, GlobalVariable all | - c.getFunc().(Attribute).getObject() = all.getALoad() and - c.getScope() = m and - all.getId() = "__all__" - ) + exists(Expr e | e = a.getValue().(List).getAnElt() | not e instanceof StrConst) + ) + or + exists(Call c, GlobalVariable all | + c.getFunc().(Attribute).getObject() = all.getALoad() and + c.getScope() = m and + all.getId() = "__all__" + ) } predicate unused_global(Name unused, GlobalVariable v) { - not exists(ImportingStmt is | is.contains(unused)) and - forex(DefinitionNode defn | defn.getNode() = unused | - not defn.getValue().getNode() instanceof FunctionExpr and - not defn.getValue().getNode() instanceof ClassExpr and - not exists(Name u | - // A use of the variable - u.uses(v) - | - // That is reachable from this definition, directly - defn.strictlyReaches(u.getAFlowNode()) - or - // indirectly - defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope() - ) and - not unused.getEnclosingModule().getAnExport() = v.getId() and - not exists(unused.getParentNode().(ClassDef).getDefinedClass().getADecorator()) and - not exists(unused.getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and - unused.defines(v) and - not name_acceptable_for_unused_variable(v) and - not complex_all(unused.getEnclosingModule()) - ) + not exists(ImportingStmt is | is.contains(unused)) and + forex(DefinitionNode defn | defn.getNode() = unused | + not defn.getValue().getNode() instanceof FunctionExpr and + not defn.getValue().getNode() instanceof ClassExpr and + not exists(Name u | + // A use of the variable + u.uses(v) + | + // That is reachable from this definition, directly + defn.strictlyReaches(u.getAFlowNode()) + or + // indirectly + defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope() + ) and + not unused.getEnclosingModule().getAnExport() = v.getId() and + not exists(unused.getParentNode().(ClassDef).getDefinedClass().getADecorator()) and + not exists(unused.getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and + unused.defines(v) and + not name_acceptable_for_unused_variable(v) and + not complex_all(unused.getEnclosingModule()) + ) } from Name unused, GlobalVariable v where - unused_global(unused, v) and - // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. - forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_global(el, _)) + unused_global(unused, v) and + // If unused is part of a tuple, count it as unused if all elements of that tuple are unused. + forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_global(el, _)) select unused, "The global variable '" + v.getId() + "' is not used." diff --git a/python/ql/src/Variables/UnusedParameter.ql b/python/ql/src/Variables/UnusedParameter.ql index af3b6c6b3cd..74e1c2ac536 100644 --- a/python/ql/src/Variables/UnusedParameter.ql +++ b/python/ql/src/Variables/UnusedParameter.ql @@ -13,24 +13,24 @@ import python import Definition predicate unused_parameter(FunctionValue f, LocalVariable v) { - v.isParameter() and - v.getScope() = f.getScope() and - not name_acceptable_for_unused_variable(v) and - not exists(NameNode u | u.uses(v)) and - not exists(Name inner, LocalVariable iv | - inner.uses(iv) and iv.getId() = v.getId() and inner.getScope().getScope() = v.getScope() - ) + v.isParameter() and + v.getScope() = f.getScope() and + not name_acceptable_for_unused_variable(v) and + not exists(NameNode u | u.uses(v)) and + not exists(Name inner, LocalVariable iv | + inner.uses(iv) and iv.getId() = v.getId() and inner.getScope().getScope() = v.getScope() + ) } predicate is_abstract(FunctionValue func) { - func.getScope().getADecorator().(Name).getId().matches("%abstract%") + func.getScope().getADecorator().(Name).getId().matches("%abstract%") } from PythonFunctionValue f, LocalVariable v where - v.getId() != "self" and - unused_parameter(f, v) and - not f.isOverridingMethod() and - not f.isOverriddenMethod() and - not is_abstract(f) + v.getId() != "self" and + unused_parameter(f, v) and + not f.isOverridingMethod() and + not f.isOverriddenMethod() and + not is_abstract(f) select f, "The parameter '" + v.getId() + "' is never used." diff --git a/python/ql/src/analysis/AlertSuppression.ql b/python/ql/src/analysis/AlertSuppression.ql index 2cd92b99a30..c8fefc92cc1 100644 --- a/python/ql/src/analysis/AlertSuppression.ql +++ b/python/ql/src/analysis/AlertSuppression.ql @@ -11,97 +11,97 @@ import python * An alert suppression comment. */ abstract class SuppressionComment extends Comment { - /** Gets the scope of this suppression. */ - abstract SuppressionScope getScope(); + /** Gets the scope of this suppression. */ + abstract SuppressionScope getScope(); - /** Gets the suppression annotation in this comment. */ - abstract string getAnnotation(); + /** Gets the suppression annotation in this comment. */ + abstract string getAnnotation(); - /** - * Holds if this comment applies to the range from column `startcolumn` of line `startline` - * to column `endcolumn` of line `endline` in file `filepath`. - */ - abstract predicate covers( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); + /** + * Holds if this comment applies to the range from column `startcolumn` of line `startline` + * to column `endcolumn` of line `endline` in file `filepath`. + */ + abstract predicate covers( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ); } /** * An alert comment that applies to a single line */ abstract class LineSuppressionComment extends SuppressionComment { - LineSuppressionComment() { - exists(string filepath, int l | - this.getLocation().hasLocationInfo(filepath, l, _, _, _) and - any(AstNode a).getLocation().hasLocationInfo(filepath, l, _, _, _) - ) - } + LineSuppressionComment() { + exists(string filepath, int l | + this.getLocation().hasLocationInfo(filepath, l, _, _, _) and + any(AstNode a).getLocation().hasLocationInfo(filepath, l, _, _, _) + ) + } - /** Gets the scope of this suppression. */ - override SuppressionScope getScope() { result = this } + /** Gets the scope of this suppression. */ + override SuppressionScope getScope() { result = this } - override predicate covers( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and - startcolumn = 1 - } + override predicate covers( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and + startcolumn = 1 + } } /** * An lgtm suppression comment. */ class LgtmSuppressionComment extends LineSuppressionComment { - string annotation; + string annotation; - LgtmSuppressionComment() { - exists(string all | all = this.getContents() | - // match `lgtm[...]` anywhere in the comment - annotation = all.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _) - or - // match `lgtm` at the start of the comment and after semicolon - annotation = all.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim() - ) - } + LgtmSuppressionComment() { + exists(string all | all = this.getContents() | + // match `lgtm[...]` anywhere in the comment + annotation = all.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _) + or + // match `lgtm` at the start of the comment and after semicolon + annotation = all.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim() + ) + } - /** Gets the suppression annotation in this comment. */ - override string getAnnotation() { result = annotation } + /** Gets the suppression annotation in this comment. */ + override string getAnnotation() { result = annotation } } /** * A noqa suppression comment. Both pylint and pyflakes respect this, so lgtm ought to too. */ class NoqaSuppressionComment extends LineSuppressionComment { - NoqaSuppressionComment() { this.getContents().toLowerCase().regexpMatch("\\s*noqa\\s*") } + NoqaSuppressionComment() { this.getContents().toLowerCase().regexpMatch("\\s*noqa\\s*") } - override string getAnnotation() { result = "lgtm" } + override string getAnnotation() { result = "lgtm" } } /** * The scope of an alert suppression comment. */ class SuppressionScope extends @py_comment { - SuppressionScope() { this instanceof SuppressionComment } + SuppressionScope() { this instanceof SuppressionComment } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn) + } - /** Gets a textual representation of this element. */ - string toString() { result = "suppression range" } + /** Gets a textual representation of this element. */ + string toString() { result = "suppression range" } } from SuppressionComment c select c, // suppression comment - c.getContents(), // text of suppression comment (excluding delimiters) - c.getAnnotation(), // text of suppression annotation - c.getScope() // scope of suppression + c.getContents(), // text of suppression comment (excluding delimiters) + c.getAnnotation(), // text of suppression annotation + c.getScope() // scope of suppression diff --git a/python/ql/src/analysis/CallGraphEfficiency.ql b/python/ql/src/analysis/CallGraphEfficiency.ql index a35e9282b10..5e36823b8ac 100644 --- a/python/ql/src/analysis/CallGraphEfficiency.ql +++ b/python/ql/src/analysis/CallGraphEfficiency.ql @@ -9,17 +9,17 @@ import semmle.python.pointsto.PointsToContext from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode call, CallableValue func | - exists(PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() - ) - ) and - total_size = - strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode call, CallableValue func | + exists(PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() + ) + ) and + total_size = + strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/CallGraphMarginalEfficiency.ql b/python/ql/src/analysis/CallGraphMarginalEfficiency.ql index 7e93e30ea0b..394bf379eeb 100644 --- a/python/ql/src/analysis/CallGraphMarginalEfficiency.ql +++ b/python/ql/src/analysis/CallGraphMarginalEfficiency.ql @@ -9,21 +9,21 @@ import semmle.python.pointsto.PointsToContext from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode call, CallableValue func | - exists(PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() and - not exists(PointsToContext shallower | - call = func.getACall(shallower) and - shallower.getDepth() < depth - ) - ) - ) and - total_size = - strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | - call = func.getACall(ctx) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode call, CallableValue func | + exists(PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() and + not exists(PointsToContext shallower | + call = func.getACall(shallower) and + shallower.getDepth() < depth + ) + ) + ) and + total_size = + strictcount(ControlFlowNode call, CallableValue func, PointsToContext ctx | + call = func.getACall(ctx) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/Consistency.ql b/python/ql/src/analysis/Consistency.ql index 9b49ed4ff90..cf84c36d0cd 100644 --- a/python/ql/src/analysis/Consistency.ql +++ b/python/ql/src/analysis/Consistency.ql @@ -8,291 +8,291 @@ import python import DefinitionTracking predicate uniqueness_error(int number, string what, string problem) { - ( - what = "toString" or - what = "getLocation" or - what = "getNode" or - what = "getDefinition" or - what = "getEntryNode" or - what = "getOrigin" or - what = "getAnInferredType" - ) and - ( - number = 0 and problem = "no results for " + what + "()" - or - number in [2 .. 10] and problem = number.toString() + " results for " + what + "()" - ) + ( + what = "toString" or + what = "getLocation" or + what = "getNode" or + what = "getDefinition" or + what = "getEntryNode" or + what = "getOrigin" or + what = "getAnInferredType" + ) and + ( + number = 0 and problem = "no results for " + what + "()" + or + number in [2 .. 10] and problem = number.toString() + " results for " + what + "()" + ) } predicate ast_consistency(string clsname, string problem, string what) { - exists(AstNode a | clsname = a.getAQlClass() | - uniqueness_error(count(a.toString()), "toString", problem) and - what = "at " + a.getLocation().toString() - or - uniqueness_error(strictcount(a.getLocation()), "getLocation", problem) and - what = a.getLocation().toString() - or - not exists(a.getLocation()) and - not a.(Module).isPackage() and - problem = "no location" and - what = a.toString() - ) + exists(AstNode a | clsname = a.getAQlClass() | + uniqueness_error(count(a.toString()), "toString", problem) and + what = "at " + a.getLocation().toString() + or + uniqueness_error(strictcount(a.getLocation()), "getLocation", problem) and + what = a.getLocation().toString() + or + not exists(a.getLocation()) and + not a.(Module).isPackage() and + problem = "no location" and + what = a.toString() + ) } predicate location_consistency(string clsname, string problem, string what) { - exists(Location l | clsname = l.getAQlClass() | - uniqueness_error(count(l.toString()), "toString", problem) and what = "at " + l.toString() - or - not exists(l.toString()) and - problem = "no toString" and - ( - exists(AstNode thing | thing.getLocation() = l | - what = "a location of a " + thing.getAQlClass() - ) - or - not exists(AstNode thing | thing.getLocation() = l) and - what = "a location" - ) - or - l.getEndLine() < l.getStartLine() and - problem = "end line before start line" and - what = "at " + l.toString() - or - l.getEndLine() = l.getStartLine() and - l.getEndColumn() < l.getStartColumn() and - problem = "end column before start column" and - what = "at " + l.toString() + exists(Location l | clsname = l.getAQlClass() | + uniqueness_error(count(l.toString()), "toString", problem) and what = "at " + l.toString() + or + not exists(l.toString()) and + problem = "no toString" and + ( + exists(AstNode thing | thing.getLocation() = l | + what = "a location of a " + thing.getAQlClass() + ) + or + not exists(AstNode thing | thing.getLocation() = l) and + what = "a location" ) + or + l.getEndLine() < l.getStartLine() and + problem = "end line before start line" and + what = "at " + l.toString() + or + l.getEndLine() = l.getStartLine() and + l.getEndColumn() < l.getStartColumn() and + problem = "end column before start column" and + what = "at " + l.toString() + ) } predicate cfg_consistency(string clsname, string problem, string what) { - exists(ControlFlowNode f | clsname = f.getAQlClass() | - uniqueness_error(count(f.getNode()), "getNode", problem) and - what = "at " + f.getLocation().toString() - or - not exists(f.getLocation()) and - not exists(Module p | p.isPackage() | p.getEntryNode() = f or p.getAnExitNode() = f) and - problem = "no location" and - what = f.toString() - or - uniqueness_error(count(f.(AttrNode).getObject()), "getValue", problem) and - what = "at " + f.getLocation().toString() - ) + exists(ControlFlowNode f | clsname = f.getAQlClass() | + uniqueness_error(count(f.getNode()), "getNode", problem) and + what = "at " + f.getLocation().toString() + or + not exists(f.getLocation()) and + not exists(Module p | p.isPackage() | p.getEntryNode() = f or p.getAnExitNode() = f) and + problem = "no location" and + what = f.toString() + or + uniqueness_error(count(f.(AttrNode).getObject()), "getValue", problem) and + what = "at " + f.getLocation().toString() + ) } predicate scope_consistency(string clsname, string problem, string what) { - exists(Scope s | clsname = s.getAQlClass() | - uniqueness_error(count(s.getEntryNode()), "getEntryNode", problem) and - what = "at " + s.getLocation().toString() - or - uniqueness_error(count(s.toString()), "toString", problem) and - what = "at " + s.getLocation().toString() - or - uniqueness_error(strictcount(s.getLocation()), "getLocation", problem) and - what = "at " + s.getLocation().toString() - or - not exists(s.getLocation()) and - problem = "no location" and - what = s.toString() and - not s.(Module).isPackage() - ) + exists(Scope s | clsname = s.getAQlClass() | + uniqueness_error(count(s.getEntryNode()), "getEntryNode", problem) and + what = "at " + s.getLocation().toString() + or + uniqueness_error(count(s.toString()), "toString", problem) and + what = "at " + s.getLocation().toString() + or + uniqueness_error(strictcount(s.getLocation()), "getLocation", problem) and + what = "at " + s.getLocation().toString() + or + not exists(s.getLocation()) and + problem = "no location" and + what = s.toString() and + not s.(Module).isPackage() + ) } string best_description_builtin_object(Object o) { - o.isBuiltin() and - ( - result = o.toString() - or - not exists(o.toString()) and py_cobjectnames(o, result) - or - not exists(o.toString()) and - not py_cobjectnames(o, _) and - result = "builtin object of type " + o.getAnInferredType().toString() - or - not exists(o.toString()) and - not py_cobjectnames(o, _) and - not exists(o.getAnInferredType().toString()) and - result = "builtin object" - ) + o.isBuiltin() and + ( + result = o.toString() + or + not exists(o.toString()) and py_cobjectnames(o, result) + or + not exists(o.toString()) and + not py_cobjectnames(o, _) and + result = "builtin object of type " + o.getAnInferredType().toString() + or + not exists(o.toString()) and + not py_cobjectnames(o, _) and + not exists(o.getAnInferredType().toString()) and + result = "builtin object" + ) } private predicate introspected_builtin_object(Object o) { - /* - * Only check objects from the extractor, missing data for objects generated from C source code analysis is OK. - * as it will be ignored if it doesn't match up with the introspected form. - */ + /* + * Only check objects from the extractor, missing data for objects generated from C source code analysis is OK. + * as it will be ignored if it doesn't match up with the introspected form. + */ - py_cobject_sources(o, 0) + py_cobject_sources(o, 0) } predicate builtin_object_consistency(string clsname, string problem, string what) { - exists(Object o | - clsname = o.getAQlClass() and - what = best_description_builtin_object(o) and - introspected_builtin_object(o) - | - not exists(o.getAnInferredType()) and - not py_cobjectnames(o, _) and - problem = "neither name nor type" - or - uniqueness_error(count(string name | py_cobjectnames(o, name)), "name", problem) - or - not exists(o.getAnInferredType()) and problem = "no results for getAnInferredType" - or - not exists(o.toString()) and - problem = "no toString" and - not exists(string name | name.prefix(7) = "_semmle" | py_special_objects(o, name)) and - not o = unknownValue() - ) + exists(Object o | + clsname = o.getAQlClass() and + what = best_description_builtin_object(o) and + introspected_builtin_object(o) + | + not exists(o.getAnInferredType()) and + not py_cobjectnames(o, _) and + problem = "neither name nor type" + or + uniqueness_error(count(string name | py_cobjectnames(o, name)), "name", problem) + or + not exists(o.getAnInferredType()) and problem = "no results for getAnInferredType" + or + not exists(o.toString()) and + problem = "no toString" and + not exists(string name | name.prefix(7) = "_semmle" | py_special_objects(o, name)) and + not o = unknownValue() + ) } predicate source_object_consistency(string clsname, string problem, string what) { - exists(Object o | clsname = o.getAQlClass() and not o.isBuiltin() | - uniqueness_error(count(o.getOrigin()), "getOrigin", problem) and - what = "at " + o.getOrigin().getLocation().toString() - or - not exists(o.getOrigin().getLocation()) and problem = "no location" and what = "??" - or - not exists(o.toString()) and - problem = "no toString" and - what = "at " + o.getOrigin().getLocation().toString() - or - strictcount(o.toString()) > 1 and problem = "multiple toStrings()" and what = o.toString() - ) + exists(Object o | clsname = o.getAQlClass() and not o.isBuiltin() | + uniqueness_error(count(o.getOrigin()), "getOrigin", problem) and + what = "at " + o.getOrigin().getLocation().toString() + or + not exists(o.getOrigin().getLocation()) and problem = "no location" and what = "??" + or + not exists(o.toString()) and + problem = "no toString" and + what = "at " + o.getOrigin().getLocation().toString() + or + strictcount(o.toString()) > 1 and problem = "multiple toStrings()" and what = o.toString() + ) } predicate ssa_consistency(string clsname, string problem, string what) { - /* Zero or one definitions of each SSA variable */ - exists(SsaVariable var | clsname = var.getAQlClass() | - uniqueness_error(strictcount(var.getDefinition()), "getDefinition", problem) and - what = var.getId() - ) - or - /* Dominance criterion: Definition *must* dominate *all* uses. */ - exists(SsaVariable var, ControlFlowNode defn, ControlFlowNode use | - defn = var.getDefinition() and use = var.getAUse() - | - not defn.strictlyDominates(use) and - not defn = use and - /* Phi nodes which share a flow node with a use come *before* the use */ - not (exists(var.getAPhiInput()) and defn = use) and - clsname = var.getAQlClass() and - problem = "a definition which does not dominate a use at " + use.getLocation() and - what = var.getId() + " at " + var.getLocation() - ) - or - /* Minimality of phi nodes */ - exists(SsaVariable var | - strictcount(var.getAPhiInput()) = 1 and - var - .getAPhiInput() - .getDefinition() - .getBasicBlock() - .strictlyDominates(var.getDefinition().getBasicBlock()) - | - clsname = var.getAQlClass() and - problem = " a definition which is dominated by the definition of an incoming phi edge." and - what = var.getId() + " at " + var.getLocation() - ) + /* Zero or one definitions of each SSA variable */ + exists(SsaVariable var | clsname = var.getAQlClass() | + uniqueness_error(strictcount(var.getDefinition()), "getDefinition", problem) and + what = var.getId() + ) + or + /* Dominance criterion: Definition *must* dominate *all* uses. */ + exists(SsaVariable var, ControlFlowNode defn, ControlFlowNode use | + defn = var.getDefinition() and use = var.getAUse() + | + not defn.strictlyDominates(use) and + not defn = use and + /* Phi nodes which share a flow node with a use come *before* the use */ + not (exists(var.getAPhiInput()) and defn = use) and + clsname = var.getAQlClass() and + problem = "a definition which does not dominate a use at " + use.getLocation() and + what = var.getId() + " at " + var.getLocation() + ) + or + /* Minimality of phi nodes */ + exists(SsaVariable var | + strictcount(var.getAPhiInput()) = 1 and + var + .getAPhiInput() + .getDefinition() + .getBasicBlock() + .strictlyDominates(var.getDefinition().getBasicBlock()) + | + clsname = var.getAQlClass() and + problem = " a definition which is dominated by the definition of an incoming phi edge." and + what = var.getId() + " at " + var.getLocation() + ) } predicate function_object_consistency(string clsname, string problem, string what) { - exists(FunctionObject func | clsname = func.getAQlClass() | - what = func.getName() and - ( - count(func.descriptiveString()) = 0 and problem = "no descriptiveString()" - or - exists(int c | c = strictcount(func.descriptiveString()) and c > 1 | - problem = c + "descriptiveString()s" - ) - ) - or - not exists(func.getName()) and what = "?" and problem = "no name" + exists(FunctionObject func | clsname = func.getAQlClass() | + what = func.getName() and + ( + count(func.descriptiveString()) = 0 and problem = "no descriptiveString()" + or + exists(int c | c = strictcount(func.descriptiveString()) and c > 1 | + problem = c + "descriptiveString()s" + ) ) + or + not exists(func.getName()) and what = "?" and problem = "no name" + ) } predicate multiple_origins_per_object(Object obj) { - not obj.isC() and - not obj instanceof ModuleObject and - exists(ControlFlowNode use, Context ctx | - strictcount(ControlFlowNode orig | use.refersTo(ctx, obj, _, orig)) > 1 - ) + not obj.isC() and + not obj instanceof ModuleObject and + exists(ControlFlowNode use, Context ctx | + strictcount(ControlFlowNode orig | use.refersTo(ctx, obj, _, orig)) > 1 + ) } predicate intermediate_origins(ControlFlowNode use, ControlFlowNode inter, Object obj) { - exists(ControlFlowNode orig, Context ctx | not inter = orig | - use.refersTo(ctx, obj, _, inter) and - inter.refersTo(ctx, obj, _, orig) and - // It can sometimes happen that two different modules (e.g. cPickle and Pickle) - // have the same attribute, but different origins. - not strictcount(Object val | inter.(AttrNode).getObject().refersTo(val)) > 1 - ) + exists(ControlFlowNode orig, Context ctx | not inter = orig | + use.refersTo(ctx, obj, _, inter) and + inter.refersTo(ctx, obj, _, orig) and + // It can sometimes happen that two different modules (e.g. cPickle and Pickle) + // have the same attribute, but different origins. + not strictcount(Object val | inter.(AttrNode).getObject().refersTo(val)) > 1 + ) } predicate points_to_consistency(string clsname, string problem, string what) { - exists(Object obj | - multiple_origins_per_object(obj) and - clsname = obj.getAQlClass() and - problem = "multiple origins for an object" and - what = obj.toString() - ) - or - exists(ControlFlowNode use, ControlFlowNode inter, Object obj | - intermediate_origins(use, inter, obj) and - clsname = use.getAQlClass() and - problem = "has intermediate origin " + inter and - what = use.toString() - ) + exists(Object obj | + multiple_origins_per_object(obj) and + clsname = obj.getAQlClass() and + problem = "multiple origins for an object" and + what = obj.toString() + ) + or + exists(ControlFlowNode use, ControlFlowNode inter, Object obj | + intermediate_origins(use, inter, obj) and + clsname = use.getAQlClass() and + problem = "has intermediate origin " + inter and + what = use.toString() + ) } predicate jump_to_definition_consistency(string clsname, string problem, string what) { - problem = "multiple (jump-to) definitions" and - exists(Expr use | - strictcount(getUniqueDefinition(use)) > 1 and - clsname = use.getAQlClass() and - what = use.toString() - ) + problem = "multiple (jump-to) definitions" and + exists(Expr use | + strictcount(getUniqueDefinition(use)) > 1 and + clsname = use.getAQlClass() and + what = use.toString() + ) } predicate file_consistency(string clsname, string problem, string what) { - exists(File file, Folder folder | - clsname = file.getAQlClass() and - problem = "has same name as a folder" and - what = file.getAbsolutePath() and - what = folder.getAbsolutePath() - ) - or - exists(Container f | - clsname = f.getAQlClass() and - uniqueness_error(count(f.toString()), "toString", problem) and - what = "file " + f.getName() - ) + exists(File file, Folder folder | + clsname = file.getAQlClass() and + problem = "has same name as a folder" and + what = file.getAbsolutePath() and + what = folder.getAbsolutePath() + ) + or + exists(Container f | + clsname = f.getAQlClass() and + uniqueness_error(count(f.toString()), "toString", problem) and + what = "file " + f.getName() + ) } predicate class_value_consistency(string clsname, string problem, string what) { - exists(ClassValue value, ClassValue sup, string attr | - what = value.getName() and - sup = value.getASuperType() and - exists(sup.lookup(attr)) and - not value.failedInference(_) and - not exists(value.lookup(attr)) and - clsname = value.getAQlClass() and - problem = "no attribute '" + attr + "', but super type '" + sup.getName() + "' does." - ) + exists(ClassValue value, ClassValue sup, string attr | + what = value.getName() and + sup = value.getASuperType() and + exists(sup.lookup(attr)) and + not value.failedInference(_) and + not exists(value.lookup(attr)) and + clsname = value.getAQlClass() and + problem = "no attribute '" + attr + "', but super type '" + sup.getName() + "' does." + ) } from string clsname, string problem, string what where - ast_consistency(clsname, problem, what) or - location_consistency(clsname, problem, what) or - scope_consistency(clsname, problem, what) or - cfg_consistency(clsname, problem, what) or - ssa_consistency(clsname, problem, what) or - builtin_object_consistency(clsname, problem, what) or - source_object_consistency(clsname, problem, what) or - function_object_consistency(clsname, problem, what) or - points_to_consistency(clsname, problem, what) or - jump_to_definition_consistency(clsname, problem, what) or - file_consistency(clsname, problem, what) or - class_value_consistency(clsname, problem, what) + ast_consistency(clsname, problem, what) or + location_consistency(clsname, problem, what) or + scope_consistency(clsname, problem, what) or + cfg_consistency(clsname, problem, what) or + ssa_consistency(clsname, problem, what) or + builtin_object_consistency(clsname, problem, what) or + source_object_consistency(clsname, problem, what) or + function_object_consistency(clsname, problem, what) or + points_to_consistency(clsname, problem, what) or + jump_to_definition_consistency(clsname, problem, what) or + file_consistency(clsname, problem, what) or + class_value_consistency(clsname, problem, what) select clsname + " " + what + " has " + problem diff --git a/python/ql/src/analysis/ContextEfficiency.ql b/python/ql/src/analysis/ContextEfficiency.ql index 9c4a6355585..205091ba1e3 100644 --- a/python/ql/src/analysis/ContextEfficiency.ql +++ b/python/ql/src/analysis/ContextEfficiency.ql @@ -9,18 +9,18 @@ import semmle.python.pointsto.PointsToContext from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode f, Object value, ClassObject cls | - exists(PointsToContext ctx | - PointsTo::points_to(f, ctx, value, cls, _) and - depth = ctx.getDepth() - ) - ) and - total_size = - strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, - ControlFlowNode orig | - PointsTo::points_to(f, ctx, value, cls, orig) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode f, Object value, ClassObject cls | + exists(PointsToContext ctx | + PointsTo::points_to(f, ctx, value, cls, _) and + depth = ctx.getDepth() + ) + ) and + total_size = + strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, + ControlFlowNode orig | + PointsTo::points_to(f, ctx, value, cls, orig) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/ContextMarginalEfficiency.ql b/python/ql/src/analysis/ContextMarginalEfficiency.ql index 68229363761..755ccad683c 100644 --- a/python/ql/src/analysis/ContextMarginalEfficiency.ql +++ b/python/ql/src/analysis/ContextMarginalEfficiency.ql @@ -8,25 +8,25 @@ import semmle.python.pointsto.PointsTo import semmle.python.pointsto.PointsToContext int depth(ControlFlowNode f, Object value, ClassObject cls) { - exists(PointsToContext ctx | - PointsTo::points_to(f, ctx, value, cls, _) and - result = ctx.getDepth() - ) + exists(PointsToContext ctx | + PointsTo::points_to(f, ctx, value, cls, _) and + result = ctx.getDepth() + ) } int shallowest(ControlFlowNode f, Object value, ClassObject cls) { - result = min(int x | x = depth(f, value, cls)) + result = min(int x | x = depth(f, value, cls)) } from int total_facts, int total_size, int depth, float efficiency where - total_facts = - strictcount(ControlFlowNode f, Object value, ClassObject cls | depth = shallowest(f, value, cls)) and - total_size = - strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, - ControlFlowNode orig | - PointsTo::points_to(f, ctx, value, cls, orig) and - depth = ctx.getDepth() - ) and - efficiency = 100.0 * total_facts / total_size + total_facts = + strictcount(ControlFlowNode f, Object value, ClassObject cls | depth = shallowest(f, value, cls)) and + total_size = + strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, + ControlFlowNode orig | + PointsTo::points_to(f, ctx, value, cls, orig) and + depth = ctx.getDepth() + ) and + efficiency = 100.0 * total_facts / total_size select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/CrossProjectDefinitions.qll b/python/ql/src/analysis/CrossProjectDefinitions.qll index 5b9a904e794..c3d2735d9c6 100644 --- a/python/ql/src/analysis/CrossProjectDefinitions.qll +++ b/python/ql/src/analysis/CrossProjectDefinitions.qll @@ -6,14 +6,14 @@ import python import semmle.python.pointsto.PointsTo private newtype TSymbol = - TModule(Module m) or - TMember(Symbol outer, string part) { - exists(Object o | outer.resolvesTo() = o | - o.(ModuleObject).hasAttribute(part) - or - o.(ClassObject).hasAttribute(part) - ) - } + TModule(Module m) or + TMember(Symbol outer, string part) { + exists(Object o | outer.resolvesTo() = o | + o.(ModuleObject).hasAttribute(part) + or + o.(ClassObject).hasAttribute(part) + ) + } /** * A "symbol" referencing an object in another module @@ -28,76 +28,76 @@ private newtype TSymbol = * then symbol for the method `m` would be "mod/C.m" */ class Symbol extends TSymbol { - string toString() { - exists(Module m | this = TModule(m) and result = m.getName()) - or - exists(TModule outer, string part | - this = TMember(outer, part) and - outer = TModule(_) and - result = outer.(Symbol).toString() + "/" + part - ) - or - exists(TMember outer, string part | - this = TMember(outer, part) and - outer = TMember(_, _) and - result = outer.(Symbol).toString() + "." + part - ) - } + string toString() { + exists(Module m | this = TModule(m) and result = m.getName()) + or + exists(TModule outer, string part | + this = TMember(outer, part) and + outer = TModule(_) and + result = outer.(Symbol).toString() + "/" + part + ) + or + exists(TMember outer, string part | + this = TMember(outer, part) and + outer = TMember(_, _) and + result = outer.(Symbol).toString() + "." + part + ) + } - /** Finds the `AstNode` that this `Symbol` refers to. */ - AstNode find() { - this = TModule(result) - or - exists(Symbol s, string name | this = TMember(s, name) | - exists(ClassObject cls | - s.resolvesTo() = cls and - cls.attributeRefersTo(name, _, result.getAFlowNode()) - ) - or - exists(ModuleObject m | - s.resolvesTo() = m and - m.attributeRefersTo(name, _, result.getAFlowNode()) - ) - ) - } + /** Finds the `AstNode` that this `Symbol` refers to. */ + AstNode find() { + this = TModule(result) + or + exists(Symbol s, string name | this = TMember(s, name) | + exists(ClassObject cls | + s.resolvesTo() = cls and + cls.attributeRefersTo(name, _, result.getAFlowNode()) + ) + or + exists(ModuleObject m | + s.resolvesTo() = m and + m.attributeRefersTo(name, _, result.getAFlowNode()) + ) + ) + } - /** - * Find the class or module `Object` that this `Symbol` refers to, if - * this `Symbol` refers to a class or module. - */ - Object resolvesTo() { - this = TModule(result.(ModuleObject).getModule()) - or - exists(Symbol s, string name, Object o | - this = TMember(s, name) and - o = s.resolvesTo() and - result = attribute_in_scope(o, name) - ) - } + /** + * Find the class or module `Object` that this `Symbol` refers to, if + * this `Symbol` refers to a class or module. + */ + Object resolvesTo() { + this = TModule(result.(ModuleObject).getModule()) + or + exists(Symbol s, string name, Object o | + this = TMember(s, name) and + o = s.resolvesTo() and + result = attribute_in_scope(o, name) + ) + } - /** - * Gets the `Module` for the module part of this `Symbol`. - * For example, this would return the `os` module for the `Symbol` "os/environ". - */ - Module getModule() { - this = TModule(result) - or - exists(Symbol outer | this = TMember(outer, _) and result = outer.getModule()) - } + /** + * Gets the `Module` for the module part of this `Symbol`. + * For example, this would return the `os` module for the `Symbol` "os/environ". + */ + Module getModule() { + this = TModule(result) + or + exists(Symbol outer | this = TMember(outer, _) and result = outer.getModule()) + } - /** Gets the `Symbol` that is the named member of this `Symbol`. */ - Symbol getMember(string name) { result = TMember(this, name) } + /** Gets the `Symbol` that is the named member of this `Symbol`. */ + Symbol getMember(string name) { result = TMember(this, name) } } /* Helper for `Symbol`.resolvesTo() */ private Object attribute_in_scope(Object obj, string name) { - exists(ClassObject cls | cls = obj | - cls.lookupAttribute(name) = result and result.(ControlFlowNode).getScope() = cls.getPyClass() - ) - or - exists(ModuleObject mod | mod = obj | - mod.attr(name) = result and - result.(ControlFlowNode).getScope() = mod.getModule() and - not result.(ControlFlowNode).isEntryNode() - ) + exists(ClassObject cls | cls = obj | + cls.lookupAttribute(name) = result and result.(ControlFlowNode).getScope() = cls.getPyClass() + ) + or + exists(ModuleObject mod | mod = obj | + mod.attr(name) = result and + result.(ControlFlowNode).getScope() = mod.getModule() and + not result.(ControlFlowNode).isEntryNode() + ) } diff --git a/python/ql/src/analysis/DefinitionTracking.qll b/python/ql/src/analysis/DefinitionTracking.qll index 2d3b0138c21..f9c4f962af9 100644 --- a/python/ql/src/analysis/DefinitionTracking.qll +++ b/python/ql/src/analysis/DefinitionTracking.qll @@ -6,138 +6,138 @@ import python import semmle.python.pointsto.PointsTo private newtype TDefinition = - TLocalDefinition(AstNode a) { a instanceof Expr or a instanceof Stmt or a instanceof Module } + TLocalDefinition(AstNode a) { a instanceof Expr or a instanceof Stmt or a instanceof Module } /** A definition for the purposes of jump-to-definition. */ class Definition extends TLocalDefinition { - /** Gets a textual representation of this element. */ - string toString() { result = "Definition " + this.getAstNode().getLocation().toString() } + /** Gets a textual representation of this element. */ + string toString() { result = "Definition " + this.getAstNode().getLocation().toString() } - AstNode getAstNode() { this = TLocalDefinition(result) } + AstNode getAstNode() { this = TLocalDefinition(result) } - Module getModule() { result = this.getAstNode().getScope().getEnclosingModule() } + Module getModule() { result = this.getAstNode().getScope().getEnclosingModule() } - Location getLocation() { result = this.getAstNode().getLocation() } + Location getLocation() { result = this.getAstNode().getLocation() } } private predicate jump_to_defn(ControlFlowNode use, Definition defn) { - exists(EssaVariable var | - use = var.getASourceUse() and - ssa_variable_defn(var, defn) - ) - or - exists(string name | - use.isLoad() and - jump_to_defn_attribute(use.(AttrNode).getObject(name), name, defn) - ) - or - exists(PythonModuleObject mod | - use.(ImportExprNode).refersTo(mod) and - defn.getAstNode() = mod.getModule() - ) - or - exists(PythonModuleObject mod, string name | - use.(ImportMemberNode).getModule(name).refersTo(mod) and - scope_jump_to_defn_attribute(mod.getModule(), name, defn) - ) - or - exists(PackageObject package | - use.(ImportExprNode).refersTo(package) and - defn.getAstNode() = package.getInitModule().getModule() - ) - or - exists(PackageObject package, string name | - use.(ImportMemberNode).getModule(name).refersTo(package) and - scope_jump_to_defn_attribute(package.getInitModule().getModule(), name, defn) - ) - or - (use instanceof PyFunctionObject or use instanceof ClassObject) and - defn.getAstNode() = use.getNode() + exists(EssaVariable var | + use = var.getASourceUse() and + ssa_variable_defn(var, defn) + ) + or + exists(string name | + use.isLoad() and + jump_to_defn_attribute(use.(AttrNode).getObject(name), name, defn) + ) + or + exists(PythonModuleObject mod | + use.(ImportExprNode).refersTo(mod) and + defn.getAstNode() = mod.getModule() + ) + or + exists(PythonModuleObject mod, string name | + use.(ImportMemberNode).getModule(name).refersTo(mod) and + scope_jump_to_defn_attribute(mod.getModule(), name, defn) + ) + or + exists(PackageObject package | + use.(ImportExprNode).refersTo(package) and + defn.getAstNode() = package.getInitModule().getModule() + ) + or + exists(PackageObject package, string name | + use.(ImportMemberNode).getModule(name).refersTo(package) and + scope_jump_to_defn_attribute(package.getInitModule().getModule(), name, defn) + ) + or + (use instanceof PyFunctionObject or use instanceof ClassObject) and + defn.getAstNode() = use.getNode() } /* Prefer class and functions to class-expressions and function-expressions. */ private predicate preferred_jump_to_defn(Expr use, Definition def) { - not use instanceof ClassExpr and - not use instanceof FunctionExpr and - jump_to_defn(use.getAFlowNode(), def) + not use instanceof ClassExpr and + not use instanceof FunctionExpr and + jump_to_defn(use.getAFlowNode(), def) } private predicate unique_jump_to_defn(Expr use, Definition def) { - preferred_jump_to_defn(use, def) and - not exists(Definition other | - other != def and - preferred_jump_to_defn(use, other) - ) + preferred_jump_to_defn(use, def) and + not exists(Definition other | + other != def and + preferred_jump_to_defn(use, other) + ) } private predicate ssa_variable_defn(EssaVariable var, Definition defn) { - ssa_defn_defn(var.getDefinition(), defn) + ssa_defn_defn(var.getDefinition(), defn) } /** Holds if the phi-function `phi` refers to (`value`, `cls`, `origin`) given the context `context`. */ private predicate ssa_phi_defn(PhiFunction phi, Definition defn) { - ssa_variable_defn(phi.getAnInput(), defn) + ssa_variable_defn(phi.getAnInput(), defn) } /** Holds if the ESSA defn `def` refers to (`value`, `cls`, `origin`) given the context `context`. */ private predicate ssa_defn_defn(EssaDefinition def, Definition defn) { - ssa_phi_defn(def, defn) - or - ssa_node_defn(def, defn) - or - ssa_filter_defn(def, defn) - or - ssa_node_refinement_defn(def, defn) + ssa_phi_defn(def, defn) + or + ssa_node_defn(def, defn) + or + ssa_filter_defn(def, defn) + or + ssa_node_refinement_defn(def, defn) } /** Holds if ESSA edge refinement, `def`, is defined by `defn` */ predicate ssa_filter_defn(PyEdgeRefinement def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /** Holds if ESSA defn, `uniphi`,is defined by `defn` */ predicate uni_edged_phi_defn(SingleSuccessorGuard uniphi, Definition defn) { - ssa_variable_defn(uniphi.getInput(), defn) + ssa_variable_defn(uniphi.getInput(), defn) } pragma[noinline] private predicate ssa_node_defn(EssaNodeDefinition def, Definition defn) { - assignment_jump_to_defn(def, defn) - or - parameter_defn(def, defn) - or - delete_defn(def, defn) - or - scope_entry_defn(def, defn) - or - implicit_submodule_defn(def, defn) + assignment_jump_to_defn(def, defn) + or + parameter_defn(def, defn) + or + delete_defn(def, defn) + or + scope_entry_defn(def, defn) + or + implicit_submodule_defn(def, defn) } /* Definition for normal assignments `def = ...` */ private predicate assignment_jump_to_defn(AssignmentDefinition def, Definition defn) { - defn = TLocalDefinition(def.getValue().getNode()) + defn = TLocalDefinition(def.getValue().getNode()) } pragma[noinline] private predicate ssa_node_refinement_defn(EssaNodeRefinement def, Definition defn) { - method_callsite_defn(def, defn) - or - import_star_defn(def, defn) - or - attribute_assignment_defn(def, defn) - or - callsite_defn(def, defn) - or - argument_defn(def, defn) - or - attribute_delete_defn(def, defn) - or - uni_edged_phi_defn(def, defn) + method_callsite_defn(def, defn) + or + import_star_defn(def, defn) + or + attribute_assignment_defn(def, defn) + or + callsite_defn(def, defn) + or + argument_defn(def, defn) + or + attribute_delete_defn(def, defn) + or + uni_edged_phi_defn(def, defn) } /* Definition for parameter. `def foo(param): ...` */ private predicate parameter_defn(ParameterDefinition def, Definition defn) { - defn.getAstNode() = def.getDefiningNode().getNode() + defn.getAstNode() = def.getDefiningNode().getNode() } /* Definition for deletion: `del name` */ @@ -145,11 +145,11 @@ private predicate delete_defn(DeletionDefinition def, Definition defn) { none() /* Implicit "defn" of the names of submodules at the start of an `__init__.py` file. */ private predicate implicit_submodule_defn(ImplicitSubModuleDefinition def, Definition defn) { - exists(PackageObject package, ModuleObject mod | - package.getInitModule().getModule() = def.getDefiningNode().getScope() and - mod = package.submodule(def.getSourceVariable().getName()) and - defn.getAstNode() = mod.getModule() - ) + exists(PackageObject package, ModuleObject mod | + package.getInitModule().getModule() = def.getDefiningNode().getScope() and + mod = package.submodule(def.getSourceVariable().getName()) and + defn.getAstNode() = mod.getModule() + ) } /* @@ -158,42 +158,42 @@ private predicate implicit_submodule_defn(ImplicitSubModuleDefinition def, Defin */ private predicate scope_entry_value_transfer_at_callsite( - EssaVariable pred_var, ScopeEntryDefinition succ_def + EssaVariable pred_var, ScopeEntryDefinition succ_def ) { - exists(CallNode callsite, FunctionObject f | - f.getACall() = callsite and - pred_var.getSourceVariable() = succ_def.getSourceVariable() and - pred_var.getAUse() = callsite and - succ_def.getDefiningNode() = f.getFunction().getEntryNode() - ) + exists(CallNode callsite, FunctionObject f | + f.getACall() = callsite and + pred_var.getSourceVariable() = succ_def.getSourceVariable() and + pred_var.getAUse() = callsite and + succ_def.getDefiningNode() = f.getFunction().getEntryNode() + ) } /* Model the transfer of values at scope-entry points. Transfer from `pred_var, pred_context` to `succ_def, succ_context` */ private predicate scope_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { - BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, _, succ_def, _) - or - scope_entry_value_transfer_at_callsite(pred_var, succ_def) - or - class_entry_value_transfer(pred_var, succ_def) + BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, _, succ_def, _) + or + scope_entry_value_transfer_at_callsite(pred_var, succ_def) + or + class_entry_value_transfer(pred_var, succ_def) } /* Helper for scope_entry_value_transfer */ private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { - exists(ImportTimeScope scope, ControlFlowNode class_def | - class_def = pred_var.getAUse() and - scope.entryEdge(class_def, succ_def.getDefiningNode()) and - pred_var.getSourceVariable() = succ_def.getSourceVariable() - ) + exists(ImportTimeScope scope, ControlFlowNode class_def | + class_def = pred_var.getAUse() and + scope.entryEdge(class_def, succ_def.getDefiningNode()) and + pred_var.getSourceVariable() = succ_def.getSourceVariable() + ) } /* Definition for implicit variable declarations at scope-entry. */ pragma[noinline] private predicate scope_entry_defn(ScopeEntryDefinition def, Definition defn) { - /* Transfer from another scope */ - exists(EssaVariable var | - scope_entry_value_transfer(var, def) and - ssa_variable_defn(var, defn) - ) + /* Transfer from another scope */ + exists(EssaVariable var | + scope_entry_value_transfer(var, def) and + ssa_variable_defn(var, defn) + ) } /* @@ -203,73 +203,73 @@ private predicate scope_entry_defn(ScopeEntryDefinition def, Definition defn) { pragma[noinline] private predicate callsite_defn(CallsiteRefinement def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /* Pass through for `self` for the implicit re-defn of `self` in `self.foo()` */ private predicate method_callsite_defn(MethodCallsiteRefinement def, Definition defn) { - /* The value of self remains the same, only the attributes may change */ - ssa_variable_defn(def.getInput(), defn) + /* The value of self remains the same, only the attributes may change */ + ssa_variable_defn(def.getInput(), defn) } /** Helpers for import_star_defn */ pragma[noinline] private predicate module_and_name_for_import_star( - ModuleObject mod, string name, ImportStarRefinement def + ModuleObject mod, string name, ImportStarRefinement def ) { - exists(ImportStarNode im_star | - module_and_name_for_import_star_helper(mod, name, im_star, def) and - mod.exports(name) - ) + exists(ImportStarNode im_star | + module_and_name_for_import_star_helper(mod, name, im_star, def) and + mod.exports(name) + ) } pragma[noinline] private predicate module_and_name_for_import_star_helper( - ModuleObject mod, string name, ImportStarNode im_star, ImportStarRefinement def + ModuleObject mod, string name, ImportStarNode im_star, ImportStarRefinement def ) { - im_star = def.getDefiningNode() and - im_star.getModule().refersTo(mod) and - name = def.getSourceVariable().getName() + im_star = def.getDefiningNode() and + im_star.getModule().refersTo(mod) and + name = def.getSourceVariable().getName() } /** Holds if `def` is technically a defn of `var`, but the `from ... import *` does not in fact define `var` */ pragma[noinline] private predicate variable_not_redefined_by_import_star(EssaVariable var, ImportStarRefinement def) { - var = def.getInput() and - exists(ModuleObject mod | - def.getDefiningNode().(ImportStarNode).getModule().refersTo(mod) and - not mod.exports(var.getSourceVariable().getName()) - ) + var = def.getInput() and + exists(ModuleObject mod | + def.getDefiningNode().(ImportStarNode).getModule().refersTo(mod) and + not mod.exports(var.getSourceVariable().getName()) + ) } /* Definition for `from ... import *` */ private predicate import_star_defn(ImportStarRefinement def, Definition defn) { - exists(ModuleObject mod, string name | module_and_name_for_import_star(mod, name, def) | - /* Attribute from imported module */ - scope_jump_to_defn_attribute(mod.getModule(), name, defn) - ) - or - exists(EssaVariable var | - /* Retain value held before import */ - variable_not_redefined_by_import_star(var, def) and - ssa_variable_defn(var, defn) - ) + exists(ModuleObject mod, string name | module_and_name_for_import_star(mod, name, def) | + /* Attribute from imported module */ + scope_jump_to_defn_attribute(mod.getModule(), name, defn) + ) + or + exists(EssaVariable var | + /* Retain value held before import */ + variable_not_redefined_by_import_star(var, def) and + ssa_variable_defn(var, defn) + ) } /** Attribute assignments have no effect as far as defn tracking is concerned */ private predicate attribute_assignment_defn(AttributeAssignment def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /** Ignore the effects of calls on their arguments. This is an approximation, but attempting to improve accuracy would be very expensive for very little gain. */ private predicate argument_defn(ArgumentRefinement def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /** Attribute deletions have no effect as far as value tracking is concerned. */ pragma[noinline] private predicate attribute_delete_defn(EssaAttributeDeletion def, Definition defn) { - ssa_variable_defn(def.getInput(), defn) + ssa_variable_defn(def.getInput(), defn) } /* @@ -284,119 +284,119 @@ private predicate attribute_delete_defn(EssaAttributeDeletion def, Definition de * Holds if the attribute `name` of the ssa variable `var` refers to (`value`, `cls`, `origin`) */ predicate ssa_variable_jump_to_defn_attribute(EssaVariable var, string name, Definition defn) { - ssa_defn_jump_to_defn_attribute(var.getDefinition(), name, defn) + ssa_defn_jump_to_defn_attribute(var.getDefinition(), name, defn) } /** Helper for ssa_variable_jump_to_defn_attribute */ private predicate ssa_defn_jump_to_defn_attribute(EssaDefinition def, string name, Definition defn) { - ssa_phi_jump_to_defn_attribute(def, name, defn) - or - ssa_node_jump_to_defn_attribute(def, name, defn) - or - ssa_node_refinement_jump_to_defn_attribute(def, name, defn) - or - ssa_filter_jump_to_defn_attribute(def, name, defn) + ssa_phi_jump_to_defn_attribute(def, name, defn) + or + ssa_node_jump_to_defn_attribute(def, name, defn) + or + ssa_node_refinement_jump_to_defn_attribute(def, name, defn) + or + ssa_filter_jump_to_defn_attribute(def, name, defn) } /** Holds if ESSA edge refinement, `def`, is defined by `defn` of `priority` */ predicate ssa_filter_jump_to_defn_attribute(PyEdgeRefinement def, string name, Definition defn) { - ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) + ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) } /** Holds if the attribute `name` of the ssa phi-function defn `phi` refers to (`value`, `cls`, `origin`) */ private predicate ssa_phi_jump_to_defn_attribute(PhiFunction phi, string name, Definition defn) { - ssa_variable_jump_to_defn_attribute(phi.getAnInput(), name, defn) + ssa_variable_jump_to_defn_attribute(phi.getAnInput(), name, defn) } /** Helper for ssa_defn_jump_to_defn_attribute */ pragma[noinline] private predicate ssa_node_jump_to_defn_attribute( - EssaNodeDefinition def, string name, Definition defn + EssaNodeDefinition def, string name, Definition defn ) { - assignment_jump_to_defn_attribute(def, name, defn) - or - self_parameter_jump_to_defn_attribute(def, name, defn) - or - scope_entry_jump_to_defn_attribute(def, name, defn) + assignment_jump_to_defn_attribute(def, name, defn) + or + self_parameter_jump_to_defn_attribute(def, name, defn) + or + scope_entry_jump_to_defn_attribute(def, name, defn) } /** Helper for ssa_defn_jump_to_defn_attribute */ pragma[noinline] private predicate ssa_node_refinement_jump_to_defn_attribute( - EssaNodeRefinement def, string name, Definition defn + EssaNodeRefinement def, string name, Definition defn ) { - attribute_assignment_jump_to_defn_attribute(def, name, defn) - or - argument_jump_to_defn_attribute(def, name, defn) + attribute_assignment_jump_to_defn_attribute(def, name, defn) + or + argument_jump_to_defn_attribute(def, name, defn) } pragma[noinline] private predicate scope_entry_jump_to_defn_attribute( - ScopeEntryDefinition def, string name, Definition defn + ScopeEntryDefinition def, string name, Definition defn ) { - exists(EssaVariable var | - scope_entry_value_transfer(var, def) and - ssa_variable_jump_to_defn_attribute(var, name, defn) - ) + exists(EssaVariable var | + scope_entry_value_transfer(var, def) and + ssa_variable_jump_to_defn_attribute(var, name, defn) + ) } private predicate scope_jump_to_defn_attribute(ImportTimeScope s, string name, Definition defn) { - exists(EssaVariable var | - BaseFlow::reaches_exit(var) and - var.getScope() = s and - var.getName() = name - | - ssa_variable_defn(var, defn) - ) + exists(EssaVariable var | + BaseFlow::reaches_exit(var) and + var.getScope() = s and + var.getName() = name + | + ssa_variable_defn(var, defn) + ) } private predicate jump_to_defn_attribute(ControlFlowNode use, string name, Definition defn) { - /* Local attribute */ - exists(EssaVariable var | - use = var.getASourceUse() and - ssa_variable_jump_to_defn_attribute(var, name, defn) - ) + /* Local attribute */ + exists(EssaVariable var | + use = var.getASourceUse() and + ssa_variable_jump_to_defn_attribute(var, name, defn) + ) + or + /* Instance attributes */ + exists(ClassObject cls | use.refersTo(_, cls, _) | + scope_jump_to_defn_attribute(cls.getPyClass(), name, defn) + ) + or + /* Super attributes */ + exists(AttrNode f, SuperBoundMethod sbm, Object function | + use = f.getObject(name) and + f.refersTo(sbm) and + function = sbm.getFunction(_) and + function.getOrigin() = defn.getAstNode() + ) + or + /* Class or module attribute */ + exists(Object obj, Scope scope | + use.refersTo(obj) and + scope_jump_to_defn_attribute(scope, name, defn) + | + obj.(ClassObject).getPyClass() = scope or - /* Instance attributes */ - exists(ClassObject cls | use.refersTo(_, cls, _) | - scope_jump_to_defn_attribute(cls.getPyClass(), name, defn) - ) + obj.(PythonModuleObject).getModule() = scope or - /* Super attributes */ - exists(AttrNode f, SuperBoundMethod sbm, Object function | - use = f.getObject(name) and - f.refersTo(sbm) and - function = sbm.getFunction(_) and - function.getOrigin() = defn.getAstNode() - ) - or - /* Class or module attribute */ - exists(Object obj, Scope scope | - use.refersTo(obj) and - scope_jump_to_defn_attribute(scope, name, defn) - | - obj.(ClassObject).getPyClass() = scope - or - obj.(PythonModuleObject).getModule() = scope - or - obj.(PackageObject).getInitModule().getModule() = scope - ) + obj.(PackageObject).getInitModule().getModule() = scope + ) } pragma[noinline] private predicate assignment_jump_to_defn_attribute( - AssignmentDefinition def, string name, Definition defn + AssignmentDefinition def, string name, Definition defn ) { - jump_to_defn_attribute(def.getValue(), name, defn) + jump_to_defn_attribute(def.getValue(), name, defn) } pragma[noinline] private predicate attribute_assignment_jump_to_defn_attribute( - AttributeAssignment def, string name, Definition defn + AttributeAssignment def, string name, Definition defn ) { - defn.getAstNode() = def.getDefiningNode().getNode() and name = def.getName() - or - ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) and not name = def.getName() + defn.getAstNode() = def.getDefiningNode().getNode() and name = def.getName() + or + ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) and not name = def.getName() } /** @@ -404,42 +404,42 @@ private predicate attribute_assignment_jump_to_defn_attribute( * `def` takes the form `setattr(use, "name")` where `use` is the input to the defn. */ private predicate sets_attribute(ArgumentRefinement def, string name) { - exists(CallNode call | - call = def.getDefiningNode() and - call.getFunction().refersTo(Object::builtin("setattr")) and - def.getInput().getAUse() = call.getArg(0) and - call.getArg(1).getNode().(StrConst).getText() = name - ) + exists(CallNode call | + call = def.getDefiningNode() and + call.getFunction().refersTo(Object::builtin("setattr")) and + def.getInput().getAUse() = call.getArg(0) and + call.getArg(1).getNode().(StrConst).getText() = name + ) } pragma[noinline] private predicate argument_jump_to_defn_attribute( - ArgumentRefinement def, string name, Definition defn + ArgumentRefinement def, string name, Definition defn ) { - if sets_attribute(def, name) - then jump_to_defn(def.getDefiningNode().(CallNode).getArg(2), defn) - else ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) + if sets_attribute(def, name) + then jump_to_defn(def.getDefiningNode().(CallNode).getArg(2), defn) + else ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) } /** Gets the (temporally) preceding variable for "self", e.g. `def` is in method foo() and `result` is in `__init__()`. */ private EssaVariable preceding_self_variable(ParameterDefinition def) { - def.isSelf() and - exists(Function preceding, Function method | - method = def.getScope() and - // Only methods - preceding.isMethod() and - preceding.precedes(method) and - BaseFlow::reaches_exit(result) and - result.getSourceVariable().(Variable).isSelf() and - result.getScope() = preceding - ) + def.isSelf() and + exists(Function preceding, Function method | + method = def.getScope() and + // Only methods + preceding.isMethod() and + preceding.precedes(method) and + BaseFlow::reaches_exit(result) and + result.getSourceVariable().(Variable).isSelf() and + result.getScope() = preceding + ) } pragma[noinline] private predicate self_parameter_jump_to_defn_attribute( - ParameterDefinition def, string name, Definition defn + ParameterDefinition def, string name, Definition defn ) { - ssa_variable_jump_to_defn_attribute(preceding_self_variable(def), name, defn) + ssa_variable_jump_to_defn_attribute(preceding_self_variable(def), name, defn) } /** @@ -447,11 +447,11 @@ private predicate self_parameter_jump_to_defn_attribute( * This exists primarily for testing use `getPreferredDefinition()` instead. */ Definition getADefinition(Expr use) { - jump_to_defn(use.getAFlowNode(), result) and - not use instanceof Call and - not use.isArtificial() and - // Not the use itself - not result = TLocalDefinition(use) + jump_to_defn(use.getAFlowNode(), result) and + not use instanceof Call and + not use.isArtificial() and + // Not the use itself + not result = TLocalDefinition(use) } /** @@ -459,44 +459,45 @@ Definition getADefinition(Expr use) { * Helper for the jump-to-definition query. */ Definition getUniqueDefinition(Expr use) { - unique_jump_to_defn(use, result) and - not use instanceof Call and - not use.isArtificial() and - // Not the use itself - not result = TLocalDefinition(use) + unique_jump_to_defn(use, result) and + not use instanceof Call and + not use.isArtificial() and + // Not the use itself + not result = TLocalDefinition(use) } /** Helper class to get suitable locations for attributes */ class NiceLocationExpr extends @py_expr { - /** Gets a textual representation of this element. */ - string toString() { result = this.(Expr).toString() } - /** - * Holds if this element is at the specified location. - * The location spans column `bc` of line `bl` to - * column `ec` of line `el` in file `f`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) { - /* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */ - exists(int abl, int abc | this.(Attribute).getLocation().hasLocationInfo(f, abl, abc, el, ec) | - bl = el and bc = ec - this.(Attribute).getName().length() + 1 - ) - or - this.(Name).getLocation().hasLocationInfo(f, bl, bc, el, ec) - or - // Show xxx for `xxx` in `from xxx import y` or - // for `import xxx` or for `import xxx as yyy`. - this.(ImportExpr).getLocation().hasLocationInfo(f, bl, bc, el, ec) - or - /* Show y for `y` in `from xxx import y` */ - exists(string name | - name = this.(ImportMember).getName() and - this.(ImportMember).getLocation().hasLocationInfo(f, _, _, el, ec) and - bl = el and - bc = ec - name.length() + 1 - ) - } + /** Gets a textual representation of this element. */ + string toString() { result = this.(Expr).toString() } + + /** + * Holds if this element is at the specified location. + * The location spans column `bc` of line `bl` to + * column `ec` of line `el` in file `f`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) { + /* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */ + exists(int abl, int abc | this.(Attribute).getLocation().hasLocationInfo(f, abl, abc, el, ec) | + bl = el and bc = ec - this.(Attribute).getName().length() + 1 + ) + or + this.(Name).getLocation().hasLocationInfo(f, bl, bc, el, ec) + or + // Show xxx for `xxx` in `from xxx import y` or + // for `import xxx` or for `import xxx as yyy`. + this.(ImportExpr).getLocation().hasLocationInfo(f, bl, bc, el, ec) + or + /* Show y for `y` in `from xxx import y` */ + exists(string name | + name = this.(ImportMember).getName() and + this.(ImportMember).getLocation().hasLocationInfo(f, _, _, el, ec) and + bl = el and + bc = ec - name.length() + 1 + ) + } } /** @@ -504,13 +505,13 @@ class NiceLocationExpr extends @py_expr { */ cached Definition definitionOf(NiceLocationExpr use, string kind) { - exists(string f, int l | - result = getUniqueDefinition(use) and - kind = "Definition" and - use.hasLocationInfo(f, l, _, _, _) and - // Ignore if the definition is on the same line as the use - not result.getLocation().hasLocationInfo(f, l, _, _, _) - ) + exists(string f, int l | + result = getUniqueDefinition(use) and + kind = "Definition" and + use.hasLocationInfo(f, l, _, _, _) and + // Ignore if the definition is on the same line as the use + not result.getLocation().hasLocationInfo(f, l, _, _, _) + ) } /** diff --git a/python/ql/src/analysis/Definitions.ql b/python/ql/src/analysis/Definitions.ql index b6b38708943..a2ed4f36bf6 100644 --- a/python/ql/src/analysis/Definitions.ql +++ b/python/ql/src/analysis/Definitions.ql @@ -10,4 +10,4 @@ import DefinitionTracking from NiceLocationExpr use, Definition defn, string kind where defn = definitionOf(use, kind) -select use, defn, kind \ No newline at end of file +select use, defn, kind diff --git a/python/ql/src/analysis/Efficiency.ql b/python/ql/src/analysis/Efficiency.ql index 634d670dc77..7f08e30502e 100644 --- a/python/ql/src/analysis/Efficiency.ql +++ b/python/ql/src/analysis/Efficiency.ql @@ -8,27 +8,27 @@ import semmle.python.pointsto.PointsTo import semmle.python.pointsto.PointsToContext predicate trivial(ControlFlowNode f) { - exists(Parameter p | p = f.getNode()) - or - f instanceof NameConstantNode - or - f.getNode() instanceof ImmutableLiteral + exists(Parameter p | p = f.getNode()) + or + f instanceof NameConstantNode + or + f.getNode() instanceof ImmutableLiteral } from int interesting_facts, int interesting_facts_in_source, int total_size, float efficiency where - interesting_facts = - strictcount(ControlFlowNode f, Object value, ClassObject cls | - f.refersTo(value, cls, _) and not trivial(f) - ) and - interesting_facts_in_source = - strictcount(ControlFlowNode f, Object value, ClassObject cls | - f.refersTo(value, cls, _) and - not trivial(f) and - exists(f.getScope().getEnclosingModule().getFile().getRelativePath()) - ) and - total_size = - strictcount(ControlFlowNode f, PointsToContext ctx, Object value, ClassObject cls, - ControlFlowNode orig | PointsTo::points_to(f, ctx, value, cls, orig)) and - efficiency = 100.0 * interesting_facts_in_source / total_size + interesting_facts = + strictcount(ControlFlowNode f, Object value, ClassObject cls | + f.refersTo(value, cls, _) and not trivial(f) + ) and + interesting_facts_in_source = + strictcount(ControlFlowNode f, Object value, ClassObject cls | + f.refersTo(value, cls, _) and + not trivial(f) and + exists(f.getScope().getEnclosingModule().getFile().getRelativePath()) + ) and + total_size = + strictcount(ControlFlowNode f, PointsToContext ctx, Object value, ClassObject cls, + ControlFlowNode orig | PointsTo::points_to(f, ctx, value, cls, orig)) and + efficiency = 100.0 * interesting_facts_in_source / total_size select interesting_facts, interesting_facts_in_source, total_size, efficiency diff --git a/python/ql/src/analysis/ImportFailure.ql b/python/ql/src/analysis/ImportFailure.ql index 9c944f9ae83..9ed1fd001c8 100644 --- a/python/ql/src/analysis/ImportFailure.ql +++ b/python/ql/src/analysis/ImportFailure.ql @@ -9,75 +9,75 @@ import python ImportExpr alternative_import(ImportExpr ie) { - exists(Alias thisalias, Alias otheralias | - (thisalias.getValue() = ie or thisalias.getValue().(ImportMember).getModule() = ie) and - (otheralias.getValue() = result or otheralias.getValue().(ImportMember).getModule() = result) and - ( - exists(If i | i.getBody().contains(ie) and i.getOrelse().contains(result)) - or - exists(If i | i.getBody().contains(result) and i.getOrelse().contains(ie)) - or - exists(Try t | t.getBody().contains(ie) and t.getAHandler().contains(result)) - or - exists(Try t | t.getBody().contains(result) and t.getAHandler().contains(ie)) - ) + exists(Alias thisalias, Alias otheralias | + (thisalias.getValue() = ie or thisalias.getValue().(ImportMember).getModule() = ie) and + (otheralias.getValue() = result or otheralias.getValue().(ImportMember).getModule() = result) and + ( + exists(If i | i.getBody().contains(ie) and i.getOrelse().contains(result)) + or + exists(If i | i.getBody().contains(result) and i.getOrelse().contains(ie)) + or + exists(Try t | t.getBody().contains(ie) and t.getAHandler().contains(result)) + or + exists(Try t | t.getBody().contains(result) and t.getAHandler().contains(ie)) ) + ) } string os_specific_import(ImportExpr ie) { - exists(string name | name = ie.getImportedModuleName() | - name.matches("org.python.%") and result = "java" - or - name.matches("java.%") and result = "java" - or - name.matches("Carbon.%") and result = "darwin" - or - result = "win32" and - ( - name = "_winapi" or - name = "_win32api" or - name = "_winreg" or - name = "nt" or - name.matches("win32%") or - name = "ntpath" - ) - or - result = "linux2" and - (name = "posix" or name = "posixpath") - or - result = "unsupported" and - (name = "__pypy__" or name = "ce" or name.matches("riscos%")) + exists(string name | name = ie.getImportedModuleName() | + name.matches("org.python.%") and result = "java" + or + name.matches("java.%") and result = "java" + or + name.matches("Carbon.%") and result = "darwin" + or + result = "win32" and + ( + name = "_winapi" or + name = "_win32api" or + name = "_winreg" or + name = "nt" or + name.matches("win32%") or + name = "ntpath" ) + or + result = "linux2" and + (name = "posix" or name = "posixpath") + or + result = "unsupported" and + (name = "__pypy__" or name = "ce" or name.matches("riscos%")) + ) } string get_os() { py_flags_versioned("sys.platform", result, major_version().toString()) } predicate ok_to_fail(ImportExpr ie) { - alternative_import(ie).refersTo(_) - or - os_specific_import(ie) != get_os() + alternative_import(ie).refersTo(_) + or + os_specific_import(ie) != get_os() } class VersionTest extends @py_flow_node { - VersionTest() { - exists(string name | - name.matches("%version%") and - this.(CompareNode).getAChild+().pointsTo(Module::named("sys").attr(name)) - ) - } + VersionTest() { + exists(string name | + name.matches("%version%") and + this.(CompareNode).getAChild+().pointsTo(Module::named("sys").attr(name)) + ) + } - string toString() { result = "VersionTest" } + string toString() { result = "VersionTest" } } /** A guard on the version of the Python interpreter */ class VersionGuard extends ConditionBlock { - VersionGuard() { this.getLastNode() instanceof VersionTest } + VersionGuard() { this.getLastNode() instanceof VersionTest } } from ImportExpr ie where - not ie.refersTo(_) and - exists(Context c | c.appliesTo(ie.getAFlowNode())) and - not ok_to_fail(ie) and - not exists(VersionGuard guard | guard.controls(ie.getAFlowNode().getBasicBlock(), _)) + not ie.refersTo(_) and + exists(Context c | c.appliesTo(ie.getAFlowNode())) and + not ok_to_fail(ie) and + not exists(VersionGuard guard | guard.controls(ie.getAFlowNode().getBasicBlock(), _)) select ie, "Unable to resolve import of '" + ie.getImportedModuleName() + "'." diff --git a/python/ql/src/analysis/KeyPointsToFailure.ql b/python/ql/src/analysis/KeyPointsToFailure.ql index 6cff87dbe85..d869d547c75 100644 --- a/python/ql/src/analysis/KeyPointsToFailure.ql +++ b/python/ql/src/analysis/KeyPointsToFailure.ql @@ -11,16 +11,16 @@ import python import semmle.python.pointsto.PointsTo predicate points_to_failure(Expr e) { - exists(ControlFlowNode f | f = e.getAFlowNode() | not PointsTo::pointsTo(f, _, _, _)) + exists(ControlFlowNode f | f = e.getAFlowNode() | not PointsTo::pointsTo(f, _, _, _)) } predicate key_points_to_failure(Expr e) { - points_to_failure(e) and - not points_to_failure(e.getASubExpression()) and - not exists(SsaVariable ssa | ssa.getAUse() = e.getAFlowNode() | - points_to_failure(ssa.getAnUltimateDefinition().getDefinition().getNode()) - ) and - not exists(Assign a | a.getATarget() = e) + points_to_failure(e) and + not points_to_failure(e.getASubExpression()) and + not exists(SsaVariable ssa | ssa.getAUse() = e.getAFlowNode() | + points_to_failure(ssa.getAnUltimateDefinition().getDefinition().getNode()) + ) and + not exists(Assign a | a.getATarget() = e) } from Attribute e diff --git a/python/ql/src/analysis/LocalDefinitions.ql b/python/ql/src/analysis/LocalDefinitions.ql index 89d0b95e0a3..f9f3da0cdee 100644 --- a/python/ql/src/analysis/LocalDefinitions.ql +++ b/python/ql/src/analysis/LocalDefinitions.ql @@ -13,7 +13,8 @@ import DefinitionTracking external string selectedSourceFile(); from NiceLocationExpr use, Definition defn, string kind, string f -where defn = definitionOf(use, kind) -and use.hasLocationInfo(f, _, _, _, _) -and getEncodedFile(selectedSourceFile()).getAbsolutePath() = f -select use, defn, kind \ No newline at end of file +where + defn = definitionOf(use, kind) and + use.hasLocationInfo(f, _, _, _, _) and + getEncodedFile(selectedSourceFile()).getAbsolutePath() = f +select use, defn, kind diff --git a/python/ql/src/analysis/LocalReferences.ql b/python/ql/src/analysis/LocalReferences.ql index 9cb812903d6..21e600b4ac1 100644 --- a/python/ql/src/analysis/LocalReferences.ql +++ b/python/ql/src/analysis/LocalReferences.ql @@ -13,6 +13,7 @@ import DefinitionTracking external string selectedSourceFile(); from NiceLocationExpr use, Definition defn, string kind -where defn = definitionOf(use, kind) -and defn.getLocation().getFile() = getEncodedFile(selectedSourceFile()) -select use, defn, kind \ No newline at end of file +where + defn = definitionOf(use, kind) and + defn.getLocation().getFile() = getEncodedFile(selectedSourceFile()) +select use, defn, kind diff --git a/python/ql/src/analysis/RatioOfDefinitions.ql b/python/ql/src/analysis/RatioOfDefinitions.ql index 72a3fed0e69..f7ef4741ee6 100644 --- a/python/ql/src/analysis/RatioOfDefinitions.ql +++ b/python/ql/src/analysis/RatioOfDefinitions.ql @@ -6,21 +6,21 @@ import python import DefinitionTracking predicate want_to_have_definition(Expr e) { - /* not builtin object like len, tuple, etc. */ - not exists(Value builtin | e.pointsTo(builtin) and builtin.isBuiltin()) and - ( - e instanceof Name and e.(Name).getCtx() instanceof Load - or - e instanceof Attribute and e.(Attribute).getCtx() instanceof Load - or - e instanceof ImportMember - or - e instanceof ImportExpr - ) + /* not builtin object like len, tuple, etc. */ + not exists(Value builtin | e.pointsTo(builtin) and builtin.isBuiltin()) and + ( + e instanceof Name and e.(Name).getCtx() instanceof Load + or + e instanceof Attribute and e.(Attribute).getCtx() instanceof Load + or + e instanceof ImportMember + or + e instanceof ImportExpr + ) } from int yes, int no where - yes = count(Expr e | want_to_have_definition(e) and exists(getUniqueDefinition(e))) and - no = count(Expr e | want_to_have_definition(e) and not exists(getUniqueDefinition(e))) + yes = count(Expr e | want_to_have_definition(e) and exists(getUniqueDefinition(e))) and + no = count(Expr e | want_to_have_definition(e) and not exists(getUniqueDefinition(e))) select yes, no, yes * 100 / (yes + no) + "%" diff --git a/python/ql/src/analysis/Summary.ql b/python/ql/src/analysis/Summary.ql index 55564edb16e..6eb92dd935d 100644 --- a/python/ql/src/analysis/Summary.ql +++ b/python/ql/src/analysis/Summary.ql @@ -6,38 +6,38 @@ import python from string key, string value where - key = "Extractor version" and py_flags_versioned("extractor.version", value, _) - or - key = "Snapshot build time" and - exists(date d | snapshotDate(d) and value = d.toString()) - or - key = "Interpreter version" and - exists(string major, string minor | - py_flags_versioned("version.major", major, _) and - py_flags_versioned("version.minor", minor, _) and - value = major + "." + minor - ) - or - key = "Build platform" and - exists(string raw | py_flags_versioned("sys.platform", raw, _) | - if raw = "win32" - then value = "Windows" - else - if raw = "linux2" - then value = "Linux" - else - if raw = "darwin" - then value = "OSX" - else value = raw - ) - or - key = "Source location" and sourceLocationPrefix(value) - or - key = "Lines of code (source)" and - value = - sum(ModuleMetrics m | exists(m.getFile().getRelativePath()) | m.getNumberOfLinesOfCode()) - .toString() - or - key = "Lines of code (total)" and - value = sum(ModuleMetrics m | any() | m.getNumberOfLinesOfCode()).toString() + key = "Extractor version" and py_flags_versioned("extractor.version", value, _) + or + key = "Snapshot build time" and + exists(date d | snapshotDate(d) and value = d.toString()) + or + key = "Interpreter version" and + exists(string major, string minor | + py_flags_versioned("version.major", major, _) and + py_flags_versioned("version.minor", minor, _) and + value = major + "." + minor + ) + or + key = "Build platform" and + exists(string raw | py_flags_versioned("sys.platform", raw, _) | + if raw = "win32" + then value = "Windows" + else + if raw = "linux2" + then value = "Linux" + else + if raw = "darwin" + then value = "OSX" + else value = raw + ) + or + key = "Source location" and sourceLocationPrefix(value) + or + key = "Lines of code (source)" and + value = + sum(ModuleMetrics m | exists(m.getFile().getRelativePath()) | m.getNumberOfLinesOfCode()) + .toString() + or + key = "Lines of code (total)" and + value = sum(ModuleMetrics m | any() | m.getNumberOfLinesOfCode()).toString() select key, value diff --git a/python/ql/src/analysis/TypeInferenceFailure.ql b/python/ql/src/analysis/TypeInferenceFailure.ql index 0e6e42e8385..d863d6bb2cc 100644 --- a/python/ql/src/analysis/TypeInferenceFailure.ql +++ b/python/ql/src/analysis/TypeInferenceFailure.ql @@ -11,6 +11,6 @@ import python from ControlFlowNode f, Object o where - f.refersTo(o) and - not exists(ClassObject c | f.refersTo(o, c, _)) + f.refersTo(o) and + not exists(ClassObject c | f.refersTo(o, c, _)) select o, "Type inference fails for 'object'." diff --git a/python/ql/src/experimental/dataflow/TaintTracking.qll b/python/ql/src/experimental/dataflow/TaintTracking.qll index b6c14f2d776..c74684803ad 100644 --- a/python/ql/src/experimental/dataflow/TaintTracking.qll +++ b/python/ql/src/experimental/dataflow/TaintTracking.qll @@ -16,4 +16,4 @@ import python */ module TaintTracking { import experimental.dataflow.internal.tainttracking1.TaintTrackingImpl -} \ No newline at end of file +} diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll index 8e18162df56..e88726b158b 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImplSpecific.qll @@ -3,7 +3,7 @@ */ module Private { import DataFlowPrivate -// import DataFlowDispatch + // import DataFlowDispatch } module Public { diff --git a/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll b/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll index 28e254613ca..d929d6ce014 100644 --- a/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/TaintTrackingPublic.qll @@ -6,13 +6,11 @@ private import python private import TaintTrackingPrivate private import experimental.dataflow.DataFlow - // /** // * Holds if taint propagates from `source` to `sink` in zero or more local // * (intra-procedural) steps. // */ // predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) } - // // /** // // * Holds if taint can flow from `e1` to `e2` in zero or more // // * local (intra-procedural) steps. @@ -20,10 +18,8 @@ private import experimental.dataflow.DataFlow // // predicate localExprTaint(Expr e1, Expr e2) { // // localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2)) // // } - // // /** A member (property or field) that is tainted if its containing object is tainted. */ // // abstract class TaintedMember extends AssignableMember { } - // /** // * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local // * (intra-procedural) step. @@ -33,4 +29,4 @@ private import experimental.dataflow.DataFlow // DataFlow::localFlowStep(nodeFrom, nodeTo) // or // localAdditionalTaintStep(nodeFrom, nodeTo) -// } \ No newline at end of file +// } diff --git a/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll index 7f9fb029103..f1b2b94d3aa 100644 --- a/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll +++ b/python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingParameter.qll @@ -3,4 +3,4 @@ import experimental.dataflow.internal.TaintTrackingPublic as Public module Private { import experimental.dataflow.DataFlow::DataFlow as DataFlow import experimental.dataflow.internal.TaintTrackingPrivate -} \ No newline at end of file +} diff --git a/python/ql/src/external/CodeDuplication.qll b/python/ql/src/external/CodeDuplication.qll index 85b5fda8fbb..01ab28d955b 100644 --- a/python/ql/src/external/CodeDuplication.qll +++ b/python/ql/src/external/CodeDuplication.qll @@ -13,80 +13,80 @@ private string relativePath(File file) { result = file.getRelativePath().replace */ pragma[noinline, nomagic] private predicate tokenLocation(File file, int sl, int sc, int ec, int el, Copy copy, int index) { - file = copy.sourceFile() and - tokens(copy, index, sl, sc, ec, el) + file = copy.sourceFile() and + tokens(copy, index, sl, sc, ec, el) } /** A token block used for detection of duplicate and similar code. */ class Copy extends @duplication_or_similarity { - private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) } + private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) } - /** Gets the index of the token in this block starting at the location `loc`, if any. */ - int tokenStartingAt(Location loc) { - tokenLocation(loc.getFile(), loc.getStartLine(), loc.getStartColumn(), _, _, this, result) - } + /** Gets the index of the token in this block starting at the location `loc`, if any. */ + int tokenStartingAt(Location loc) { + tokenLocation(loc.getFile(), loc.getStartLine(), loc.getStartColumn(), _, _, this, result) + } - /** Gets the index of the token in this block ending at the location `loc`, if any. */ - int tokenEndingAt(Location loc) { - tokenLocation(loc.getFile(), _, _, loc.getEndLine(), loc.getEndColumn(), this, result) - } + /** Gets the index of the token in this block ending at the location `loc`, if any. */ + int tokenEndingAt(Location loc) { + tokenLocation(loc.getFile(), _, _, loc.getEndLine(), loc.getEndColumn(), this, result) + } - /** Gets the line on which the first token in this block starts. */ - int sourceStartLine() { tokens(this, 0, result, _, _, _) } + /** Gets the line on which the first token in this block starts. */ + int sourceStartLine() { tokens(this, 0, result, _, _, _) } - /** Gets the column on which the first token in this block starts. */ - int sourceStartColumn() { tokens(this, 0, _, result, _, _) } + /** Gets the column on which the first token in this block starts. */ + int sourceStartColumn() { tokens(this, 0, _, result, _, _) } - /** Gets the line on which the last token in this block ends. */ - int sourceEndLine() { tokens(this, this.lastToken(), _, _, result, _) } + /** Gets the line on which the last token in this block ends. */ + int sourceEndLine() { tokens(this, this.lastToken(), _, _, result, _) } - /** Gets the column on which the last token in this block ends. */ - int sourceEndColumn() { tokens(this, this.lastToken(), _, _, _, result) } + /** Gets the column on which the last token in this block ends. */ + int sourceEndColumn() { tokens(this, this.lastToken(), _, _, _, result) } - /** Gets the number of lines containing at least (part of) one token in this block. */ - int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() } + /** Gets the number of lines containing at least (part of) one token in this block. */ + int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() } - /** Gets an opaque identifier for the equivalence class of this block. */ - int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) } + /** Gets an opaque identifier for the equivalence class of this block. */ + int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) } - /** Gets the source file in which this block appears. */ - File sourceFile() { - exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) | - name.replaceAll("\\", "/") = relativePath(result) - ) - } + /** Gets the source file in which this block appears. */ + File sourceFile() { + exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) | + name.replaceAll("\\", "/") = relativePath(result) + ) + } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - sourceFile().getAbsolutePath() = filepath and - startline = sourceStartLine() and - startcolumn = sourceStartColumn() and - endline = sourceEndLine() and - endcolumn = sourceEndColumn() - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + sourceFile().getAbsolutePath() = filepath and + startline = sourceStartLine() and + startcolumn = sourceStartColumn() and + endline = sourceEndLine() and + endcolumn = sourceEndColumn() + } - /** Gets a textual representation of this element. */ - string toString() { result = "Copy" } + /** Gets a textual representation of this element. */ + string toString() { result = "Copy" } - /** - * Gets a block that extends this one, that is, its first token is also - * covered by this block, but they are not the same block. - */ - Copy extendingBlock() { - exists(File file, int sl, int sc, int ec, int el | - tokenLocation(file, sl, sc, ec, el, this, _) and - tokenLocation(file, sl, sc, ec, el, result, 0) - ) and - this != result - } + /** + * Gets a block that extends this one, that is, its first token is also + * covered by this block, but they are not the same block. + */ + Copy extendingBlock() { + exists(File file, int sl, int sc, int ec, int el | + tokenLocation(file, sl, sc, ec, el, this, _) and + tokenLocation(file, sl, sc, ec, el, result, 0) + ) and + this != result + } } /** @@ -96,18 +96,18 @@ class Copy extends @duplication_or_similarity { * `start2`, and `end` the equivalence class of `end1` and `end2`. */ predicate similar_extension( - SimilarBlock start1, SimilarBlock start2, SimilarBlock ext1, SimilarBlock ext2, int start, int ext + SimilarBlock start1, SimilarBlock start2, SimilarBlock ext1, SimilarBlock ext2, int start, int ext ) { - start1.getEquivalenceClass() = start and - start2.getEquivalenceClass() = start and - ext1.getEquivalenceClass() = ext and - ext2.getEquivalenceClass() = ext and - start1 != start2 and - ( - ext1 = start1 and ext2 = start2 - or - similar_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) - ) + start1.getEquivalenceClass() = start and + start2.getEquivalenceClass() = start and + ext1.getEquivalenceClass() = ext and + ext2.getEquivalenceClass() = ext and + start1 != start2 and + ( + ext1 = start1 and ext2 = start2 + or + similar_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) + ) } /** @@ -117,31 +117,31 @@ predicate similar_extension( * `start2`, and `end` the equivalence class of `end1` and `end2`. */ predicate duplicate_extension( - DuplicateBlock start1, DuplicateBlock start2, DuplicateBlock ext1, DuplicateBlock ext2, int start, - int ext + DuplicateBlock start1, DuplicateBlock start2, DuplicateBlock ext1, DuplicateBlock ext2, int start, + int ext ) { - start1.getEquivalenceClass() = start and - start2.getEquivalenceClass() = start and - ext1.getEquivalenceClass() = ext and - ext2.getEquivalenceClass() = ext and - start1 != start2 and - ( - ext1 = start1 and ext2 = start2 - or - duplicate_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) - ) + start1.getEquivalenceClass() = start and + start2.getEquivalenceClass() = start and + ext1.getEquivalenceClass() = ext and + ext2.getEquivalenceClass() = ext and + start1 != start2 and + ( + ext1 = start1 and ext2 = start2 + or + duplicate_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) + ) } /** A block of duplicated code. */ class DuplicateBlock extends Copy, @duplication { - override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." } + override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." } } /** A block of similar code. */ class SimilarBlock extends Copy, @similarity { - override string toString() { - result = "Similar code: " + sourceLines() + " almost duplicated lines." - } + override string toString() { + result = "Similar code: " + sourceLines() + " almost duplicated lines." + } } /** @@ -149,14 +149,14 @@ class SimilarBlock extends Copy, @similarity { * respectively, where `scope1` and `scope2` are not the same. */ predicate duplicateStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt stmt2) { - exists(int equivstart, int equivend, int first, int last | - scope1.contains(stmt1) and - scope2.contains(stmt2) and - duplicateCoversStatement(equivstart, equivend, first, last, stmt1) and - duplicateCoversStatement(equivstart, equivend, first, last, stmt2) and - stmt1 != stmt2 and - scope1 != scope2 - ) + exists(int equivstart, int equivend, int first, int last | + scope1.contains(stmt1) and + scope2.contains(stmt2) and + duplicateCoversStatement(equivstart, equivend, first, last, stmt1) and + duplicateCoversStatement(equivstart, equivend, first, last, stmt2) and + stmt1 != stmt2 and + scope1 != scope2 + ) } /** @@ -167,17 +167,17 @@ predicate duplicateStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt stmt2) * block, respectively. */ private predicate duplicateCoversStatement( - int equivstart, int equivend, int first, int last, Stmt stmt + int equivstart, int equivend, int first, int last, Stmt stmt ) { - exists(DuplicateBlock b1, DuplicateBlock b2, Location startloc, Location endloc | - stmt.getLocation() = startloc and - stmt.getLastStatement().getLocation() = endloc and - first = b1.tokenStartingAt(startloc) and - last = b2.tokenEndingAt(endloc) and - b1.getEquivalenceClass() = equivstart and - b2.getEquivalenceClass() = equivend and - duplicate_extension(b1, _, b2, _, equivstart, equivend) - ) + exists(DuplicateBlock b1, DuplicateBlock b2, Location startloc, Location endloc | + stmt.getLocation() = startloc and + stmt.getLastStatement().getLocation() = endloc and + first = b1.tokenStartingAt(startloc) and + last = b2.tokenEndingAt(endloc) and + b1.getEquivalenceClass() = equivstart and + b2.getEquivalenceClass() = equivend and + duplicate_extension(b1, _, b2, _, equivstart, equivend) + ) } /** @@ -185,23 +185,23 @@ private predicate duplicateCoversStatement( * toplevel that has `duplicate` lines in common with `scope1`. */ predicate duplicateStatements(Scope scope1, Scope scope2, int duplicate, int total) { - duplicate = strictcount(Stmt stmt | duplicateStatement(scope1, scope2, stmt, _)) and - total = strictcount(Stmt stmt | scope1.contains(stmt)) + duplicate = strictcount(Stmt stmt | duplicateStatement(scope1, scope2, stmt, _)) and + total = strictcount(Stmt stmt | scope1.contains(stmt)) } /** * Find pairs of scopes that are identical or almost identical */ predicate duplicateScopes(Scope s, Scope other, float percent, string message) { - exists(int total, int duplicate | duplicateStatements(s, other, duplicate, total) | - percent = 100.0 * duplicate / total and - percent >= 80.0 and - if duplicate = total - then message = "All " + total + " statements in " + s.getName() + " are identical in $@." - else - message = - duplicate + " out of " + total + " statements in " + s.getName() + " are duplicated in $@." - ) + exists(int total, int duplicate | duplicateStatements(s, other, duplicate, total) | + percent = 100.0 * duplicate / total and + percent >= 80.0 and + if duplicate = total + then message = "All " + total + " statements in " + s.getName() + " are identical in $@." + else + message = + duplicate + " out of " + total + " statements in " + s.getName() + " are duplicated in $@." + ) } /** @@ -209,14 +209,14 @@ predicate duplicateScopes(Scope s, Scope other, float percent, string message) { * respectively, where `scope1` and `scope2` are not the same. */ private predicate similarStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt stmt2) { - exists(int start, int end, int first, int last | - scope1.contains(stmt1) and - scope2.contains(stmt2) and - similarCoversStatement(start, end, first, last, stmt1) and - similarCoversStatement(start, end, first, last, stmt2) and - stmt1 != stmt2 and - scope1 != scope2 - ) + exists(int start, int end, int first, int last | + scope1.contains(stmt1) and + scope2.contains(stmt2) and + similarCoversStatement(start, end, first, last, stmt1) and + similarCoversStatement(start, end, first, last, stmt2) and + stmt1 != stmt2 and + scope1 != scope2 + ) } /** @@ -227,17 +227,17 @@ private predicate similarStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt * block, respectively. */ private predicate similarCoversStatement( - int equivstart, int equivend, int first, int last, Stmt stmt + int equivstart, int equivend, int first, int last, Stmt stmt ) { - exists(SimilarBlock b1, SimilarBlock b2, Location startloc, Location endloc | - stmt.getLocation() = startloc and - stmt.getLastStatement().getLocation() = endloc and - first = b1.tokenStartingAt(startloc) and - last = b2.tokenEndingAt(endloc) and - b1.getEquivalenceClass() = equivstart and - b2.getEquivalenceClass() = equivend and - similar_extension(b1, _, b2, _, equivstart, equivend) - ) + exists(SimilarBlock b1, SimilarBlock b2, Location startloc, Location endloc | + stmt.getLocation() = startloc and + stmt.getLastStatement().getLocation() = endloc and + first = b1.tokenStartingAt(startloc) and + last = b2.tokenEndingAt(endloc) and + b1.getEquivalenceClass() = equivstart and + b2.getEquivalenceClass() = equivend and + similar_extension(b1, _, b2, _, equivstart, equivend) + ) } /** @@ -245,23 +245,23 @@ private predicate similarCoversStatement( * toplevel that has `similar` similar lines to `scope1`. */ private predicate similarStatements(Scope scope1, Scope scope2, int similar, int total) { - similar = strictcount(Stmt stmt | similarStatement(scope1, scope2, stmt, _)) and - total = strictcount(Stmt stmt | scope1.contains(stmt)) + similar = strictcount(Stmt stmt | similarStatement(scope1, scope2, stmt, _)) and + total = strictcount(Stmt stmt | scope1.contains(stmt)) } /** * Find pairs of scopes that are similar */ predicate similarScopes(Scope s, Scope other, float percent, string message) { - exists(int total, int similar | similarStatements(s, other, similar, total) | - percent = 100.0 * similar / total and - percent >= 80.0 and - if similar = total - then message = "All statements in " + s.getName() + " are similar in $@." - else - message = - similar + " out of " + total + " statements in " + s.getName() + " are similar in $@." - ) + exists(int total, int similar | similarStatements(s, other, similar, total) | + percent = 100.0 * similar / total and + percent >= 80.0 and + if similar = total + then message = "All statements in " + s.getName() + " are similar in $@." + else + message = + similar + " out of " + total + " statements in " + s.getName() + " are similar in $@." + ) } /** @@ -269,5 +269,5 @@ predicate similarScopes(Scope s, Scope other, float percent, string message) { * This is true for blocks of import statements. */ predicate allowlistedLineForDuplication(File f, int line) { - exists(ImportingStmt i | i.getLocation().getFile() = f and i.getLocation().getStartLine() = line) + exists(ImportingStmt i | i.getLocation().getFile() = f and i.getLocation().getStartLine() = line) } diff --git a/python/ql/src/external/DefectFilter.qll b/python/ql/src/external/DefectFilter.qll index 4c4bdcf779a..62704b9fd0e 100644 --- a/python/ql/src/external/DefectFilter.qll +++ b/python/ql/src/external/DefectFilter.qll @@ -11,71 +11,71 @@ import semmle.python.Files * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). */ external predicate defectResults( - int id, string queryPath, string filepath, int startline, int startcol, int endline, int endcol, - string message + int id, string queryPath, string filepath, int startline, int startcol, int endline, int endcol, + string message ); /** * A defect query result stored in a dashboard database. */ class DefectResult extends int { - DefectResult() { defectResults(this, _, _, _, _, _, _, _) } + DefectResult() { defectResults(this, _, _, _, _, _, _, _) } - /** Gets the path of the query that reported the result. */ - string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) } + /** Gets the path of the query that reported the result. */ + string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) } - /** Gets the file in which this query result was reported. */ - File getFile() { - exists(string path | - defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path - ) - } + /** Gets the file in which this query result was reported. */ + File getFile() { + exists(string path | + defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path + ) + } - /** Gets the file path in which this query result was reported. */ - string getFilePath() { defectResults(this, _, result, _, _, _, _, _) } + /** Gets the file path in which this query result was reported. */ + string getFilePath() { defectResults(this, _, result, _, _, _, _, _) } - /** Gets the line on which the location of this query result starts. */ - int getStartLine() { defectResults(this, _, _, result, _, _, _, _) } + /** Gets the line on which the location of this query result starts. */ + int getStartLine() { defectResults(this, _, _, result, _, _, _, _) } - /** Gets the column on which the location of this query result starts. */ - int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) } + /** Gets the column on which the location of this query result starts. */ + int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) } - /** Gets the line on which the location of this query result ends. */ - int getEndLine() { defectResults(this, _, _, _, _, result, _, _) } + /** Gets the line on which the location of this query result ends. */ + int getEndLine() { defectResults(this, _, _, _, _, result, _, _) } - /** Gets the column on which the location of this query result ends. */ - int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) } + /** Gets the column on which the location of this query result ends. */ + int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) } - /** Gets the message associated with this query result. */ - string getMessage() { defectResults(this, _, _, _, _, _, _, result) } + /** Gets the message associated with this query result. */ + string getMessage() { defectResults(this, _, _, _, _, _, _, result) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - defectResults(this, _, filepath, startline, startcolumn, endline, endcolumn, _) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + defectResults(this, _, filepath, startline, startcolumn, endline, endcolumn, _) + } - /** Gets the URL corresponding to the location of this query result. */ - string getURL() { - result = - "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + - getEndLine() + ":" + getEndColumn() - } + /** Gets the URL corresponding to the location of this query result. */ + string getURL() { + result = + "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + + getEndLine() + ":" + getEndColumn() + } } // crude containment by line number only predicate contains(Location l, DefectResult res) { - exists(string path, int bl1, int el1, int bl2, int el2 | - l.hasLocationInfo(path, bl1, _, el1, _) and - res.hasLocationInfo(path, bl2, _, el2, _) and - bl1 <= bl2 and - el1 >= el2 - ) + exists(string path, int bl1, int el1, int bl2, int el2 | + l.hasLocationInfo(path, bl1, _, el1, _) and + res.hasLocationInfo(path, bl2, _, el2, _) and + bl1 <= bl2 and + el1 >= el2 + ) } diff --git a/python/ql/src/external/DuplicateBlock.ql b/python/ql/src/external/DuplicateBlock.ql index 38aed20739f..90067fa834c 100644 --- a/python/ql/src/external/DuplicateBlock.ql +++ b/python/ql/src/external/DuplicateBlock.ql @@ -19,16 +19,16 @@ import python import CodeDuplication predicate sorted_by_location(DuplicateBlock x, DuplicateBlock y) { - if x.sourceFile() = y.sourceFile() - then x.sourceStartLine() < y.sourceStartLine() - else x.sourceFile().getAbsolutePath() < y.sourceFile().getAbsolutePath() + if x.sourceFile() = y.sourceFile() + then x.sourceStartLine() < y.sourceStartLine() + else x.sourceFile().getAbsolutePath() < y.sourceFile().getAbsolutePath() } from DuplicateBlock d, DuplicateBlock other where - d.sourceLines() > 10 and - other.getEquivalenceClass() = d.getEquivalenceClass() and - sorted_by_location(other, d) + d.sourceLines() > 10 and + other.getEquivalenceClass() = d.getEquivalenceClass() and + sorted_by_location(other, d) select d, - "Duplicate code: " + d.sourceLines() + " lines are duplicated at " + - other.sourceFile().getShortName() + ":" + other.sourceStartLine().toString() + "Duplicate code: " + d.sourceLines() + " lines are duplicated at " + + other.sourceFile().getShortName() + ":" + other.sourceStartLine().toString() diff --git a/python/ql/src/external/DuplicateFunction.ql b/python/ql/src/external/DuplicateFunction.ql index b638f6fb5b2..57a566449d3 100644 --- a/python/ql/src/external/DuplicateFunction.ql +++ b/python/ql/src/external/DuplicateFunction.ql @@ -21,9 +21,9 @@ predicate relevant(Function m) { m.getMetrics().getNumberOfLinesOfCode() > 5 } from Function m, Function other, string message, int percent where - duplicateScopes(m, other, percent, message) and - relevant(m) and - percent > 95.0 and - not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and - not duplicateScopes(m.getScope(), other.getScope(), _, _) + duplicateScopes(m, other, percent, message) and + relevant(m) and + percent > 95.0 and + not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and + not duplicateScopes(m.getScope(), other.getScope(), _, _) select m, message, other, other.getName() diff --git a/python/ql/src/external/ExternalArtifact.qll b/python/ql/src/external/ExternalArtifact.qll index 210152b7687..3aa4095a823 100644 --- a/python/ql/src/external/ExternalArtifact.qll +++ b/python/ql/src/external/ExternalArtifact.qll @@ -5,83 +5,83 @@ import python class ExternalDefect extends @externalDefect { - string getQueryPath() { - exists(string path | - externalDefects(this, path, _, _, _) and - result = path.replaceAll("\\", "/") - ) - } + string getQueryPath() { + exists(string path | + externalDefects(this, path, _, _, _) and + result = path.replaceAll("\\", "/") + ) + } - string getMessage() { externalDefects(this, _, _, result, _) } + string getMessage() { externalDefects(this, _, _, result, _) } - float getSeverity() { externalDefects(this, _, _, _, result) } + float getSeverity() { externalDefects(this, _, _, _, result) } - Location getLocation() { externalDefects(this, _, result, _, _) } + Location getLocation() { externalDefects(this, _, result, _, _) } - /** Gets a textual representation of this element. */ - string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getMessage() } + /** Gets a textual representation of this element. */ + string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getMessage() } } class ExternalMetric extends @externalMetric { - string getQueryPath() { externalMetrics(this, result, _, _) } + string getQueryPath() { externalMetrics(this, result, _, _) } - float getValue() { externalMetrics(this, _, _, result) } + float getValue() { externalMetrics(this, _, _, result) } - Location getLocation() { externalMetrics(this, _, result, _) } + Location getLocation() { externalMetrics(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() } + /** Gets a textual representation of this element. */ + string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() } } /** * An external data item. */ class ExternalData extends @externalDataElement { - /** Gets the path of the file this data was loaded from. */ - string getDataPath() { externalData(this, result, _, _) } + /** Gets the path of the file this data was loaded from. */ + string getDataPath() { externalData(this, result, _, _) } - /** - * Gets the path of the file this data was loaded from, with its - * extension replaced by `.ql`. - */ - string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } + /** + * Gets the path of the file this data was loaded from, with its + * extension replaced by `.ql`. + */ + string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } - /** Gets the number of fields in this data item. */ - int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } + /** Gets the number of fields in this data item. */ + int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } - /** Gets the value of the field at position `index` of this data item. */ - string getField(int index) { externalData(this, _, index, result) } + /** Gets the value of the field at position `index` of this data item. */ + string getField(int index) { externalData(this, _, index, result) } - /** Gets the integer value of the field at position `index` of this data item. */ - int getFieldAsInt(int index) { result = getField(index).toInt() } + /** Gets the integer value of the field at position `index` of this data item. */ + int getFieldAsInt(int index) { result = getField(index).toInt() } - /** Gets the floating-point value of the field at position `index` of this data item. */ - float getFieldAsFloat(int index) { result = getField(index).toFloat() } + /** Gets the floating-point value of the field at position `index` of this data item. */ + float getFieldAsFloat(int index) { result = getField(index).toFloat() } - /** Gets the value of the field at position `index` of this data item, interpreted as a date. */ - date getFieldAsDate(int index) { result = getField(index).toDate() } + /** Gets the value of the field at position `index` of this data item, interpreted as a date. */ + date getFieldAsDate(int index) { result = getField(index).toDate() } - /** Gets a textual representation of this data item. */ - string toString() { result = getQueryPath() + ": " + buildTupleString(0) } + /** Gets a textual representation of this data item. */ + string toString() { result = getQueryPath() + ": " + buildTupleString(0) } - /** Gets a textual representation of this data item, starting with the field at position `start`. */ - private string buildTupleString(int start) { - start = getNumFields() - 1 and result = getField(start) - or - start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1) - } + /** Gets a textual representation of this data item, starting with the field at position `start`. */ + private string buildTupleString(int start) { + start = getNumFields() - 1 and result = getField(start) + or + start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1) + } } /** * External data with a location, and a message, as produced by tools that used to produce QLDs. */ class DefectExternalData extends ExternalData { - DefectExternalData() { - this.getField(0).regexpMatch("\\w+://.*:[0-9]+:[0-9]+:[0-9]+:[0-9]+$") and - this.getNumFields() = 2 - } + DefectExternalData() { + this.getField(0).regexpMatch("\\w+://.*:[0-9]+:[0-9]+:[0-9]+:[0-9]+$") and + this.getNumFields() = 2 + } - string getURL() { result = getField(0) } + string getURL() { result = getField(0) } - string getMessage() { result = getField(1) } + string getMessage() { result = getField(1) } } diff --git a/python/ql/src/external/MostlyDuplicateClass.ql b/python/ql/src/external/MostlyDuplicateClass.ql index 88169ab897f..9cdcd4502f2 100644 --- a/python/ql/src/external/MostlyDuplicateClass.ql +++ b/python/ql/src/external/MostlyDuplicateClass.ql @@ -19,7 +19,7 @@ import CodeDuplication from Class c, Class other, string message where - duplicateScopes(c, other, _, message) and - count(c.getAStmt()) > 3 and - not duplicateScopes(c.getEnclosingModule(), _, _, _) + duplicateScopes(c, other, _, message) and + count(c.getAStmt()) > 3 and + not duplicateScopes(c.getEnclosingModule(), _, _, _) select c, message, other, other.getName() diff --git a/python/ql/src/external/SimilarFunction.ql b/python/ql/src/external/SimilarFunction.ql index bcd63a41dcf..9e8db82dcd4 100644 --- a/python/ql/src/external/SimilarFunction.ql +++ b/python/ql/src/external/SimilarFunction.ql @@ -21,10 +21,10 @@ predicate relevant(Function m) { m.getMetrics().getNumberOfLinesOfCode() > 10 } from Function m, Function other, string message, int percent where - similarScopes(m, other, percent, message) and - relevant(m) and - percent > 95.0 and - not duplicateScopes(m, other, _, _) and - not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and - not duplicateScopes(m.getScope(), other.getScope(), _, _) + similarScopes(m, other, percent, message) and + relevant(m) and + percent > 95.0 and + not duplicateScopes(m, other, _, _) and + not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and + not duplicateScopes(m.getScope(), other.getScope(), _, _) select m, message, other, other.getName() diff --git a/python/ql/src/external/Thrift.qll b/python/ql/src/external/Thrift.qll index efb9ff9f33e..f9f8d67701d 100644 --- a/python/ql/src/external/Thrift.qll +++ b/python/ql/src/external/Thrift.qll @@ -7,197 +7,201 @@ import external.ExternalArtifact /** An item in the parse tree of the IDL file */ class ThriftElement extends ExternalData { - string kind; + string kind; - ThriftElement() { this.getDataPath() = "thrift-" + kind } + ThriftElement() { this.getDataPath() = "thrift-" + kind } - string getKind() { result = kind } + string getKind() { result = kind } - string getId() { result = getField(0) } + string getId() { result = getField(0) } - int getIndex() { result = getFieldAsInt(1) } + int getIndex() { result = getFieldAsInt(1) } - ThriftElement getParent() { result.getId() = this.getField(2) } + ThriftElement getParent() { result.getId() = this.getField(2) } - string getValue() { result = this.getField(3) } + string getValue() { result = this.getField(3) } - ThriftElement getChild(int n) { result.getIndex() = n and result.getParent() = this } + ThriftElement getChild(int n) { result.getIndex() = n and result.getParent() = this } - ThriftElement getAChild() { result = this.getChild(_) } + ThriftElement getAChild() { result = this.getChild(_) } - override string toString() { result = this.getKind() } + override string toString() { result = this.getKind() } - string getPath() { result = this.getField(4) } + string getPath() { result = this.getField(4) } - private int line() { result = this.getFieldAsInt(5) } + private int line() { result = this.getFieldAsInt(5) } - private int column() { result = this.getFieldAsInt(6) } + private int column() { result = this.getFieldAsInt(6) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = this.getPath() and - startline = this.line() and - startcolumn = this.column() and - endline = this.line() and - endcolumn = this.column() + this.getValue().length() - 1 - or - exists(ThriftElement first, ThriftElement last | - first = this.getChild(min(int l | exists(this.getChild(l)))) and - last = this.getChild(max(int l | exists(this.getChild(l)))) and - first.hasLocationInfo(filepath, startline, startcolumn, _, _) and - last.hasLocationInfo(filepath, _, _, endline, endcolumn) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = this.getPath() and + startline = this.line() and + startcolumn = this.column() and + endline = this.line() and + endcolumn = this.column() + this.getValue().length() - 1 + or + exists(ThriftElement first, ThriftElement last | + first = this.getChild(min(int l | exists(this.getChild(l)))) and + last = this.getChild(max(int l | exists(this.getChild(l)))) and + first.hasLocationInfo(filepath, startline, startcolumn, _, _) and + last.hasLocationInfo(filepath, _, _, endline, endcolumn) + ) + } - File getFile() { this.hasLocationInfo(result.getAbsolutePath(), _, _, _, _) } + File getFile() { this.hasLocationInfo(result.getAbsolutePath(), _, _, _, _) } } abstract class ThriftNamedElement extends ThriftElement { - abstract ThriftElement getNameElement(); + abstract ThriftElement getNameElement(); - final string getName() { result = this.getNameElement().getValue() } + final string getName() { result = this.getNameElement().getValue() } - override string toString() { - result = this.getKind() + " " + this.getName() - or - not exists(this.getName()) and result = this.getKind() + " ???" - } + override string toString() { + result = this.getKind() + " " + this.getName() + or + not exists(this.getName()) and result = this.getKind() + " ???" + } - override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { - exists(ThriftElement first | - first = this.getChild(min(int l | exists(this.getChild(l)))) and - first.hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getNameElement().hasLocationInfo(filepath, _, _, endline, endcolumn) - ) - } + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(ThriftElement first | + first = this.getChild(min(int l | exists(this.getChild(l)))) and + first.hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getNameElement().hasLocationInfo(filepath, _, _, endline, endcolumn) + ) + } } class ThriftType extends ThriftNamedElement { - ThriftType() { kind.matches("%type") } + ThriftType() { kind.matches("%type") } - override ThriftElement getNameElement() { - result = this.getChild(0) - or - result = this.getChild(0).(ThriftType).getNameElement() - } + override ThriftElement getNameElement() { + result = this.getChild(0) + or + result = this.getChild(0).(ThriftType).getNameElement() + } - override string toString() { result = "type " + this.getName() } + override string toString() { result = "type " + this.getName() } - predicate references(ThriftStruct struct) { - this.getName() = struct.getName() and - exists(string path | - this.hasLocationInfo(path, _, _, _, _) and - struct.hasLocationInfo(path, _, _, _, _) - ) - } + predicate references(ThriftStruct struct) { + this.getName() = struct.getName() and + exists(string path | + this.hasLocationInfo(path, _, _, _, _) and + struct.hasLocationInfo(path, _, _, _, _) + ) + } } /** A thrift typedef */ class ThriftTypeDef extends ThriftNamedElement { - ThriftTypeDef() { kind.matches("typedef") } + ThriftTypeDef() { kind.matches("typedef") } - override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } } /** A thrift enum declaration */ class ThriftEnum extends ThriftNamedElement { - ThriftEnum() { kind.matches("enum") } + ThriftEnum() { kind.matches("enum") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } } /** A thrift enum field */ class ThriftEnumField extends ThriftNamedElement { - ThriftEnumField() { kind.matches("enumfield") } + ThriftEnumField() { kind.matches("enumfield") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } } /** A thrift service declaration */ class ThriftService extends ThriftNamedElement { - ThriftService() { kind.matches("service") } + ThriftService() { kind.matches("service") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } - ThriftFunction getAFunction() { result = this.getChild(_) } + ThriftFunction getAFunction() { result = this.getChild(_) } - ThriftFunction getFunction(string name) { - result.getName() = name and - result = this.getAFunction() - } + ThriftFunction getFunction(string name) { + result.getName() = name and + result = this.getAFunction() + } } /** A thrift function declaration */ class ThriftFunction extends ThriftNamedElement { - ThriftFunction() { kind.matches("function") } + ThriftFunction() { kind.matches("function") } - override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(2).getChild(0) } - ThriftField getArgument(int n) { result = this.getChild(n + 3) } + ThriftField getArgument(int n) { result = this.getChild(n + 3) } - ThriftField getAnArgument() { result = this.getArgument(_) } + ThriftField getAnArgument() { result = this.getArgument(_) } - private ThriftThrows getAllThrows() { result = this.getChild(_) } + private ThriftThrows getAllThrows() { result = this.getChild(_) } - ThriftField getAThrows() { result = this.getAllThrows().getAChild() } + ThriftField getAThrows() { result = this.getAllThrows().getAChild() } - ThriftType getReturnType() { result = this.getChild(1).getChild(0) } + ThriftType getReturnType() { result = this.getChild(1).getChild(0) } - override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { - this.getChild(1).hasLocationInfo(filepath, startline, startcolumn, _, _) and - this.getChild(2).hasLocationInfo(filepath, _, _, endline, endcolumn) - } + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getChild(1).hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.getChild(2).hasLocationInfo(filepath, _, _, endline, endcolumn) + } - ThriftService getService() { result.getAFunction() = this } + ThriftService getService() { result.getAFunction() = this } - string getQualifiedName() { result = this.getService().getName() + "." + this.getName() } + string getQualifiedName() { result = this.getService().getName() + "." + this.getName() } } class ThriftField extends ThriftNamedElement { - ThriftField() { kind.matches("field") } + ThriftField() { kind.matches("field") } - override ThriftElement getNameElement() { result = this.getChild(4) } + override ThriftElement getNameElement() { result = this.getChild(4) } - ThriftType getType() { result = this.getChild(2) } + ThriftType getType() { result = this.getChild(2) } } class ThriftStruct extends ThriftNamedElement { - ThriftStruct() { kind.matches("struct") } + ThriftStruct() { kind.matches("struct") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } - ThriftField getMember(int n) { result = this.getChild(n + 1) } + ThriftField getMember(int n) { result = this.getChild(n + 1) } - ThriftField getAMember() { result = this.getMember(_) } + ThriftField getAMember() { result = this.getMember(_) } } class ThriftException extends ThriftNamedElement { - ThriftException() { kind.matches("exception") } + ThriftException() { kind.matches("exception") } - override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } + override ThriftElement getNameElement() { result = this.getChild(0).getChild(0) } - ThriftField getMember(int n) { result = this.getChild(n + 1) } + ThriftField getMember(int n) { result = this.getChild(n + 1) } - ThriftField getAMember() { result = this.getMember(_) } + ThriftField getAMember() { result = this.getMember(_) } } class ThriftThrows extends ThriftElement { - ThriftThrows() { kind.matches("throws") } + ThriftThrows() { kind.matches("throws") } - ThriftField getAThrows() { result = this.getChild(_) } + ThriftField getAThrows() { result = this.getChild(_) } } /** A parse tree element that holds a primitive value */ class ThriftValue extends ThriftElement { - ThriftValue() { exists(this.getValue()) } + ThriftValue() { exists(this.getValue()) } - override string toString() { result = this.getKind() + " " + this.getValue() } + override string toString() { result = this.getKind() + " " + this.getValue() } } diff --git a/python/ql/src/external/VCS.qll b/python/ql/src/external/VCS.qll index c7d7af334c9..068ef881e4a 100644 --- a/python/ql/src/external/VCS.qll +++ b/python/ql/src/external/VCS.qll @@ -1,77 +1,79 @@ import python class Commit extends @svnentry { - Commit() { - svnaffectedfiles(this, _, _) and - exists(date svnDate, date snapshotDate | - svnentries(this, _, _, svnDate, _) and - snapshotDate(snapshotDate) and - svnDate <= snapshotDate - ) - } + Commit() { + svnaffectedfiles(this, _, _) and + exists(date svnDate, date snapshotDate | + svnentries(this, _, _, svnDate, _) and + snapshotDate(snapshotDate) and + svnDate <= snapshotDate + ) + } - /** Gets a textual representation of this element. */ - string toString() { result = this.getRevisionName() } + /** Gets a textual representation of this element. */ + string toString() { result = this.getRevisionName() } - string getRevisionName() { svnentries(this, result, _, _, _) } + string getRevisionName() { svnentries(this, result, _, _, _) } - string getAuthor() { svnentries(this, _, result, _, _) } + string getAuthor() { svnentries(this, _, result, _, _) } - date getDate() { svnentries(this, _, _, result, _) } + date getDate() { svnentries(this, _, _, result, _) } - int getChangeSize() { svnentries(this, _, _, _, result) } + int getChangeSize() { svnentries(this, _, _, _, result) } - string getMessage() { svnentrymsg(this, result) } + string getMessage() { svnentrymsg(this, result) } - string getAnAffectedFilePath(string action) { - exists(File rawFile | svnaffectedfiles(this, rawFile, action) | result = rawFile.getAbsolutePath()) - } + string getAnAffectedFilePath(string action) { + exists(File rawFile | svnaffectedfiles(this, rawFile, action) | + result = rawFile.getAbsolutePath() + ) + } - string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) } + string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) } - File getAnAffectedFile(string action) { svnaffectedfiles(this, result, action) } + File getAnAffectedFile(string action) { svnaffectedfiles(this, result, action) } - File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) } + File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) } - predicate isRecent() { recentCommit(this) } + predicate isRecent() { recentCommit(this) } - int daysToNow() { - exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0) - } + int daysToNow() { + exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0) + } - int getRecentAdditionsForFile(File f) { svnchurn(this, f, result, _) } + int getRecentAdditionsForFile(File f) { svnchurn(this, f, result, _) } - int getRecentDeletionsForFile(File f) { svnchurn(this, f, _, result) } + int getRecentDeletionsForFile(File f) { svnchurn(this, f, _, result) } - int getRecentChurnForFile(File f) { - result = getRecentAdditionsForFile(f) + getRecentDeletionsForFile(f) - } + int getRecentChurnForFile(File f) { + result = getRecentAdditionsForFile(f) + getRecentDeletionsForFile(f) + } } class Author extends string { - Author() { exists(Commit e | this = e.getAuthor()) } + Author() { exists(Commit e | this = e.getAuthor()) } - Commit getACommit() { result.getAuthor() = this } + Commit getACommit() { result.getAuthor() = this } - File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() } + File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() } } predicate recentCommit(Commit e) { - exists(date snapshotDate, date commitDate, int days | - snapshotDate(snapshotDate) and - e.getDate() = commitDate and - days = commitDate.daysTo(snapshotDate) and - days >= 0 and - days <= 60 - ) + exists(date snapshotDate, date commitDate, int days | + snapshotDate(snapshotDate) and + e.getDate() = commitDate and + days = commitDate.daysTo(snapshotDate) and + days >= 0 and + days <= 60 + ) } date firstChange(File f) { - result = min(Commit e, date toMin | f = e.getAnAffectedFile() and toMin = e.getDate() | toMin) + result = min(Commit e, date toMin | f = e.getAnAffectedFile() and toMin = e.getDate() | toMin) } predicate firstCommit(Commit e) { - not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate()) + not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate()) } predicate artificialChange(Commit e) { firstCommit(e) or e.getChangeSize() >= 50000 } diff --git a/python/ql/src/semmle/crypto/Crypto.qll b/python/ql/src/semmle/crypto/Crypto.qll index 4eaa3eb8dbd..ff13b559198 100644 --- a/python/ql/src/semmle/crypto/Crypto.qll +++ b/python/ql/src/semmle/crypto/Crypto.qll @@ -15,80 +15,80 @@ * The names are inspired by the names used in real world crypto libraries. */ private module AlgorithmNames { - predicate isStrongHashingAlgorithm(string name) { - name = "DSA" or - name = "ED25519" or - name = "ES256" or - name = "ECDSA256" or - name = "ES384" or - name = "ECDSA384" or - name = "ES512" or - name = "ECDSA512" or - name = "SHA2" or - name = "SHA224" or - name = "SHA256" or - name = "SHA384" or - name = "SHA512" or - name = "SHA3" - } + predicate isStrongHashingAlgorithm(string name) { + name = "DSA" or + name = "ED25519" or + name = "ES256" or + name = "ECDSA256" or + name = "ES384" or + name = "ECDSA384" or + name = "ES512" or + name = "ECDSA512" or + name = "SHA2" or + name = "SHA224" or + name = "SHA256" or + name = "SHA384" or + name = "SHA512" or + name = "SHA3" + } - predicate isWeakHashingAlgorithm(string name) { - name = "HAVEL128" or - name = "MD2" or - name = "MD4" or - name = "MD5" or - name = "PANAMA" or - name = "RIPEMD" or - name = "RIPEMD128" or - name = "RIPEMD256" or - name = "RIPEMD160" or - name = "RIPEMD320" or - name = "SHA0" or - name = "SHA1" - } + predicate isWeakHashingAlgorithm(string name) { + name = "HAVEL128" or + name = "MD2" or + name = "MD4" or + name = "MD5" or + name = "PANAMA" or + name = "RIPEMD" or + name = "RIPEMD128" or + name = "RIPEMD256" or + name = "RIPEMD160" or + name = "RIPEMD320" or + name = "SHA0" or + name = "SHA1" + } - predicate isStrongEncryptionAlgorithm(string name) { - name = "AES" or - name = "AES128" or - name = "AES192" or - name = "AES256" or - name = "AES512" or - name = "RSA" or - name = "RABBIT" or - name = "BLOWFISH" - } + predicate isStrongEncryptionAlgorithm(string name) { + name = "AES" or + name = "AES128" or + name = "AES192" or + name = "AES256" or + name = "AES512" or + name = "RSA" or + name = "RABBIT" or + name = "BLOWFISH" + } - predicate isWeakEncryptionAlgorithm(string name) { - name = "DES" or - name = "3DES" or - name = "TRIPLEDES" or - name = "TDEA" or - name = "TRIPLEDEA" or - name = "ARC2" or - name = "RC2" or - name = "ARC4" or - name = "RC4" or - name = "ARCFOUR" or - name = "ARC5" or - name = "RC5" - } + predicate isWeakEncryptionAlgorithm(string name) { + name = "DES" or + name = "3DES" or + name = "TRIPLEDES" or + name = "TDEA" or + name = "TRIPLEDEA" or + name = "ARC2" or + name = "RC2" or + name = "ARC4" or + name = "RC4" or + name = "ARCFOUR" or + name = "ARC5" or + name = "RC5" + } - predicate isStrongPasswordHashingAlgorithm(string name) { - name = "ARGON2" or - name = "PBKDF2" or - name = "BCRYPT" or - name = "SCRYPT" - } + predicate isStrongPasswordHashingAlgorithm(string name) { + name = "ARGON2" or + name = "PBKDF2" or + name = "BCRYPT" or + name = "SCRYPT" + } - predicate isWeakPasswordHashingAlgorithm(string name) { none() } + predicate isWeakPasswordHashingAlgorithm(string name) { none() } - /** - * Normalizes `name`: upper-case, no spaces, dashes or underscores. - * - * All names of this module are in this normalized form. - */ - bindingset[name] - string normalizeName(string name) { result = name.toUpperCase().regexpReplaceAll("[-_ ]", "") } + /** + * Normalizes `name`: upper-case, no spaces, dashes or underscores. + * + * All names of this module are in this normalized form. + */ + bindingset[name] + string normalizeName(string name) { result = name.toUpperCase().regexpReplaceAll("[-_ ]", "") } } private import AlgorithmNames @@ -97,78 +97,78 @@ private import AlgorithmNames * A cryptographic algorithm. */ private newtype TCryptographicAlgorithm = - MkHashingAlgorithm(string name, boolean isWeak) { - isStrongHashingAlgorithm(name) and isWeak = false - or - isWeakHashingAlgorithm(name) and isWeak = true - } or - MkEncryptionAlgorithm(string name, boolean isWeak) { - isStrongEncryptionAlgorithm(name) and isWeak = false - or - isWeakEncryptionAlgorithm(name) and isWeak = true - } or - MkPasswordHashingAlgorithm(string name, boolean isWeak) { - isStrongPasswordHashingAlgorithm(name) and isWeak = false - or - isWeakPasswordHashingAlgorithm(name) and isWeak = true - } + MkHashingAlgorithm(string name, boolean isWeak) { + isStrongHashingAlgorithm(name) and isWeak = false + or + isWeakHashingAlgorithm(name) and isWeak = true + } or + MkEncryptionAlgorithm(string name, boolean isWeak) { + isStrongEncryptionAlgorithm(name) and isWeak = false + or + isWeakEncryptionAlgorithm(name) and isWeak = true + } or + MkPasswordHashingAlgorithm(string name, boolean isWeak) { + isStrongPasswordHashingAlgorithm(name) and isWeak = false + or + isWeakPasswordHashingAlgorithm(name) and isWeak = true + } /** * A cryptographic algorithm. */ abstract class CryptographicAlgorithm extends TCryptographicAlgorithm { - /** Gets a textual representation of this element. */ - string toString() { result = getName() } + /** Gets a textual representation of this element. */ + string toString() { result = getName() } - /** - * Gets the name of the algorithm. - */ - abstract string getName(); + /** + * Gets the name of the algorithm. + */ + abstract string getName(); - /** - * Holds if this algorithm is weak. - */ - abstract predicate isWeak(); + /** + * Holds if this algorithm is weak. + */ + abstract predicate isWeak(); } /** * A hashing algorithm such as `MD5` or `SHA512`. */ class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; + string name; + boolean isWeak; - HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) } + HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) } - override string getName() { result = name } + override string getName() { result = name } - override predicate isWeak() { isWeak = true } + override predicate isWeak() { isWeak = true } } /** * An encryption algorithm such as `DES` or `AES512`. */ class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; + string name; + boolean isWeak; - EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) } + EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) } - override string getName() { result = name } + override string getName() { result = name } - override predicate isWeak() { isWeak = true } + override predicate isWeak() { isWeak = true } } /** * A password hashing algorithm such as `PBKDF2` or `SCRYPT`. */ class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; + string name; + boolean isWeak; - PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) } + PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) } - override string getName() { result = name } + override string getName() { result = name } - override predicate isWeak() { isWeak = true } + override predicate isWeak() { isWeak = true } } diff --git a/python/ql/src/semmle/python/AstExtended.qll b/python/ql/src/semmle/python/AstExtended.qll index 8a858c5fefc..7767050f40f 100644 --- a/python/ql/src/semmle/python/AstExtended.qll +++ b/python/ql/src/semmle/python/AstExtended.qll @@ -2,59 +2,59 @@ import python /** Syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */ abstract class AstNode extends AstNode_ { - /* - * Special comment for documentation generation. - * All subclasses of `AstNode` that represent concrete syntax should have - * a comment of the form: - */ + /* + * Special comment for documentation generation. + * All subclasses of `AstNode` that represent concrete syntax should have + * a comment of the form: + */ - /* syntax: ... */ - /** Gets the scope that this node occurs in */ - abstract Scope getScope(); + /* syntax: ... */ + /** Gets the scope that this node occurs in */ + abstract Scope getScope(); - /** - * Gets a flow node corresponding directly to this node. - * NOTE: For some statements and other purely syntactic elements, - * there may not be a `ControlFlowNode` - */ - ControlFlowNode getAFlowNode() { py_flow_bb_node(result, this, _, _) } + /** + * Gets a flow node corresponding directly to this node. + * NOTE: For some statements and other purely syntactic elements, + * there may not be a `ControlFlowNode` + */ + ControlFlowNode getAFlowNode() { py_flow_bb_node(result, this, _, _) } - /** Gets the location for this AST node */ - Location getLocation() { none() } + /** Gets the location for this AST node */ + Location getLocation() { none() } - /** - * Whether this syntactic element is artificial, that is it is generated - * by the compiler and is not present in the source - */ - predicate isArtificial() { none() } + /** + * Whether this syntactic element is artificial, that is it is generated + * by the compiler and is not present in the source + */ + predicate isArtificial() { none() } - /** - * Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST - * and other experiments. The child-parent relation may not be meaningful. - * For a more meaningful relation in terms of dependency use - * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or - * Scope.getAStmt(). - */ - abstract AstNode getAChildNode(); + /** + * Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST + * and other experiments. The child-parent relation may not be meaningful. + * For a more meaningful relation in terms of dependency use + * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or + * Scope.getAStmt(). + */ + abstract AstNode getAChildNode(); - /** - * Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST - * and other experiments. The child-parent relation may not be meaningful. - * For a more meaningful relation in terms of dependency use - * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or - * Scope.getAStmt() applied to the parent. - */ - AstNode getParentNode() { result.getAChildNode() = this } + /** + * Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST + * and other experiments. The child-parent relation may not be meaningful. + * For a more meaningful relation in terms of dependency use + * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or + * Scope.getAStmt() applied to the parent. + */ + AstNode getParentNode() { result.getAChildNode() = this } - /** Whether this contains `inner` syntactically */ - predicate contains(AstNode inner) { this.getAChildNode+() = inner } + /** Whether this contains `inner` syntactically */ + predicate contains(AstNode inner) { this.getAChildNode+() = inner } - /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ - predicate containsInScope(AstNode inner) { - this.contains(inner) and - this.getScope() = inner.getScope() and - not inner instanceof Scope - } + /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ + predicate containsInScope(AstNode inner) { + this.contains(inner) and + this.getScope() = inner.getScope() and + not inner instanceof Scope + } } /* Parents */ @@ -80,32 +80,32 @@ library class StrListParent extends StrListParent_ { } library class ExprParent extends ExprParent_ { } library class DictItem extends DictItem_, AstNode { - override string toString() { result = DictItem_.super.toString() } + override string toString() { result = DictItem_.super.toString() } - override AstNode getAChildNode() { none() } + override AstNode getAChildNode() { none() } - override Scope getScope() { none() } + override Scope getScope() { none() } } /** A comprehension part, the 'for a in seq' part of [ a * a for a in seq ] */ class Comprehension extends Comprehension_, AstNode { - /** Gets the scope of this comprehension */ - override Scope getScope() { - /* Comprehensions exists only in Python 2 list comprehensions, so their scope is that of the list comp. */ - exists(ListComp l | this = l.getAGenerator() | result = l.getScope()) - } + /** Gets the scope of this comprehension */ + override Scope getScope() { + /* Comprehensions exists only in Python 2 list comprehensions, so their scope is that of the list comp. */ + exists(ListComp l | this = l.getAGenerator() | result = l.getScope()) + } - override string toString() { result = "Comprehension" } + override string toString() { result = "Comprehension" } - override Location getLocation() { result = Comprehension_.super.getLocation() } + override Location getLocation() { result = Comprehension_.super.getLocation() } - override AstNode getAChildNode() { result = this.getASubExpression() } + override AstNode getAChildNode() { result = this.getASubExpression() } - Expr getASubExpression() { - result = this.getIter() or - result = this.getAnIf() or - result = this.getTarget() - } + Expr getASubExpression() { + result = this.getIter() or + result = this.getAnIf() or + result = this.getTarget() + } } class BytesOrStr extends BytesOrStr_ { } @@ -116,17 +116,17 @@ class BytesOrStr extends BytesOrStr_ { } * would be composed of three `StringPart`s. */ class StringPart extends StringPart_, AstNode { - override Scope getScope() { - exists(Bytes b | this = b.getAnImplicitlyConcatenatedPart() | result = b.getScope()) - or - exists(Unicode u | this = u.getAnImplicitlyConcatenatedPart() | result = u.getScope()) - } + override Scope getScope() { + exists(Bytes b | this = b.getAnImplicitlyConcatenatedPart() | result = b.getScope()) + or + exists(Unicode u | this = u.getAnImplicitlyConcatenatedPart() | result = u.getScope()) + } - override AstNode getAChildNode() { none() } + override AstNode getAChildNode() { none() } - override string toString() { result = StringPart_.super.toString() } + override string toString() { result = StringPart_.super.toString() } - override Location getLocation() { result = StringPart_.super.getLocation() } + override Location getLocation() { result = StringPart_.super.getLocation() } } class StringPartList extends StringPartList_ { } @@ -134,21 +134,21 @@ class StringPartList extends StringPartList_ { } /* **** Lists ***/ /** A parameter list */ class ParameterList extends @py_parameter_list { - Function getParent() { py_parameter_lists(this, result) } + Function getParent() { py_parameter_lists(this, result) } - /** Gets a parameter */ - Parameter getAnItem() { - /* Item can be a Name or a Tuple, both of which are expressions */ - py_exprs(result, _, this, _) - } + /** Gets a parameter */ + Parameter getAnItem() { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, _) + } - /** Gets the nth parameter */ - Parameter getItem(int index) { - /* Item can be a Name or a Tuple, both of which are expressions */ - py_exprs(result, _, this, index) - } + /** Gets the nth parameter */ + Parameter getItem(int index) { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, index) + } - string toString() { result = "ParameterList" } + string toString() { result = "ParameterList" } } /** A list of Comprehensions (for generating parts of a set, list or dictionary comprehension) */ @@ -156,7 +156,7 @@ class ComprehensionList extends ComprehensionList_ { } /** A list of expressions */ class ExprList extends ExprList_ { - /* syntax: Expr, ... */ + /* syntax: Expr, ... */ } library class DictItemList extends DictItemList_ { } diff --git a/python/ql/src/semmle/python/AstGenerated.qll b/python/ql/src/semmle/python/AstGenerated.qll index 6c1802f58c2..146fea4d18e 100644 --- a/python/ql/src/semmle/python/AstGenerated.qll +++ b/python/ql/src/semmle/python/AstGenerated.qll @@ -8,1639 +8,1639 @@ import python /** INTERNAL: See the class `Add` for further information. */ library class Add_ extends @py_Add, Operator { - override string toString() { result = "Add" } + override string toString() { result = "Add" } } /** INTERNAL: See the class `And` for further information. */ library class And_ extends @py_And, Boolop { - override string toString() { result = "And" } + override string toString() { result = "And" } } /** INTERNAL: See the class `AnnAssign` for further information. */ library class AnnAssign_ extends @py_AnnAssign, Stmt { - /** Gets the value of this annotated assignment. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this annotated assignment. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the annotation of this annotated assignment. */ - Expr getAnnotation() { py_exprs(result, _, this, 2) } + /** Gets the annotation of this annotated assignment. */ + Expr getAnnotation() { py_exprs(result, _, this, 2) } - /** Gets the target of this annotated assignment. */ - Expr getTarget() { py_exprs(result, _, this, 3) } + /** Gets the target of this annotated assignment. */ + Expr getTarget() { py_exprs(result, _, this, 3) } - override string toString() { result = "AnnAssign" } + override string toString() { result = "AnnAssign" } } /** INTERNAL: See the class `Assert` for further information. */ library class Assert_ extends @py_Assert, Stmt { - /** Gets the value being tested of this assert statement. */ - Expr getTest() { py_exprs(result, _, this, 1) } + /** Gets the value being tested of this assert statement. */ + Expr getTest() { py_exprs(result, _, this, 1) } - /** Gets the failure message of this assert statement. */ - Expr getMsg() { py_exprs(result, _, this, 2) } + /** Gets the failure message of this assert statement. */ + Expr getMsg() { py_exprs(result, _, this, 2) } - override string toString() { result = "Assert" } + override string toString() { result = "Assert" } } /** INTERNAL: See the class `Assign` for further information. */ library class Assign_ extends @py_Assign, Stmt { - /** Gets the value of this assignment statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this assignment statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the targets of this assignment statement. */ - ExprList getTargets() { py_expr_lists(result, this, 2) } + /** Gets the targets of this assignment statement. */ + ExprList getTargets() { py_expr_lists(result, this, 2) } - /** Gets the nth target of this assignment statement. */ - Expr getTarget(int index) { result = this.getTargets().getItem(index) } + /** Gets the nth target of this assignment statement. */ + Expr getTarget(int index) { result = this.getTargets().getItem(index) } - /** Gets a target of this assignment statement. */ - Expr getATarget() { result = this.getTargets().getAnItem() } + /** Gets a target of this assignment statement. */ + Expr getATarget() { result = this.getTargets().getAnItem() } - override string toString() { result = "Assign" } + override string toString() { result = "Assign" } } /** INTERNAL: See the class `AssignExpr` for further information. */ library class AssignExpr_ extends @py_AssignExpr, Expr { - /** Gets the value of this assignment expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this assignment expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the target of this assignment expression. */ - Expr getTarget() { py_exprs(result, _, this, 3) } + /** Gets the target of this assignment expression. */ + Expr getTarget() { py_exprs(result, _, this, 3) } - override string toString() { result = "AssignExpr" } + override string toString() { result = "AssignExpr" } } /** INTERNAL: See the class `Attribute` for further information. */ library class Attribute_ extends @py_Attribute, Expr { - /** Gets the object of this attribute expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the object of this attribute expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the attribute name of this attribute expression. */ - string getAttr() { py_strs(result, this, 3) } + /** Gets the attribute name of this attribute expression. */ + string getAttr() { py_strs(result, this, 3) } - /** Gets the context of this attribute expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this attribute expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "Attribute" } + override string toString() { result = "Attribute" } } /** INTERNAL: See the class `AugAssign` for further information. */ library class AugAssign_ extends @py_AugAssign, Stmt { - /** Gets the operation of this augmented assignment statement. */ - BinaryExpr getOperation() { py_exprs(result, _, this, 1) } + /** Gets the operation of this augmented assignment statement. */ + BinaryExpr getOperation() { py_exprs(result, _, this, 1) } - override string toString() { result = "AugAssign" } + override string toString() { result = "AugAssign" } } /** INTERNAL: See the class `AugLoad` for further information. */ library class AugLoad_ extends @py_AugLoad, ExprContext { - override string toString() { result = "AugLoad" } + override string toString() { result = "AugLoad" } } /** INTERNAL: See the class `AugStore` for further information. */ library class AugStore_ extends @py_AugStore, ExprContext { - override string toString() { result = "AugStore" } + override string toString() { result = "AugStore" } } /** INTERNAL: See the class `Await` for further information. */ library class Await_ extends @py_Await, Expr { - /** Gets the expression waited upon of this await expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the expression waited upon of this await expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "Await" } + override string toString() { result = "Await" } } /** INTERNAL: See the class `BinaryExpr` for further information. */ library class BinaryExpr_ extends @py_BinaryExpr, Expr { - /** Gets the left sub-expression of this binary expression. */ - Expr getLeft() { py_exprs(result, _, this, 2) } + /** Gets the left sub-expression of this binary expression. */ + Expr getLeft() { py_exprs(result, _, this, 2) } - /** Gets the operator of this binary expression. */ - Operator getOp() { py_operators(result, _, this) } + /** Gets the operator of this binary expression. */ + Operator getOp() { py_operators(result, _, this) } - /** Gets the right sub-expression of this binary expression. */ - Expr getRight() { py_exprs(result, _, this, 4) } + /** Gets the right sub-expression of this binary expression. */ + Expr getRight() { py_exprs(result, _, this, 4) } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "BinaryExpr" } + override string toString() { result = "BinaryExpr" } } /** INTERNAL: See the class `BitAnd` for further information. */ library class BitAnd_ extends @py_BitAnd, Operator { - override string toString() { result = "BitAnd" } + override string toString() { result = "BitAnd" } } /** INTERNAL: See the class `BitOr` for further information. */ library class BitOr_ extends @py_BitOr, Operator { - override string toString() { result = "BitOr" } + override string toString() { result = "BitOr" } } /** INTERNAL: See the class `BitXor` for further information. */ library class BitXor_ extends @py_BitXor, Operator { - override string toString() { result = "BitXor" } + override string toString() { result = "BitXor" } } /** INTERNAL: See the class `BoolExpr` for further information. */ library class BoolExpr_ extends @py_BoolExpr, Expr { - /** Gets the operator of this boolean expression. */ - Boolop getOp() { py_boolops(result, _, this) } + /** Gets the operator of this boolean expression. */ + Boolop getOp() { py_boolops(result, _, this) } - /** Gets the sub-expressions of this boolean expression. */ - ExprList getValues() { py_expr_lists(result, this, 3) } + /** Gets the sub-expressions of this boolean expression. */ + ExprList getValues() { py_expr_lists(result, this, 3) } - /** Gets the nth sub-expression of this boolean expression. */ - Expr getValue(int index) { result = this.getValues().getItem(index) } + /** Gets the nth sub-expression of this boolean expression. */ + Expr getValue(int index) { result = this.getValues().getItem(index) } - /** Gets a sub-expression of this boolean expression. */ - Expr getAValue() { result = this.getValues().getAnItem() } + /** Gets a sub-expression of this boolean expression. */ + Expr getAValue() { result = this.getValues().getAnItem() } - override string toString() { result = "BoolExpr" } + override string toString() { result = "BoolExpr" } } /** INTERNAL: See the class `Break` for further information. */ library class Break_ extends @py_Break, Stmt { - override string toString() { result = "Break" } + override string toString() { result = "Break" } } /** INTERNAL: See the class `Bytes` for further information. */ library class Bytes_ extends @py_Bytes, Expr { - /** Gets the value of this bytes expression. */ - string getS() { py_bytes(result, this, 2) } + /** Gets the value of this bytes expression. */ + string getS() { py_bytes(result, this, 2) } - /** Gets the prefix of this bytes expression. */ - string getPrefix() { py_bytes(result, this, 3) } + /** Gets the prefix of this bytes expression. */ + string getPrefix() { py_bytes(result, this, 3) } - /** Gets the implicitly_concatenated_parts of this bytes expression. */ - StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } + /** Gets the implicitly_concatenated_parts of this bytes expression. */ + StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } - /** Gets the nth implicitly_concatenated_part of this bytes expression. */ - StringPart getImplicitlyConcatenatedPart(int index) { - result = this.getImplicitlyConcatenatedParts().getItem(index) - } + /** Gets the nth implicitly_concatenated_part of this bytes expression. */ + StringPart getImplicitlyConcatenatedPart(int index) { + result = this.getImplicitlyConcatenatedParts().getItem(index) + } - /** Gets an implicitly_concatenated_part of this bytes expression. */ - StringPart getAnImplicitlyConcatenatedPart() { - result = this.getImplicitlyConcatenatedParts().getAnItem() - } + /** Gets an implicitly_concatenated_part of this bytes expression. */ + StringPart getAnImplicitlyConcatenatedPart() { + result = this.getImplicitlyConcatenatedParts().getAnItem() + } - override string toString() { result = "Bytes" } + override string toString() { result = "Bytes" } } /** INTERNAL: See the class `BytesOrStr` for further information. */ library class BytesOrStr_ extends @py_Bytes_or_Str { - /** Gets a textual representation of this element. */ - string toString() { result = "BytesOrStr" } + /** Gets a textual representation of this element. */ + string toString() { result = "BytesOrStr" } } /** INTERNAL: See the class `Call` for further information. */ library class Call_ extends @py_Call, Expr { - /** Gets the callable of this call expression. */ - Expr getFunc() { py_exprs(result, _, this, 2) } + /** Gets the callable of this call expression. */ + Expr getFunc() { py_exprs(result, _, this, 2) } - /** Gets the positional arguments of this call expression. */ - ExprList getPositionalArgs() { py_expr_lists(result, this, 3) } + /** Gets the positional arguments of this call expression. */ + ExprList getPositionalArgs() { py_expr_lists(result, this, 3) } - /** Gets the nth positional argument of this call expression. */ - Expr getPositionalArg(int index) { result = this.getPositionalArgs().getItem(index) } + /** Gets the nth positional argument of this call expression. */ + Expr getPositionalArg(int index) { result = this.getPositionalArgs().getItem(index) } - /** Gets a positional argument of this call expression. */ - Expr getAPositionalArg() { result = this.getPositionalArgs().getAnItem() } + /** Gets a positional argument of this call expression. */ + Expr getAPositionalArg() { result = this.getPositionalArgs().getAnItem() } - /** Gets the named arguments of this call expression. */ - DictItemList getNamedArgs() { py_dict_item_lists(result, this) } + /** Gets the named arguments of this call expression. */ + DictItemList getNamedArgs() { py_dict_item_lists(result, this) } - /** Gets the nth named argument of this call expression. */ - DictItem getNamedArg(int index) { result = this.getNamedArgs().getItem(index) } + /** Gets the nth named argument of this call expression. */ + DictItem getNamedArg(int index) { result = this.getNamedArgs().getItem(index) } - /** Gets a named argument of this call expression. */ - DictItem getANamedArg() { result = this.getNamedArgs().getAnItem() } + /** Gets a named argument of this call expression. */ + DictItem getANamedArg() { result = this.getNamedArgs().getAnItem() } - override string toString() { result = "Call" } + override string toString() { result = "Call" } } /** INTERNAL: See the class `Class` for further information. */ library class Class_ extends @py_Class { - /** Gets the name of this class. */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this class. */ + string getName() { py_strs(result, this, 0) } - /** Gets the body of this class. */ - StmtList getBody() { py_stmt_lists(result, this, 1) } + /** Gets the body of this class. */ + StmtList getBody() { py_stmt_lists(result, this, 1) } - /** Gets the nth statement of this class. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this class. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this class. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this class. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - ClassExpr getParent() { py_Classes(this, result) } + ClassExpr getParent() { py_Classes(this, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Class" } + /** Gets a textual representation of this element. */ + string toString() { result = "Class" } } /** INTERNAL: See the class `ClassExpr` for further information. */ library class ClassExpr_ extends @py_ClassExpr, Expr { - /** Gets the name of this class definition. */ - string getName() { py_strs(result, this, 2) } + /** Gets the name of this class definition. */ + string getName() { py_strs(result, this, 2) } - /** Gets the bases of this class definition. */ - ExprList getBases() { py_expr_lists(result, this, 3) } + /** Gets the bases of this class definition. */ + ExprList getBases() { py_expr_lists(result, this, 3) } - /** Gets the nth base of this class definition. */ - Expr getBase(int index) { result = this.getBases().getItem(index) } + /** Gets the nth base of this class definition. */ + Expr getBase(int index) { result = this.getBases().getItem(index) } - /** Gets a base of this class definition. */ - Expr getABase() { result = this.getBases().getAnItem() } + /** Gets a base of this class definition. */ + Expr getABase() { result = this.getBases().getAnItem() } - /** Gets the keyword arguments of this class definition. */ - DictItemList getKeywords() { py_dict_item_lists(result, this) } + /** Gets the keyword arguments of this class definition. */ + DictItemList getKeywords() { py_dict_item_lists(result, this) } - /** Gets the nth keyword argument of this class definition. */ - DictItem getKeyword(int index) { result = this.getKeywords().getItem(index) } + /** Gets the nth keyword argument of this class definition. */ + DictItem getKeyword(int index) { result = this.getKeywords().getItem(index) } - /** Gets a keyword argument of this class definition. */ - DictItem getAKeyword() { result = this.getKeywords().getAnItem() } + /** Gets a keyword argument of this class definition. */ + DictItem getAKeyword() { result = this.getKeywords().getAnItem() } - /** Gets the class scope of this class definition. */ - Class getInnerScope() { py_Classes(result, this) } + /** Gets the class scope of this class definition. */ + Class getInnerScope() { py_Classes(result, this) } - override string toString() { result = "ClassExpr" } + override string toString() { result = "ClassExpr" } } /** INTERNAL: See the class `Compare` for further information. */ library class Compare_ extends @py_Compare, Expr { - /** Gets the left sub-expression of this compare expression. */ - Expr getLeft() { py_exprs(result, _, this, 2) } + /** Gets the left sub-expression of this compare expression. */ + Expr getLeft() { py_exprs(result, _, this, 2) } - /** Gets the comparison operators of this compare expression. */ - CmpopList getOps() { py_cmpop_lists(result, this) } + /** Gets the comparison operators of this compare expression. */ + CmpopList getOps() { py_cmpop_lists(result, this) } - /** Gets the nth comparison operator of this compare expression. */ - Cmpop getOp(int index) { result = this.getOps().getItem(index) } + /** Gets the nth comparison operator of this compare expression. */ + Cmpop getOp(int index) { result = this.getOps().getItem(index) } - /** Gets a comparison operator of this compare expression. */ - Cmpop getAnOp() { result = this.getOps().getAnItem() } + /** Gets a comparison operator of this compare expression. */ + Cmpop getAnOp() { result = this.getOps().getAnItem() } - /** Gets the right sub-expressions of this compare expression. */ - ExprList getComparators() { py_expr_lists(result, this, 4) } + /** Gets the right sub-expressions of this compare expression. */ + ExprList getComparators() { py_expr_lists(result, this, 4) } - /** Gets the nth right sub-expression of this compare expression. */ - Expr getComparator(int index) { result = this.getComparators().getItem(index) } + /** Gets the nth right sub-expression of this compare expression. */ + Expr getComparator(int index) { result = this.getComparators().getItem(index) } - /** Gets a right sub-expression of this compare expression. */ - Expr getAComparator() { result = this.getComparators().getAnItem() } + /** Gets a right sub-expression of this compare expression. */ + Expr getAComparator() { result = this.getComparators().getAnItem() } - override string toString() { result = "Compare" } + override string toString() { result = "Compare" } } /** INTERNAL: See the class `Continue` for further information. */ library class Continue_ extends @py_Continue, Stmt { - override string toString() { result = "Continue" } + override string toString() { result = "Continue" } } /** INTERNAL: See the class `Del` for further information. */ library class Del_ extends @py_Del, ExprContext { - override string toString() { result = "Del" } + override string toString() { result = "Del" } } /** INTERNAL: See the class `Delete` for further information. */ library class Delete_ extends @py_Delete, Stmt { - /** Gets the targets of this delete statement. */ - ExprList getTargets() { py_expr_lists(result, this, 1) } + /** Gets the targets of this delete statement. */ + ExprList getTargets() { py_expr_lists(result, this, 1) } - /** Gets the nth target of this delete statement. */ - Expr getTarget(int index) { result = this.getTargets().getItem(index) } + /** Gets the nth target of this delete statement. */ + Expr getTarget(int index) { result = this.getTargets().getItem(index) } - /** Gets a target of this delete statement. */ - Expr getATarget() { result = this.getTargets().getAnItem() } + /** Gets a target of this delete statement. */ + Expr getATarget() { result = this.getTargets().getAnItem() } - override string toString() { result = "Delete" } + override string toString() { result = "Delete" } } /** INTERNAL: See the class `Dict` for further information. */ library class Dict_ extends @py_Dict, Expr { - /** Gets the items of this dictionary expression. */ - DictItemList getItems() { py_dict_item_lists(result, this) } + /** Gets the items of this dictionary expression. */ + DictItemList getItems() { py_dict_item_lists(result, this) } - /** Gets the nth item of this dictionary expression. */ - DictItem getItem(int index) { result = this.getItems().getItem(index) } + /** Gets the nth item of this dictionary expression. */ + DictItem getItem(int index) { result = this.getItems().getItem(index) } - /** Gets an item of this dictionary expression. */ - DictItem getAnItem() { result = this.getItems().getAnItem() } + /** Gets an item of this dictionary expression. */ + DictItem getAnItem() { result = this.getItems().getAnItem() } - override string toString() { result = "Dict" } + override string toString() { result = "Dict" } } /** INTERNAL: See the class `DictComp` for further information. */ library class DictComp_ extends @py_DictComp, Expr { - /** Gets the implementation of this dictionary comprehension. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this dictionary comprehension. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this dictionary comprehension. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this dictionary comprehension. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - override string toString() { result = "DictComp" } + override string toString() { result = "DictComp" } } /** INTERNAL: See the class `DictUnpacking` for further information. */ library class DictUnpacking_ extends @py_DictUnpacking, DictItem { - /** Gets the location of this dictionary unpacking. */ - override Location getLocation() { py_locations(result, this) } + /** Gets the location of this dictionary unpacking. */ + override Location getLocation() { py_locations(result, this) } - /** Gets the value of this dictionary unpacking. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this dictionary unpacking. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "DictUnpacking" } + override string toString() { result = "DictUnpacking" } } /** INTERNAL: See the class `Div` for further information. */ library class Div_ extends @py_Div, Operator { - override string toString() { result = "Div" } + override string toString() { result = "Div" } } /** INTERNAL: See the class `Ellipsis` for further information. */ library class Ellipsis_ extends @py_Ellipsis, Expr { - override string toString() { result = "Ellipsis" } + override string toString() { result = "Ellipsis" } } /** INTERNAL: See the class `Eq` for further information. */ library class Eq_ extends @py_Eq, Cmpop { - override string toString() { result = "Eq" } + override string toString() { result = "Eq" } } /** INTERNAL: See the class `ExceptStmt` for further information. */ library class ExceptStmt_ extends @py_ExceptStmt, Stmt { - /** Gets the type of this except block. */ - Expr getType() { py_exprs(result, _, this, 1) } + /** Gets the type of this except block. */ + Expr getType() { py_exprs(result, _, this, 1) } - /** Gets the name of this except block. */ - Expr getName() { py_exprs(result, _, this, 2) } + /** Gets the name of this except block. */ + Expr getName() { py_exprs(result, _, this, 2) } - /** Gets the body of this except block. */ - StmtList getBody() { py_stmt_lists(result, this, 3) } + /** Gets the body of this except block. */ + StmtList getBody() { py_stmt_lists(result, this, 3) } - /** Gets the nth statement of this except block. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this except block. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this except block. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this except block. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - override string toString() { result = "ExceptStmt" } + override string toString() { result = "ExceptStmt" } } /** INTERNAL: See the class `Exec` for further information. */ library class Exec_ extends @py_Exec, Stmt { - /** Gets the body of this exec statement. */ - Expr getBody() { py_exprs(result, _, this, 1) } + /** Gets the body of this exec statement. */ + Expr getBody() { py_exprs(result, _, this, 1) } - /** Gets the globals of this exec statement. */ - Expr getGlobals() { py_exprs(result, _, this, 2) } + /** Gets the globals of this exec statement. */ + Expr getGlobals() { py_exprs(result, _, this, 2) } - /** Gets the locals of this exec statement. */ - Expr getLocals() { py_exprs(result, _, this, 3) } + /** Gets the locals of this exec statement. */ + Expr getLocals() { py_exprs(result, _, this, 3) } - override string toString() { result = "Exec" } + override string toString() { result = "Exec" } } /** INTERNAL: See the class `ExprStmt` for further information. */ library class ExprStmt_ extends @py_Expr_stmt, Stmt { - /** Gets the value of this expr statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this expr statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "ExprStmt" } + override string toString() { result = "ExprStmt" } } /** INTERNAL: See the class `Filter` for further information. */ library class Filter_ extends @py_Filter, Expr { - /** Gets the filtered value of this template filter expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the filtered value of this template filter expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the filter of this template filter expression. */ - Expr getFilter() { py_exprs(result, _, this, 3) } + /** Gets the filter of this template filter expression. */ + Expr getFilter() { py_exprs(result, _, this, 3) } - override string toString() { result = "Filter" } + override string toString() { result = "Filter" } } /** INTERNAL: See the class `FloorDiv` for further information. */ library class FloorDiv_ extends @py_FloorDiv, Operator { - override string toString() { result = "FloorDiv" } + override string toString() { result = "FloorDiv" } } /** INTERNAL: See the class `For` for further information. */ library class For_ extends @py_For, Stmt { - /** Gets the target of this for statement. */ - Expr getTarget() { py_exprs(result, _, this, 1) } + /** Gets the target of this for statement. */ + Expr getTarget() { py_exprs(result, _, this, 1) } - /** Gets the iterable of this for statement. */ - Expr getIter() { py_exprs(result, _, this, 2) } + /** Gets the iterable of this for statement. */ + Expr getIter() { py_exprs(result, _, this, 2) } - /** Gets the body of this for statement. */ - StmtList getBody() { py_stmt_lists(result, this, 3) } + /** Gets the body of this for statement. */ + StmtList getBody() { py_stmt_lists(result, this, 3) } - /** Gets the nth statement of this for statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this for statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this for statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this for statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the else block of this for statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 4) } + /** Gets the else block of this for statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 4) } - /** Gets the nth else statement of this for statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth else statement of this for statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an else statement of this for statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an else statement of this for statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - /** Whether the async property of this for statement is true. */ - predicate isAsync() { py_bools(this, 5) } + /** Whether the async property of this for statement is true. */ + predicate isAsync() { py_bools(this, 5) } - override string toString() { result = "For" } + override string toString() { result = "For" } } /** INTERNAL: See the class `FormattedValue` for further information. */ library class FormattedValue_ extends @py_FormattedValue, Expr { - /** Gets the expression to be formatted of this formatted value. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the expression to be formatted of this formatted value. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the type conversion of this formatted value. */ - string getConversion() { py_strs(result, this, 3) } + /** Gets the type conversion of this formatted value. */ + string getConversion() { py_strs(result, this, 3) } - /** Gets the format specifier of this formatted value. */ - Fstring getFormatSpec() { py_exprs(result, _, this, 4) } + /** Gets the format specifier of this formatted value. */ + Fstring getFormatSpec() { py_exprs(result, _, this, 4) } - override string toString() { result = "FormattedValue" } + override string toString() { result = "FormattedValue" } } /** INTERNAL: See the class `Function` for further information. */ library class Function_ extends @py_Function { - /** Gets the name of this function. */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this function. */ + string getName() { py_strs(result, this, 0) } - /** Gets the positional parameter list of this function. */ - ParameterList getArgs() { py_parameter_lists(result, this) } + /** Gets the positional parameter list of this function. */ + ParameterList getArgs() { py_parameter_lists(result, this) } - /** Gets the nth positional parameter of this function. */ - Parameter getArg(int index) { result = this.getArgs().getItem(index) } + /** Gets the nth positional parameter of this function. */ + Parameter getArg(int index) { result = this.getArgs().getItem(index) } - /** Gets a positional parameter of this function. */ - Parameter getAnArg() { result = this.getArgs().getAnItem() } + /** Gets a positional parameter of this function. */ + Parameter getAnArg() { result = this.getArgs().getAnItem() } - /** Gets the tuple (*) parameter of this function. */ - Expr getVararg() { py_exprs(result, _, this, 2) } + /** Gets the tuple (*) parameter of this function. */ + Expr getVararg() { py_exprs(result, _, this, 2) } - /** Gets the keyword-only parameter list of this function. */ - ExprList getKwonlyargs() { py_expr_lists(result, this, 3) } + /** Gets the keyword-only parameter list of this function. */ + ExprList getKwonlyargs() { py_expr_lists(result, this, 3) } - /** Gets the nth keyword-only parameter of this function. */ - Expr getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } + /** Gets the nth keyword-only parameter of this function. */ + Expr getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } - /** Gets a keyword-only parameter of this function. */ - Expr getAKwonlyarg() { result = this.getKwonlyargs().getAnItem() } + /** Gets a keyword-only parameter of this function. */ + Expr getAKwonlyarg() { result = this.getKwonlyargs().getAnItem() } - /** Gets the dictionary (**) parameter of this function. */ - Expr getKwarg() { py_exprs(result, _, this, 4) } + /** Gets the dictionary (**) parameter of this function. */ + Expr getKwarg() { py_exprs(result, _, this, 4) } - /** Gets the body of this function. */ - StmtList getBody() { py_stmt_lists(result, this, 5) } + /** Gets the body of this function. */ + StmtList getBody() { py_stmt_lists(result, this, 5) } - /** Gets the nth statement of this function. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this function. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this function. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this function. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Whether the async property of this function is true. */ - predicate isAsync() { py_bools(this, 6) } + /** Whether the async property of this function is true. */ + predicate isAsync() { py_bools(this, 6) } - FunctionParent getParent() { py_Functions(this, result) } + FunctionParent getParent() { py_Functions(this, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Function" } + /** Gets a textual representation of this element. */ + string toString() { result = "Function" } } /** INTERNAL: See the class `FunctionExpr` for further information. */ library class FunctionExpr_ extends @py_FunctionExpr, Expr { - /** Gets the name of this function definition. */ - string getName() { py_strs(result, this, 2) } + /** Gets the name of this function definition. */ + string getName() { py_strs(result, this, 2) } - /** Gets the parameters of this function definition. */ - Arguments getArgs() { py_arguments(result, this) } + /** Gets the parameters of this function definition. */ + Arguments getArgs() { py_arguments(result, this) } - /** Gets the return annotation of this function definition. */ - Expr getReturns() { py_exprs(result, _, this, 4) } + /** Gets the return annotation of this function definition. */ + Expr getReturns() { py_exprs(result, _, this, 4) } - /** Gets the function scope of this function definition. */ - Function getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this function definition. */ + Function getInnerScope() { py_Functions(result, this) } - override string toString() { result = "FunctionExpr" } + override string toString() { result = "FunctionExpr" } } /** INTERNAL: See the class `FunctionParent` for further information. */ library class FunctionParent_ extends @py_Function_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "FunctionParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "FunctionParent" } } /** INTERNAL: See the class `GeneratorExp` for further information. */ library class GeneratorExp_ extends @py_GeneratorExp, Expr { - /** Gets the implementation of this generator expression. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this generator expression. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this generator expression. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this generator expression. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - override string toString() { result = "GeneratorExp" } + override string toString() { result = "GeneratorExp" } } /** INTERNAL: See the class `Global` for further information. */ library class Global_ extends @py_Global, Stmt { - /** Gets the names of this global statement. */ - StringList getNames() { py_str_lists(result, this) } + /** Gets the names of this global statement. */ + StringList getNames() { py_str_lists(result, this) } - /** Gets the nth name of this global statement. */ - string getName(int index) { result = this.getNames().getItem(index) } + /** Gets the nth name of this global statement. */ + string getName(int index) { result = this.getNames().getItem(index) } - /** Gets a name of this global statement. */ - string getAName() { result = this.getNames().getAnItem() } + /** Gets a name of this global statement. */ + string getAName() { result = this.getNames().getAnItem() } - override string toString() { result = "Global" } + override string toString() { result = "Global" } } /** INTERNAL: See the class `Gt` for further information. */ library class Gt_ extends @py_Gt, Cmpop { - override string toString() { result = "Gt" } + override string toString() { result = "Gt" } } /** INTERNAL: See the class `GtE` for further information. */ library class GtE_ extends @py_GtE, Cmpop { - override string toString() { result = "GtE" } + override string toString() { result = "GtE" } } /** INTERNAL: See the class `If` for further information. */ library class If_ extends @py_If, Stmt { - /** Gets the test of this if statement. */ - Expr getTest() { py_exprs(result, _, this, 1) } + /** Gets the test of this if statement. */ + Expr getTest() { py_exprs(result, _, this, 1) } - /** Gets the if-true block of this if statement. */ - StmtList getBody() { py_stmt_lists(result, this, 2) } + /** Gets the if-true block of this if statement. */ + StmtList getBody() { py_stmt_lists(result, this, 2) } - /** Gets the nth if-true statement of this if statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth if-true statement of this if statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets an if-true statement of this if statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets an if-true statement of this if statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the if-false block of this if statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 3) } + /** Gets the if-false block of this if statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 3) } - /** Gets the nth if-false statement of this if statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth if-false statement of this if statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an if-false statement of this if statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an if-false statement of this if statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - override string toString() { result = "If" } + override string toString() { result = "If" } } /** INTERNAL: See the class `IfExp` for further information. */ library class IfExp_ extends @py_IfExp, Expr { - /** Gets the test of this if expression. */ - Expr getTest() { py_exprs(result, _, this, 2) } + /** Gets the test of this if expression. */ + Expr getTest() { py_exprs(result, _, this, 2) } - /** Gets the if-true expression of this if expression. */ - Expr getBody() { py_exprs(result, _, this, 3) } + /** Gets the if-true expression of this if expression. */ + Expr getBody() { py_exprs(result, _, this, 3) } - /** Gets the if-false expression of this if expression. */ - Expr getOrelse() { py_exprs(result, _, this, 4) } + /** Gets the if-false expression of this if expression. */ + Expr getOrelse() { py_exprs(result, _, this, 4) } - override string toString() { result = "IfExp" } + override string toString() { result = "IfExp" } } /** INTERNAL: See the class `Import` for further information. */ library class Import_ extends @py_Import, Stmt { - /** Gets the alias list of this import statement. */ - AliasList getNames() { py_alias_lists(result, this) } + /** Gets the alias list of this import statement. */ + AliasList getNames() { py_alias_lists(result, this) } - /** Gets the nth alias of this import statement. */ - Alias getName(int index) { result = this.getNames().getItem(index) } + /** Gets the nth alias of this import statement. */ + Alias getName(int index) { result = this.getNames().getItem(index) } - /** Gets an alias of this import statement. */ - Alias getAName() { result = this.getNames().getAnItem() } + /** Gets an alias of this import statement. */ + Alias getAName() { result = this.getNames().getAnItem() } - override string toString() { result = "Import" } + override string toString() { result = "Import" } } /** INTERNAL: See the class `ImportExpr` for further information. */ library class ImportExpr_ extends @py_ImportExpr, Expr { - /** Gets the level of this import expression. */ - int getLevel() { py_ints(result, this) } + /** Gets the level of this import expression. */ + int getLevel() { py_ints(result, this) } - /** Gets the name of this import expression. */ - string getName() { py_strs(result, this, 3) } + /** Gets the name of this import expression. */ + string getName() { py_strs(result, this, 3) } - /** Whether the top level property of this import expression is true. */ - predicate isTop() { py_bools(this, 4) } + /** Whether the top level property of this import expression is true. */ + predicate isTop() { py_bools(this, 4) } - override string toString() { result = "ImportExpr" } + override string toString() { result = "ImportExpr" } } /** INTERNAL: See the class `ImportStar` for further information. */ library class ImportStar_ extends @py_ImportStar, Stmt { - /** Gets the module of this import * statement. */ - Expr getModule() { py_exprs(result, _, this, 1) } + /** Gets the module of this import * statement. */ + Expr getModule() { py_exprs(result, _, this, 1) } - override string toString() { result = "ImportStar" } + override string toString() { result = "ImportStar" } } /** INTERNAL: See the class `ImportMember` for further information. */ library class ImportMember_ extends @py_ImportMember, Expr { - /** Gets the module of this from import. */ - Expr getModule() { py_exprs(result, _, this, 2) } + /** Gets the module of this from import. */ + Expr getModule() { py_exprs(result, _, this, 2) } - /** Gets the name of this from import. */ - string getName() { py_strs(result, this, 3) } + /** Gets the name of this from import. */ + string getName() { py_strs(result, this, 3) } - override string toString() { result = "ImportMember" } + override string toString() { result = "ImportMember" } } /** INTERNAL: See the class `In` for further information. */ library class In_ extends @py_In, Cmpop { - override string toString() { result = "In" } + override string toString() { result = "In" } } /** INTERNAL: See the class `Invert` for further information. */ library class Invert_ extends @py_Invert, Unaryop { - override string toString() { result = "Invert" } + override string toString() { result = "Invert" } } /** INTERNAL: See the class `Is` for further information. */ library class Is_ extends @py_Is, Cmpop { - override string toString() { result = "Is" } + override string toString() { result = "Is" } } /** INTERNAL: See the class `IsNot` for further information. */ library class IsNot_ extends @py_IsNot, Cmpop { - override string toString() { result = "IsNot" } + override string toString() { result = "IsNot" } } /** INTERNAL: See the class `Fstring` for further information. */ library class Fstring_ extends @py_Fstring, Expr { - /** Gets the values of this formatted string literal. */ - ExprList getValues() { py_expr_lists(result, this, 2) } + /** Gets the values of this formatted string literal. */ + ExprList getValues() { py_expr_lists(result, this, 2) } - /** Gets the nth value of this formatted string literal. */ - Expr getValue(int index) { result = this.getValues().getItem(index) } + /** Gets the nth value of this formatted string literal. */ + Expr getValue(int index) { result = this.getValues().getItem(index) } - /** Gets a value of this formatted string literal. */ - Expr getAValue() { result = this.getValues().getAnItem() } + /** Gets a value of this formatted string literal. */ + Expr getAValue() { result = this.getValues().getAnItem() } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "Fstring" } + override string toString() { result = "Fstring" } } /** INTERNAL: See the class `KeyValuePair` for further information. */ library class KeyValuePair_ extends @py_KeyValuePair, DictItem { - /** Gets the location of this key-value pair. */ - override Location getLocation() { py_locations(result, this) } + /** Gets the location of this key-value pair. */ + override Location getLocation() { py_locations(result, this) } - /** Gets the value of this key-value pair. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this key-value pair. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the key of this key-value pair. */ - Expr getKey() { py_exprs(result, _, this, 2) } + /** Gets the key of this key-value pair. */ + Expr getKey() { py_exprs(result, _, this, 2) } - override string toString() { result = "KeyValuePair" } + override string toString() { result = "KeyValuePair" } } /** INTERNAL: See the class `LShift` for further information. */ library class LShift_ extends @py_LShift, Operator { - override string toString() { result = "LShift" } + override string toString() { result = "LShift" } } /** INTERNAL: See the class `Lambda` for further information. */ library class Lambda_ extends @py_Lambda, Expr { - /** Gets the arguments of this lambda expression. */ - Arguments getArgs() { py_arguments(result, this) } + /** Gets the arguments of this lambda expression. */ + Arguments getArgs() { py_arguments(result, this) } - /** Gets the function scope of this lambda expression. */ - Function getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this lambda expression. */ + Function getInnerScope() { py_Functions(result, this) } - override string toString() { result = "Lambda" } + override string toString() { result = "Lambda" } } /** INTERNAL: See the class `List` for further information. */ library class List_ extends @py_List, Expr { - /** Gets the element list of this list expression. */ - ExprList getElts() { py_expr_lists(result, this, 2) } + /** Gets the element list of this list expression. */ + ExprList getElts() { py_expr_lists(result, this, 2) } - /** Gets the nth element of this list expression. */ - Expr getElt(int index) { result = this.getElts().getItem(index) } + /** Gets the nth element of this list expression. */ + Expr getElt(int index) { result = this.getElts().getItem(index) } - /** Gets an element of this list expression. */ - Expr getAnElt() { result = this.getElts().getAnItem() } + /** Gets an element of this list expression. */ + Expr getAnElt() { result = this.getElts().getAnItem() } - /** Gets the context of this list expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this list expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "List" } + override string toString() { result = "List" } } /** INTERNAL: See the class `ListComp` for further information. */ library class ListComp_ extends @py_ListComp, Expr { - /** Gets the implementation of this list comprehension. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this list comprehension. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this list comprehension. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this list comprehension. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - /** Gets the generators of this list comprehension. */ - ComprehensionList getGenerators() { py_comprehension_lists(result, this) } + /** Gets the generators of this list comprehension. */ + ComprehensionList getGenerators() { py_comprehension_lists(result, this) } - /** Gets the nth generator of this list comprehension. */ - Comprehension getGenerator(int index) { result = this.getGenerators().getItem(index) } + /** Gets the nth generator of this list comprehension. */ + Comprehension getGenerator(int index) { result = this.getGenerators().getItem(index) } - /** Gets a generator of this list comprehension. */ - Comprehension getAGenerator() { result = this.getGenerators().getAnItem() } + /** Gets a generator of this list comprehension. */ + Comprehension getAGenerator() { result = this.getGenerators().getAnItem() } - /** Gets the elements of this list comprehension. */ - Expr getElt() { py_exprs(result, _, this, 5) } + /** Gets the elements of this list comprehension. */ + Expr getElt() { py_exprs(result, _, this, 5) } - override string toString() { result = "ListComp" } + override string toString() { result = "ListComp" } } /** INTERNAL: See the class `Load` for further information. */ library class Load_ extends @py_Load, ExprContext { - override string toString() { result = "Load" } + override string toString() { result = "Load" } } /** INTERNAL: See the class `Lt` for further information. */ library class Lt_ extends @py_Lt, Cmpop { - override string toString() { result = "Lt" } + override string toString() { result = "Lt" } } /** INTERNAL: See the class `LtE` for further information. */ library class LtE_ extends @py_LtE, Cmpop { - override string toString() { result = "LtE" } + override string toString() { result = "LtE" } } /** INTERNAL: See the class `MatMult` for further information. */ library class MatMult_ extends @py_MatMult, Operator { - override string toString() { result = "MatMult" } + override string toString() { result = "MatMult" } } /** INTERNAL: See the class `Mod` for further information. */ library class Mod_ extends @py_Mod, Operator { - override string toString() { result = "Mod" } + override string toString() { result = "Mod" } } /** INTERNAL: See the class `Module` for further information. */ library class Module_ extends @py_Module { - /** Gets the name of this module. */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this module. */ + string getName() { py_strs(result, this, 0) } - /** Gets the hash (not populated) of this module. */ - string getHash() { py_strs(result, this, 1) } + /** Gets the hash (not populated) of this module. */ + string getHash() { py_strs(result, this, 1) } - /** Gets the body of this module. */ - StmtList getBody() { py_stmt_lists(result, this, 2) } + /** Gets the body of this module. */ + StmtList getBody() { py_stmt_lists(result, this, 2) } - /** Gets the nth statement of this module. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this module. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this module. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this module. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the kind of this module. */ - string getKind() { py_strs(result, this, 3) } + /** Gets the kind of this module. */ + string getKind() { py_strs(result, this, 3) } - /** Gets a textual representation of this element. */ - string toString() { result = "Module" } + /** Gets a textual representation of this element. */ + string toString() { result = "Module" } } /** INTERNAL: See the class `Mult` for further information. */ library class Mult_ extends @py_Mult, Operator { - override string toString() { result = "Mult" } + override string toString() { result = "Mult" } } /** INTERNAL: See the class `Name` for further information. */ library class Name_ extends @py_Name, Expr { - /** Gets the variable of this name expression. */ - Variable getVariable() { py_variables(result, this) } + /** Gets the variable of this name expression. */ + Variable getVariable() { py_variables(result, this) } - /** Gets the context of this name expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this name expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "Name" } + override string toString() { result = "Name" } } /** INTERNAL: See the class `Nonlocal` for further information. */ library class Nonlocal_ extends @py_Nonlocal, Stmt { - /** Gets the names of this nonlocal statement. */ - StringList getNames() { py_str_lists(result, this) } + /** Gets the names of this nonlocal statement. */ + StringList getNames() { py_str_lists(result, this) } - /** Gets the nth name of this nonlocal statement. */ - string getName(int index) { result = this.getNames().getItem(index) } + /** Gets the nth name of this nonlocal statement. */ + string getName(int index) { result = this.getNames().getItem(index) } - /** Gets a name of this nonlocal statement. */ - string getAName() { result = this.getNames().getAnItem() } + /** Gets a name of this nonlocal statement. */ + string getAName() { result = this.getNames().getAnItem() } - override string toString() { result = "Nonlocal" } + override string toString() { result = "Nonlocal" } } /** INTERNAL: See the class `Not` for further information. */ library class Not_ extends @py_Not, Unaryop { - override string toString() { result = "Not" } + override string toString() { result = "Not" } } /** INTERNAL: See the class `NotEq` for further information. */ library class NotEq_ extends @py_NotEq, Cmpop { - override string toString() { result = "NotEq" } + override string toString() { result = "NotEq" } } /** INTERNAL: See the class `NotIn` for further information. */ library class NotIn_ extends @py_NotIn, Cmpop { - override string toString() { result = "NotIn" } + override string toString() { result = "NotIn" } } /** INTERNAL: See the class `Num` for further information. */ library class Num_ extends @py_Num, Expr { - /** Gets the value of this numeric literal. */ - string getN() { py_numbers(result, this, 2) } + /** Gets the value of this numeric literal. */ + string getN() { py_numbers(result, this, 2) } - /** Gets the text of this numeric literal. */ - string getText() { py_numbers(result, this, 3) } + /** Gets the text of this numeric literal. */ + string getText() { py_numbers(result, this, 3) } - override string toString() { result = "Num" } + override string toString() { result = "Num" } } /** INTERNAL: See the class `Or` for further information. */ library class Or_ extends @py_Or, Boolop { - override string toString() { result = "Or" } + override string toString() { result = "Or" } } /** INTERNAL: See the class `Param` for further information. */ library class Param_ extends @py_Param, ExprContext { - override string toString() { result = "Param" } + override string toString() { result = "Param" } } /** INTERNAL: See the class `Pass` for further information. */ library class Pass_ extends @py_Pass, Stmt { - override string toString() { result = "Pass" } + override string toString() { result = "Pass" } } /** INTERNAL: See the class `PlaceHolder` for further information. */ library class PlaceHolder_ extends @py_PlaceHolder, Expr { - /** Gets the variable of this template place-holder expression. */ - Variable getVariable() { py_variables(result, this) } + /** Gets the variable of this template place-holder expression. */ + Variable getVariable() { py_variables(result, this) } - /** Gets the context of this template place-holder expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this template place-holder expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "PlaceHolder" } + override string toString() { result = "PlaceHolder" } } /** INTERNAL: See the class `Pow` for further information. */ library class Pow_ extends @py_Pow, Operator { - override string toString() { result = "Pow" } + override string toString() { result = "Pow" } } /** INTERNAL: See the class `Print` for further information. */ library class Print_ extends @py_Print, Stmt { - /** Gets the destination of this print statement. */ - Expr getDest() { py_exprs(result, _, this, 1) } + /** Gets the destination of this print statement. */ + Expr getDest() { py_exprs(result, _, this, 1) } - /** Gets the values of this print statement. */ - ExprList getValues() { py_expr_lists(result, this, 2) } + /** Gets the values of this print statement. */ + ExprList getValues() { py_expr_lists(result, this, 2) } - /** Gets the nth value of this print statement. */ - Expr getValue(int index) { result = this.getValues().getItem(index) } + /** Gets the nth value of this print statement. */ + Expr getValue(int index) { result = this.getValues().getItem(index) } - /** Gets a value of this print statement. */ - Expr getAValue() { result = this.getValues().getAnItem() } + /** Gets a value of this print statement. */ + Expr getAValue() { result = this.getValues().getAnItem() } - /** Whether the new line property of this print statement is true. */ - predicate isNl() { py_bools(this, 3) } + /** Whether the new line property of this print statement is true. */ + predicate isNl() { py_bools(this, 3) } - override string toString() { result = "Print" } + override string toString() { result = "Print" } } /** INTERNAL: See the class `RShift` for further information. */ library class RShift_ extends @py_RShift, Operator { - override string toString() { result = "RShift" } + override string toString() { result = "RShift" } } /** INTERNAL: See the class `Raise` for further information. */ library class Raise_ extends @py_Raise, Stmt { - /** Gets the exception of this raise statement. */ - Expr getExc() { py_exprs(result, _, this, 1) } + /** Gets the exception of this raise statement. */ + Expr getExc() { py_exprs(result, _, this, 1) } - /** Gets the cause of this raise statement. */ - Expr getCause() { py_exprs(result, _, this, 2) } + /** Gets the cause of this raise statement. */ + Expr getCause() { py_exprs(result, _, this, 2) } - /** Gets the type of this raise statement. */ - Expr getType() { py_exprs(result, _, this, 3) } + /** Gets the type of this raise statement. */ + Expr getType() { py_exprs(result, _, this, 3) } - /** Gets the instance of this raise statement. */ - Expr getInst() { py_exprs(result, _, this, 4) } + /** Gets the instance of this raise statement. */ + Expr getInst() { py_exprs(result, _, this, 4) } - /** Gets the traceback of this raise statement. */ - Expr getTback() { py_exprs(result, _, this, 5) } + /** Gets the traceback of this raise statement. */ + Expr getTback() { py_exprs(result, _, this, 5) } - override string toString() { result = "Raise" } + override string toString() { result = "Raise" } } /** INTERNAL: See the class `Repr` for further information. */ library class Repr_ extends @py_Repr, Expr { - /** Gets the value of this backtick expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this backtick expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "Repr" } + override string toString() { result = "Repr" } } /** INTERNAL: See the class `Return` for further information. */ library class Return_ extends @py_Return, Stmt { - /** Gets the value of this return statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this return statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "Return" } + override string toString() { result = "Return" } } /** INTERNAL: See the class `Set` for further information. */ library class Set_ extends @py_Set, Expr { - /** Gets the elements of this set expression. */ - ExprList getElts() { py_expr_lists(result, this, 2) } + /** Gets the elements of this set expression. */ + ExprList getElts() { py_expr_lists(result, this, 2) } - /** Gets the nth element of this set expression. */ - Expr getElt(int index) { result = this.getElts().getItem(index) } + /** Gets the nth element of this set expression. */ + Expr getElt(int index) { result = this.getElts().getItem(index) } - /** Gets an element of this set expression. */ - Expr getAnElt() { result = this.getElts().getAnItem() } + /** Gets an element of this set expression. */ + Expr getAnElt() { result = this.getElts().getAnItem() } - override string toString() { result = "Set" } + override string toString() { result = "Set" } } /** INTERNAL: See the class `SetComp` for further information. */ library class SetComp_ extends @py_SetComp, Expr { - /** Gets the implementation of this set comprehension. */ - Function getFunction() { py_Functions(result, this) } + /** Gets the implementation of this set comprehension. */ + Function getFunction() { py_Functions(result, this) } - /** Gets the iterable of this set comprehension. */ - Expr getIterable() { py_exprs(result, _, this, 3) } + /** Gets the iterable of this set comprehension. */ + Expr getIterable() { py_exprs(result, _, this, 3) } - override string toString() { result = "SetComp" } + override string toString() { result = "SetComp" } } /** INTERNAL: See the class `Slice` for further information. */ library class Slice_ extends @py_Slice, Expr { - /** Gets the start of this slice. */ - Expr getStart() { py_exprs(result, _, this, 2) } + /** Gets the start of this slice. */ + Expr getStart() { py_exprs(result, _, this, 2) } - /** Gets the stop of this slice. */ - Expr getStop() { py_exprs(result, _, this, 3) } + /** Gets the stop of this slice. */ + Expr getStop() { py_exprs(result, _, this, 3) } - /** Gets the step of this slice. */ - Expr getStep() { py_exprs(result, _, this, 4) } + /** Gets the step of this slice. */ + Expr getStep() { py_exprs(result, _, this, 4) } - override string toString() { result = "Slice" } + override string toString() { result = "Slice" } } /** INTERNAL: See the class `SpecialOperation` for further information. */ library class SpecialOperation_ extends @py_SpecialOperation, Expr { - /** Gets the name of this special operation. */ - string getName() { py_strs(result, this, 2) } + /** Gets the name of this special operation. */ + string getName() { py_strs(result, this, 2) } - /** Gets the arguments of this special operation. */ - ExprList getArguments() { py_expr_lists(result, this, 3) } + /** Gets the arguments of this special operation. */ + ExprList getArguments() { py_expr_lists(result, this, 3) } - /** Gets the nth argument of this special operation. */ - Expr getArgument(int index) { result = this.getArguments().getItem(index) } + /** Gets the nth argument of this special operation. */ + Expr getArgument(int index) { result = this.getArguments().getItem(index) } - /** Gets an argument of this special operation. */ - Expr getAnArgument() { result = this.getArguments().getAnItem() } + /** Gets an argument of this special operation. */ + Expr getAnArgument() { result = this.getArguments().getAnItem() } - override string toString() { result = "SpecialOperation" } + override string toString() { result = "SpecialOperation" } } /** INTERNAL: See the class `Starred` for further information. */ library class Starred_ extends @py_Starred, Expr { - /** Gets the value of this starred expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this starred expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the context of this starred expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this starred expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "Starred" } + override string toString() { result = "Starred" } } /** INTERNAL: See the class `Store` for further information. */ library class Store_ extends @py_Store, ExprContext { - override string toString() { result = "Store" } + override string toString() { result = "Store" } } /** INTERNAL: See the class `Str` for further information. */ library class Str_ extends @py_Str, Expr { - /** Gets the text of this string literal. */ - string getS() { py_strs(result, this, 2) } + /** Gets the text of this string literal. */ + string getS() { py_strs(result, this, 2) } - /** Gets the prefix of this string literal. */ - string getPrefix() { py_strs(result, this, 3) } + /** Gets the prefix of this string literal. */ + string getPrefix() { py_strs(result, this, 3) } - /** Gets the implicitly_concatenated_parts of this string literal. */ - StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } + /** Gets the implicitly_concatenated_parts of this string literal. */ + StringPartList getImplicitlyConcatenatedParts() { py_StringPart_lists(result, this) } - /** Gets the nth implicitly_concatenated_part of this string literal. */ - StringPart getImplicitlyConcatenatedPart(int index) { - result = this.getImplicitlyConcatenatedParts().getItem(index) - } + /** Gets the nth implicitly_concatenated_part of this string literal. */ + StringPart getImplicitlyConcatenatedPart(int index) { + result = this.getImplicitlyConcatenatedParts().getItem(index) + } - /** Gets an implicitly_concatenated_part of this string literal. */ - StringPart getAnImplicitlyConcatenatedPart() { - result = this.getImplicitlyConcatenatedParts().getAnItem() - } + /** Gets an implicitly_concatenated_part of this string literal. */ + StringPart getAnImplicitlyConcatenatedPart() { + result = this.getImplicitlyConcatenatedParts().getAnItem() + } - override string toString() { result = "Str" } + override string toString() { result = "Str" } } /** INTERNAL: See the class `StringPart` for further information. */ library class StringPart_ extends @py_StringPart { - /** Gets the text of this implicitly concatenated part. */ - string getText() { py_strs(result, this, 0) } + /** Gets the text of this implicitly concatenated part. */ + string getText() { py_strs(result, this, 0) } - /** Gets the location of this implicitly concatenated part. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this implicitly concatenated part. */ + Location getLocation() { py_locations(result, this) } - StringPartList getParent() { py_StringParts(this, result, _) } + StringPartList getParent() { py_StringParts(this, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "StringPart" } + /** Gets a textual representation of this element. */ + string toString() { result = "StringPart" } } /** INTERNAL: See the class `StringPartList` for further information. */ library class StringPartList_ extends @py_StringPart_list { - BytesOrStr getParent() { py_StringPart_lists(this, result) } + BytesOrStr getParent() { py_StringPart_lists(this, result) } - /** Gets an item of this implicitly concatenated part list */ - StringPart getAnItem() { py_StringParts(result, this, _) } + /** Gets an item of this implicitly concatenated part list */ + StringPart getAnItem() { py_StringParts(result, this, _) } - /** Gets the nth item of this implicitly concatenated part list */ - StringPart getItem(int index) { py_StringParts(result, this, index) } + /** Gets the nth item of this implicitly concatenated part list */ + StringPart getItem(int index) { py_StringParts(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "StringPartList" } + /** Gets a textual representation of this element. */ + string toString() { result = "StringPartList" } } /** INTERNAL: See the class `Sub` for further information. */ library class Sub_ extends @py_Sub, Operator { - override string toString() { result = "Sub" } + override string toString() { result = "Sub" } } /** INTERNAL: See the class `Subscript` for further information. */ library class Subscript_ extends @py_Subscript, Expr { - /** Gets the value of this subscript expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this subscript expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the index of this subscript expression. */ - Expr getIndex() { py_exprs(result, _, this, 3) } + /** Gets the index of this subscript expression. */ + Expr getIndex() { py_exprs(result, _, this, 3) } - /** Gets the context of this subscript expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this subscript expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "Subscript" } + override string toString() { result = "Subscript" } } /** INTERNAL: See the class `TemplateDottedNotation` for further information. */ library class TemplateDottedNotation_ extends @py_TemplateDottedNotation, Expr { - /** Gets the object of this template dotted notation expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the object of this template dotted notation expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - /** Gets the attribute name of this template dotted notation expression. */ - string getAttr() { py_strs(result, this, 3) } + /** Gets the attribute name of this template dotted notation expression. */ + string getAttr() { py_strs(result, this, 3) } - /** Gets the context of this template dotted notation expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this template dotted notation expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override string toString() { result = "TemplateDottedNotation" } + override string toString() { result = "TemplateDottedNotation" } } /** INTERNAL: See the class `TemplateWrite` for further information. */ library class TemplateWrite_ extends @py_TemplateWrite, Stmt { - /** Gets the value of this template write statement. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this template write statement. */ + Expr getValue() { py_exprs(result, _, this, 1) } - override string toString() { result = "TemplateWrite" } + override string toString() { result = "TemplateWrite" } } /** INTERNAL: See the class `Try` for further information. */ library class Try_ extends @py_Try, Stmt { - /** Gets the body of this try statement. */ - StmtList getBody() { py_stmt_lists(result, this, 1) } + /** Gets the body of this try statement. */ + StmtList getBody() { py_stmt_lists(result, this, 1) } - /** Gets the nth statement of this try statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this try statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this try statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this try statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the else block of this try statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 2) } + /** Gets the else block of this try statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 2) } - /** Gets the nth else statement of this try statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth else statement of this try statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an else statement of this try statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an else statement of this try statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - /** Gets the exception handlers of this try statement. */ - StmtList getHandlers() { py_stmt_lists(result, this, 3) } + /** Gets the exception handlers of this try statement. */ + StmtList getHandlers() { py_stmt_lists(result, this, 3) } - /** Gets the nth exception handler of this try statement. */ - Stmt getHandler(int index) { result = this.getHandlers().getItem(index) } + /** Gets the nth exception handler of this try statement. */ + Stmt getHandler(int index) { result = this.getHandlers().getItem(index) } - /** Gets an exception handler of this try statement. */ - Stmt getAHandler() { result = this.getHandlers().getAnItem() } + /** Gets an exception handler of this try statement. */ + Stmt getAHandler() { result = this.getHandlers().getAnItem() } - /** Gets the finally block of this try statement. */ - StmtList getFinalbody() { py_stmt_lists(result, this, 4) } + /** Gets the finally block of this try statement. */ + StmtList getFinalbody() { py_stmt_lists(result, this, 4) } - /** Gets the nth finally statement of this try statement. */ - Stmt getFinalstmt(int index) { result = this.getFinalbody().getItem(index) } + /** Gets the nth finally statement of this try statement. */ + Stmt getFinalstmt(int index) { result = this.getFinalbody().getItem(index) } - /** Gets a finally statement of this try statement. */ - Stmt getAFinalstmt() { result = this.getFinalbody().getAnItem() } + /** Gets a finally statement of this try statement. */ + Stmt getAFinalstmt() { result = this.getFinalbody().getAnItem() } - override string toString() { result = "Try" } + override string toString() { result = "Try" } } /** INTERNAL: See the class `Tuple` for further information. */ library class Tuple_ extends @py_Tuple, Expr { - /** Gets the elements of this tuple expression. */ - ExprList getElts() { py_expr_lists(result, this, 2) } + /** Gets the elements of this tuple expression. */ + ExprList getElts() { py_expr_lists(result, this, 2) } - /** Gets the nth element of this tuple expression. */ - Expr getElt(int index) { result = this.getElts().getItem(index) } + /** Gets the nth element of this tuple expression. */ + Expr getElt(int index) { result = this.getElts().getItem(index) } - /** Gets an element of this tuple expression. */ - Expr getAnElt() { result = this.getElts().getAnItem() } + /** Gets an element of this tuple expression. */ + Expr getAnElt() { result = this.getElts().getAnItem() } - /** Gets the context of this tuple expression. */ - ExprContext getCtx() { py_expr_contexts(result, _, this) } + /** Gets the context of this tuple expression. */ + ExprContext getCtx() { py_expr_contexts(result, _, this) } - override ExprParent getParent() { py_exprs(this, _, result, _) } + override ExprParent getParent() { py_exprs(this, _, result, _) } - override string toString() { result = "Tuple" } + override string toString() { result = "Tuple" } } /** INTERNAL: See the class `UAdd` for further information. */ library class UAdd_ extends @py_UAdd, Unaryop { - override string toString() { result = "UAdd" } + override string toString() { result = "UAdd" } } /** INTERNAL: See the class `USub` for further information. */ library class USub_ extends @py_USub, Unaryop { - override string toString() { result = "USub" } + override string toString() { result = "USub" } } /** INTERNAL: See the class `UnaryExpr` for further information. */ library class UnaryExpr_ extends @py_UnaryExpr, Expr { - /** Gets the operator of this unary expression. */ - Unaryop getOp() { py_unaryops(result, _, this) } + /** Gets the operator of this unary expression. */ + Unaryop getOp() { py_unaryops(result, _, this) } - /** Gets the operand of this unary expression. */ - Expr getOperand() { py_exprs(result, _, this, 3) } + /** Gets the operand of this unary expression. */ + Expr getOperand() { py_exprs(result, _, this, 3) } - override string toString() { result = "UnaryExpr" } + override string toString() { result = "UnaryExpr" } } /** INTERNAL: See the class `While` for further information. */ library class While_ extends @py_While, Stmt { - /** Gets the test of this while statement. */ - Expr getTest() { py_exprs(result, _, this, 1) } + /** Gets the test of this while statement. */ + Expr getTest() { py_exprs(result, _, this, 1) } - /** Gets the body of this while statement. */ - StmtList getBody() { py_stmt_lists(result, this, 2) } + /** Gets the body of this while statement. */ + StmtList getBody() { py_stmt_lists(result, this, 2) } - /** Gets the nth statement of this while statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this while statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this while statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this while statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Gets the else block of this while statement. */ - StmtList getOrelse() { py_stmt_lists(result, this, 3) } + /** Gets the else block of this while statement. */ + StmtList getOrelse() { py_stmt_lists(result, this, 3) } - /** Gets the nth else statement of this while statement. */ - Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } + /** Gets the nth else statement of this while statement. */ + Stmt getOrelse(int index) { result = this.getOrelse().getItem(index) } - /** Gets an else statement of this while statement. */ - Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } + /** Gets an else statement of this while statement. */ + Stmt getAnOrelse() { result = this.getOrelse().getAnItem() } - override string toString() { result = "While" } + override string toString() { result = "While" } } /** INTERNAL: See the class `With` for further information. */ library class With_ extends @py_With, Stmt { - /** Gets the context manager of this with statement. */ - Expr getContextExpr() { py_exprs(result, _, this, 1) } + /** Gets the context manager of this with statement. */ + Expr getContextExpr() { py_exprs(result, _, this, 1) } - /** Gets the optional variable of this with statement. */ - Expr getOptionalVars() { py_exprs(result, _, this, 2) } + /** Gets the optional variable of this with statement. */ + Expr getOptionalVars() { py_exprs(result, _, this, 2) } - /** Gets the body of this with statement. */ - StmtList getBody() { py_stmt_lists(result, this, 3) } + /** Gets the body of this with statement. */ + StmtList getBody() { py_stmt_lists(result, this, 3) } - /** Gets the nth statement of this with statement. */ - Stmt getStmt(int index) { result = this.getBody().getItem(index) } + /** Gets the nth statement of this with statement. */ + Stmt getStmt(int index) { result = this.getBody().getItem(index) } - /** Gets a statement of this with statement. */ - Stmt getAStmt() { result = this.getBody().getAnItem() } + /** Gets a statement of this with statement. */ + Stmt getAStmt() { result = this.getBody().getAnItem() } - /** Whether the async property of this with statement is true. */ - predicate isAsync() { py_bools(this, 4) } + /** Whether the async property of this with statement is true. */ + predicate isAsync() { py_bools(this, 4) } - override string toString() { result = "With" } + override string toString() { result = "With" } } /** INTERNAL: See the class `Yield` for further information. */ library class Yield_ extends @py_Yield, Expr { - /** Gets the value of this yield expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this yield expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "Yield" } + override string toString() { result = "Yield" } } /** INTERNAL: See the class `YieldFrom` for further information. */ library class YieldFrom_ extends @py_YieldFrom, Expr { - /** Gets the value of this yield-from expression. */ - Expr getValue() { py_exprs(result, _, this, 2) } + /** Gets the value of this yield-from expression. */ + Expr getValue() { py_exprs(result, _, this, 2) } - override string toString() { result = "YieldFrom" } + override string toString() { result = "YieldFrom" } } /** INTERNAL: See the class `Alias` for further information. */ library class Alias_ extends @py_alias { - /** Gets the value of this alias. */ - Expr getValue() { py_exprs(result, _, this, 0) } + /** Gets the value of this alias. */ + Expr getValue() { py_exprs(result, _, this, 0) } - /** Gets the name of this alias. */ - Expr getAsname() { py_exprs(result, _, this, 1) } + /** Gets the name of this alias. */ + Expr getAsname() { py_exprs(result, _, this, 1) } - AliasList getParent() { py_aliases(this, result, _) } + AliasList getParent() { py_aliases(this, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Alias" } + /** Gets a textual representation of this element. */ + string toString() { result = "Alias" } } /** INTERNAL: See the class `AliasList` for further information. */ library class AliasList_ extends @py_alias_list { - Import getParent() { py_alias_lists(this, result) } + Import getParent() { py_alias_lists(this, result) } - /** Gets an item of this alias list */ - Alias getAnItem() { py_aliases(result, this, _) } + /** Gets an item of this alias list */ + Alias getAnItem() { py_aliases(result, this, _) } - /** Gets the nth item of this alias list */ - Alias getItem(int index) { py_aliases(result, this, index) } + /** Gets the nth item of this alias list */ + Alias getItem(int index) { py_aliases(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "AliasList" } + /** Gets a textual representation of this element. */ + string toString() { result = "AliasList" } } /** INTERNAL: See the class `Arguments` for further information. */ library class Arguments_ extends @py_arguments { - /** Gets the keyword-only default values of this parameters definition. */ - ExprList getKwDefaults() { py_expr_lists(result, this, 0) } + /** Gets the keyword-only default values of this parameters definition. */ + ExprList getKwDefaults() { py_expr_lists(result, this, 0) } - /** Gets the nth keyword-only default value of this parameters definition. */ - Expr getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } + /** Gets the nth keyword-only default value of this parameters definition. */ + Expr getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } - /** Gets a keyword-only default value of this parameters definition. */ - Expr getAKwDefault() { result = this.getKwDefaults().getAnItem() } + /** Gets a keyword-only default value of this parameters definition. */ + Expr getAKwDefault() { result = this.getKwDefaults().getAnItem() } - /** Gets the default values of this parameters definition. */ - ExprList getDefaults() { py_expr_lists(result, this, 1) } + /** Gets the default values of this parameters definition. */ + ExprList getDefaults() { py_expr_lists(result, this, 1) } - /** Gets the nth default value of this parameters definition. */ - Expr getDefault(int index) { result = this.getDefaults().getItem(index) } + /** Gets the nth default value of this parameters definition. */ + Expr getDefault(int index) { result = this.getDefaults().getItem(index) } - /** Gets a default value of this parameters definition. */ - Expr getADefault() { result = this.getDefaults().getAnItem() } + /** Gets a default value of this parameters definition. */ + Expr getADefault() { result = this.getDefaults().getAnItem() } - /** Gets the annotations of this parameters definition. */ - ExprList getAnnotations() { py_expr_lists(result, this, 2) } + /** Gets the annotations of this parameters definition. */ + ExprList getAnnotations() { py_expr_lists(result, this, 2) } - /** Gets the nth annotation of this parameters definition. */ - Expr getAnnotation(int index) { result = this.getAnnotations().getItem(index) } + /** Gets the nth annotation of this parameters definition. */ + Expr getAnnotation(int index) { result = this.getAnnotations().getItem(index) } - /** Gets an annotation of this parameters definition. */ - Expr getAnAnnotation() { result = this.getAnnotations().getAnItem() } + /** Gets an annotation of this parameters definition. */ + Expr getAnAnnotation() { result = this.getAnnotations().getAnItem() } - /** Gets the *arg annotation of this parameters definition. */ - Expr getVarargannotation() { py_exprs(result, _, this, 3) } + /** Gets the *arg annotation of this parameters definition. */ + Expr getVarargannotation() { py_exprs(result, _, this, 3) } - /** Gets the **kwarg annotation of this parameters definition. */ - Expr getKwargannotation() { py_exprs(result, _, this, 4) } + /** Gets the **kwarg annotation of this parameters definition. */ + Expr getKwargannotation() { py_exprs(result, _, this, 4) } - /** Gets the keyword-only annotations of this parameters definition. */ - ExprList getKwAnnotations() { py_expr_lists(result, this, 5) } + /** Gets the keyword-only annotations of this parameters definition. */ + ExprList getKwAnnotations() { py_expr_lists(result, this, 5) } - /** Gets the nth keyword-only annotation of this parameters definition. */ - Expr getKwAnnotation(int index) { result = this.getKwAnnotations().getItem(index) } + /** Gets the nth keyword-only annotation of this parameters definition. */ + Expr getKwAnnotation(int index) { result = this.getKwAnnotations().getItem(index) } - /** Gets a keyword-only annotation of this parameters definition. */ - Expr getAKwAnnotation() { result = this.getKwAnnotations().getAnItem() } + /** Gets a keyword-only annotation of this parameters definition. */ + Expr getAKwAnnotation() { result = this.getKwAnnotations().getAnItem() } - ArgumentsParent getParent() { py_arguments(this, result) } + ArgumentsParent getParent() { py_arguments(this, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Arguments" } + /** Gets a textual representation of this element. */ + string toString() { result = "Arguments" } } /** INTERNAL: See the class `ArgumentsParent` for further information. */ library class ArgumentsParent_ extends @py_arguments_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ArgumentsParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ArgumentsParent" } } /** INTERNAL: See the class `AstNode` for further information. */ library class AstNode_ extends @py_ast_node { - /** Gets a textual representation of this element. */ - string toString() { result = "AstNode" } + /** Gets a textual representation of this element. */ + string toString() { result = "AstNode" } } /** INTERNAL: See the class `BoolParent` for further information. */ library class BoolParent_ extends @py_bool_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "BoolParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "BoolParent" } } /** INTERNAL: See the class `Boolop` for further information. */ library class Boolop_ extends @py_boolop { - BoolExpr getParent() { py_boolops(this, _, result) } + BoolExpr getParent() { py_boolops(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Boolop" } + /** Gets a textual representation of this element. */ + string toString() { result = "Boolop" } } /** INTERNAL: See the class `Cmpop` for further information. */ library class Cmpop_ extends @py_cmpop { - CmpopList getParent() { py_cmpops(this, _, result, _) } + CmpopList getParent() { py_cmpops(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Cmpop" } + /** Gets a textual representation of this element. */ + string toString() { result = "Cmpop" } } /** INTERNAL: See the class `CmpopList` for further information. */ library class CmpopList_ extends @py_cmpop_list { - Compare getParent() { py_cmpop_lists(this, result) } + Compare getParent() { py_cmpop_lists(this, result) } - /** Gets an item of this comparison operator list */ - Cmpop getAnItem() { py_cmpops(result, _, this, _) } + /** Gets an item of this comparison operator list */ + Cmpop getAnItem() { py_cmpops(result, _, this, _) } - /** Gets the nth item of this comparison operator list */ - Cmpop getItem(int index) { py_cmpops(result, _, this, index) } + /** Gets the nth item of this comparison operator list */ + Cmpop getItem(int index) { py_cmpops(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "CmpopList" } + /** Gets a textual representation of this element. */ + string toString() { result = "CmpopList" } } /** INTERNAL: See the class `Comprehension` for further information. */ library class Comprehension_ extends @py_comprehension { - /** Gets the location of this comprehension. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this comprehension. */ + Location getLocation() { py_locations(result, this) } - /** Gets the iterable of this comprehension. */ - Expr getIter() { py_exprs(result, _, this, 1) } + /** Gets the iterable of this comprehension. */ + Expr getIter() { py_exprs(result, _, this, 1) } - /** Gets the target of this comprehension. */ - Expr getTarget() { py_exprs(result, _, this, 2) } + /** Gets the target of this comprehension. */ + Expr getTarget() { py_exprs(result, _, this, 2) } - /** Gets the conditions of this comprehension. */ - ExprList getIfs() { py_expr_lists(result, this, 3) } + /** Gets the conditions of this comprehension. */ + ExprList getIfs() { py_expr_lists(result, this, 3) } - /** Gets the nth condition of this comprehension. */ - Expr getIf(int index) { result = this.getIfs().getItem(index) } + /** Gets the nth condition of this comprehension. */ + Expr getIf(int index) { result = this.getIfs().getItem(index) } - /** Gets a condition of this comprehension. */ - Expr getAnIf() { result = this.getIfs().getAnItem() } + /** Gets a condition of this comprehension. */ + Expr getAnIf() { result = this.getIfs().getAnItem() } - ComprehensionList getParent() { py_comprehensions(this, result, _) } + ComprehensionList getParent() { py_comprehensions(this, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Comprehension" } + /** Gets a textual representation of this element. */ + string toString() { result = "Comprehension" } } /** INTERNAL: See the class `ComprehensionList` for further information. */ library class ComprehensionList_ extends @py_comprehension_list { - ListComp getParent() { py_comprehension_lists(this, result) } + ListComp getParent() { py_comprehension_lists(this, result) } - /** Gets an item of this comprehension list */ - Comprehension getAnItem() { py_comprehensions(result, this, _) } + /** Gets an item of this comprehension list */ + Comprehension getAnItem() { py_comprehensions(result, this, _) } - /** Gets the nth item of this comprehension list */ - Comprehension getItem(int index) { py_comprehensions(result, this, index) } + /** Gets the nth item of this comprehension list */ + Comprehension getItem(int index) { py_comprehensions(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "ComprehensionList" } + /** Gets a textual representation of this element. */ + string toString() { result = "ComprehensionList" } } /** INTERNAL: See the class `DictItem` for further information. */ library class DictItem_ extends @py_dict_item { - DictItemList getParent() { py_dict_items(this, _, result, _) } + DictItemList getParent() { py_dict_items(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "DictItem" } + /** Gets a textual representation of this element. */ + string toString() { result = "DictItem" } } /** INTERNAL: See the class `DictItemList` for further information. */ library class DictItemList_ extends @py_dict_item_list { - DictItemListParent getParent() { py_dict_item_lists(this, result) } + DictItemListParent getParent() { py_dict_item_lists(this, result) } - /** Gets an item of this dict_item list */ - DictItem getAnItem() { py_dict_items(result, _, this, _) } + /** Gets an item of this dict_item list */ + DictItem getAnItem() { py_dict_items(result, _, this, _) } - /** Gets the nth item of this dict_item list */ - DictItem getItem(int index) { py_dict_items(result, _, this, index) } + /** Gets the nth item of this dict_item list */ + DictItem getItem(int index) { py_dict_items(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "DictItemList" } + /** Gets a textual representation of this element. */ + string toString() { result = "DictItemList" } } /** INTERNAL: See the class `DictItemListParent` for further information. */ library class DictItemListParent_ extends @py_dict_item_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "DictItemListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "DictItemListParent" } } /** INTERNAL: See the class `Expr` for further information. */ library class Expr_ extends @py_expr { - /** Gets the location of this expression. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this expression. */ + Location getLocation() { py_locations(result, this) } - /** Whether the parenthesised property of this expression is true. */ - predicate isParenthesised() { py_bools(this, 1) } + /** Whether the parenthesised property of this expression is true. */ + predicate isParenthesised() { py_bools(this, 1) } - ExprParent getParent() { py_exprs(this, _, result, _) } + ExprParent getParent() { py_exprs(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Expr" } + /** Gets a textual representation of this element. */ + string toString() { result = "Expr" } } /** INTERNAL: See the class `ExprContext` for further information. */ library class ExprContext_ extends @py_expr_context { - ExprContextParent getParent() { py_expr_contexts(this, _, result) } + ExprContextParent getParent() { py_expr_contexts(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "ExprContext" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprContext" } } /** INTERNAL: See the class `ExprContextParent` for further information. */ library class ExprContextParent_ extends @py_expr_context_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprContextParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprContextParent" } } /** INTERNAL: See the class `ExprList` for further information. */ library class ExprList_ extends @py_expr_list { - ExprListParent getParent() { py_expr_lists(this, result, _) } + ExprListParent getParent() { py_expr_lists(this, result, _) } - /** Gets an item of this expression list */ - Expr getAnItem() { py_exprs(result, _, this, _) } + /** Gets an item of this expression list */ + Expr getAnItem() { py_exprs(result, _, this, _) } - /** Gets the nth item of this expression list */ - Expr getItem(int index) { py_exprs(result, _, this, index) } + /** Gets the nth item of this expression list */ + Expr getItem(int index) { py_exprs(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "ExprList" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprList" } } /** INTERNAL: See the class `ExprListParent` for further information. */ library class ExprListParent_ extends @py_expr_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprListParent" } } /** INTERNAL: See the class `ExprOrStmt` for further information. */ library class ExprOrStmt_ extends @py_expr_or_stmt { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprOrStmt" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprOrStmt" } } /** INTERNAL: See the class `ExprParent` for further information. */ library class ExprParent_ extends @py_expr_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "ExprParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "ExprParent" } } /** INTERNAL: See the class `Keyword` for further information. */ library class Keyword_ extends @py_keyword, DictItem { - /** Gets the location of this keyword argument. */ - override Location getLocation() { py_locations(result, this) } + /** Gets the location of this keyword argument. */ + override Location getLocation() { py_locations(result, this) } - /** Gets the value of this keyword argument. */ - Expr getValue() { py_exprs(result, _, this, 1) } + /** Gets the value of this keyword argument. */ + Expr getValue() { py_exprs(result, _, this, 1) } - /** Gets the arg of this keyword argument. */ - string getArg() { py_strs(result, this, 2) } + /** Gets the arg of this keyword argument. */ + string getArg() { py_strs(result, this, 2) } - override string toString() { result = "Keyword" } + override string toString() { result = "Keyword" } } /** INTERNAL: See the class `LocationParent` for further information. */ library class LocationParent_ extends @py_location_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "LocationParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "LocationParent" } } /** INTERNAL: See the class `Operator` for further information. */ library class Operator_ extends @py_operator { - BinaryExpr getParent() { py_operators(this, _, result) } + BinaryExpr getParent() { py_operators(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Operator" } + /** Gets a textual representation of this element. */ + string toString() { result = "Operator" } } /** INTERNAL: See the class `Parameter` for further information. */ library class Parameter_ extends @py_parameter { - /** Gets a textual representation of this element. */ - string toString() { result = "Parameter" } + /** Gets a textual representation of this element. */ + string toString() { result = "Parameter" } } /** INTERNAL: See the class `Scope` for further information. */ library class Scope_ extends @py_scope { - /** Gets a textual representation of this element. */ - string toString() { result = "Scope" } + /** Gets a textual representation of this element. */ + string toString() { result = "Scope" } } /** INTERNAL: See the class `Stmt` for further information. */ library class Stmt_ extends @py_stmt { - /** Gets the location of this statement. */ - Location getLocation() { py_locations(result, this) } + /** Gets the location of this statement. */ + Location getLocation() { py_locations(result, this) } - StmtList getParent() { py_stmts(this, _, result, _) } + StmtList getParent() { py_stmts(this, _, result, _) } - /** Gets a textual representation of this element. */ - string toString() { result = "Stmt" } + /** Gets a textual representation of this element. */ + string toString() { result = "Stmt" } } /** INTERNAL: See the class `StmtList` for further information. */ library class StmtList_ extends @py_stmt_list { - StmtListParent getParent() { py_stmt_lists(this, result, _) } + StmtListParent getParent() { py_stmt_lists(this, result, _) } - /** Gets an item of this statement list */ - Stmt getAnItem() { py_stmts(result, _, this, _) } + /** Gets an item of this statement list */ + Stmt getAnItem() { py_stmts(result, _, this, _) } - /** Gets the nth item of this statement list */ - Stmt getItem(int index) { py_stmts(result, _, this, index) } + /** Gets the nth item of this statement list */ + Stmt getItem(int index) { py_stmts(result, _, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "StmtList" } + /** Gets a textual representation of this element. */ + string toString() { result = "StmtList" } } /** INTERNAL: See the class `StmtListParent` for further information. */ library class StmtListParent_ extends @py_stmt_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "StmtListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "StmtListParent" } } /** INTERNAL: See the class `StringList` for further information. */ library class StringList_ extends @py_str_list { - StrListParent getParent() { py_str_lists(this, result) } + StrListParent getParent() { py_str_lists(this, result) } - /** Gets an item of this string list */ - string getAnItem() { py_strs(result, this, _) } + /** Gets an item of this string list */ + string getAnItem() { py_strs(result, this, _) } - /** Gets the nth item of this string list */ - string getItem(int index) { py_strs(result, this, index) } + /** Gets the nth item of this string list */ + string getItem(int index) { py_strs(result, this, index) } - /** Gets a textual representation of this element. */ - string toString() { result = "StringList" } + /** Gets a textual representation of this element. */ + string toString() { result = "StringList" } } /** INTERNAL: See the class `StrListParent` for further information. */ library class StrListParent_ extends @py_str_list_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "StrListParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "StrListParent" } } /** INTERNAL: See the class `StrParent` for further information. */ library class StrParent_ extends @py_str_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "StrParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "StrParent" } } /** INTERNAL: See the class `Unaryop` for further information. */ library class Unaryop_ extends @py_unaryop { - UnaryExpr getParent() { py_unaryops(this, _, result) } + UnaryExpr getParent() { py_unaryops(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Unaryop" } + /** Gets a textual representation of this element. */ + string toString() { result = "Unaryop" } } /** INTERNAL: See the class `VariableParent` for further information. */ library class VariableParent_ extends @py_variable_parent { - /** Gets a textual representation of this element. */ - string toString() { result = "VariableParent" } + /** Gets a textual representation of this element. */ + string toString() { result = "VariableParent" } } diff --git a/python/ql/src/semmle/python/Class.qll b/python/ql/src/semmle/python/Class.qll index 3c7c74fa194..a2a4c88a53e 100644 --- a/python/ql/src/semmle/python/Class.qll +++ b/python/ql/src/semmle/python/Class.qll @@ -9,167 +9,167 @@ import python * It is recommended to use `ClassDef` instead. */ class ClassExpr extends ClassExpr_ { - /** Gets the metaclass expression */ - Expr getMetaClass() { - if major_version() = 3 - then - exists(Keyword metacls | - this.getAKeyword() = metacls and - metacls.getArg() = "metaclass" and - result = metacls.getValue() - ) - else - exists(Assign a | - a = this.getInnerScope().getAStmt() and - a.getATarget().(Name).getId() = "__metaclass__" and - result = a.getValue() - ) - } + /** Gets the metaclass expression */ + Expr getMetaClass() { + if major_version() = 3 + then + exists(Keyword metacls | + this.getAKeyword() = metacls and + metacls.getArg() = "metaclass" and + result = metacls.getValue() + ) + else + exists(Assign a | + a = this.getInnerScope().getAStmt() and + a.getATarget().(Name).getId() = "__metaclass__" and + result = a.getValue() + ) + } - /** Gets the nth keyword argument of this class definition. */ - override DictUnpackingOrKeyword getKeyword(int index) { - result = this.getKeywords().getItem(index) - } + /** Gets the nth keyword argument of this class definition. */ + override DictUnpackingOrKeyword getKeyword(int index) { + result = this.getKeywords().getItem(index) + } - /** Gets a keyword argument of this class definition. */ - override DictUnpackingOrKeyword getAKeyword() { result = this.getKeywords().getAnItem() } + /** Gets a keyword argument of this class definition. */ + override DictUnpackingOrKeyword getAKeyword() { result = this.getKeywords().getAnItem() } - override Expr getASubExpression() { - result = this.getABase() or - result = this.getAKeyword().getValue() or - result = this.getKwargs() or - result = this.getStarargs() - } + override Expr getASubExpression() { + result = this.getABase() or + result = this.getAKeyword().getValue() or + result = this.getKwargs() or + result = this.getStarargs() + } - /** Gets a call corresponding to a decorator of this class definition. */ - Call getADecoratorCall() { - result.getArg(0) = this or - result.getArg(0) = this.getADecoratorCall() - } + /** Gets a call corresponding to a decorator of this class definition. */ + Call getADecoratorCall() { + result.getArg(0) = this or + result.getArg(0) = this.getADecoratorCall() + } - /** Gets a decorator of this function expression */ - Expr getADecorator() { result = this.getADecoratorCall().getFunc() } + /** Gets a decorator of this function expression */ + Expr getADecorator() { result = this.getADecoratorCall().getFunc() } - override AstNode getAChildNode() { - result = this.getASubExpression() - or - result = this.getInnerScope() - } + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getInnerScope() + } - /** Gets a tuple (*) argument of this class definition. */ - Expr getStarargs() { result = this.getABase().(Starred).getValue() } + /** Gets a tuple (*) argument of this class definition. */ + Expr getStarargs() { result = this.getABase().(Starred).getValue() } - /** Gets a dictionary (**) argument of this class definition. */ - Expr getKwargs() { result = this.getAKeyword().(DictUnpacking).getValue() } + /** Gets a dictionary (**) argument of this class definition. */ + Expr getKwargs() { result = this.getAKeyword().(DictUnpacking).getValue() } } /** A class statement. Note that ClassDef extends Assign as a class definition binds the newly created class */ class ClassDef extends Assign { - /* syntax: class name(...): ... */ - ClassDef() { - /* This is an artificial assignment the rhs of which is a (possibly decorated) ClassExpr */ - exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall()) - } + /* syntax: class name(...): ... */ + ClassDef() { + /* This is an artificial assignment the rhs of which is a (possibly decorated) ClassExpr */ + exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall()) + } - override string toString() { result = "ClassDef" } + override string toString() { result = "ClassDef" } - /** Gets the class for this statement */ - Class getDefinedClass() { - exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall() | - result = c.getInnerScope() - ) - } + /** Gets the class for this statement */ + Class getDefinedClass() { + exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall() | + result = c.getInnerScope() + ) + } - override Stmt getLastStatement() { result = this.getDefinedClass().getLastStatement() } + override Stmt getLastStatement() { result = this.getDefinedClass().getLastStatement() } } /** The scope of a class. This is the scope of all the statements within the class definition */ class Class extends Class_, Scope, AstNode { - /** - * Use getADecorator() instead of getDefinition().getADecorator() - * Use getMetaClass() instead of getDefinition().getMetaClass() - */ - deprecated ClassExpr getDefinition() { result = this.getParent() } + /** + * Use getADecorator() instead of getDefinition().getADecorator() + * Use getMetaClass() instead of getDefinition().getMetaClass() + */ + deprecated ClassExpr getDefinition() { result = this.getParent() } - /** Gets a defined init method of this class */ - Function getInitMethod() { result.getScope() = this and result.isInitMethod() } + /** Gets a defined init method of this class */ + Function getInitMethod() { result.getScope() = this and result.isInitMethod() } - /** Gets a method defined in this class */ - Function getAMethod() { result.getScope() = this } + /** Gets a method defined in this class */ + Function getAMethod() { result.getScope() = this } - override Location getLocation() { py_scope_location(result, this) } + override Location getLocation() { py_scope_location(result, this) } - /** Gets the scope (module, class or function) in which this class is defined */ - override Scope getEnclosingScope() { result = this.getParent().getScope() } + /** Gets the scope (module, class or function) in which this class is defined */ + override Scope getEnclosingScope() { result = this.getParent().getScope() } - /** Use getEnclosingScope() instead */ - override Scope getScope() { result = this.getParent().getScope() } + /** Use getEnclosingScope() instead */ + override Scope getScope() { result = this.getParent().getScope() } - override string toString() { result = "Class " + this.getName() } + override string toString() { result = "Class " + this.getName() } - /** Gets the statements forming the body of this class */ - override StmtList getBody() { result = Class_.super.getBody() } + /** Gets the statements forming the body of this class */ + override StmtList getBody() { result = Class_.super.getBody() } - /** Gets the nth statement in the class */ - override Stmt getStmt(int index) { result = Class_.super.getStmt(index) } + /** Gets the nth statement in the class */ + override Stmt getStmt(int index) { result = Class_.super.getStmt(index) } - /** Gets a statement in the class */ - override Stmt getAStmt() { result = Class_.super.getAStmt() } + /** Gets a statement in the class */ + override Stmt getAStmt() { result = Class_.super.getAStmt() } - /** Gets the name used to define this class */ - override string getName() { result = Class_.super.getName() } + /** Gets the name used to define this class */ + override string getName() { result = Class_.super.getName() } - /** Holds if this expression may have a side effect (as determined purely from its syntax). */ - predicate hasSideEffects() { any() } + /** Holds if this expression may have a side effect (as determined purely from its syntax). */ + predicate hasSideEffects() { any() } - /** Holds if this is probably a mixin (has 'mixin' or similar in name or docstring) */ - predicate isProbableMixin() { - ( - this.getName().toLowerCase().matches("%mixin%") - or - this.getDocString().getText().toLowerCase().matches("%mixin%") - or - this.getDocString().getText().toLowerCase().matches("%mix-in%") - ) - } + /** Holds if this is probably a mixin (has 'mixin' or similar in name or docstring) */ + predicate isProbableMixin() { + ( + this.getName().toLowerCase().matches("%mixin%") + or + this.getDocString().getText().toLowerCase().matches("%mixin%") + or + this.getDocString().getText().toLowerCase().matches("%mix-in%") + ) + } - override AstNode getAChildNode() { result = this.getAStmt() } + override AstNode getAChildNode() { result = this.getAStmt() } - /** Gets a decorator of this class. */ - Expr getADecorator() { result = this.getParent().getADecorator() } + /** Gets a decorator of this class. */ + Expr getADecorator() { result = this.getParent().getADecorator() } - /** Gets the metaclass expression */ - Expr getMetaClass() { result = this.getParent().getMetaClass() } + /** Gets the metaclass expression */ + Expr getMetaClass() { result = this.getParent().getMetaClass() } - /** Gets the ClassObject corresponding to this class */ - ClassObject getClassObject() { result.getOrigin() = this.getParent() } + /** Gets the ClassObject corresponding to this class */ + ClassObject getClassObject() { result.getOrigin() = this.getParent() } - /** Gets the nth base of this class definition. */ - Expr getBase(int index) { result = this.getParent().getBase(index) } + /** Gets the nth base of this class definition. */ + Expr getBase(int index) { result = this.getParent().getBase(index) } - /** Gets a base of this class definition. */ - Expr getABase() { result = this.getParent().getABase() } + /** Gets a base of this class definition. */ + Expr getABase() { result = this.getParent().getABase() } - /** Gets the metrics for this class */ - ClassMetrics getMetrics() { result = this } + /** Gets the metrics for this class */ + ClassMetrics getMetrics() { result = this } - /** - * Gets the qualified name for this class. - * Should return the same name as the `__qualname__` attribute on classes in Python 3. - */ - string getQualifiedName() { - this.getScope() instanceof Module and result = this.getName() - or - exists(string enclosing_name | - enclosing_name = this.getScope().(Function).getQualifiedName() - or - enclosing_name = this.getScope().(Class).getQualifiedName() - | - result = enclosing_name + "." + this.getName() - ) - } + /** + * Gets the qualified name for this class. + * Should return the same name as the `__qualname__` attribute on classes in Python 3. + */ + string getQualifiedName() { + this.getScope() instanceof Module and result = this.getName() + or + exists(string enclosing_name | + enclosing_name = this.getScope().(Function).getQualifiedName() + or + enclosing_name = this.getScope().(Class).getQualifiedName() + | + result = enclosing_name + "." + this.getName() + ) + } - override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } + override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } - override predicate contains(AstNode inner) { Scope.super.contains(inner) } + override predicate contains(AstNode inner) { Scope.super.contains(inner) } } diff --git a/python/ql/src/semmle/python/Comment.qll b/python/ql/src/semmle/python/Comment.qll index ce90b631308..94dd429e404 100644 --- a/python/ql/src/semmle/python/Comment.qll +++ b/python/ql/src/semmle/python/Comment.qll @@ -6,96 +6,99 @@ import python /** A source code comment */ class Comment extends @py_comment { - /** Gets the full text of the comment including the leading '#' */ - string getText() { py_comments(this, result, _) } + /** Gets the full text of the comment including the leading '#' */ + string getText() { py_comments(this, result, _) } - /** Gets the contents of the comment excluding the leading '#' */ - string getContents() { result = this.getText().suffix(1) } + /** Gets the contents of the comment excluding the leading '#' */ + string getContents() { result = this.getText().suffix(1) } - Location getLocation() { py_comments(this, _, result) } + Location getLocation() { py_comments(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Comment " + this.getText() } + /** Gets a textual representation of this element. */ + string toString() { result = "Comment " + this.getText() } - /** - * Gets this immediately following comment. - * Blanks line are allowed between this comment and the following comment, - * but code or other comments are not. - */ - Comment getFollowing() { - exists(File f, int n | this.file_line(f, n) | - result.file_line(f, n + 1) - or - result.file_line(f, n + 2) and f.emptyLine(n + 1) - or - result.file_line(f, n + 3) and f.emptyLine(n + 2) and f.emptyLine(n + 1) - ) - } + /** + * Gets this immediately following comment. + * Blanks line are allowed between this comment and the following comment, + * but code or other comments are not. + */ + Comment getFollowing() { + exists(File f, int n | this.file_line(f, n) | + result.file_line(f, n + 1) + or + result.file_line(f, n + 2) and f.emptyLine(n + 1) + or + result.file_line(f, n + 3) and f.emptyLine(n + 2) and f.emptyLine(n + 1) + ) + } - private predicate file_line(File f, int n) { - this.getLocation().getFile() = f and - this.getLocation().getStartLine() = n - } + private predicate file_line(File f, int n) { + this.getLocation().getFile() = f and + this.getLocation().getStartLine() = n + } } private predicate comment_block_part(Comment start, Comment part, int i) { - not exists(Comment prev | prev.getFollowing() = part) and - exists(Comment following | part.getFollowing() = following) and - start = part and - i = 1 - or - exists(Comment prev | - comment_block_part(start, prev, i - 1) and - part = prev.getFollowing() - ) + not exists(Comment prev | prev.getFollowing() = part) and + exists(Comment following | part.getFollowing() = following) and + start = part and + i = 1 + or + exists(Comment prev | + comment_block_part(start, prev, i - 1) and + part = prev.getFollowing() + ) } /** A block of consecutive comments */ class CommentBlock extends @py_comment { - CommentBlock() { comment_block_part(this, _, _) } + CommentBlock() { comment_block_part(this, _, _) } - private Comment last() { comment_block_part(this, result, this.length()) } + private Comment last() { comment_block_part(this, result, this.length()) } - /** Gets a textual representation of this element. */ - string toString() { result = "Comment block" } + /** Gets a textual representation of this element. */ + string toString() { result = "Comment block" } - /** The length of this comment block (in comments) */ - int length() { result = max(int i | comment_block_part(this, _, i)) } + /** The length of this comment block (in comments) */ + int length() { result = max(int i | comment_block_part(this, _, i)) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and - exists(Comment end | end = this.last() | end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn)) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + exists(Comment end | end = this.last() | + end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn) + ) + } - /** Holds if this comment block contains `c`. */ - predicate contains(Comment c) { - comment_block_part(this, c, _) + /** Holds if this comment block contains `c`. */ + predicate contains(Comment c) { + comment_block_part(this, c, _) + or + this = c + } + + /** Gets a string representation of this comment block. */ + string getContents() { + result = + concat(Comment c, int i | + comment_block_part(this, c, i) or - this = c - } - - /** Gets a string representation of this comment block. */ - string getContents() { - result = - concat(Comment c, int i | - comment_block_part(this, c, i) - or - this = c and i = 0 - | - c.getContents() order by i - ) - } + this = c and i = 0 + | + c.getContents() order by i + ) + } } /** A type-hint comment. Any comment that starts with `# type:` */ class TypeHintComment extends Comment { - TypeHintComment() { this.getText().regexpMatch("# +type:.*") } + TypeHintComment() { this.getText().regexpMatch("# +type:.*") } } diff --git a/python/ql/src/semmle/python/Comparisons.qll b/python/ql/src/semmle/python/Comparisons.qll index dd0a1773791..a82d7a8a9a2 100644 --- a/python/ql/src/semmle/python/Comparisons.qll +++ b/python/ql/src/semmle/python/Comparisons.qll @@ -6,74 +6,74 @@ import python /** A class representing the six comparison operators, ==, !=, <, <=, > and >=. */ class CompareOp extends int { - CompareOp() { this in [1 .. 6] } + CompareOp() { this in [1 .. 6] } - /** Gets the logical inverse operator */ - CompareOp invert() { - this = eq() and result = ne() - or - this = ne() and result = eq() - or - this = lt() and result = ge() - or - this = gt() and result = le() - or - this = le() and result = gt() - or - this = ge() and result = lt() - } + /** Gets the logical inverse operator */ + CompareOp invert() { + this = eq() and result = ne() + or + this = ne() and result = eq() + or + this = lt() and result = ge() + or + this = gt() and result = le() + or + this = le() and result = gt() + or + this = ge() and result = lt() + } - /** Gets the reverse operator (swapping the operands) */ - CompareOp reverse() { - this = eq() and result = eq() - or - this = ne() and result = ne() - or - this = lt() and result = gt() - or - this = gt() and result = lt() - or - this = le() and result = ge() - or - this = ge() and result = le() - } + /** Gets the reverse operator (swapping the operands) */ + CompareOp reverse() { + this = eq() and result = eq() + or + this = ne() and result = ne() + or + this = lt() and result = gt() + or + this = gt() and result = lt() + or + this = le() and result = ge() + or + this = ge() and result = le() + } - /** Gets the textual representation of `this`. */ - string repr() { - this = eq() and result = "==" - or - this = ne() and result = "!=" - or - this = lt() and result = "<" - or - this = gt() and result = ">" - or - this = le() and result = "<=" - or - this = ge() and result = ">=" - } + /** Gets the textual representation of `this`. */ + string repr() { + this = eq() and result = "==" + or + this = ne() and result = "!=" + or + this = lt() and result = "<" + or + this = gt() and result = ">" + or + this = le() and result = "<=" + or + this = ge() and result = ">=" + } - /** Holds if `op` is the `Cmpop` corresponding to `this`. */ - predicate forOp(Cmpop op) { - op instanceof Eq and this = eq() - or - op instanceof NotEq and this = ne() - or - op instanceof Lt and this = lt() - or - op instanceof LtE and this = le() - or - op instanceof Gt and this = gt() - or - op instanceof GtE and this = ge() - } + /** Holds if `op` is the `Cmpop` corresponding to `this`. */ + predicate forOp(Cmpop op) { + op instanceof Eq and this = eq() + or + op instanceof NotEq and this = ne() + or + op instanceof Lt and this = lt() + or + op instanceof LtE and this = le() + or + op instanceof Gt and this = gt() + or + op instanceof GtE and this = ge() + } - /** Return this if isTrue is true, otherwise returns the inverse */ - CompareOp conditional(boolean isTrue) { - result = this and isTrue = true - or - result = this.invert() and isTrue = false - } + /** Return this if isTrue is true, otherwise returns the inverse */ + CompareOp conditional(boolean isTrue) { + result = this and isTrue = true + or + result = this.invert() and isTrue = false + } } /** The `CompareOp` for "equals". */ @@ -97,74 +97,74 @@ CompareOp ge() { result = 6 } /* Workaround precision limits in floating point numbers */ bindingset[x] private predicate ok_magnitude(float x) { - x > -9007199254740992.0 and // -2**53 - x < 9007199254740992.0 // 2**53 + x > -9007199254740992.0 and // -2**53 + x < 9007199254740992.0 // 2**53 } bindingset[x, y] private float add(float x, float y) { - ok_magnitude(x) and - ok_magnitude(y) and - ok_magnitude(result) and - result = x + y + ok_magnitude(x) and + ok_magnitude(y) and + ok_magnitude(result) and + result = x + y } bindingset[x, y] private float sub(float x, float y) { - ok_magnitude(x) and - ok_magnitude(y) and - ok_magnitude(result) and - result = x - y + ok_magnitude(x) and + ok_magnitude(y) and + ok_magnitude(result) and + result = x - y } /** Normalise equality cmp into the form `left op right + k`. */ private predicate test( - ControlFlowNode cmp, ControlFlowNode left, CompareOp op, ControlFlowNode right, float k + ControlFlowNode cmp, ControlFlowNode left, CompareOp op, ControlFlowNode right, float k ) { - simple_test(cmp, left, op, right) and k = 0 - or - add_test(cmp, left, op, right, k) - or - not_test(cmp, left, op, right, k) - or - subtract_test(cmp, left, op, right, k) - or - exists(float c | test(cmp, right, op.reverse(), left, c) and k = -c) + simple_test(cmp, left, op, right) and k = 0 + or + add_test(cmp, left, op, right, k) + or + not_test(cmp, left, op, right, k) + or + subtract_test(cmp, left, op, right, k) + or + exists(float c | test(cmp, right, op.reverse(), left, c) and k = -c) } /** Various simple tests in left op right + k form. */ private predicate simple_test(CompareNode cmp, ControlFlowNode l, CompareOp cmpop, ControlFlowNode r) { - exists(Cmpop op | cmp.operands(l, op, r) and cmpop.forOp(op)) + exists(Cmpop op | cmp.operands(l, op, r) and cmpop.forOp(op)) } private predicate add_test_left( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode lhs, float c, float x, Num n | - lhs.getNode().getOp() instanceof Add and - test(cmp, lhs, op, r, c) and - x = n.getN().toFloat() and - k = sub(c, x) - | - l = lhs.getLeft() and n = lhs.getRight().getNode() - or - l = lhs.getRight() and n = lhs.getLeft().getNode() - ) + exists(BinaryExprNode lhs, float c, float x, Num n | + lhs.getNode().getOp() instanceof Add and + test(cmp, lhs, op, r, c) and + x = n.getN().toFloat() and + k = sub(c, x) + | + l = lhs.getLeft() and n = lhs.getRight().getNode() + or + l = lhs.getRight() and n = lhs.getLeft().getNode() + ) } private predicate add_test_right( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode rhs, float c, float x, Num n | - rhs.getNode().getOp() instanceof Add and - test(cmp, l, op, rhs, c) and - x = n.getN().toFloat() and - k = add(c, x) - | - r = rhs.getLeft() and n = rhs.getRight().getNode() - or - r = rhs.getRight() and n = rhs.getLeft().getNode() - ) + exists(BinaryExprNode rhs, float c, float x, Num n | + rhs.getNode().getOp() instanceof Add and + test(cmp, l, op, rhs, c) and + x = n.getN().toFloat() and + k = add(c, x) + | + r = rhs.getLeft() and n = rhs.getRight().getNode() + or + r = rhs.getRight() and n = rhs.getLeft().getNode() + ) } /* @@ -173,39 +173,39 @@ private predicate add_test_right( */ private predicate add_test( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - add_test_left(cmp, l, op, r, k) - or - add_test_right(cmp, l, op, r, k) + add_test_left(cmp, l, op, r, k) + or + add_test_right(cmp, l, op, r, k) } private predicate subtract_test_left( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode lhs, float c, float x, Num n | - lhs.getNode().getOp() instanceof Sub and - test(cmp, lhs, op, r, c) and - l = lhs.getLeft() and - n = lhs.getRight().getNode() and - x = n.getN().toFloat() - | - k = add(c, x) - ) + exists(BinaryExprNode lhs, float c, float x, Num n | + lhs.getNode().getOp() instanceof Sub and + test(cmp, lhs, op, r, c) and + l = lhs.getLeft() and + n = lhs.getRight().getNode() and + x = n.getN().toFloat() + | + k = add(c, x) + ) } private predicate subtract_test_right( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - exists(BinaryExprNode rhs, float c, float x, Num n | - rhs.getNode().getOp() instanceof Sub and - test(cmp, l, op, rhs, c) and - r = rhs.getRight() and - n = rhs.getLeft().getNode() and - x = n.getN().toFloat() - | - k = sub(c, x) - ) + exists(BinaryExprNode rhs, float c, float x, Num n | + rhs.getNode().getOp() instanceof Sub and + test(cmp, l, op, rhs, c) and + r = rhs.getRight() and + n = rhs.getLeft().getNode() and + x = n.getN().toFloat() + | + k = sub(c, x) + ) } /* @@ -214,18 +214,18 @@ private predicate subtract_test_right( */ private predicate subtract_test( - CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - subtract_test_left(cmp, l, op, r, k) - or - subtract_test_right(cmp, l, op, r, k) + subtract_test_left(cmp, l, op, r, k) + or + subtract_test_right(cmp, l, op, r, k) } private predicate not_test( - UnaryExprNode u, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k + UnaryExprNode u, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k ) { - u.getNode().getOp() instanceof Not and - test(u.getOperand(), l, op.invert(), r, k) + u.getNode().getOp() instanceof Not and + test(u.getOperand(), l, op.invert(), r, k) } /** @@ -233,243 +233,243 @@ private predicate not_test( * `k` is a floating point constant and `OP` is one of `<=`, `>`, `==` or `!=`. */ class Comparison extends ControlFlowNode { - Comparison() { test(this, _, _, _, _) } + Comparison() { test(this, _, _, _, _) } - /** Whether this condition tests `l op r + k` */ - predicate tests(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { - test(this, l, op, r, k) - } + /** Whether this condition tests `l op r + k` */ + predicate tests(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { + test(this, l, op, r, k) + } - /** Whether this condition tests `l op k` */ - predicate tests(ControlFlowNode l, CompareOp op, float k) { - exists(ControlFlowNode r, float x, float c | test(this, l, op, r, c) | - x = r.getNode().(Num).getN().toFloat() and - k = add(c, x) - ) - } + /** Whether this condition tests `l op k` */ + predicate tests(ControlFlowNode l, CompareOp op, float k) { + exists(ControlFlowNode r, float x, float c | test(this, l, op, r, c) | + x = r.getNode().(Num).getN().toFloat() and + k = add(c, x) + ) + } - /* - * The following predicates determine whether this test, when its result is `thisIsTrue`, - * is equivalent to the predicate `v OP k` or `v1 OP v2 + k`. - * For example, the test `x <= y` being false, is equivalent to the predicate `x > y`. - */ + /* + * The following predicates determine whether this test, when its result is `thisIsTrue`, + * is equivalent to the predicate `v OP k` or `v1 OP v2 + k`. + * For example, the test `x <= y` being false, is equivalent to the predicate `x > y`. + */ - private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), eq().conditional(thisIsTrue), k) - } + private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), eq().conditional(thisIsTrue), k) + } - private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), ne().conditional(thisIsTrue), k) - } + private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), ne().conditional(thisIsTrue), k) + } - private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), lt().conditional(thisIsTrue), k) - } + private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), lt().conditional(thisIsTrue), k) + } - private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), le().conditional(thisIsTrue), k) - } + private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), le().conditional(thisIsTrue), k) + } - private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), gt().conditional(thisIsTrue), k) - } + private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), gt().conditional(thisIsTrue), k) + } - private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v, float k) { - this.tests(v.getAUse(), ge().conditional(thisIsTrue), k) - } + private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), ge().conditional(thisIsTrue), k) + } - private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), eq().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), eq().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), ne().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), ne().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), lt().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), lt().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), le().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), le().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), gt().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), gt().conditional(thisIsTrue), v2.getAUse(), k) + } - private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { - this.tests(v1.getAUse(), ge().conditional(thisIsTrue), v2.getAUse(), k) - } + private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), ge().conditional(thisIsTrue), v2.getAUse(), k) + } - /** - * Whether the result of this comparison being `thisIsTrue` implies that the result of `that` is `isThatTrue`. - * In other words, does the predicate that is equivalent to the result of `this` being `thisIsTrue` - * imply the predicate that is equivalent to the result of `that` being `thatIsTrue`. - * For example, assume that there are two tests, which when normalised have the form `x < y` and `x > y + 1`. - * Then the test `x < y` having a true result, implies that the test `x > y + 1` will have a false result. - * (`x < y` having a false result implies nothing about `x > y + 1`) - */ - predicate impliesThat(boolean thisIsTrue, Comparison that, boolean thatIsTrue) { - /* `v == k` => `v == k` */ - exists(SsaVariable v, float k1, float k2 | - this.equivalentToEq(thisIsTrue, v, k1) and - that.equivalentToEq(thatIsTrue, v, k2) and - eq(k1, k2) - or - this.equivalentToNotEq(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - eq(k1, k2) - ) - or - exists(SsaVariable v, float k1, float k2 | - /* `v < k1` => `v != k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - le(k1, k2) - or - /* `v <= k1` => `v != k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - lt(k1, k2) - or - /* `v > k1` => `v != k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - ge(k1, k2) - or - /* `v >= k1` => `v != k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v, k1) and - that.equivalentToNotEq(thatIsTrue, v, k2) and - gt(k1, k2) - ) - or - exists(SsaVariable v, float k1, float k2 | - /* `v < k1` => `v < k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v, k1) and - that.equivalentToLt(thatIsTrue, v, k2) and - le(k1, k2) - or - /* `v < k1` => `v <= k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v, k1) and - that.equivalentToLtEq(thatIsTrue, v, k2) and - le(k1, k2) - or - /* `v <= k1` => `v < k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v, k1) and - that.equivalentToLt(thatIsTrue, v, k2) and - lt(k1, k2) - or - /* `v <= k1` => `v <= k2` iff k1 <= k2 */ - this.equivalentToLtEq(thisIsTrue, v, k1) and - that.equivalentToLtEq(thatIsTrue, v, k2) and - le(k1, k2) - ) - or - exists(SsaVariable v, float k1, float k2 | - /* `v > k1` => `v >= k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v, k1) and - that.equivalentToGt(thatIsTrue, v, k2) and - ge(k1, k2) - or - /* `v > k1` => `v >= k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v, k1) and - that.equivalentToGtEq(thatIsTrue, v, k2) and - ge(k1, k2) - or - /* `v >= k1` => `v > k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v, k1) and - that.equivalentToGt(thatIsTrue, v, k2) and - gt(k1, k2) - or - /* `v >= k1` => `v >= k2` iff k1 >= k2 */ - this.equivalentToGtEq(thisIsTrue, v, k1) and - that.equivalentToGtEq(thatIsTrue, v, k2) and - ge(k1, k2) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k | - /* `v1 == v2 + k` => `v1 == v2 + k` */ - this.equivalentToEq(thisIsTrue, v1, v2, k) and - that.equivalentToEq(thatIsTrue, v1, v2, k) - or - this.equivalentToNotEq(thisIsTrue, v1, v2, k) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | - /* `v1 < v2 + k1` => `v1 != v2 + k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - or - /* `v1 <= v2 + k1` => `v1 != v2 + k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - lt(k1, k2) - or - /* `v1 > v2 + k1` => `v1 != v2 + k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - or - /* `v1 >= v2 + k1` => `v1 != v2 + k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and - gt(k1, k2) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | - /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - or - /* `v1 < v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ - this.equivalentToLt(thisIsTrue, v1, v2, k1) and - that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - or - /* `v1 <= v2 + k1` => `v1 < v2 + k2` iff k1 < k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToLt(thatIsTrue, v1, v2, k2) and - lt(k1, k2) - or - /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ - this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and - le(k1, k2) - ) - or - exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | - /* `v1 > v2 + k1` => `v1 > v2 + k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v1, v2, k1) and - that.equivalentToGt(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - or - /* `v1 > v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ - this.equivalentToGt(thisIsTrue, v1, v2, k1) and - that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - or - /* `v1 >= v2 + k1` => `v2 > v2 + k2` iff k1 > k2 */ - this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToGt(thatIsTrue, v1, v2, k2) and - gt(k1, k2) - or - /* `v1 >= v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ - this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and - that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and - ge(k1, k2) - ) - } + /** + * Whether the result of this comparison being `thisIsTrue` implies that the result of `that` is `isThatTrue`. + * In other words, does the predicate that is equivalent to the result of `this` being `thisIsTrue` + * imply the predicate that is equivalent to the result of `that` being `thatIsTrue`. + * For example, assume that there are two tests, which when normalised have the form `x < y` and `x > y + 1`. + * Then the test `x < y` having a true result, implies that the test `x > y + 1` will have a false result. + * (`x < y` having a false result implies nothing about `x > y + 1`) + */ + predicate impliesThat(boolean thisIsTrue, Comparison that, boolean thatIsTrue) { + /* `v == k` => `v == k` */ + exists(SsaVariable v, float k1, float k2 | + this.equivalentToEq(thisIsTrue, v, k1) and + that.equivalentToEq(thatIsTrue, v, k2) and + eq(k1, k2) + or + this.equivalentToNotEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + eq(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v < k1` => `v != k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v <= k1` => `v != k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + lt(k1, k2) + or + /* `v > k1` => `v != k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v >= k1` => `v != k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + gt(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v < k1` => `v < k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToLt(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v < k1` => `v <= k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToLtEq(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v <= k1` => `v < k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToLt(thatIsTrue, v, k2) and + lt(k1, k2) + or + /* `v <= k1` => `v <= k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToLtEq(thatIsTrue, v, k2) and + le(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v > k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToGt(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v > k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToGtEq(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v >= k1` => `v > k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToGt(thatIsTrue, v, k2) and + gt(k1, k2) + or + /* `v >= k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToGtEq(thatIsTrue, v, k2) and + ge(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k | + /* `v1 == v2 + k` => `v1 == v2 + k` */ + this.equivalentToEq(thisIsTrue, v1, v2, k) and + that.equivalentToEq(thatIsTrue, v1, v2, k) + or + this.equivalentToNotEq(thisIsTrue, v1, v2, k) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 < v2 + k1` => `v1 != v2 + k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 != v2 + k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + lt(k1, k2) + or + /* `v1 > v2 + k1` => `v1 != v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 >= v2 + k1` => `v1 != v2 + k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + gt(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 < v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 < v2 + k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLt(thatIsTrue, v1, v2, k2) and + lt(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 > v2 + k1` => `v1 > v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToGt(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 > v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 >= v2 + k1` => `v2 > v2 + k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToGt(thatIsTrue, v1, v2, k2) and + gt(k1, k2) + or + /* `v1 >= v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + ) + } } /* Work around differences in floating-point comparisons between Python and QL */ private predicate is_zero(float x) { - x = 0.0 - or - x = -0.0 + x = 0.0 + or + x = -0.0 } bindingset[x, y] @@ -492,33 +492,33 @@ private predicate ge(float x, float y) { lt(y, x) or eq(x, y) } * in which the condition is an instance of `Comparison` */ class ComparisonControlBlock extends ConditionBlock { - ComparisonControlBlock() { this.getLastNode() instanceof Comparison } + ComparisonControlBlock() { this.getLastNode() instanceof Comparison } - /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ - predicate controls(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k, BasicBlock b) { - exists(boolean control | - this.controls(b, control) and this.getTest().tests(l, op, r, k) and control = true - or - this.controls(b, control) and this.getTest().tests(l, op.invert(), r, k) and control = false - ) - } + /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ + predicate controls(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k, BasicBlock b) { + exists(boolean control | + this.controls(b, control) and this.getTest().tests(l, op, r, k) and control = true + or + this.controls(b, control) and this.getTest().tests(l, op.invert(), r, k) and control = false + ) + } - /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ - predicate controls(ControlFlowNode l, CompareOp op, float k, BasicBlock b) { - exists(boolean control | - this.controls(b, control) and this.getTest().tests(l, op, k) and control = true - or - this.controls(b, control) and this.getTest().tests(l, op.invert(), k) and control = false - ) - } + /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ + predicate controls(ControlFlowNode l, CompareOp op, float k, BasicBlock b) { + exists(boolean control | + this.controls(b, control) and this.getTest().tests(l, op, k) and control = true + or + this.controls(b, control) and this.getTest().tests(l, op.invert(), k) and control = false + ) + } - Comparison getTest() { this.getLastNode() = result } + Comparison getTest() { this.getLastNode() = result } - /** Whether this conditional guard implies that, in block `b`, the result of `that` is `thatIsTrue` */ - predicate impliesThat(BasicBlock b, Comparison that, boolean thatIsTrue) { - exists(boolean controlSense | - this.controls(b, controlSense) and - this.getTest().impliesThat(controlSense, that, thatIsTrue) - ) - } + /** Whether this conditional guard implies that, in block `b`, the result of `that` is `thatIsTrue` */ + predicate impliesThat(BasicBlock b, Comparison that, boolean thatIsTrue) { + exists(boolean controlSense | + this.controls(b, controlSense) and + this.getTest().impliesThat(controlSense, that, thatIsTrue) + ) + } } diff --git a/python/ql/src/semmle/python/Comprehensions.qll b/python/ql/src/semmle/python/Comprehensions.qll index d3cd82d4fd6..1e8d3cc109b 100644 --- a/python/ql/src/semmle/python/Comprehensions.qll +++ b/python/ql/src/semmle/python/Comprehensions.qll @@ -2,109 +2,109 @@ import python /** Base class for list, set and dictionary comprehensions, and generator expressions. */ abstract class Comp extends Expr { - abstract Function getFunction(); + abstract Function getFunction(); - /** Gets the iteration variable for the nth innermost generator of this list comprehension */ - Variable getIterationVariable(int n) { - result.getAnAccess() = this.getNthInnerLoop(n).getTarget() - } + /** Gets the iteration variable for the nth innermost generator of this list comprehension */ + Variable getIterationVariable(int n) { + result.getAnAccess() = this.getNthInnerLoop(n).getTarget() + } - private For getNthInnerLoop(int n) { - n = 0 and result = this.getFunction().getStmt(0) - or - result = this.getNthInnerLoop(n - 1).getStmt(0) - } + private For getNthInnerLoop(int n) { + n = 0 and result = this.getFunction().getStmt(0) + or + result = this.getNthInnerLoop(n - 1).getStmt(0) + } - /** Gets the iteration variable for a generator of this list comprehension */ - Variable getAnIterationVariable() { result = this.getIterationVariable(_) } + /** Gets the iteration variable for a generator of this list comprehension */ + Variable getAnIterationVariable() { result = this.getIterationVariable(_) } - /** Gets the scope in which the body of this list comprehension evaluates. */ - Scope getEvaluatingScope() { result = this.getFunction() } + /** Gets the scope in which the body of this list comprehension evaluates. */ + Scope getEvaluatingScope() { result = this.getFunction() } - /** Gets the expression for elements of this comprehension. */ - Expr getElt() { - exists(Yield yield, Stmt body | - result = yield.getValue() and - body = this.getNthInnerLoop(_).getAStmt() - | - yield = body.(ExprStmt).getValue() - or - yield = body.(If).getStmt(0).(ExprStmt).getValue() - ) - } + /** Gets the expression for elements of this comprehension. */ + Expr getElt() { + exists(Yield yield, Stmt body | + result = yield.getValue() and + body = this.getNthInnerLoop(_).getAStmt() + | + yield = body.(ExprStmt).getValue() + or + yield = body.(If).getStmt(0).(ExprStmt).getValue() + ) + } } /** A list comprehension, such as `[ chr(x) for x in range(ord('A'), ord('Z')+1) ]` */ class ListComp extends ListComp_, Comp { - override Expr getASubExpression() { - result = this.getAGenerator().getASubExpression() or - result = this.getElt() or - result = this.getIterable() - } + override Expr getASubExpression() { + result = this.getAGenerator().getASubExpression() or + result = this.getElt() or + result = this.getIterable() + } - override AstNode getAChildNode() { - result = this.getAGenerator() or - result = this.getIterable() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getAGenerator() or + result = this.getIterable() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - /** Gets the scope in which the body of this list comprehension evaluates. */ - override Scope getEvaluatingScope() { - major_version() = 2 and result = this.getScope() - or - major_version() = 3 and result = this.getFunction() - } + /** Gets the scope in which the body of this list comprehension evaluates. */ + override Scope getEvaluatingScope() { + major_version() = 2 and result = this.getScope() + or + major_version() = 3 and result = this.getFunction() + } - /** Gets the iteration variable for the nth innermost generator of this list comprehension */ - override Variable getIterationVariable(int n) { result = Comp.super.getIterationVariable(n) } + /** Gets the iteration variable for the nth innermost generator of this list comprehension */ + override Variable getIterationVariable(int n) { result = Comp.super.getIterationVariable(n) } - override Function getFunction() { result = ListComp_.super.getFunction() } + override Function getFunction() { result = ListComp_.super.getFunction() } - override string toString() { result = ListComp_.super.toString() } + override string toString() { result = ListComp_.super.toString() } - override Expr getElt() { result = Comp.super.getElt() } + override Expr getElt() { result = Comp.super.getElt() } } /** A set comprehension such as `{ v for v in "0123456789" }` */ class SetComp extends SetComp_, Comp { - override Expr getASubExpression() { result = this.getIterable() } + override Expr getASubExpression() { result = this.getIterable() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override Function getFunction() { result = SetComp_.super.getFunction() } + override Function getFunction() { result = SetComp_.super.getFunction() } } /** A dictionary comprehension, such as `{ k:v for k, v in enumerate("0123456789") }` */ class DictComp extends DictComp_, Comp { - override Expr getASubExpression() { result = this.getIterable() } + override Expr getASubExpression() { result = this.getIterable() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override Function getFunction() { result = DictComp_.super.getFunction() } + override Function getFunction() { result = DictComp_.super.getFunction() } } /** A generator expression, such as `(var for var in iterable)` */ class GeneratorExp extends GeneratorExp_, Comp { - override Expr getASubExpression() { result = this.getIterable() } + override Expr getASubExpression() { result = this.getIterable() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getFunction() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override Function getFunction() { result = GeneratorExp_.super.getFunction() } + override Function getFunction() { result = GeneratorExp_.super.getFunction() } } diff --git a/python/ql/src/semmle/python/Constants.qll b/python/ql/src/semmle/python/Constants.qll index 6ca694a3ad1..3faa6072acc 100644 --- a/python/ql/src/semmle/python/Constants.qll +++ b/python/ql/src/semmle/python/Constants.qll @@ -4,31 +4,31 @@ import python /** the Python major version number */ int major_version() { - explicit_major_version(result) - or - not explicit_major_version(_) and - /* If there is more than one version, prefer 2 for backwards compatibilty */ - (if py_flags_versioned("version.major", "2", "2") then result = 2 else result = 3) + explicit_major_version(result) + or + not explicit_major_version(_) and + /* If there is more than one version, prefer 2 for backwards compatibilty */ + (if py_flags_versioned("version.major", "2", "2") then result = 2 else result = 3) } /** the Python minor version number */ int minor_version() { - exists(string v | py_flags_versioned("version.minor", v, major_version().toString()) | - result = v.toInt() - ) + exists(string v | py_flags_versioned("version.minor", v, major_version().toString()) | + result = v.toInt() + ) } /** the Python micro version number */ int micro_version() { - exists(string v | py_flags_versioned("version.micro", v, major_version().toString()) | - result = v.toInt() - ) + exists(string v | py_flags_versioned("version.micro", v, major_version().toString()) | + result = v.toInt() + ) } private predicate explicit_major_version(int v) { - exists(string version | py_flags_versioned("language.version", version, _) | - version.charAt(0) = "2" and v = 2 - or - version.charAt(0) = "3" and v = 3 - ) + exists(string version | py_flags_versioned("language.version", version, _) | + version.charAt(0) = "2" and v = 2 + or + version.charAt(0) = "3" and v = 3 + ) } diff --git a/python/ql/src/semmle/python/Exprs.qll b/python/ql/src/semmle/python/Exprs.qll index e2dc663bd1b..553e12103ad 100644 --- a/python/ql/src/semmle/python/Exprs.qll +++ b/python/ql/src/semmle/python/Exprs.qll @@ -4,284 +4,284 @@ private import semmle.python.objects.ObjectInternal /** An expression */ class Expr extends Expr_, AstNode { - /** Gets the scope of this expression */ - override Scope getScope() { py_scopes(this, result) } + /** Gets the scope of this expression */ + override Scope getScope() { py_scopes(this, result) } - /** Gets a textual representation of this element. */ - override string toString() { result = "Expression" } + /** Gets a textual representation of this element. */ + override string toString() { result = "Expression" } - /** Gets the module in which this expression occurs */ - Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } + /** Gets the module in which this expression occurs */ + Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } - /** - * Whether this expression defines variable `v` - * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. - */ - predicate defines(Variable v) { this.getASubExpression+().defines(v) } + /** + * Whether this expression defines variable `v` + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. + */ + predicate defines(Variable v) { this.getASubExpression+().defines(v) } - /** Whether this expression may have a side effect (as determined purely from its syntax) */ - predicate hasSideEffects() { - /* If an exception raised by this expression handled, count that as a side effect */ - this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt - or - this.getASubExpression().hasSideEffects() - } + /** Whether this expression may have a side effect (as determined purely from its syntax) */ + predicate hasSideEffects() { + /* If an exception raised by this expression handled, count that as a side effect */ + this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt + or + this.getASubExpression().hasSideEffects() + } - /** Whether this expression is a constant */ - predicate isConstant() { not this.isVariable() } + /** Whether this expression is a constant */ + predicate isConstant() { not this.isVariable() } - /** Use isParenthesized instead. */ - deprecated override predicate isParenthesised() { this.isParenthesized() } + /** Use isParenthesized instead. */ + deprecated override predicate isParenthesised() { this.isParenthesized() } - /** Whether the parenthesized property of this expression is true. */ - predicate isParenthesized() { Expr_.super.isParenthesised() } + /** Whether the parenthesized property of this expression is true. */ + predicate isParenthesized() { Expr_.super.isParenthesised() } - private predicate isVariable() { - this.hasSideEffects() - or - this instanceof Name - or - exists(Expr e | e = this.getASubExpression() and e.isVariable()) - } + private predicate isVariable() { + this.hasSideEffects() + or + this instanceof Name + or + exists(Expr e | e = this.getASubExpression() and e.isVariable()) + } - override Location getLocation() { result = Expr_.super.getLocation() } + override Location getLocation() { result = Expr_.super.getLocation() } - /** Gets an immediate (non-nested) sub-expression of this expression */ - Expr getASubExpression() { none() } + /** Gets an immediate (non-nested) sub-expression of this expression */ + Expr getASubExpression() { none() } - /** Use StrConst.getText() instead */ - deprecated string strValue() { none() } + /** Use StrConst.getText() instead */ + deprecated string strValue() { none() } - override AstNode getAChildNode() { result = this.getASubExpression() } + override AstNode getAChildNode() { result = this.getASubExpression() } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to - * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly - * precise, but may not provide information for a significant number of flow-nodes. - * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. - * NOTE: For complex dataflow, involving multiple stages of points-to analysis, it may be more precise to use - * `ControlFlowNode.refersTo(...)` instead. - */ - predicate refersTo(Object obj, ClassObject cls, AstNode origin) { - this.refersTo(_, obj, cls, origin) - } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to + * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly + * precise, but may not provide information for a significant number of flow-nodes. + * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. + * NOTE: For complex dataflow, involving multiple stages of points-to analysis, it may be more precise to use + * `ControlFlowNode.refersTo(...)` instead. + */ + predicate refersTo(Object obj, ClassObject cls, AstNode origin) { + this.refersTo(_, obj, cls, origin) + } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Gets what this expression might "refer-to" in the given `context`. - */ - predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) { - this.getAFlowNode().refersTo(context, obj, cls, origin.getAFlowNode()) - } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Gets what this expression might "refer-to" in the given `context`. + */ + predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) { + this.getAFlowNode().refersTo(context, obj, cls, origin.getAFlowNode()) + } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Holds if this expression might "refer-to" to `value` which is from `origin` - * Unlike `this.refersTo(value, _, origin)`, this predicate includes results - * where the class cannot be inferred. - */ - pragma[nomagic] - predicate refersTo(Object obj, AstNode origin) { - this.getAFlowNode().refersTo(obj, origin.getAFlowNode()) - } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Holds if this expression might "refer-to" to `value` which is from `origin` + * Unlike `this.refersTo(value, _, origin)`, this predicate includes results + * where the class cannot be inferred. + */ + pragma[nomagic] + predicate refersTo(Object obj, AstNode origin) { + this.getAFlowNode().refersTo(obj, origin.getAFlowNode()) + } - /** - * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. - * Equivalent to `this.refersTo(value, _)` - */ - predicate refersTo(Object obj) { this.refersTo(obj, _) } + /** + * NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead. + * Equivalent to `this.refersTo(value, _)` + */ + predicate refersTo(Object obj) { this.refersTo(obj, _) } - /** - * Holds if this expression might "point-to" to `value` which is from `origin` - * in the given `context`. - */ - predicate pointsTo(Context context, Value value, AstNode origin) { - this.getAFlowNode().pointsTo(context, value, origin.getAFlowNode()) - } + /** + * Holds if this expression might "point-to" to `value` which is from `origin` + * in the given `context`. + */ + predicate pointsTo(Context context, Value value, AstNode origin) { + this.getAFlowNode().pointsTo(context, value, origin.getAFlowNode()) + } - /** - * Holds if this expression might "point-to" to `value` which is from `origin`. - */ - predicate pointsTo(Value value, AstNode origin) { - this.getAFlowNode().pointsTo(value, origin.getAFlowNode()) - } + /** + * Holds if this expression might "point-to" to `value` which is from `origin`. + */ + predicate pointsTo(Value value, AstNode origin) { + this.getAFlowNode().pointsTo(value, origin.getAFlowNode()) + } - /** - * Holds if this expression might "point-to" to `value`. - */ - predicate pointsTo(Value value) { this.pointsTo(value, _) } + /** + * Holds if this expression might "point-to" to `value`. + */ + predicate pointsTo(Value value) { this.pointsTo(value, _) } - /** Gets a value that this expression might "point-to". */ - Value pointsTo() { this.pointsTo(result) } + /** Gets a value that this expression might "point-to". */ + Value pointsTo() { this.pointsTo(result) } } /** An assignment expression, such as `x := y` */ class AssignExpr extends AssignExpr_ { - override Expr getASubExpression() { - result = this.getValue() or - result = this.getTarget() - } + override Expr getASubExpression() { + result = this.getValue() or + result = this.getTarget() + } } /** An attribute expression, such as `value.attr` */ class Attribute extends Attribute_ { - /* syntax: Expr.name */ - override Expr getASubExpression() { result = this.getObject() } + /* syntax: Expr.name */ + override Expr getASubExpression() { result = this.getObject() } - override AttrNode getAFlowNode() { result = super.getAFlowNode() } + override AttrNode getAFlowNode() { result = super.getAFlowNode() } - /** Gets the name of this attribute. That is the `name` in `obj.name` */ - string getName() { result = Attribute_.super.getAttr() } + /** Gets the name of this attribute. That is the `name` in `obj.name` */ + string getName() { result = Attribute_.super.getAttr() } - /** Gets the object of this attribute. That is the `obj` in `obj.name` */ - Expr getObject() { result = Attribute_.super.getValue() } + /** Gets the object of this attribute. That is the `obj` in `obj.name` */ + Expr getObject() { result = Attribute_.super.getValue() } - /** - * Gets the expression corresponding to the object of the attribute, if the name of the attribute is `name`. - * Equivalent to `this.getObject() and this.getName() = name`. - */ - Expr getObject(string name) { - result = Attribute_.super.getValue() and - name = Attribute_.super.getAttr() - } + /** + * Gets the expression corresponding to the object of the attribute, if the name of the attribute is `name`. + * Equivalent to `this.getObject() and this.getName() = name`. + */ + Expr getObject(string name) { + result = Attribute_.super.getValue() and + name = Attribute_.super.getAttr() + } } /** A subscript expression, such as `value[slice]` */ class Subscript extends Subscript_ { - /* syntax: Expr[Expr] */ - override Expr getASubExpression() { - result = this.getIndex() - or - result = this.getObject() - } + /* syntax: Expr[Expr] */ + override Expr getASubExpression() { + result = this.getIndex() + or + result = this.getObject() + } - Expr getObject() { result = Subscript_.super.getValue() } + Expr getObject() { result = Subscript_.super.getValue() } - override SubscriptNode getAFlowNode() { result = super.getAFlowNode() } + override SubscriptNode getAFlowNode() { result = super.getAFlowNode() } } /** A call expression, such as `func(...)` */ class Call extends Call_ { - /* syntax: Expr(...) */ - override Expr getASubExpression() { - result = this.getAPositionalArg() or - result = this.getAKeyword().getValue() or - result = this.getFunc() - } + /* syntax: Expr(...) */ + override Expr getASubExpression() { + result = this.getAPositionalArg() or + result = this.getAKeyword().getValue() or + result = this.getFunc() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - override string toString() { result = this.getFunc().toString() + "()" } + override string toString() { result = this.getFunc().toString() + "()" } - override CallNode getAFlowNode() { result = super.getAFlowNode() } + override CallNode getAFlowNode() { result = super.getAFlowNode() } - /** Gets a tuple (*) argument of this call. */ - Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() } + /** Gets a tuple (*) argument of this call. */ + Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() } - /** Gets a dictionary (**) argument of this call. */ - Expr getKwargs() { result = this.getANamedArg().(DictUnpacking).getValue() } + /** Gets a dictionary (**) argument of this call. */ + Expr getKwargs() { result = this.getANamedArg().(DictUnpacking).getValue() } - /* Backwards compatibility */ - /** - * Gets the nth keyword argument of this call expression, provided it is not preceded by a double-starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getNamedArg(index) instead. - */ - Keyword getKeyword(int index) { - result = this.getNamedArg(index) and - not exists(DictUnpacking d, int lower | d = this.getNamedArg(lower) and lower < index) - } + /* Backwards compatibility */ + /** + * Gets the nth keyword argument of this call expression, provided it is not preceded by a double-starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getNamedArg(index) instead. + */ + Keyword getKeyword(int index) { + result = this.getNamedArg(index) and + not exists(DictUnpacking d, int lower | d = this.getNamedArg(lower) and lower < index) + } - /** - * Gets a keyword argument of this call expression, provided it is not preceded by a double-starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getANamedArg() instead. - */ - Keyword getAKeyword() { result = this.getKeyword(_) } + /** + * Gets a keyword argument of this call expression, provided it is not preceded by a double-starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getANamedArg() instead. + */ + Keyword getAKeyword() { result = this.getKeyword(_) } - /** - * Gets the positional argument at `index`, provided it is not preceded by a starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getPositionalArg(index) instead. - */ - Expr getArg(int index) { - result = this.getPositionalArg(index) and - not result instanceof Starred and - not exists(Starred s, int lower | s = this.getPositionalArg(lower) and lower < index) - } + /** + * Gets the positional argument at `index`, provided it is not preceded by a starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getPositionalArg(index) instead. + */ + Expr getArg(int index) { + result = this.getPositionalArg(index) and + not result instanceof Starred and + not exists(Starred s, int lower | s = this.getPositionalArg(lower) and lower < index) + } - /** - * Gets a positional argument, provided it is not preceded by a starred argument. - * This exists primarily for backwards compatibility. You are recommended to use - * Call.getAPositionalArg() instead. - */ - Expr getAnArg() { result = this.getArg(_) } + /** + * Gets a positional argument, provided it is not preceded by a starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getAPositionalArg() instead. + */ + Expr getAnArg() { result = this.getArg(_) } - override AstNode getAChildNode() { - result = this.getAPositionalArg() or - result = this.getANamedArg() or - result = this.getFunc() - } + override AstNode getAChildNode() { + result = this.getAPositionalArg() or + result = this.getANamedArg() or + result = this.getFunc() + } - /** Gets the name of a named argument, including those passed in dict literals. */ - string getANamedArgumentName() { - result = this.getAKeyword().getArg() - or - result = this.getKwargs().(Dict).getAKey().(StrConst).getText() - } + /** Gets the name of a named argument, including those passed in dict literals. */ + string getANamedArgumentName() { + result = this.getAKeyword().getArg() + or + result = this.getKwargs().(Dict).getAKey().(StrConst).getText() + } - /** Gets the positional argument count of this call, provided there is no more than one tuple (*) argument. */ - int getPositionalArgumentCount() { - count(this.getStarargs()) < 2 and - result = count(Expr arg | arg = this.getAPositionalArg() and not arg instanceof Starred) - } + /** Gets the positional argument count of this call, provided there is no more than one tuple (*) argument. */ + int getPositionalArgumentCount() { + count(this.getStarargs()) < 2 and + result = count(Expr arg | arg = this.getAPositionalArg() and not arg instanceof Starred) + } - /** Gets the tuple (*) argument of this call, provided there is exactly one. */ - Expr getStarArg() { - count(this.getStarargs()) < 2 and - result = getStarargs() - } + /** Gets the tuple (*) argument of this call, provided there is exactly one. */ + Expr getStarArg() { + count(this.getStarargs()) < 2 and + result = getStarargs() + } } /** A conditional expression such as, `body if test else orelse` */ class IfExp extends IfExp_ { - /* syntax: Expr if Expr else Expr */ - override Expr getASubExpression() { - result = this.getTest() or result = this.getBody() or result = this.getOrelse() - } + /* syntax: Expr if Expr else Expr */ + override Expr getASubExpression() { + result = this.getTest() or result = this.getBody() or result = this.getOrelse() + } - override IfExprNode getAFlowNode() { result = super.getAFlowNode() } + override IfExprNode getAFlowNode() { result = super.getAFlowNode() } } /** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */ class Starred extends Starred_ { - /* syntax: *Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: *Expr */ + override Expr getASubExpression() { result = this.getValue() } } /** A yield expression, such as `yield value` */ class Yield extends Yield_ { - /* syntax: yield Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: yield Expr */ + override Expr getASubExpression() { result = this.getValue() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } } /** A yield expression, such as `yield from value` */ class YieldFrom extends YieldFrom_ { - /* syntax: yield from Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: yield from Expr */ + override Expr getASubExpression() { result = this.getValue() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } } /** A repr (backticks) expression, such as `` `value` `` */ class Repr extends Repr_ { - /* syntax: `Expr` */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: `Expr` */ + override Expr getASubExpression() { result = this.getValue() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } } /* Constants */ @@ -290,28 +290,28 @@ class Repr extends Repr_ { * `"hello"` are treated as Bytes for Python2, but Unicode for Python3. */ class Bytes extends StrConst { - /* syntax: b"hello" */ - Bytes() { not this.isUnicode() } + /* syntax: b"hello" */ + Bytes() { not this.isUnicode() } - override Object getLiteralObject() { - py_cobjecttypes(result, theBytesType()) and - py_cobjectnames(result, this.quotedString()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theBytesType()) and + py_cobjectnames(result, this.quotedString()) + } - /** - * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). - * The following predicate help us match up a string/byte literals in the source - * which the equivalent object. - */ - private string quotedString() { - exists(string b_unquoted | b_unquoted = this.getS() | result = "b'" + b_unquoted + "'") - } + /** + * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). + * The following predicate help us match up a string/byte literals in the source + * which the equivalent object. + */ + private string quotedString() { + exists(string b_unquoted | b_unquoted = this.getS() | result = "b'" + b_unquoted + "'") + } } /** An ellipsis expression, such as `...` */ class Ellipsis extends Ellipsis_ { - /* syntax: ... */ - override Expr getASubExpression() { none() } + /* syntax: ... */ + override Expr getASubExpression() { none() } } /** @@ -319,117 +319,117 @@ class Ellipsis extends Ellipsis_ { * Consists of string (both unicode and byte) literals and numeric literals. */ abstract class ImmutableLiteral extends Expr { - abstract Object getLiteralObject(); + abstract Object getLiteralObject(); - abstract boolean booleanValue(); + abstract boolean booleanValue(); - final Value getLiteralValue() { result.(ConstantObjectInternal).getLiteral() = this } + final Value getLiteralValue() { result.(ConstantObjectInternal).getLiteral() = this } } /** A numerical constant expression, such as `7` or `4.2` */ abstract class Num extends Num_, ImmutableLiteral { - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - /* We want to declare this abstract, but currently we cannot. */ - override string toString() { result = "Num with missing toString" } + /* We want to declare this abstract, but currently we cannot. */ + override string toString() { result = "Num with missing toString" } } /** An integer numeric constant, such as `7` or `0x9` */ class IntegerLiteral extends Num { - /* syntax: 4 */ - IntegerLiteral() { not this instanceof FloatLiteral and not this instanceof ImaginaryLiteral } + /* syntax: 4 */ + IntegerLiteral() { not this instanceof FloatLiteral and not this instanceof ImaginaryLiteral } - /** - * Gets the (integer) value of this constant. Will not return a result if the value does not fit into - * a 32 bit signed value - */ - int getValue() { result = this.getN().toInt() } + /** + * Gets the (integer) value of this constant. Will not return a result if the value does not fit into + * a 32 bit signed value + */ + int getValue() { result = this.getN().toInt() } - override string toString() { result = "IntegerLiteral" } + override string toString() { result = "IntegerLiteral" } - override Object getLiteralObject() { - py_cobjecttypes(result, theIntType()) and py_cobjectnames(result, this.getN()) - or - py_cobjecttypes(result, theLongType()) and py_cobjectnames(result, this.getN()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theIntType()) and py_cobjectnames(result, this.getN()) + or + py_cobjecttypes(result, theLongType()) and py_cobjectnames(result, this.getN()) + } - override boolean booleanValue() { - this.getValue() = 0 and result = false - or - this.getValue() != 0 and result = true - } + override boolean booleanValue() { + this.getValue() = 0 and result = false + or + this.getValue() != 0 and result = true + } } /** A floating point numeric constant, such as `0.4` or `4e3` */ class FloatLiteral extends Num { - /* syntax: 4.2 */ - FloatLiteral() { - not this instanceof ImaginaryLiteral and - this.getN().regexpMatch(".*[.eE].*") - } + /* syntax: 4.2 */ + FloatLiteral() { + not this instanceof ImaginaryLiteral and + this.getN().regexpMatch(".*[.eE].*") + } - float getValue() { result = this.getN().toFloat() } + float getValue() { result = this.getN().toFloat() } - override string toString() { result = "FloatLiteral" } + override string toString() { result = "FloatLiteral" } - override Object getLiteralObject() { - py_cobjecttypes(result, theFloatType()) and py_cobjectnames(result, this.getN()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theFloatType()) and py_cobjectnames(result, this.getN()) + } - override boolean booleanValue() { - this.getValue() = 0.0 and result = false - or - // In QL 0.0 != -0.0 - this.getValue() = -0.0 and result = false - or - this.getValue() != 0.0 and this.getValue() != -0.0 and result = true - } + override boolean booleanValue() { + this.getValue() = 0.0 and result = false + or + // In QL 0.0 != -0.0 + this.getValue() = -0.0 and result = false + or + this.getValue() != 0.0 and this.getValue() != -0.0 and result = true + } } /** An imaginary numeric constant, such as `3j` */ class ImaginaryLiteral extends Num { - private float value; + private float value; - /* syntax: 1.0j */ - ImaginaryLiteral() { value = this.getN().regexpCapture("(.+)j.*", 1).toFloat() } + /* syntax: 1.0j */ + ImaginaryLiteral() { value = this.getN().regexpCapture("(.+)j.*", 1).toFloat() } - /** Gets the value of this constant as a floating point value */ - float getValue() { result = value } + /** Gets the value of this constant as a floating point value */ + float getValue() { result = value } - override string toString() { result = "ImaginaryLiteral" } + override string toString() { result = "ImaginaryLiteral" } - override Object getLiteralObject() { - py_cobjecttypes(result, theComplexType()) and py_cobjectnames(result, this.getN()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theComplexType()) and py_cobjectnames(result, this.getN()) + } - override boolean booleanValue() { - this.getValue() = 0.0 and result = false - or - // In QL 0.0 != -0.0 - this.getValue() = -0.0 and result = false - or - this.getValue() != 0.0 and this.getValue() != -0.0 and result = true - } + override boolean booleanValue() { + this.getValue() = 0.0 and result = false + or + // In QL 0.0 != -0.0 + this.getValue() = -0.0 and result = false + or + this.getValue() != 0.0 and this.getValue() != -0.0 and result = true + } } class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr { - NegativeIntegerLiteral() { - this.getOp() instanceof USub and - this.getOperand() instanceof IntegerLiteral - } + NegativeIntegerLiteral() { + this.getOp() instanceof USub and + this.getOperand() instanceof IntegerLiteral + } - override boolean booleanValue() { result = this.getOperand().(IntegerLiteral).booleanValue() } + override boolean booleanValue() { result = this.getOperand().(IntegerLiteral).booleanValue() } - override Object getLiteralObject() { - (py_cobjecttypes(result, theIntType()) or py_cobjecttypes(result, theLongType())) and - py_cobjectnames(result, "-" + this.getOperand().(IntegerLiteral).getN()) - } + override Object getLiteralObject() { + (py_cobjecttypes(result, theIntType()) or py_cobjecttypes(result, theLongType())) and + py_cobjectnames(result, "-" + this.getOperand().(IntegerLiteral).getN()) + } - /** - * Gets the (integer) value of this constant. Will not return a result if the value does not fit into - * a 32 bit signed value - */ - int getValue() { result = -this.getOperand().(IntegerLiteral).getValue() } + /** + * Gets the (integer) value of this constant. Will not return a result if the value does not fit into + * a 32 bit signed value + */ + int getValue() { result = -this.getOperand().(IntegerLiteral).getValue() } } /** @@ -437,68 +437,68 @@ class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr { * "hello" are treated as Bytes for Python2, but Unicode for Python3. */ class Unicode extends StrConst { - /* syntax: "hello" */ - Unicode() { this.isUnicode() } + /* syntax: "hello" */ + Unicode() { this.isUnicode() } - override Object getLiteralObject() { - py_cobjecttypes(result, theUnicodeType()) and - py_cobjectnames(result, this.quotedString()) - } + override Object getLiteralObject() { + py_cobjecttypes(result, theUnicodeType()) and + py_cobjectnames(result, this.quotedString()) + } - /** - * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). - * The following predicate help us match up a string/byte literals in the source - * which the equivalent object. - */ - string quotedString() { - exists(string u_unquoted | u_unquoted = this.getS() | result = "u'" + u_unquoted + "'") - } + /** + * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). + * The following predicate help us match up a string/byte literals in the source + * which the equivalent object. + */ + string quotedString() { + exists(string u_unquoted | u_unquoted = this.getS() | result = "u'" + u_unquoted + "'") + } } /* Compound Values */ /** A dictionary expression, such as `{'key':'value'}` */ class Dict extends Dict_ { - /* syntax: {Expr: Expr, ...} */ - /** Gets the value of an item of this dict display */ - Expr getAValue() { result = this.getAnItem().(DictDisplayItem).getValue() } + /* syntax: {Expr: Expr, ...} */ + /** Gets the value of an item of this dict display */ + Expr getAValue() { result = this.getAnItem().(DictDisplayItem).getValue() } - /** - * Gets the key of an item of this dict display, for those items that have keys - * E.g, in {'a':1, **b} this returns only 'a' - */ - Expr getAKey() { result = this.getAnItem().(KeyValuePair).getKey() } + /** + * Gets the key of an item of this dict display, for those items that have keys + * E.g, in {'a':1, **b} this returns only 'a' + */ + Expr getAKey() { result = this.getAnItem().(KeyValuePair).getKey() } - override Expr getASubExpression() { result = this.getAValue() or result = this.getAKey() } + override Expr getASubExpression() { result = this.getAValue() or result = this.getAKey() } - override AstNode getAChildNode() { result = this.getAnItem() } + override AstNode getAChildNode() { result = this.getAnItem() } } /** A list expression, such as `[ 1, 3, 5, 7, 9 ]` */ class List extends List_ { - /* syntax: [Expr, ...] */ - override Expr getASubExpression() { result = this.getAnElt() } + /* syntax: [Expr, ...] */ + override Expr getASubExpression() { result = this.getAnElt() } } /** A set expression such as `{ 1, 3, 5, 7, 9 }` */ class Set extends Set_ { - /* syntax: {Expr, ...} */ - override Expr getASubExpression() { result = this.getAnElt() } + /* syntax: {Expr, ...} */ + override Expr getASubExpression() { result = this.getAnElt() } } class PlaceHolder extends PlaceHolder_ { - string getId() { result = this.getVariable().getId() } + string getId() { result = this.getVariable().getId() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override string toString() { result = "$" + this.getId() } + override string toString() { result = "$" + this.getId() } - override NameNode getAFlowNode() { result = super.getAFlowNode() } + override NameNode getAFlowNode() { result = super.getAFlowNode() } } /** A tuple expression such as `( 1, 3, 5, 7, 9 )` */ class Tuple extends Tuple_ { - /* syntax: (Expr, ...) */ - override Expr getASubExpression() { result = this.getAnElt() } + /* syntax: (Expr, ...) */ + override Expr getASubExpression() { result = this.getAnElt() } } /** @@ -506,138 +506,138 @@ class Tuple extends Tuple_ { * `None`, `True` and `False` are excluded. */ class Name extends Name_ { - /* syntax: name */ - string getId() { result = this.getVariable().getId() } + /* syntax: name */ + string getId() { result = this.getVariable().getId() } - /** Whether this expression is a definition */ - predicate isDefinition() { - py_expr_contexts(_, 5, this) - or - /* Treat Param as a definition (which it is) */ - py_expr_contexts(_, 4, this) - or - /* The target in an augmented assignment is also a definition (and a use) */ - exists(AugAssign aa | aa.getTarget() = this) - } + /** Whether this expression is a definition */ + predicate isDefinition() { + py_expr_contexts(_, 5, this) + or + /* Treat Param as a definition (which it is) */ + py_expr_contexts(_, 4, this) + or + /* The target in an augmented assignment is also a definition (and a use) */ + exists(AugAssign aa | aa.getTarget() = this) + } - /** - * Whether this expression defines variable `v` - * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. - */ - override predicate defines(Variable v) { - this.isDefinition() and - v = this.getVariable() - } + /** + * Whether this expression defines variable `v` + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. + */ + override predicate defines(Variable v) { + this.isDefinition() and + v = this.getVariable() + } - /** Whether this expression is a deletion */ - predicate isDeletion() { py_expr_contexts(_, 2, this) } + /** Whether this expression is a deletion */ + predicate isDeletion() { py_expr_contexts(_, 2, this) } - /** - * Whether this expression deletes variable `v`. - * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. - */ - predicate deletes(Variable v) { - this.isDeletion() and - v = this.getVariable() - } + /** + * Whether this expression deletes variable `v`. + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. + */ + predicate deletes(Variable v) { + this.isDeletion() and + v = this.getVariable() + } - /** Whether this expression is a use */ - predicate isUse() { py_expr_contexts(_, 3, this) } + /** Whether this expression is a use */ + predicate isUse() { py_expr_contexts(_, 3, this) } - /** - * Whether this expression is a use of variable `v` - * If doing dataflow, then consider using SsaVariable.getAUse() for more precision. - */ - predicate uses(Variable v) { - this.isUse() and - v = this.getVariable() - } + /** + * Whether this expression is a use of variable `v` + * If doing dataflow, then consider using SsaVariable.getAUse() for more precision. + */ + predicate uses(Variable v) { + this.isUse() and + v = this.getVariable() + } - override predicate isConstant() { none() } + override predicate isConstant() { none() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override string toString() { result = this.getId() } + override string toString() { result = this.getId() } - override NameNode getAFlowNode() { result = super.getAFlowNode() } + override NameNode getAFlowNode() { result = super.getAFlowNode() } - override predicate isArtificial() { - /* Artificial variable names in comprehensions all start with "." */ - this.getId().charAt(0) = "." - } + override predicate isArtificial() { + /* Artificial variable names in comprehensions all start with "." */ + this.getId().charAt(0) = "." + } } class Filter extends Filter_ { - override Expr getASubExpression() { - result = this.getFilter() - or - result = this.getValue() - } + override Expr getASubExpression() { + result = this.getFilter() + or + result = this.getValue() + } } /** A slice. E.g `0:1` in the expression `x[0:1]` */ class Slice extends Slice_ { - override Expr getASubExpression() { - result = this.getStart() or - result = this.getStop() or - result = this.getStep() - } + override Expr getASubExpression() { + result = this.getStart() or + result = this.getStop() or + result = this.getStep() + } } /** A string constant. */ class StrConst extends Str_, ImmutableLiteral { - /* syntax: "hello" */ - predicate isUnicode() { - this.getPrefix().charAt(_) = "u" - or - this.getPrefix().charAt(_) = "U" - or - not this.getPrefix().charAt(_) = "b" and major_version() = 3 - or - not this.getPrefix().charAt(_) = "b" and - this.getEnclosingModule().hasFromFuture("unicode_literals") - } + /* syntax: "hello" */ + predicate isUnicode() { + this.getPrefix().charAt(_) = "u" + or + this.getPrefix().charAt(_) = "U" + or + not this.getPrefix().charAt(_) = "b" and major_version() = 3 + or + not this.getPrefix().charAt(_) = "b" and + this.getEnclosingModule().hasFromFuture("unicode_literals") + } - deprecated override string strValue() { result = this.getS() } + deprecated override string strValue() { result = this.getS() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override AstNode getAChildNode() { result = this.getAnImplicitlyConcatenatedPart() } + override AstNode getAChildNode() { result = this.getAnImplicitlyConcatenatedPart() } - /** Gets the text of this str constant */ - string getText() { result = this.getS() } + /** Gets the text of this str constant */ + string getText() { result = this.getS() } - /** Whether this is a docstring */ - predicate isDocString() { exists(Scope s | s.getDocString() = this) } + /** Whether this is a docstring */ + predicate isDocString() { exists(Scope s | s.getDocString() = this) } - override boolean booleanValue() { - this.getText() = "" and result = false - or - this.getText() != "" and result = true - } + override boolean booleanValue() { + this.getText() = "" and result = false + or + this.getText() != "" and result = true + } - override Object getLiteralObject() { none() } + override Object getLiteralObject() { none() } } private predicate name_consts(Name_ n, string id) { - exists(Variable v | py_variables(v, n) and id = v.getId() | - id = "True" or id = "False" or id = "None" - ) + exists(Variable v | py_variables(v, n) and id = v.getId() | + id = "True" or id = "False" or id = "None" + ) } /** A named constant, one of `None`, `True` or `False` */ abstract class NameConstant extends Name, ImmutableLiteral { - NameConstant() { name_consts(this, _) } + NameConstant() { name_consts(this, _) } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override string toString() { name_consts(this, result) } + override string toString() { name_consts(this, result) } - override predicate isConstant() { any() } + override predicate isConstant() { any() } - override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() } + override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() } - override predicate isArtificial() { none() } + override predicate isArtificial() { none() } } /** A boolean named constant, either `True` or `False` */ @@ -645,44 +645,44 @@ abstract class BooleanLiteral extends NameConstant { } /** The boolean named constant `True` */ class True extends BooleanLiteral { - /* syntax: True */ - True() { name_consts(this, "True") } + /* syntax: True */ + True() { name_consts(this, "True") } - override Object getLiteralObject() { name_consts(this, "True") and result = theTrueObject() } + override Object getLiteralObject() { name_consts(this, "True") and result = theTrueObject() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } } /** The boolean named constant `False` */ class False extends BooleanLiteral { - /* syntax: False */ - False() { name_consts(this, "False") } + /* syntax: False */ + False() { name_consts(this, "False") } - override Object getLiteralObject() { name_consts(this, "False") and result = theFalseObject() } + override Object getLiteralObject() { name_consts(this, "False") and result = theFalseObject() } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } /** `None` */ class None extends NameConstant { - /* syntax: None */ - None() { name_consts(this, "None") } + /* syntax: None */ + None() { name_consts(this, "None") } - override Object getLiteralObject() { name_consts(this, "None") and result = theNoneObject() } + override Object getLiteralObject() { name_consts(this, "None") and result = theNoneObject() } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } /** An await expression such as `await coro`. */ class Await extends Await_ { - /* syntax: await Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: await Expr */ + override Expr getASubExpression() { result = this.getValue() } } /** A formatted string literal expression, such as `f'hello {world!s}'` */ class Fstring extends Fstring_ { - /* syntax: f"Yes!" */ - override Expr getASubExpression() { result = this.getAValue() } + /* syntax: f"Yes!" */ + override Expr getASubExpression() { result = this.getAValue() } } /** @@ -690,10 +690,10 @@ class Fstring extends Fstring_ { * For example, in the string `f'hello {world!s}'` the formatted value is `world!s`. */ class FormattedValue extends FormattedValue_ { - override Expr getASubExpression() { - result = this.getValue() or - result = this.getFormatSpec() - } + override Expr getASubExpression() { + result = this.getValue() or + result = this.getFormatSpec() + } } /* Expression Contexts */ diff --git a/python/ql/src/semmle/python/Files.qll b/python/ql/src/semmle/python/Files.qll index c4b71372858..f7cf07caa56 100644 --- a/python/ql/src/semmle/python/Files.qll +++ b/python/ql/src/semmle/python/Files.qll @@ -2,125 +2,125 @@ import python /** A file */ class File extends Container { - File() { files(this, _, _, _, _) } + File() { files(this, _, _, _, _) } - /** DEPRECATED: Use `getAbsolutePath` instead. */ - deprecated override string getName() { result = this.getAbsolutePath() } + /** DEPRECATED: Use `getAbsolutePath` instead. */ + deprecated override string getName() { result = this.getAbsolutePath() } - /** DEPRECATED: Use `getAbsolutePath` instead. */ - deprecated string getFullName() { result = this.getAbsolutePath() } + /** DEPRECATED: Use `getAbsolutePath` instead. */ + deprecated string getFullName() { result = this.getAbsolutePath() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getAbsolutePath() = filepath and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getAbsolutePath() = filepath and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - /** Whether this file is a source code file. */ - predicate fromSource() { - /* If we start to analyse .pyc files, then this will have to change. */ - any() - } + /** Whether this file is a source code file. */ + predicate fromSource() { + /* If we start to analyse .pyc files, then this will have to change. */ + any() + } - /** Gets a short name for this file (just the file name) */ - string getShortName() { - exists(string simple, string ext | files(this, _, simple, ext, _) | result = simple + ext) - } + /** Gets a short name for this file (just the file name) */ + string getShortName() { + exists(string simple, string ext | files(this, _, simple, ext, _) | result = simple + ext) + } - private int lastLine() { - result = max(int i | exists(Location l | l.getFile() = this and l.getEndLine() = i)) - } + private int lastLine() { + result = max(int i | exists(Location l | l.getFile() = this and l.getEndLine() = i)) + } - /** Whether line n is empty (it contains neither code nor comment). */ - predicate emptyLine(int n) { - n in [0 .. this.lastLine()] and - not occupied_line(this, n) - } + /** Whether line n is empty (it contains neither code nor comment). */ + predicate emptyLine(int n) { + n in [0 .. this.lastLine()] and + not occupied_line(this, n) + } - string getSpecifiedEncoding() { - exists(Comment c, Location l | l = c.getLocation() and l.getFile() = this | - l.getStartLine() < 3 and - result = c.getText().regexpCapture(".*coding[:=]\\s*([-\\w.]+).*", 1) - ) - } + string getSpecifiedEncoding() { + exists(Comment c, Location l | l = c.getLocation() and l.getFile() = this | + l.getStartLine() < 3 and + result = c.getText().regexpCapture(".*coding[:=]\\s*([-\\w.]+).*", 1) + ) + } - override string getAbsolutePath() { files(this, result, _, _, _) } + override string getAbsolutePath() { files(this, result, _, _, _) } - /** Gets the URL of this file. */ - override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } + /** Gets the URL of this file. */ + override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } - override Container getImportRoot(int n) { - /* File stem must be a legal Python identifier */ - this.getStem().regexpMatch("[^\\d\\W]\\w*") and - result = this.getParent().getImportRoot(n) - } + override Container getImportRoot(int n) { + /* File stem must be a legal Python identifier */ + this.getStem().regexpMatch("[^\\d\\W]\\w*") and + result = this.getParent().getImportRoot(n) + } - /** - * Gets the contents of this file as a string. - * This will only work for those non-python files that - * are specified to be extracted. - */ - string getContents() { file_contents(this, result) } + /** + * Gets the contents of this file as a string. + * This will only work for those non-python files that + * are specified to be extracted. + */ + string getContents() { file_contents(this, result) } } private predicate occupied_line(File f, int n) { - exists(Location l | l.getFile() = f | - l.getStartLine() = n - or - exists(StrConst s | s.getLocation() = l | n in [l.getStartLine() .. l.getEndLine()]) - ) + exists(Location l | l.getFile() = f | + l.getStartLine() = n + or + exists(StrConst s | s.getLocation() = l | n in [l.getStartLine() .. l.getEndLine()]) + ) } /** A folder (directory) */ class Folder extends Container { - Folder() { folders(this, _, _) } + Folder() { folders(this, _, _) } - /** DEPRECATED: Use `getAbsolutePath` instead. */ - deprecated override string getName() { result = this.getAbsolutePath() } + /** DEPRECATED: Use `getAbsolutePath` instead. */ + deprecated override string getName() { result = this.getAbsolutePath() } - /** DEPRECATED: Use `getBaseName` instead. */ - deprecated string getSimple() { folders(this, _, result) } + /** DEPRECATED: Use `getBaseName` instead. */ + deprecated string getSimple() { folders(this, _, result) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getAbsolutePath() = filepath and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getAbsolutePath() = filepath and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - override string getAbsolutePath() { folders(this, result, _) } + override string getAbsolutePath() { folders(this, result, _) } - /** Gets the URL of this folder. */ - override string getURL() { result = "folder://" + this.getAbsolutePath() } + /** Gets the URL of this folder. */ + override string getURL() { result = "folder://" + this.getAbsolutePath() } - override Container getImportRoot(int n) { - this.isImportRoot(n) and result = this - or - /* Folder must be a legal Python identifier */ - this.getBaseName().regexpMatch("[^\\d\\W]\\w*") and - result = this.getParent().getImportRoot(n) - } + override Container getImportRoot(int n) { + this.isImportRoot(n) and result = this + or + /* Folder must be a legal Python identifier */ + this.getBaseName().regexpMatch("[^\\d\\W]\\w*") and + result = this.getParent().getImportRoot(n) + } } /** @@ -128,327 +128,331 @@ class Folder extends Container { * hold elements of interest. */ abstract class Container extends @container { - Container getParent() { containerparent(result, this) } + Container getParent() { containerparent(result, this) } - /** Gets a child of this container */ - deprecated Container getChild() { containerparent(this, result) } + /** Gets a child of this container */ + deprecated Container getChild() { containerparent(this, result) } - /** - * Gets a textual representation of the path of this container. - * - * This is the absolute path of the container. + /** + * Gets a textual representation of the path of this container. + * + * This is the absolute path of the container. + */ + string toString() { result = this.getAbsolutePath() } + + /** Gets the name of this container */ + abstract string getName(); + + /** + * Gets the relative path of this file or folder from the root folder of the + * analyzed source location. The relative path of the root folder itself is + * the empty string. + * + * This has no result if the container is outside the source root, that is, + * if the root folder is not a reflexive, transitive parent of this container. + */ + string getRelativePath() { + exists(string absPath, string pref | + absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) + | + absPath = pref and result = "" + or + absPath = pref.regexpReplaceAll("/$", "") + "/" + result and + not result.matches("/%") + ) + } + + /** Whether this file or folder is part of the standard library */ + predicate inStdlib() { this.inStdlib(_, _) } + + /** + * Whether this file or folder is part of the standard library + * for version `major.minor` + */ + predicate inStdlib(int major, int minor) { + exists(Module m | + m.getPath() = this and + m.inStdLib(major, minor) + ) + } + + /* Standard cross-language API */ + /** Gets a file or sub-folder in this container. */ + Container getAChildContainer() { containerparent(this, result) } + + /** Gets a file in this container. */ + File getAFile() { result = this.getAChildContainer() } + + /** Gets a sub-folder in this container. */ + Folder getAFolder() { result = this.getAChildContainer() } + + /** + * Gets the absolute, canonical path of this container, using forward slashes + * as path separator. + * + * The path starts with a _root prefix_ followed by zero or more _path + * segments_ separated by forward slashes. + * + * The root prefix is of one of the following forms: + * + * 1. A single forward slash `/` (Unix-style) + * 2. An upper-case drive letter followed by a colon and a forward slash, + * such as `C:/` (Windows-style) + * 3. Two forward slashes, a computer name, and then another forward slash, + * such as `//FileServer/` (UNC-style) + * + * Path segments are never empty (that is, absolute paths never contain two + * contiguous slashes, except as part of a UNC-style root prefix). Also, path + * segments never contain forward slashes, and no path segment is of the + * form `.` (one dot) or `..` (two dots). + * + * Note that an absolute path never ends with a forward slash, except if it is + * a bare root prefix, that is, the path has no path segments. A container + * whose absolute path has no segments is always a `Folder`, not a `File`. + */ + abstract string getAbsolutePath(); + + /** + * Gets the base name of this container including extension, that is, the last + * segment of its absolute path, or the empty string if it has no segments. + * + * Here are some examples of absolute paths and the corresponding base names + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + * + *
Absolute pathBase name
"/tmp/tst.py""tst.py"
"C:/Program Files (x86)""Program Files (x86)"
"/"""
"C:/"""
"D:/"""
"//FileServer/"""
+ */ + string getBaseName() { + result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) + } + + /** + * Gets the extension of this container, that is, the suffix of its base name + * after the last dot character, if any. + * + * In particular, + * + * - if the name does not include a dot, there is no extension, so this + * predicate has no result; + * - if the name ends in a dot, the extension is the empty string; + * - if the name contains multiple dots, the extension follows the last dot. + * + * Here are some examples of absolute paths and the corresponding extensions + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + *
Absolute pathExtension
"/tmp/tst.py""py"
"/tmp/.gitignore""gitignore"
"/bin/bash"not defined
"/tmp/tst2."""
"/tmp/x.tar.gz""gz"
+ */ + string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) } + + /** + * Gets the stem of this container, that is, the prefix of its base name up to + * (but not including) the last dot character if there is one, or the entire + * base name if there is not. + * + * Here are some examples of absolute paths and the corresponding stems + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + *
Absolute pathStem
"/tmp/tst.py""tst"
"/tmp/.gitignore"""
"/bin/bash""bash"
"/tmp/tst2.""tst2"
"/tmp/x.tar.gz""x.tar"
+ */ + string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) } + + File getFile(string baseName) { + result = this.getAFile() and + result.getBaseName() = baseName + } + + Folder getFolder(string baseName) { + result = this.getAFolder() and + result.getBaseName() = baseName + } + + Container getParentContainer() { this = result.getAChildContainer() } + + Container getChildContainer(string baseName) { + result = this.getAChildContainer() and + result.getBaseName() = baseName + } + + /** + * Gets a URL representing the location of this container. + * + * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls). + */ + abstract string getURL(); + + /** Holds if this folder is on the import path. */ + predicate isImportRoot() { this.isImportRoot(_) } + + /** + * Holds if this folder is on the import path, at index `n` in the list of + * paths. The list of paths is composed of the paths passed to the extractor and + * `sys.path`. + */ + predicate isImportRoot(int n) { this.getName() = import_path_element(n) } + + /** Holds if this folder is the root folder for the standard library. */ + predicate isStdLibRoot(int major, int minor) { + major = major_version() and + minor = minor_version() and + this.isStdLibRoot() + } + + /** Holds if this folder is the root folder for the standard library. */ + predicate isStdLibRoot() { + /* + * Look for a standard lib module and find its import path + * We use `os` as it is the most likely to be imported and + * `tty` because it is small for testing. */ - string toString() { result = this.getAbsolutePath() } - /** Gets the name of this container */ - abstract string getName(); + exists(Module m | m.getName() = "os" or m.getName() = "tty" | + m.getFile().getImportRoot() = this + ) + } - /** - * Gets the relative path of this file or folder from the root folder of the - * analyzed source location. The relative path of the root folder itself is - * the empty string. - * - * This has no result if the container is outside the source root, that is, - * if the root folder is not a reflexive, transitive parent of this container. - */ - string getRelativePath() { - exists(string absPath, string pref | - absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) - | - absPath = pref and result = "" - or - absPath = pref.regexpReplaceAll("/$", "") + "/" + result and - not result.matches("/%") - ) - } + /** Gets the path element from which this container would be loaded. */ + Container getImportRoot() { + exists(int n | + result = this.getImportRoot(n) and + not exists(int m | + exists(this.getImportRoot(m)) and + m < n + ) + ) + } - /** Whether this file or folder is part of the standard library */ - predicate inStdlib() { this.inStdlib(_, _) } - - /** - * Whether this file or folder is part of the standard library - * for version `major.minor` - */ - predicate inStdlib(int major, int minor) { - exists(Module m | - m.getPath() = this and - m.inStdLib(major, minor) - ) - } - - /* Standard cross-language API */ - /** Gets a file or sub-folder in this container. */ - Container getAChildContainer() { containerparent(this, result) } - - /** Gets a file in this container. */ - File getAFile() { result = this.getAChildContainer() } - - /** Gets a sub-folder in this container. */ - Folder getAFolder() { result = this.getAChildContainer() } - - /** - * Gets the absolute, canonical path of this container, using forward slashes - * as path separator. - * - * The path starts with a _root prefix_ followed by zero or more _path - * segments_ separated by forward slashes. - * - * The root prefix is of one of the following forms: - * - * 1. A single forward slash `/` (Unix-style) - * 2. An upper-case drive letter followed by a colon and a forward slash, - * such as `C:/` (Windows-style) - * 3. Two forward slashes, a computer name, and then another forward slash, - * such as `//FileServer/` (UNC-style) - * - * Path segments are never empty (that is, absolute paths never contain two - * contiguous slashes, except as part of a UNC-style root prefix). Also, path - * segments never contain forward slashes, and no path segment is of the - * form `.` (one dot) or `..` (two dots). - * - * Note that an absolute path never ends with a forward slash, except if it is - * a bare root prefix, that is, the path has no path segments. A container - * whose absolute path has no segments is always a `Folder`, not a `File`. - */ - abstract string getAbsolutePath(); - - /** - * Gets the base name of this container including extension, that is, the last - * segment of its absolute path, or the empty string if it has no segments. - * - * Here are some examples of absolute paths and the corresponding base names - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - * - *
Absolute pathBase name
"/tmp/tst.py""tst.py"
"C:/Program Files (x86)""Program Files (x86)"
"/"""
"C:/"""
"D:/"""
"//FileServer/"""
- */ - string getBaseName() { - result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) - } - - /** - * Gets the extension of this container, that is, the suffix of its base name - * after the last dot character, if any. - * - * In particular, - * - * - if the name does not include a dot, there is no extension, so this - * predicate has no result; - * - if the name ends in a dot, the extension is the empty string; - * - if the name contains multiple dots, the extension follows the last dot. - * - * Here are some examples of absolute paths and the corresponding extensions - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - *
Absolute pathExtension
"/tmp/tst.py""py"
"/tmp/.gitignore""gitignore"
"/bin/bash"not defined
"/tmp/tst2."""
"/tmp/x.tar.gz""gz"
- */ - string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) } - - /** - * Gets the stem of this container, that is, the prefix of its base name up to - * (but not including) the last dot character if there is one, or the entire - * base name if there is not. - * - * Here are some examples of absolute paths and the corresponding stems - * (surrounded with quotes to avoid ambiguity): - * - * - * - * - * - * - * - * - *
Absolute pathStem
"/tmp/tst.py""tst"
"/tmp/.gitignore"""
"/bin/bash""bash"
"/tmp/tst2.""tst2"
"/tmp/x.tar.gz""x.tar"
- */ - string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) } - - File getFile(string baseName) { - result = this.getAFile() and - result.getBaseName() = baseName - } - - Folder getFolder(string baseName) { - result = this.getAFolder() and - result.getBaseName() = baseName - } - - Container getParentContainer() { this = result.getAChildContainer() } - - Container getChildContainer(string baseName) { - result = this.getAChildContainer() and - result.getBaseName() = baseName - } - - /** - * Gets a URL representing the location of this container. - * - * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls). - */ - abstract string getURL(); - - /** Holds if this folder is on the import path. */ - predicate isImportRoot() { this.isImportRoot(_) } - - /** - * Holds if this folder is on the import path, at index `n` in the list of - * paths. The list of paths is composed of the paths passed to the extractor and - * `sys.path`. - */ - predicate isImportRoot(int n) { this.getName() = import_path_element(n) } - - /** Holds if this folder is the root folder for the standard library. */ - predicate isStdLibRoot(int major, int minor) { - major = major_version() and - minor = minor_version() and - this.isStdLibRoot() - } - - /** Holds if this folder is the root folder for the standard library. */ - predicate isStdLibRoot() { - /* - * Look for a standard lib module and find its import path - * We use `os` as it is the most likely to be imported and - * `tty` because it is small for testing. - */ - - exists(Module m | m.getName() = "os" or m.getName() = "tty" | - m.getFile().getImportRoot() = this - ) - } - - /** Gets the path element from which this container would be loaded. */ - Container getImportRoot() { - exists(int n | - result = this.getImportRoot(n) and - not exists(int m | - exists(this.getImportRoot(m)) and - m < n - ) - ) - } - - /** Gets the path element from which this container would be loaded, given the index into the list of possible paths `n`. */ - abstract Container getImportRoot(int n); + /** Gets the path element from which this container would be loaded, given the index into the list of possible paths `n`. */ + abstract Container getImportRoot(int n); } private string import_path_element(int n) { - exists(string path, string pathsep, int k | - path = get_path("extractor.path") and k = 0 - or - path = get_path("sys.path") and k = count(get_path("extractor.path").splitAt(pathsep)) - | - py_flags_versioned("os.pathsep", pathsep, _) and - result = path.splitAt(pathsep, n - k).replaceAll("\\", "/") - ) + exists(string path, string pathsep, int k | + path = get_path("extractor.path") and k = 0 + or + path = get_path("sys.path") and k = count(get_path("extractor.path").splitAt(pathsep)) + | + py_flags_versioned("os.pathsep", pathsep, _) and + result = path.splitAt(pathsep, n - k).replaceAll("\\", "/") + ) } private string get_path(string name) { py_flags_versioned(name, result, _) } class Location extends @location { - /** Gets the file for this location */ - File getFile() { result = this.getPath() } + /** Gets the file for this location */ + File getFile() { result = this.getPath() } - private Container getPath() { - locations_default(this, result, _, _, _, _) - or - exists(Module m | locations_ast(this, m, _, _, _, _) | result = m.getPath()) - } + private Container getPath() { + locations_default(this, result, _, _, _, _) + or + exists(Module m | locations_ast(this, m, _, _, _, _) | result = m.getPath()) + } - /** Gets the start line of this location */ - int getStartLine() { - locations_default(this, _, result, _, _, _) or - locations_ast(this, _, result, _, _, _) - } + /** Gets the start line of this location */ + int getStartLine() { + locations_default(this, _, result, _, _, _) or + locations_ast(this, _, result, _, _, _) + } - /** Gets the start column of this location */ - int getStartColumn() { - locations_default(this, _, _, result, _, _) or - locations_ast(this, _, _, result, _, _) - } + /** Gets the start column of this location */ + int getStartColumn() { + locations_default(this, _, _, result, _, _) or + locations_ast(this, _, _, result, _, _) + } - /** Gets the end line of this location */ - int getEndLine() { - locations_default(this, _, _, _, result, _) or - locations_ast(this, _, _, _, result, _) - } + /** Gets the end line of this location */ + int getEndLine() { + locations_default(this, _, _, _, result, _) or + locations_ast(this, _, _, _, result, _) + } - /** Gets the end column of this location */ - int getEndColumn() { - locations_default(this, _, _, _, _, result) or - locations_ast(this, _, _, _, _, result) - } + /** Gets the end column of this location */ + int getEndColumn() { + locations_default(this, _, _, _, _, result) or + locations_ast(this, _, _, _, _, result) + } - /** Gets a textual representation of this element. */ - string toString() { - result = this.getPath().getAbsolutePath() + ":" + this.getStartLine().toString() - } + /** Gets a textual representation of this element. */ + string toString() { + result = this.getPath().getAbsolutePath() + ":" + this.getStartLine().toString() + } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { exists(File f | f.getAbsolutePath() = filepath | - locations_default(this, f, startline, startcolumn, endline, endcolumn) - or - exists(Module m | m.getFile() = f | locations_ast(this, m, startline, startcolumn, endline, endcolumn)) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(File f | f.getAbsolutePath() = filepath | + locations_default(this, f, startline, startcolumn, endline, endcolumn) + or + exists(Module m | m.getFile() = f | + locations_ast(this, m, startline, startcolumn, endline, endcolumn) + ) + ) + } } /** A non-empty line in the source code */ class Line extends @py_line { - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { exists(Module m | - m.getFile().getAbsolutePath() = filepath and - endline = startline and - startcolumn = 1 and - py_line_lengths(this, m, startline, endcolumn) - ) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(Module m | + m.getFile().getAbsolutePath() = filepath and + endline = startline and + startcolumn = 1 and + py_line_lengths(this, m, startline, endcolumn) + ) + } - /** Gets a textual representation of this element. */ - string toString() { - exists(Module m | py_line_lengths(this, m, _, _) | - result = m.getFile().getShortName() + ":" + this.getLineNumber().toString() - ) - } + /** Gets a textual representation of this element. */ + string toString() { + exists(Module m | py_line_lengths(this, m, _, _) | + result = m.getFile().getShortName() + ":" + this.getLineNumber().toString() + ) + } - /** Gets the line number of this line */ - int getLineNumber() { py_line_lengths(this, _, result, _) } + /** Gets the line number of this line */ + int getLineNumber() { py_line_lengths(this, _, result, _) } - /** Gets the length of this line */ - int getLength() { py_line_lengths(this, _, _, result) } + /** Gets the length of this line */ + int getLength() { py_line_lengths(this, _, _, result) } - /** Gets the file for this line */ - Module getModule() { py_line_lengths(this, result, _, _) } + /** Gets the file for this line */ + Module getModule() { py_line_lengths(this, result, _, _) } } /** @@ -456,12 +460,12 @@ class Line extends @py_line { * much information about that module will be lost */ class SyntaxError extends Location { - SyntaxError() { py_syntax_error_versioned(this, _, major_version().toString()) } + SyntaxError() { py_syntax_error_versioned(this, _, major_version().toString()) } - override string toString() { result = "Syntax Error" } + override string toString() { result = "Syntax Error" } - /** Gets the message corresponding to this syntax error */ - string getMessage() { py_syntax_error_versioned(this, result, major_version().toString()) } + /** Gets the message corresponding to this syntax error */ + string getMessage() { py_syntax_error_versioned(this, result, major_version().toString()) } } /** @@ -469,10 +473,10 @@ class SyntaxError extends Location { * much information about that module will be lost */ class EncodingError extends SyntaxError { - EncodingError() { - /* Leave spaces around 'decode' in unlikely event it occurs as a name in a syntax error */ - this.getMessage().toLowerCase().matches("% decode %") - } + EncodingError() { + /* Leave spaces around 'decode' in unlikely event it occurs as a name in a syntax error */ + this.getMessage().toLowerCase().matches("% decode %") + } - override string toString() { result = "Encoding Error" } + override string toString() { result = "Encoding Error" } } diff --git a/python/ql/src/semmle/python/Flow.qll b/python/ql/src/semmle/python/Flow.qll index 08d0504f398..e68800022ed 100755 --- a/python/ql/src/semmle/python/Flow.qll +++ b/python/ql/src/semmle/python/Flow.qll @@ -13,11 +13,11 @@ private import semmle.python.pointsto.PointsTo */ private predicate augstore(ControlFlowNode load, ControlFlowNode store) { - exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) | - toAst(load) = load_store and - toAst(store) = load_store and - load.strictlyDominates(store) - ) + exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) | + toAst(load) = load_store and + toAst(store) = load_store and + load.strictlyDominates(store) + ) } /** A non-dispatched getNode() to avoid negative recursion issues */ @@ -29,306 +29,306 @@ private AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) } * Edges between control flow nodes include exceptional as well as normal control flow. */ class ControlFlowNode extends @py_flow_node { - /** Whether this control flow node is a load (including those in augmented assignments) */ - predicate isLoad() { - exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this)) - } + /** Whether this control flow node is a load (including those in augmented assignments) */ + predicate isLoad() { + exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this)) + } - /** Whether this control flow node is a store (including those in augmented assignments) */ - predicate isStore() { - exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this)) - } + /** Whether this control flow node is a store (including those in augmented assignments) */ + predicate isStore() { + exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this)) + } - /** Whether this control flow node is a delete */ - predicate isDelete() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) } + /** Whether this control flow node is a delete */ + predicate isDelete() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) } - /** Whether this control flow node is a parameter */ - predicate isParameter() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) } + /** Whether this control flow node is a parameter */ + predicate isParameter() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) } - /** Whether this control flow node is a store in an augmented assignment */ - predicate isAugStore() { augstore(_, this) } + /** Whether this control flow node is a store in an augmented assignment */ + predicate isAugStore() { augstore(_, this) } - /** Whether this control flow node is a load in an augmented assignment */ - predicate isAugLoad() { augstore(this, _) } + /** Whether this control flow node is a load in an augmented assignment */ + predicate isAugLoad() { augstore(this, _) } - /** Whether this flow node corresponds to a literal */ - predicate isLiteral() { - toAst(this) instanceof Bytes - or - toAst(this) instanceof Dict - or - toAst(this) instanceof DictComp - or - toAst(this) instanceof Set - or - toAst(this) instanceof SetComp - or - toAst(this) instanceof Ellipsis - or - toAst(this) instanceof GeneratorExp - or - toAst(this) instanceof Lambda - or - toAst(this) instanceof ListComp - or - toAst(this) instanceof List - or - toAst(this) instanceof Num - or - toAst(this) instanceof Tuple - or - toAst(this) instanceof Unicode - or - toAst(this) instanceof NameConstant - } + /** Whether this flow node corresponds to a literal */ + predicate isLiteral() { + toAst(this) instanceof Bytes + or + toAst(this) instanceof Dict + or + toAst(this) instanceof DictComp + or + toAst(this) instanceof Set + or + toAst(this) instanceof SetComp + or + toAst(this) instanceof Ellipsis + or + toAst(this) instanceof GeneratorExp + or + toAst(this) instanceof Lambda + or + toAst(this) instanceof ListComp + or + toAst(this) instanceof List + or + toAst(this) instanceof Num + or + toAst(this) instanceof Tuple + or + toAst(this) instanceof Unicode + or + toAst(this) instanceof NameConstant + } - /** Use NameNode.isLoad() instead */ - deprecated predicate isUse() { toAst(this) instanceof Name and this.isLoad() } + /** Use NameNode.isLoad() instead */ + deprecated predicate isUse() { toAst(this) instanceof Name and this.isLoad() } - /** Use NameNode.isStore() */ - deprecated predicate isDefinition() { toAst(this) instanceof Name and this.isStore() } + /** Use NameNode.isStore() */ + deprecated predicate isDefinition() { toAst(this) instanceof Name and this.isStore() } - /** Whether this flow node corresponds to an attribute expression */ - predicate isAttribute() { toAst(this) instanceof Attribute } + /** Whether this flow node corresponds to an attribute expression */ + predicate isAttribute() { toAst(this) instanceof Attribute } - /** Use AttrNode.isLoad() instead */ - deprecated predicate isAttributeLoad() { toAst(this) instanceof Attribute and this.isLoad() } + /** Use AttrNode.isLoad() instead */ + deprecated predicate isAttributeLoad() { toAst(this) instanceof Attribute and this.isLoad() } - /** Use AttrNode.isStore() instead */ - deprecated predicate isAttributeStore() { toAst(this) instanceof Attribute and this.isStore() } + /** Use AttrNode.isStore() instead */ + deprecated predicate isAttributeStore() { toAst(this) instanceof Attribute and this.isStore() } - /** Whether this flow node corresponds to an subscript expression */ - predicate isSubscript() { toAst(this) instanceof Subscript } + /** Whether this flow node corresponds to an subscript expression */ + predicate isSubscript() { toAst(this) instanceof Subscript } - /** Use SubscriptNode.isLoad() instead */ - deprecated predicate isSubscriptLoad() { toAst(this) instanceof Subscript and this.isLoad() } + /** Use SubscriptNode.isLoad() instead */ + deprecated predicate isSubscriptLoad() { toAst(this) instanceof Subscript and this.isLoad() } - /** Use SubscriptNode.isStore() instead */ - deprecated predicate isSubscriptStore() { toAst(this) instanceof Subscript and this.isStore() } + /** Use SubscriptNode.isStore() instead */ + deprecated predicate isSubscriptStore() { toAst(this) instanceof Subscript and this.isStore() } - /** Whether this flow node corresponds to an import member */ - predicate isImportMember() { toAst(this) instanceof ImportMember } + /** Whether this flow node corresponds to an import member */ + predicate isImportMember() { toAst(this) instanceof ImportMember } - /** Whether this flow node corresponds to a call */ - predicate isCall() { toAst(this) instanceof Call } + /** Whether this flow node corresponds to a call */ + predicate isCall() { toAst(this) instanceof Call } - /** Whether this flow node is the first in a module */ - predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module } + /** Whether this flow node is the first in a module */ + predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module } - /** Whether this flow node corresponds to an import */ - predicate isImport() { toAst(this) instanceof ImportExpr } + /** Whether this flow node corresponds to an import */ + predicate isImport() { toAst(this) instanceof ImportExpr } - /** Whether this flow node corresponds to a conditional expression */ - predicate isIfExp() { toAst(this) instanceof IfExp } + /** Whether this flow node corresponds to a conditional expression */ + predicate isIfExp() { toAst(this) instanceof IfExp } - /** Whether this flow node corresponds to a function definition expression */ - predicate isFunction() { toAst(this) instanceof FunctionExpr } + /** Whether this flow node corresponds to a function definition expression */ + predicate isFunction() { toAst(this) instanceof FunctionExpr } - /** Whether this flow node corresponds to a class definition expression */ - predicate isClass() { toAst(this) instanceof ClassExpr } + /** Whether this flow node corresponds to a class definition expression */ + predicate isClass() { toAst(this) instanceof ClassExpr } - /** Gets a predecessor of this flow node */ - ControlFlowNode getAPredecessor() { this = result.getASuccessor() } + /** Gets a predecessor of this flow node */ + ControlFlowNode getAPredecessor() { this = result.getASuccessor() } - /** Gets a successor of this flow node */ - ControlFlowNode getASuccessor() { py_successors(this, result) } + /** Gets a successor of this flow node */ + ControlFlowNode getASuccessor() { py_successors(this, result) } - /** Gets the immediate dominator of this flow node */ - ControlFlowNode getImmediateDominator() { py_idoms(this, result) } + /** Gets the immediate dominator of this flow node */ + ControlFlowNode getImmediateDominator() { py_idoms(this, result) } - /** Gets the syntactic element corresponding to this flow node */ - AstNode getNode() { py_flow_bb_node(this, result, _, _) } + /** Gets the syntactic element corresponding to this flow node */ + AstNode getNode() { py_flow_bb_node(this, result, _, _) } - /** Gets a textual representation of this element. */ - string toString() { - exists(Scope s | s.getEntryNode() = this | result = "Entry node for " + s.toString()) - or - exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString()) - or - not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and - result = "ControlFlowNode for " + this.getNode().toString() - } + /** Gets a textual representation of this element. */ + string toString() { + exists(Scope s | s.getEntryNode() = this | result = "Entry node for " + s.toString()) + or + exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString()) + or + not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and + result = "ControlFlowNode for " + this.getNode().toString() + } - /** Gets the location of this ControlFlowNode */ - Location getLocation() { result = this.getNode().getLocation() } + /** Gets the location of this ControlFlowNode */ + Location getLocation() { result = this.getNode().getLocation() } - /** Whether this flow node is the first in its scope */ - predicate isEntryNode() { py_scope_flow(this, _, -1) } + /** Whether this flow node is the first in its scope */ + predicate isEntryNode() { py_scope_flow(this, _, -1) } - /** The value that this ControlFlowNode points-to. */ - predicate pointsTo(Value value) { this.pointsTo(_, value, _) } + /** The value that this ControlFlowNode points-to. */ + predicate pointsTo(Value value) { this.pointsTo(_, value, _) } - /** Gets the value that this ControlFlowNode points-to. */ - Value pointsTo() { this.pointsTo(_, result, _) } + /** Gets the value that this ControlFlowNode points-to. */ + Value pointsTo() { this.pointsTo(_, result, _) } - /** Gets a value that this ControlFlowNode may points-to. */ - Value inferredValue() { this.pointsTo(_, result, _) } + /** Gets a value that this ControlFlowNode may points-to. */ + Value inferredValue() { this.pointsTo(_, result, _) } - /** The value and origin that this ControlFlowNode points-to. */ - predicate pointsTo(Value value, ControlFlowNode origin) { this.pointsTo(_, value, origin) } + /** The value and origin that this ControlFlowNode points-to. */ + predicate pointsTo(Value value, ControlFlowNode origin) { this.pointsTo(_, value, origin) } - /** The value and origin that this ControlFlowNode points-to, given the context. */ - predicate pointsTo(Context context, Value value, ControlFlowNode origin) { - PointsTo::pointsTo(this, context, value, origin) - } + /** The value and origin that this ControlFlowNode points-to, given the context. */ + predicate pointsTo(Context context, Value value, ControlFlowNode origin) { + PointsTo::pointsTo(this, context, value, origin) + } - /** - * Gets what this flow node might "refer-to". Performs a combination of localized (intra-procedural) points-to - * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly - * precise, but may not provide information for a significant number of flow-nodes. - * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. - */ - pragma[nomagic] - predicate refersTo(Object obj, ClassObject cls, ControlFlowNode origin) { - this.refersTo(_, obj, cls, origin) - } + /** + * Gets what this flow node might "refer-to". Performs a combination of localized (intra-procedural) points-to + * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly + * precise, but may not provide information for a significant number of flow-nodes. + * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. + */ + pragma[nomagic] + predicate refersTo(Object obj, ClassObject cls, ControlFlowNode origin) { + this.refersTo(_, obj, cls, origin) + } - /** Gets what this expression might "refer-to" in the given `context`. */ - pragma[nomagic] - predicate refersTo(Context context, Object obj, ClassObject cls, ControlFlowNode origin) { - not obj = unknownValue() and - not cls = theUnknownType() and - PointsTo::points_to(this, context, obj, cls, origin) - } + /** Gets what this expression might "refer-to" in the given `context`. */ + pragma[nomagic] + predicate refersTo(Context context, Object obj, ClassObject cls, ControlFlowNode origin) { + not obj = unknownValue() and + not cls = theUnknownType() and + PointsTo::points_to(this, context, obj, cls, origin) + } - /** - * Whether this flow node might "refer-to" to `value` which is from `origin` - * Unlike `this.refersTo(value, _, origin)` this predicate includes results - * where the class cannot be inferred. - */ - pragma[nomagic] - predicate refersTo(Object obj, ControlFlowNode origin) { - not obj = unknownValue() and - PointsTo::points_to(this, _, obj, _, origin) - } + /** + * Whether this flow node might "refer-to" to `value` which is from `origin` + * Unlike `this.refersTo(value, _, origin)` this predicate includes results + * where the class cannot be inferred. + */ + pragma[nomagic] + predicate refersTo(Object obj, ControlFlowNode origin) { + not obj = unknownValue() and + PointsTo::points_to(this, _, obj, _, origin) + } - /** Equivalent to `this.refersTo(value, _)` */ - predicate refersTo(Object obj) { this.refersTo(obj, _) } + /** Equivalent to `this.refersTo(value, _)` */ + predicate refersTo(Object obj) { this.refersTo(obj, _) } - /** Gets the basic block containing this flow node */ - BasicBlock getBasicBlock() { result.contains(this) } + /** Gets the basic block containing this flow node */ + BasicBlock getBasicBlock() { result.contains(this) } - /** Gets the scope containing this flow node */ - Scope getScope() { - if this.getNode() instanceof Scope - then - /* Entry or exit node */ - result = this.getNode() - else result = this.getNode().getScope() - } + /** Gets the scope containing this flow node */ + Scope getScope() { + if this.getNode() instanceof Scope + then + /* Entry or exit node */ + result = this.getNode() + else result = this.getNode().getScope() + } - /** Gets the enclosing module */ - Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } + /** Gets the enclosing module */ + Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } - /** Gets a successor for this node if the relevant condition is True. */ - ControlFlowNode getATrueSuccessor() { - result = this.getASuccessor() and - py_true_successors(this, result) - } + /** Gets a successor for this node if the relevant condition is True. */ + ControlFlowNode getATrueSuccessor() { + result = this.getASuccessor() and + py_true_successors(this, result) + } - /** Gets a successor for this node if the relevant condition is False. */ - ControlFlowNode getAFalseSuccessor() { - result = this.getASuccessor() and - py_false_successors(this, result) - } + /** Gets a successor for this node if the relevant condition is False. */ + ControlFlowNode getAFalseSuccessor() { + result = this.getASuccessor() and + py_false_successors(this, result) + } - /** Gets a successor for this node if an exception is raised. */ - ControlFlowNode getAnExceptionalSuccessor() { - result = this.getASuccessor() and - py_exception_successors(this, result) - } + /** Gets a successor for this node if an exception is raised. */ + ControlFlowNode getAnExceptionalSuccessor() { + result = this.getASuccessor() and + py_exception_successors(this, result) + } - /** Gets a successor for this node if no exception is raised. */ - ControlFlowNode getANormalSuccessor() { - result = this.getASuccessor() and - not py_exception_successors(this, result) - } + /** Gets a successor for this node if no exception is raised. */ + ControlFlowNode getANormalSuccessor() { + result = this.getASuccessor() and + not py_exception_successors(this, result) + } - /** Whether the scope may be exited as a result of this node raising an exception */ - predicate isExceptionalExit(Scope s) { py_scope_flow(this, s, 1) } + /** Whether the scope may be exited as a result of this node raising an exception */ + predicate isExceptionalExit(Scope s) { py_scope_flow(this, s, 1) } - /** Whether this node is a normal (non-exceptional) exit */ - predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) } + /** Whether this node is a normal (non-exceptional) exit */ + predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) } - /** Whether it is unlikely that this ControlFlowNode can be reached */ - predicate unlikelyReachable() { - not start_bb_likely_reachable(this.getBasicBlock()) - or - exists(BasicBlock b | - start_bb_likely_reachable(b) and - not end_bb_likely_reachable(b) and - // If there is an unlikely successor edge earlier in the BB - // than this node, then this node must be unreachable. - exists(ControlFlowNode p, int i, int j | - p.(RaisingNode).unlikelySuccessor(_) and - p = b.getNode(i) and - this = b.getNode(j) and - i < j - ) - ) - } + /** Whether it is unlikely that this ControlFlowNode can be reached */ + predicate unlikelyReachable() { + not start_bb_likely_reachable(this.getBasicBlock()) + or + exists(BasicBlock b | + start_bb_likely_reachable(b) and + not end_bb_likely_reachable(b) and + // If there is an unlikely successor edge earlier in the BB + // than this node, then this node must be unreachable. + exists(ControlFlowNode p, int i, int j | + p.(RaisingNode).unlikelySuccessor(_) and + p = b.getNode(i) and + this = b.getNode(j) and + i < j + ) + ) + } - /** - * Check whether this control-flow node has complete points-to information. - * This would mean that the analysis managed to infer an over approximation - * of possible values at runtime. - */ - predicate hasCompletePointsToSet() { - // If the tracking failed, then `this` will be its own "origin". In that - // case, we want to exclude nodes for which there is also a different - // origin, as that would indicate that some paths failed and some did not. - this.refersTo(_, _, this) and - not exists(ControlFlowNode other | other != this and this.refersTo(_, _, other)) - or - // If `this` is a use of a variable, then we must have complete points-to - // for that variable. - exists(SsaVariable v | v.getAUse() = this | varHasCompletePointsToSet(v)) - } + /** + * Check whether this control-flow node has complete points-to information. + * This would mean that the analysis managed to infer an over approximation + * of possible values at runtime. + */ + predicate hasCompletePointsToSet() { + // If the tracking failed, then `this` will be its own "origin". In that + // case, we want to exclude nodes for which there is also a different + // origin, as that would indicate that some paths failed and some did not. + this.refersTo(_, _, this) and + not exists(ControlFlowNode other | other != this and this.refersTo(_, _, other)) + or + // If `this` is a use of a variable, then we must have complete points-to + // for that variable. + exists(SsaVariable v | v.getAUse() = this | varHasCompletePointsToSet(v)) + } - /** Whether this strictly dominates other. */ - pragma[inline] - predicate strictlyDominates(ControlFlowNode other) { - // This predicate is gigantic, so it must be inlined. - // About 1.4 billion tuples for OpenStack Cinder. - this.getBasicBlock().strictlyDominates(other.getBasicBlock()) - or - exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) - } + /** Whether this strictly dominates other. */ + pragma[inline] + predicate strictlyDominates(ControlFlowNode other) { + // This predicate is gigantic, so it must be inlined. + // About 1.4 billion tuples for OpenStack Cinder. + this.getBasicBlock().strictlyDominates(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) + } - /** - * Whether this dominates other. - * Note that all nodes dominate themselves. - */ - pragma[inline] - predicate dominates(ControlFlowNode other) { - // This predicate is gigantic, so it must be inlined. - this.getBasicBlock().strictlyDominates(other.getBasicBlock()) - or - exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i <= j) - } + /** + * Whether this dominates other. + * Note that all nodes dominate themselves. + */ + pragma[inline] + predicate dominates(ControlFlowNode other) { + // This predicate is gigantic, so it must be inlined. + this.getBasicBlock().strictlyDominates(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i <= j) + } - /** Whether this strictly reaches other. */ - pragma[inline] - predicate strictlyReaches(ControlFlowNode other) { - // This predicate is gigantic, even larger than strictlyDominates, - // so it must be inlined. - this.getBasicBlock().strictlyReaches(other.getBasicBlock()) - or - exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) - } + /** Whether this strictly reaches other. */ + pragma[inline] + predicate strictlyReaches(ControlFlowNode other) { + // This predicate is gigantic, even larger than strictlyDominates, + // so it must be inlined. + this.getBasicBlock().strictlyReaches(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j) + } - /* Holds if this CFG node is a branch */ - predicate isBranch() { py_true_successors(this, _) or py_false_successors(this, _) } + /* Holds if this CFG node is a branch */ + predicate isBranch() { py_true_successors(this, _) or py_false_successors(this, _) } - ControlFlowNode getAChild() { result = this.getExprChild(this.getBasicBlock()) } + ControlFlowNode getAChild() { result = this.getExprChild(this.getBasicBlock()) } - /* join-ordering helper for `getAChild() */ - pragma[noinline] - private ControlFlowNode getExprChild(BasicBlock dom) { - this.getNode().(Expr).getAChildNode() = result.getNode() and - result.getBasicBlock().dominates(dom) and - not this instanceof UnaryExprNode - } + /* join-ordering helper for `getAChild() */ + pragma[noinline] + private ControlFlowNode getExprChild(BasicBlock dom) { + this.getNode().(Expr).getAChildNode() = result.getNode() and + result.getBasicBlock().dominates(dom) and + not this instanceof UnaryExprNode + } } /* @@ -338,7 +338,7 @@ class ControlFlowNode extends @py_flow_node { */ private class AnyNode extends ControlFlowNode { - override AstNode getNode() { result = super.getNode() } + override AstNode getNode() { result = super.getNode() } } /** @@ -347,300 +347,300 @@ private class AnyNode extends ControlFlowNode { * of possible values at runtime. */ private predicate varHasCompletePointsToSet(SsaVariable var) { - // Global variables may be modified non-locally or concurrently. - not var.getVariable() instanceof GlobalVariable and - ( - // If we have complete points-to information on the definition of - // this variable, then the variable has complete information. - var.getDefinition().(DefinitionNode).getValue().hasCompletePointsToSet() - or - // If this variable is a phi output, then we have complete - // points-to information about it if all phi inputs had complete - // information. - forex(SsaVariable phiInput | phiInput = var.getAPhiInput() | - varHasCompletePointsToSet(phiInput) - ) + // Global variables may be modified non-locally or concurrently. + not var.getVariable() instanceof GlobalVariable and + ( + // If we have complete points-to information on the definition of + // this variable, then the variable has complete information. + var.getDefinition().(DefinitionNode).getValue().hasCompletePointsToSet() + or + // If this variable is a phi output, then we have complete + // points-to information about it if all phi inputs had complete + // information. + forex(SsaVariable phiInput | phiInput = var.getAPhiInput() | + varHasCompletePointsToSet(phiInput) ) + ) } /** A control flow node corresponding to a call expression, such as `func(...)` */ class CallNode extends ControlFlowNode { - CallNode() { toAst(this) instanceof Call } + CallNode() { toAst(this) instanceof Call } - /** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */ - ControlFlowNode getFunction() { - exists(Call c | - this.getNode() = c and - c.getFunc() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */ + ControlFlowNode getFunction() { + exists(Call c | + this.getNode() = c and + c.getFunc() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the flow node corresponding to the nth argument of the call corresponding to this flow node */ - ControlFlowNode getArg(int n) { - exists(Call c | - this.getNode() = c and - c.getArg(n) = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the nth argument of the call corresponding to this flow node */ + ControlFlowNode getArg(int n) { + exists(Call c | + this.getNode() = c and + c.getArg(n) = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */ - ControlFlowNode getArgByName(string name) { - exists(Call c, Keyword k | - this.getNode() = c and - k = c.getAKeyword() and - k.getValue() = result.getNode() and - k.getArg() = name and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */ + ControlFlowNode getArgByName(string name) { + exists(Call c, Keyword k | + this.getNode() = c and + k = c.getAKeyword() and + k.getValue() = result.getNode() and + k.getArg() = name and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the flow node corresponding to an argument of the call corresponding to this flow node */ - ControlFlowNode getAnArg() { - exists(int n | result = this.getArg(n)) - or - exists(string name | result = this.getArgByName(name)) - } + /** Gets the flow node corresponding to an argument of the call corresponding to this flow node */ + ControlFlowNode getAnArg() { + exists(int n | result = this.getArg(n)) + or + exists(string name | result = this.getArgByName(name)) + } - override Call getNode() { result = super.getNode() } + override Call getNode() { result = super.getNode() } - predicate isDecoratorCall() { - this.isClassDecoratorCall() - or - this.isFunctionDecoratorCall() - } + predicate isDecoratorCall() { + this.isClassDecoratorCall() + or + this.isFunctionDecoratorCall() + } - predicate isClassDecoratorCall() { - exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall()) - } + predicate isClassDecoratorCall() { + exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall()) + } - predicate isFunctionDecoratorCall() { - exists(FunctionExpr func | this.getNode() = func.getADecoratorCall()) - } + predicate isFunctionDecoratorCall() { + exists(FunctionExpr func | this.getNode() = func.getADecoratorCall()) + } - /** Gets the tuple (*) argument of this call, provided there is exactly one. */ - ControlFlowNode getStarArg() { - result.getNode() = this.getNode().getStarArg() and - result.getBasicBlock().dominates(this.getBasicBlock()) - } + /** Gets the tuple (*) argument of this call, provided there is exactly one. */ + ControlFlowNode getStarArg() { + result.getNode() = this.getNode().getStarArg() and + result.getBasicBlock().dominates(this.getBasicBlock()) + } } /** A control flow corresponding to an attribute expression, such as `value.attr` */ class AttrNode extends ControlFlowNode { - AttrNode() { toAst(this) instanceof Attribute } + AttrNode() { toAst(this) instanceof Attribute } - /** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */ - ControlFlowNode getObject() { - exists(Attribute a | - this.getNode() = a and - a.getObject() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */ + ControlFlowNode getObject() { + exists(Attribute a | + this.getNode() = a and + a.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Use getObject() instead */ - deprecated ControlFlowNode getValue() { result = this.getObject() } + /** Use getObject() instead */ + deprecated ControlFlowNode getValue() { result = this.getObject() } - /** Use getObject(name) instead */ - deprecated ControlFlowNode getValue(string name) { result = this.getObject(name) } + /** Use getObject(name) instead */ + deprecated ControlFlowNode getValue(string name) { result = this.getObject(name) } - /** - * Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node, - * with the matching name - */ - ControlFlowNode getObject(string name) { - exists(Attribute a | - this.getNode() = a and - a.getObject() = result.getNode() and - a.getName() = name and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** + * Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node, + * with the matching name + */ + ControlFlowNode getObject(string name) { + exists(Attribute a | + this.getNode() = a and + a.getObject() = result.getNode() and + a.getName() = name and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** Gets the attribute name of the attribute expression corresponding to this flow node */ - string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) } + /** Gets the attribute name of the attribute expression corresponding to this flow node */ + string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) } - override Attribute getNode() { result = super.getNode() } + override Attribute getNode() { result = super.getNode() } } /** A control flow node corresponding to a `from ... import ...` expression */ class ImportMemberNode extends ControlFlowNode { - ImportMemberNode() { toAst(this) instanceof ImportMember } + ImportMemberNode() { toAst(this) instanceof ImportMember } - /** - * Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node, - * with the matching name - */ - ControlFlowNode getModule(string name) { - exists(ImportMember i | this.getNode() = i and i.getModule() = result.getNode() | - i.getName() = name and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** + * Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node, + * with the matching name + */ + ControlFlowNode getModule(string name) { + exists(ImportMember i | this.getNode() = i and i.getModule() = result.getNode() | + i.getName() = name and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - override ImportMember getNode() { result = super.getNode() } + override ImportMember getNode() { result = super.getNode() } } /** A control flow node corresponding to an artificial expression representing an import */ class ImportExprNode extends ControlFlowNode { - ImportExprNode() { toAst(this) instanceof ImportExpr } + ImportExprNode() { toAst(this) instanceof ImportExpr } - override ImportExpr getNode() { result = super.getNode() } + override ImportExpr getNode() { result = super.getNode() } } /** A control flow node corresponding to a `from ... import *` statement */ class ImportStarNode extends ControlFlowNode { - ImportStarNode() { toAst(this) instanceof ImportStar } + ImportStarNode() { toAst(this) instanceof ImportStar } - /** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */ - ControlFlowNode getModule() { - exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() | - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */ + ControlFlowNode getModule() { + exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() | + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - override ImportStar getNode() { result = super.getNode() } + override ImportStar getNode() { result = super.getNode() } } /** A control flow node corresponding to a subscript expression, such as `value[slice]` */ class SubscriptNode extends ControlFlowNode { - SubscriptNode() { toAst(this) instanceof Subscript } + SubscriptNode() { toAst(this) instanceof Subscript } - /** - * DEPRECATED: Use `getObject()` instead. - * This will be formally deprecated before the end 2018 and removed in 2019. - */ - deprecated ControlFlowNode getValue() { - exists(Subscript s | - this.getNode() = s and - s.getObject() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** + * DEPRECATED: Use `getObject()` instead. + * This will be formally deprecated before the end 2018 and removed in 2019. + */ + deprecated ControlFlowNode getValue() { + exists(Subscript s | + this.getNode() = s and + s.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** flow node corresponding to the value of the sequence in a subscript operation */ - ControlFlowNode getObject() { - exists(Subscript s | - this.getNode() = s and - s.getObject() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** flow node corresponding to the value of the sequence in a subscript operation */ + ControlFlowNode getObject() { + exists(Subscript s | + this.getNode() = s and + s.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** flow node corresponding to the index in a subscript operation */ - ControlFlowNode getIndex() { - exists(Subscript s | - this.getNode() = s and - s.getIndex() = result.getNode() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** flow node corresponding to the index in a subscript operation */ + ControlFlowNode getIndex() { + exists(Subscript s | + this.getNode() = s and + s.getIndex() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - override Subscript getNode() { result = super.getNode() } + override Subscript getNode() { result = super.getNode() } } /** A control flow node corresponding to a comparison operation, such as `x DeletionNode -> NameNode('b') -> AttrNode('y') -> DeletionNode`. */ class DeletionNode extends ControlFlowNode { - DeletionNode() { toAst(this) instanceof Delete } + DeletionNode() { toAst(this) instanceof Delete } - /** Gets the unique target of this deletion node. */ - ControlFlowNode getTarget() { result.getASuccessor() = this } + /** Gets the unique target of this deletion node. */ + ControlFlowNode getTarget() { result.getASuccessor() = this } } /** A control flow node corresponding to a sequence (tuple or list) literal */ abstract class SequenceNode extends ControlFlowNode { - SequenceNode() { - toAst(this) instanceof Tuple - or - toAst(this) instanceof List - } + SequenceNode() { + toAst(this) instanceof Tuple + or + toAst(this) instanceof List + } - /** Gets the control flow node for an element of this sequence */ - ControlFlowNode getAnElement() { result = this.getElement(_) } + /** Gets the control flow node for an element of this sequence */ + ControlFlowNode getAnElement() { result = this.getElement(_) } - /** Gets the control flow node for the nth element of this sequence */ - abstract ControlFlowNode getElement(int n); + /** Gets the control flow node for the nth element of this sequence */ + abstract ControlFlowNode getElement(int n); } /** A control flow node corresponding to a tuple expression such as `( 1, 3, 5, 7, 9 )` */ class TupleNode extends SequenceNode { - TupleNode() { toAst(this) instanceof Tuple } + TupleNode() { toAst(this) instanceof Tuple } - override ControlFlowNode getElement(int n) { - exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and - ( - result.getBasicBlock().dominates(this.getBasicBlock()) - or - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + override ControlFlowNode getElement(int n) { + exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } } /** A control flow node corresponding to a list expression, such as `[ 1, 3, 5, 7, 9 ]` */ class ListNode extends SequenceNode { - ListNode() { toAst(this) instanceof List } + ListNode() { toAst(this) instanceof List } - override ControlFlowNode getElement(int n) { - exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and - ( - result.getBasicBlock().dominates(this.getBasicBlock()) - or - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + override ControlFlowNode getElement(int n) { + exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } } class SetNode extends ControlFlowNode { - SetNode() { toAst(this) instanceof Set } + SetNode() { toAst(this) instanceof Set } - ControlFlowNode getAnElement() { - exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and - ( - result.getBasicBlock().dominates(this.getBasicBlock()) - or - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + ControlFlowNode getAnElement() { + exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } } /** A control flow node corresponding to a dictionary literal, such as `{ 'a': 1, 'b': 2 }` */ class DictNode extends ControlFlowNode { - DictNode() { toAst(this) instanceof Dict } + DictNode() { toAst(this) instanceof Dict } - /** - * Gets a key of this dictionary literal node, for those items that have keys - * E.g, in {'a':1, **b} this returns only 'a' - */ - ControlFlowNode getAKey() { - exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and - result.getBasicBlock().dominates(this.getBasicBlock()) - } + /** + * Gets a key of this dictionary literal node, for those items that have keys + * E.g, in {'a':1, **b} this returns only 'a' + */ + ControlFlowNode getAKey() { + exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and + result.getBasicBlock().dominates(this.getBasicBlock()) + } - /** Gets a value of this dictionary literal node */ - ControlFlowNode getAValue() { - exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and - result.getBasicBlock().dominates(this.getBasicBlock()) - } + /** Gets a value of this dictionary literal node */ + ControlFlowNode getAValue() { + exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and + result.getBasicBlock().dominates(this.getBasicBlock()) + } } private AstNode assigned_value(Expr lhs) { - /* lhs = result */ - exists(Assign a | a.getATarget() = lhs and result = a.getValue()) - or - /* import result as lhs */ - exists(Alias a | a.getAsname() = lhs and result = a.getValue()) - or - /* lhs += x => result = (lhs + x) */ - exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft()) - or - /* - * ..., lhs, ... = ..., result, ... - * or - * ..., (..., lhs, ...), ... = ..., (..., result, ...), ... - */ + /* lhs = result */ + exists(Assign a | a.getATarget() = lhs and result = a.getValue()) + or + /* import result as lhs */ + exists(Alias a | a.getAsname() = lhs and result = a.getValue()) + or + /* lhs += x => result = (lhs + x) */ + exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft()) + or + /* + * ..., lhs, ... = ..., result, ... + * or + * ..., (..., lhs, ...), ... = ..., (..., result, ...), ... + */ - exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result)) - or - /* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */ - result.(For).getTarget() = lhs + exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result)) + or + /* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */ + result.(For).getTarget() = lhs } predicate nested_sequence_assign( - Expr left_parent, Expr right_parent, Expr left_result, Expr right_result + Expr left_parent, Expr right_parent, Expr left_result, Expr right_result ) { - exists(Assign a | - a.getATarget().getASubExpression*() = left_parent and - a.getValue().getASubExpression*() = right_parent + exists(Assign a | + a.getATarget().getASubExpression*() = left_parent and + a.getValue().getASubExpression*() = right_parent + ) and + exists(int i, Expr left_elem, Expr right_elem | + ( + left_elem = left_parent.(Tuple).getElt(i) + or + left_elem = left_parent.(List).getElt(i) ) and - exists(int i, Expr left_elem, Expr right_elem | - ( - left_elem = left_parent.(Tuple).getElt(i) - or - left_elem = left_parent.(List).getElt(i) - ) and - ( - right_elem = right_parent.(Tuple).getElt(i) - or - right_elem = right_parent.(List).getElt(i) - ) - | - left_result = left_elem and right_result = right_elem - or - nested_sequence_assign(left_elem, right_elem, left_result, right_result) + ( + right_elem = right_parent.(Tuple).getElt(i) + or + right_elem = right_parent.(List).getElt(i) ) + | + left_result = left_elem and right_result = right_elem + or + nested_sequence_assign(left_elem, right_elem, left_result, right_result) + ) } /** A flow node for a `for` statement. */ class ForNode extends ControlFlowNode { - ForNode() { toAst(this) instanceof For } + ForNode() { toAst(this) instanceof For } - override For getNode() { result = super.getNode() } + override For getNode() { result = super.getNode() } - /** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */ - predicate iterates(ControlFlowNode target, ControlFlowNode sequence) { - sequence = getSequence() and - target = possibleTarget() and - not target = unrolledSuffix().possibleTarget() - } + /** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */ + predicate iterates(ControlFlowNode target, ControlFlowNode sequence) { + sequence = getSequence() and + target = possibleTarget() and + not target = unrolledSuffix().possibleTarget() + } - /** Gets the sequence node for this `for` statement. */ - ControlFlowNode getSequence() { - exists(For for | - toAst(this) = for and - for.getIter() = result.getNode() - | - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the sequence node for this `for` statement. */ + ControlFlowNode getSequence() { + exists(For for | + toAst(this) = for and + for.getIter() = result.getNode() + | + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - /** A possible `target` for this `for` statement, not accounting for loop unrolling */ - private ControlFlowNode possibleTarget() { - exists(For for | - toAst(this) = for and - for.getTarget() = result.getNode() and - this.getBasicBlock().dominates(result.getBasicBlock()) - ) - } + /** A possible `target` for this `for` statement, not accounting for loop unrolling */ + private ControlFlowNode possibleTarget() { + exists(For for | + toAst(this) = for and + for.getTarget() = result.getNode() and + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } - /** The unrolled `for` statement node matching this one */ - private ForNode unrolledSuffix() { - not this = result and - toAst(this) = toAst(result) and - this.getBasicBlock().dominates(result.getBasicBlock()) - } + /** The unrolled `for` statement node matching this one */ + private ForNode unrolledSuffix() { + not this = result and + toAst(this) = toAst(result) and + this.getBasicBlock().dominates(result.getBasicBlock()) + } } /** A flow node for a `raise` statement */ class RaiseStmtNode extends ControlFlowNode { - RaiseStmtNode() { toAst(this) instanceof Raise } + RaiseStmtNode() { toAst(this) instanceof Raise } - /** Gets the control flow node for the exception raised by this raise statement */ - ControlFlowNode getException() { - exists(Raise r | - r = toAst(this) and - r.getException() = toAst(result) and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the control flow node for the exception raised by this raise statement */ + ControlFlowNode getException() { + exists(Raise r | + r = toAst(this) and + r.getException() = toAst(result) and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } } /** @@ -877,320 +877,321 @@ class RaiseStmtNode extends ControlFlowNode { * `None`, `True` and `False` are excluded. */ class NameNode extends ControlFlowNode { - NameNode() { - exists(Name n | py_flow_bb_node(this, n, _, _)) - or - exists(PlaceHolder p | py_flow_bb_node(this, p, _, _)) - } + NameNode() { + exists(Name n | py_flow_bb_node(this, n, _, _)) + or + exists(PlaceHolder p | py_flow_bb_node(this, p, _, _)) + } - /** Whether this flow node defines the variable `v`. */ - predicate defines(Variable v) { - exists(Name d | this.getNode() = d and d.defines(v)) and - not this.isLoad() - } + /** Whether this flow node defines the variable `v`. */ + predicate defines(Variable v) { + exists(Name d | this.getNode() = d and d.defines(v)) and + not this.isLoad() + } - /** Whether this flow node deletes the variable `v`. */ - predicate deletes(Variable v) { exists(Name d | this.getNode() = d and d.deletes(v)) } + /** Whether this flow node deletes the variable `v`. */ + predicate deletes(Variable v) { exists(Name d | this.getNode() = d and d.deletes(v)) } - /** Whether this flow node uses the variable `v`. */ - predicate uses(Variable v) { - this.isLoad() and - exists(Name u | this.getNode() = u and u.uses(v)) - or - exists(PlaceHolder u | - this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load - ) - or - Scopes::use_of_global_variable(this, v.getScope(), v.getId()) - } + /** Whether this flow node uses the variable `v`. */ + predicate uses(Variable v) { + this.isLoad() and + exists(Name u | this.getNode() = u and u.uses(v)) + or + exists(PlaceHolder u | + this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load + ) + or + Scopes::use_of_global_variable(this, v.getScope(), v.getId()) + } - string getId() { - result = this.getNode().(Name).getId() - or - result = this.getNode().(PlaceHolder).getId() - } + string getId() { + result = this.getNode().(Name).getId() + or + result = this.getNode().(PlaceHolder).getId() + } - /** Whether this is a use of a local variable. */ - predicate isLocal() { Scopes::local(this) } + /** Whether this is a use of a local variable. */ + predicate isLocal() { Scopes::local(this) } - /** Whether this is a use of a non-local variable. */ - predicate isNonLocal() { Scopes::non_local(this) } + /** Whether this is a use of a non-local variable. */ + predicate isNonLocal() { Scopes::non_local(this) } - /** Whether this is a use of a global (including builtin) variable. */ - predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) } + /** Whether this is a use of a global (including builtin) variable. */ + predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) } - predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) } + predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) } } /** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */ class NameConstantNode extends NameNode { - NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) } + NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) } - deprecated override predicate defines(Variable v) { none() } + deprecated override predicate defines(Variable v) { none() } - deprecated override predicate deletes(Variable v) { none() } - /* - * We ought to override uses as well, but that has - * a serious performance impact. - * deprecated predicate uses(Variable v) { none() } - */ + deprecated override predicate deletes(Variable v) { none() } + /* + * We ought to override uses as well, but that has + * a serious performance impact. + * deprecated predicate uses(Variable v) { none() } + */ - } + } /** A control flow node correspoinding to a starred expression, `*a`. */ class StarredNode extends ControlFlowNode { - StarredNode() { toAst(this) instanceof Starred } + StarredNode() { toAst(this) instanceof Starred } - ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() } + ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() } } private module Scopes { - private predicate fast_local(NameNode n) { - exists(FastLocalVariable v | - n.uses(v) and - v.getScope() = n.getScope() - ) - } + private predicate fast_local(NameNode n) { + exists(FastLocalVariable v | + n.uses(v) and + v.getScope() = n.getScope() + ) + } - predicate local(NameNode n) { - fast_local(n) - or - exists(SsaVariable var | - var.getAUse() = n and - n.getScope() instanceof Class and - exists(var.getDefinition()) - ) - } + predicate local(NameNode n) { + fast_local(n) + or + exists(SsaVariable var | + var.getAUse() = n and + n.getScope() instanceof Class and + exists(var.getDefinition()) + ) + } - predicate non_local(NameNode n) { - exists(FastLocalVariable flv | - flv.getALoad() = n.getNode() and - not flv.getScope() = n.getScope() - ) - } + predicate non_local(NameNode n) { + exists(FastLocalVariable flv | + flv.getALoad() = n.getNode() and + not flv.getScope() = n.getScope() + ) + } - // magic is fine, but we get questionable join-ordering of it - pragma[nomagic] - predicate use_of_global_variable(NameNode n, Module scope, string name) { - n.isLoad() and - not non_local(n) and - not exists(SsaVariable var | var.getAUse() = n | - var.getVariable() instanceof FastLocalVariable - or - n.getScope() instanceof Class and - not maybe_undefined(var) - ) and - name = n.getId() and - scope = n.getEnclosingModule() - } + // magic is fine, but we get questionable join-ordering of it + pragma[nomagic] + predicate use_of_global_variable(NameNode n, Module scope, string name) { + n.isLoad() and + not non_local(n) and + not exists(SsaVariable var | var.getAUse() = n | + var.getVariable() instanceof FastLocalVariable + or + n.getScope() instanceof Class and + not maybe_undefined(var) + ) and + name = n.getId() and + scope = n.getEnclosingModule() + } - private predicate maybe_defined(SsaVariable var) { - exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete() - or - exists(SsaVariable input | input = var.getAPhiInput() | maybe_defined(input)) - } + private predicate maybe_defined(SsaVariable var) { + exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete() + or + exists(SsaVariable input | input = var.getAPhiInput() | maybe_defined(input)) + } - private predicate maybe_undefined(SsaVariable var) { - not exists(var.getDefinition()) and not py_ssa_phi(var, _) - or - var.getDefinition().isDelete() - or - maybe_undefined(var.getAPhiInput()) - or - exists(BasicBlock incoming | - exists(var.getAPhiInput()) and - incoming.getASuccessor() = var.getDefinition().getBasicBlock() and - not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) - ) - } + private predicate maybe_undefined(SsaVariable var) { + not exists(var.getDefinition()) and not py_ssa_phi(var, _) + or + var.getDefinition().isDelete() + or + maybe_undefined(var.getAPhiInput()) + or + exists(BasicBlock incoming | + exists(var.getAPhiInput()) and + incoming.getASuccessor() = var.getDefinition().getBasicBlock() and + not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) + } } /** A basic block (ignoring exceptional flow edges to scope exit) */ class BasicBlock extends @py_flow_node { - BasicBlock() { py_flow_bb_node(_, _, this, _) } + BasicBlock() { py_flow_bb_node(_, _, this, _) } - /** Whether this basic block contains the specified node */ - predicate contains(ControlFlowNode node) { py_flow_bb_node(node, _, this, _) } + /** Whether this basic block contains the specified node */ + predicate contains(ControlFlowNode node) { py_flow_bb_node(node, _, this, _) } - /** Gets the nth node in this basic block */ - ControlFlowNode getNode(int n) { py_flow_bb_node(result, _, this, n) } + /** Gets the nth node in this basic block */ + ControlFlowNode getNode(int n) { py_flow_bb_node(result, _, this, n) } - /** Gets a textual representation of this element. */ - string toString() { result = "BasicBlock" } + /** Gets a textual representation of this element. */ + string toString() { result = "BasicBlock" } - /** Whether this basic block strictly dominates the other */ - pragma[nomagic] - predicate strictlyDominates(BasicBlock other) { other.getImmediateDominator+() = this } + /** Whether this basic block strictly dominates the other */ + pragma[nomagic] + predicate strictlyDominates(BasicBlock other) { other.getImmediateDominator+() = this } - /** Whether this basic block dominates the other */ - pragma[nomagic] - predicate dominates(BasicBlock other) { - this = other - or - this.strictlyDominates(other) - } + /** Whether this basic block dominates the other */ + pragma[nomagic] + predicate dominates(BasicBlock other) { + this = other + or + this.strictlyDominates(other) + } - cached - BasicBlock getImmediateDominator() { - this.firstNode().getImmediateDominator().getBasicBlock() = result - } + cached + BasicBlock getImmediateDominator() { + this.firstNode().getImmediateDominator().getBasicBlock() = result + } - /** - * Dominance frontier of a node x is the set of all nodes `other` such that `this` dominates a predecessor - * of `other` but does not strictly dominate `other` - */ - pragma[noinline] - predicate dominanceFrontier(BasicBlock other) { - this.dominates(other.getAPredecessor()) and not this.strictlyDominates(other) - } + /** + * Dominance frontier of a node x is the set of all nodes `other` such that `this` dominates a predecessor + * of `other` but does not strictly dominate `other` + */ + pragma[noinline] + predicate dominanceFrontier(BasicBlock other) { + this.dominates(other.getAPredecessor()) and not this.strictlyDominates(other) + } - private ControlFlowNode firstNode() { result = this } + private ControlFlowNode firstNode() { result = this } - /** Gets the last node in this basic block */ - ControlFlowNode getLastNode() { - exists(int i | - this.getNode(i) = result and - i = max(int j | py_flow_bb_node(_, _, this, j)) - ) - } + /** Gets the last node in this basic block */ + ControlFlowNode getLastNode() { + exists(int i | + this.getNode(i) = result and + i = max(int j | py_flow_bb_node(_, _, this, j)) + ) + } - private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() } + private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() } - private predicate startLocationInfo(string file, int line, int col) { - if this.firstNode().getNode() instanceof Scope - then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _) - else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _) - } + private predicate startLocationInfo(string file, int line, int col) { + if this.firstNode().getNode() instanceof Scope + then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _) + else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _) + } - private predicate endLocationInfo(int endl, int endc) { - if this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock() - then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc) - else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc) - } + private predicate endLocationInfo(int endl, int endc) { + if this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock() + then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc) + else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc) + } - /** Gets a successor to this basic block */ - BasicBlock getASuccessor() { result = this.getLastNode().getASuccessor().getBasicBlock() } + /** Gets a successor to this basic block */ + BasicBlock getASuccessor() { result = this.getLastNode().getASuccessor().getBasicBlock() } - /** Gets a predecessor to this basic block */ - BasicBlock getAPredecessor() { result.getASuccessor() = this } + /** Gets a predecessor to this basic block */ + BasicBlock getAPredecessor() { result.getASuccessor() = this } - /** Whether flow from this basic block reaches a normal exit from its scope */ - predicate reachesExit() { - exists(Scope s | s.getANormalExit().getBasicBlock() = this) - or - this.getASuccessor().reachesExit() - } + /** Whether flow from this basic block reaches a normal exit from its scope */ + predicate reachesExit() { + exists(Scope s | s.getANormalExit().getBasicBlock() = this) + or + this.getASuccessor().reachesExit() + } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.startLocationInfo(filepath, startline, startcolumn) and - this.endLocationInfo(endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.startLocationInfo(filepath, startline, startcolumn) and + this.endLocationInfo(endline, endcolumn) + } - /** Gets a true successor to this basic block */ - BasicBlock getATrueSuccessor() { result = this.getLastNode().getATrueSuccessor().getBasicBlock() } + /** Gets a true successor to this basic block */ + BasicBlock getATrueSuccessor() { result = this.getLastNode().getATrueSuccessor().getBasicBlock() } - /** Gets a false successor to this basic block */ - BasicBlock getAFalseSuccessor() { - result = this.getLastNode().getAFalseSuccessor().getBasicBlock() - } + /** Gets a false successor to this basic block */ + BasicBlock getAFalseSuccessor() { + result = this.getLastNode().getAFalseSuccessor().getBasicBlock() + } - /** Gets an unconditional successor to this basic block */ - BasicBlock getAnUnconditionalSuccessor() { - result = this.getASuccessor() and - not result = this.getATrueSuccessor() and - not result = this.getAFalseSuccessor() - } + /** Gets an unconditional successor to this basic block */ + BasicBlock getAnUnconditionalSuccessor() { + result = this.getASuccessor() and + not result = this.getATrueSuccessor() and + not result = this.getAFalseSuccessor() + } - /** Gets an exceptional successor to this basic block */ - BasicBlock getAnExceptionalSuccessor() { - result = this.getLastNode().getAnExceptionalSuccessor().getBasicBlock() - } + /** Gets an exceptional successor to this basic block */ + BasicBlock getAnExceptionalSuccessor() { + result = this.getLastNode().getAnExceptionalSuccessor().getBasicBlock() + } - /** Gets the scope of this block */ - pragma[nomagic] - Scope getScope() { - exists(ControlFlowNode n | n.getBasicBlock() = this | - /* Take care not to use an entry or exit node as that node's scope will be the outer scope */ - not py_scope_flow(n, _, -1) and - not py_scope_flow(n, _, 0) and - not py_scope_flow(n, _, 2) and - result = n.getScope() - or - py_scope_flow(n, result, _) - ) - } + /** Gets the scope of this block */ + pragma[nomagic] + Scope getScope() { + exists(ControlFlowNode n | n.getBasicBlock() = this | + /* Take care not to use an entry or exit node as that node's scope will be the outer scope */ + not py_scope_flow(n, _, -1) and + not py_scope_flow(n, _, 0) and + not py_scope_flow(n, _, 2) and + result = n.getScope() + or + py_scope_flow(n, result, _) + ) + } - /** - * Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. - */ - predicate unlikelySuccessor(BasicBlock succ) { - this.getLastNode().(RaisingNode).unlikelySuccessor(succ.firstNode()) - or - not end_bb_likely_reachable(this) and succ = this.getASuccessor() - } + /** + * Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. + */ + predicate unlikelySuccessor(BasicBlock succ) { + this.getLastNode().(RaisingNode).unlikelySuccessor(succ.firstNode()) + or + not end_bb_likely_reachable(this) and succ = this.getASuccessor() + } - /** Holds if this basic block strictly reaches the other. Is the start of other reachable from the end of this. */ - predicate strictlyReaches(BasicBlock other) { this.getASuccessor+() = other } + /** Holds if this basic block strictly reaches the other. Is the start of other reachable from the end of this. */ + predicate strictlyReaches(BasicBlock other) { this.getASuccessor+() = other } - /** Holds if this basic block reaches the other. Is the start of other reachable from the end of this. */ - predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) } + /** Holds if this basic block reaches the other. Is the start of other reachable from the end of this. */ + predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) } - /** - * Whether (as inferred by type inference) this basic block is likely to be reachable. - */ - predicate likelyReachable() { start_bb_likely_reachable(this) } + /** + * Whether (as inferred by type inference) this basic block is likely to be reachable. + */ + predicate likelyReachable() { start_bb_likely_reachable(this) } - /** - * Gets the `ConditionBlock`, if any, that controls this block and - * does not control any other `ConditionBlock`s that control this block. - * That is the `ConditionBlock` that is closest dominator. - */ - ConditionBlock getImmediatelyControllingBlock() { - result = this.nonControllingImmediateDominator*().getImmediateDominator() - } + /** + * Gets the `ConditionBlock`, if any, that controls this block and + * does not control any other `ConditionBlock`s that control this block. + * That is the `ConditionBlock` that is closest dominator. + */ + ConditionBlock getImmediatelyControllingBlock() { + result = this.nonControllingImmediateDominator*().getImmediateDominator() + } - private BasicBlock nonControllingImmediateDominator() { - result = this.getImmediateDominator() and - not result.(ConditionBlock).controls(this, _) - } + private BasicBlock nonControllingImmediateDominator() { + result = this.getImmediateDominator() and + not result.(ConditionBlock).controls(this, _) + } - /** - * Holds if flow from this BasicBlock always reaches `succ` - */ - predicate alwaysReaches(BasicBlock succ) { - succ = this - or - strictcount(this.getASuccessor()) = 1 and - succ = this.getASuccessor() - or - forex(BasicBlock immsucc | immsucc = this.getASuccessor() | immsucc.alwaysReaches(succ)) - } + /** + * Holds if flow from this BasicBlock always reaches `succ` + */ + predicate alwaysReaches(BasicBlock succ) { + succ = this + or + strictcount(this.getASuccessor()) = 1 and + succ = this.getASuccessor() + or + forex(BasicBlock immsucc | immsucc = this.getASuccessor() | immsucc.alwaysReaches(succ)) + } } private predicate start_bb_likely_reachable(BasicBlock b) { - exists(Scope s | s.getEntryNode() = b.getNode(_)) - or - exists(BasicBlock pred | - pred = b.getAPredecessor() and - end_bb_likely_reachable(pred) and - not pred.getLastNode().(RaisingNode).unlikelySuccessor(b) - ) + exists(Scope s | s.getEntryNode() = b.getNode(_)) + or + exists(BasicBlock pred | + pred = b.getAPredecessor() and + end_bb_likely_reachable(pred) and + not pred.getLastNode().(RaisingNode).unlikelySuccessor(b) + ) } private predicate end_bb_likely_reachable(BasicBlock b) { - start_bb_likely_reachable(b) and - not exists(ControlFlowNode p, ControlFlowNode s | - p.(RaisingNode).unlikelySuccessor(s) and - p = b.getNode(_) and - s = b.getNode(_) and - not p = b.getLastNode() - ) + start_bb_likely_reachable(b) and + not exists(ControlFlowNode p, ControlFlowNode s | + p.(RaisingNode).unlikelySuccessor(s) and + p = b.getNode(_) and + s = b.getNode(_) and + not p = b.getLastNode() + ) } diff --git a/python/ql/src/semmle/python/Function.qll b/python/ql/src/semmle/python/Function.qll index b5cd9b4db4f..4ec4576bcd8 100644 --- a/python/ql/src/semmle/python/Function.qll +++ b/python/ql/src/semmle/python/Function.qll @@ -5,336 +5,336 @@ import python * It is the syntactic entity that is compiled to a code object. */ class Function extends Function_, Scope, AstNode { - /** The expression defining this function */ - CallableExpr getDefinition() { result = this.getParent() } + /** The expression defining this function */ + CallableExpr getDefinition() { result = this.getParent() } - /** - * The scope in which this function occurs, will be a class for a method, - * another function for nested functions, generator expressions or comprehensions, - * or a module for a plain function. - */ - override Scope getEnclosingScope() { result = this.getParent().(Expr).getScope() } + /** + * The scope in which this function occurs, will be a class for a method, + * another function for nested functions, generator expressions or comprehensions, + * or a module for a plain function. + */ + override Scope getEnclosingScope() { result = this.getParent().(Expr).getScope() } - override Scope getScope() { result = this.getEnclosingScope() } + override Scope getScope() { result = this.getEnclosingScope() } - /** Whether this function is declared in a class */ - predicate isMethod() { exists(Class cls | this.getEnclosingScope() = cls) } + /** Whether this function is declared in a class */ + predicate isMethod() { exists(Class cls | this.getEnclosingScope() = cls) } - /** Whether this is a special method, that is does its name have the form `__xxx__` (except `__init__`) */ - predicate isSpecialMethod() { - this.isMethod() and - exists(string name | this.getName() = name | - name.matches("\\_\\_%\\_\\_") and - name != "__init__" - ) - } + /** Whether this is a special method, that is does its name have the form `__xxx__` (except `__init__`) */ + predicate isSpecialMethod() { + this.isMethod() and + exists(string name | this.getName() = name | + name.matches("\\_\\_%\\_\\_") and + name != "__init__" + ) + } - /** - * Whether this function is a generator function, - * that is whether it contains a yield or yield-from expression - */ - predicate isGenerator() { - exists(Yield y | y.getScope() = this) - or - exists(YieldFrom y | y.getScope() = this) - } + /** + * Whether this function is a generator function, + * that is whether it contains a yield or yield-from expression + */ + predicate isGenerator() { + exists(Yield y | y.getScope() = this) + or + exists(YieldFrom y | y.getScope() = this) + } - /** Whether this function is declared in a class and is named `__init__` */ - predicate isInitMethod() { this.isMethod() and this.getName() = "__init__" } + /** Whether this function is declared in a class and is named `__init__` */ + predicate isInitMethod() { this.isMethod() and this.getName() = "__init__" } - /** Gets a decorator of this function */ - Expr getADecorator() { result = this.getDefinition().(FunctionExpr).getADecorator() } + /** Gets a decorator of this function */ + Expr getADecorator() { result = this.getDefinition().(FunctionExpr).getADecorator() } - /** Gets the name of the nth argument (for simple arguments) */ - string getArgName(int index) { result = this.getArg(index).(Name).getId() } + /** Gets the name of the nth argument (for simple arguments) */ + string getArgName(int index) { result = this.getArg(index).(Name).getId() } - Parameter getArgByName(string name) { - ( - result = this.getAnArg() - or - result = this.getAKeywordOnlyArg() - ) and - result.(Name).getId() = name - } + Parameter getArgByName(string name) { + ( + result = this.getAnArg() + or + result = this.getAKeywordOnlyArg() + ) and + result.(Name).getId() = name + } - override Location getLocation() { py_scope_location(result, this) } + override Location getLocation() { py_scope_location(result, this) } - override string toString() { result = "Function " + this.getName() } + override string toString() { result = "Function " + this.getName() } - /** Gets the statements forming the body of this function */ - override StmtList getBody() { result = Function_.super.getBody() } + /** Gets the statements forming the body of this function */ + override StmtList getBody() { result = Function_.super.getBody() } - /** Gets the nth statement in the function */ - override Stmt getStmt(int index) { result = Function_.super.getStmt(index) } + /** Gets the nth statement in the function */ + override Stmt getStmt(int index) { result = Function_.super.getStmt(index) } - /** Gets a statement in the function */ - override Stmt getAStmt() { result = Function_.super.getAStmt() } + /** Gets a statement in the function */ + override Stmt getAStmt() { result = Function_.super.getAStmt() } - /** Gets the name used to define this function */ - override string getName() { result = Function_.super.getName() } + /** Gets the name used to define this function */ + override string getName() { result = Function_.super.getName() } - /** Gets the metrics for this function */ - FunctionMetrics getMetrics() { result = this } + /** Gets the metrics for this function */ + FunctionMetrics getMetrics() { result = this } - /** Gets the FunctionObject corresponding to this function */ - FunctionObject getFunctionObject() { result.getOrigin() = this.getDefinition() } + /** Gets the FunctionObject corresponding to this function */ + FunctionObject getFunctionObject() { result.getOrigin() = this.getDefinition() } - /** - * Whether this function is a procedure, that is, it has no explicit return statement and always returns None. - * Note that generator and async functions are not procedures as they return generators and coroutines respectively. - */ - predicate isProcedure() { - not exists(this.getReturnNode()) and - exists(this.getFallthroughNode()) and - not this.isGenerator() and - not this.isAsync() - } + /** + * Whether this function is a procedure, that is, it has no explicit return statement and always returns None. + * Note that generator and async functions are not procedures as they return generators and coroutines respectively. + */ + predicate isProcedure() { + not exists(this.getReturnNode()) and + exists(this.getFallthroughNode()) and + not this.isGenerator() and + not this.isAsync() + } - /** Gets the number of positional parameters */ - int getPositionalParameterCount() { result = count(this.getAnArg()) } + /** Gets the number of positional parameters */ + int getPositionalParameterCount() { result = count(this.getAnArg()) } - /** Gets the number of keyword-only parameters */ - int getKeywordOnlyParameterCount() { result = count(this.getAKeywordOnlyArg()) } + /** Gets the number of keyword-only parameters */ + int getKeywordOnlyParameterCount() { result = count(this.getAKeywordOnlyArg()) } - /** Whether this function accepts a variable number of arguments. That is, whether it has a starred (*arg) parameter. */ - predicate hasVarArg() { exists(this.getVararg()) } + /** Whether this function accepts a variable number of arguments. That is, whether it has a starred (*arg) parameter. */ + predicate hasVarArg() { exists(this.getVararg()) } - /** Whether this function accepts arbitrary keyword arguments. That is, whether it has a double-starred (**kwarg) parameter. */ - predicate hasKwArg() { exists(this.getKwarg()) } + /** Whether this function accepts arbitrary keyword arguments. That is, whether it has a double-starred (**kwarg) parameter. */ + predicate hasKwArg() { exists(this.getKwarg()) } - override AstNode getAChildNode() { - result = this.getAStmt() or - result = this.getAnArg() or - result = this.getVararg() or - result = this.getAKeywordOnlyArg() or - result = this.getKwarg() - } + override AstNode getAChildNode() { + result = this.getAStmt() or + result = this.getAnArg() or + result = this.getVararg() or + result = this.getAKeywordOnlyArg() or + result = this.getKwarg() + } - /** - * Gets the qualified name for this function. - * Should return the same name as the `__qualname__` attribute on functions in Python 3. - */ - string getQualifiedName() { - this.getEnclosingScope() instanceof Module and result = this.getName() - or - exists(string enclosing_name | - enclosing_name = this.getEnclosingScope().(Function).getQualifiedName() - or - enclosing_name = this.getEnclosingScope().(Class).getQualifiedName() - | - result = enclosing_name + "." + this.getName() - ) - } + /** + * Gets the qualified name for this function. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ + string getQualifiedName() { + this.getEnclosingScope() instanceof Module and result = this.getName() + or + exists(string enclosing_name | + enclosing_name = this.getEnclosingScope().(Function).getQualifiedName() + or + enclosing_name = this.getEnclosingScope().(Class).getQualifiedName() + | + result = enclosing_name + "." + this.getName() + ) + } - /** Gets the nth keyword-only parameter of this function. */ - Name getKeywordOnlyArg(int n) { result = Function_.super.getKwonlyarg(n) } + /** Gets the nth keyword-only parameter of this function. */ + Name getKeywordOnlyArg(int n) { result = Function_.super.getKwonlyarg(n) } - /** Gets a keyword-only parameter of this function. */ - Name getAKeywordOnlyArg() { result = this.getKeywordOnlyArg(_) } + /** Gets a keyword-only parameter of this function. */ + Name getAKeywordOnlyArg() { result = this.getKeywordOnlyArg(_) } - override Scope getEvaluatingScope() { - major_version() = 2 and - exists(Comp comp | comp.getFunction() = this | result = comp.getEvaluatingScope()) - or - not exists(Comp comp | comp.getFunction() = this) and result = this - or - major_version() = 3 and result = this - } + override Scope getEvaluatingScope() { + major_version() = 2 and + exists(Comp comp | comp.getFunction() = this | result = comp.getEvaluatingScope()) + or + not exists(Comp comp | comp.getFunction() = this) and result = this + or + major_version() = 3 and result = this + } - override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } + override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } - override predicate contains(AstNode inner) { Scope.super.contains(inner) } + override predicate contains(AstNode inner) { Scope.super.contains(inner) } - /** Gets a control flow node for a return value of this function */ - ControlFlowNode getAReturnValueFlowNode() { - exists(Return ret | - ret.getScope() = this and - ret.getValue() = result.getNode() - ) - } + /** Gets a control flow node for a return value of this function */ + ControlFlowNode getAReturnValueFlowNode() { + exists(Return ret | + ret.getScope() = this and + ret.getValue() = result.getNode() + ) + } } /** A def statement. Note that FunctionDef extends Assign as a function definition binds the newly created function */ class FunctionDef extends Assign { - /* syntax: def name(...): ... */ - FunctionDef() { - /* This is an artificial assignment the rhs of which is a (possibly decorated) FunctionExpr */ - exists(FunctionExpr f | this.getValue() = f or this.getValue() = f.getADecoratorCall()) - } + /* syntax: def name(...): ... */ + FunctionDef() { + /* This is an artificial assignment the rhs of which is a (possibly decorated) FunctionExpr */ + exists(FunctionExpr f | this.getValue() = f or this.getValue() = f.getADecoratorCall()) + } - override string toString() { result = "FunctionDef" } + override string toString() { result = "FunctionDef" } - /** Gets the function for this statement */ - Function getDefinedFunction() { - exists(FunctionExpr func | this.containsInScope(func) and result = func.getInnerScope()) - } + /** Gets the function for this statement */ + Function getDefinedFunction() { + exists(FunctionExpr func | this.containsInScope(func) and result = func.getInnerScope()) + } - override Stmt getLastStatement() { result = this.getDefinedFunction().getLastStatement() } + override Stmt getLastStatement() { result = this.getDefinedFunction().getLastStatement() } } class FastLocalsFunction extends Function { - /** A function that uses 'fast' locals, stored in the frame not in a dictionary. */ - FastLocalsFunction() { - not exists(ImportStar i | i.getScope() = this) and - not exists(Exec e | e.getScope() = this) - } + /** A function that uses 'fast' locals, stored in the frame not in a dictionary. */ + FastLocalsFunction() { + not exists(ImportStar i | i.getScope() = this) and + not exists(Exec e | e.getScope() = this) + } } /** A parameter. Either a Tuple or a Name (always a Name for Python 3) */ class Parameter extends Parameter_ { - Parameter() { - /* Parameter_ is just defined as a Name or Tuple, narrow to actual parameters */ - exists(ParameterList pl | py_exprs(this, _, pl, _)) - or - exists(Function f | - f.getVararg() = this - or - f.getKwarg() = this - or - f.getAKeywordOnlyArg() = this - ) - } + Parameter() { + /* Parameter_ is just defined as a Name or Tuple, narrow to actual parameters */ + exists(ParameterList pl | py_exprs(this, _, pl, _)) + or + exists(Function f | + f.getVararg() = this + or + f.getKwarg() = this + or + f.getAKeywordOnlyArg() = this + ) + } - Location getLocation() { - result = this.asName().getLocation() - or - result = this.asTuple().getLocation() - } + Location getLocation() { + result = this.asName().getLocation() + or + result = this.asTuple().getLocation() + } - /** Gets this parameter if it is a Name (not a Tuple) */ - Name asName() { result = this } + /** Gets this parameter if it is a Name (not a Tuple) */ + Name asName() { result = this } - /** Gets this parameter if it is a Tuple (not a Name) */ - Tuple asTuple() { result = this } + /** Gets this parameter if it is a Tuple (not a Name) */ + Tuple asTuple() { result = this } - /** Gets the expression for the default value of this parameter */ - Expr getDefault() { - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // positional (normal) - f.getArg(i) = this and - result = args.getDefault(i) - ) - or - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // keyword-only - f.getKeywordOnlyArg(i) = this and - result = args.getKwDefault(i) - ) - } + /** Gets the expression for the default value of this parameter */ + Expr getDefault() { + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // positional (normal) + f.getArg(i) = this and + result = args.getDefault(i) + ) + or + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // keyword-only + f.getKeywordOnlyArg(i) = this and + result = args.getKwDefault(i) + ) + } - /** Gets the annotation expression of this parameter */ - Expr getAnnotation() { - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // positional (normal) - f.getArg(i) = this and - result = args.getAnnotation(i) - ) - or - exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | - // keyword-only - f.getKeywordOnlyArg(i) = this and - result = args.getKwAnnotation(i) - ) - or - exists(Function f, Arguments args | args = f.getDefinition().getArgs() | - f.getKwarg() = this and - result = args.getKwargannotation() - or - f.getVararg() = this and - result = args.getVarargannotation() - ) - } + /** Gets the annotation expression of this parameter */ + Expr getAnnotation() { + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // positional (normal) + f.getArg(i) = this and + result = args.getAnnotation(i) + ) + or + exists(Function f, int i, Arguments args | args = f.getDefinition().getArgs() | + // keyword-only + f.getKeywordOnlyArg(i) = this and + result = args.getKwAnnotation(i) + ) + or + exists(Function f, Arguments args | args = f.getDefinition().getArgs() | + f.getKwarg() = this and + result = args.getKwargannotation() + or + f.getVararg() = this and + result = args.getVarargannotation() + ) + } - Variable getVariable() { result.getAnAccess() = this.asName() } + Variable getVariable() { result.getAnAccess() = this.asName() } - /** - * Gets the position of this parameter (if any). - * No result if this is a "varargs", "kwargs", or keyword-only parameter. - */ - int getPosition() { exists(Function f | f.getArg(result) = this) } + /** + * Gets the position of this parameter (if any). + * No result if this is a "varargs", "kwargs", or keyword-only parameter. + */ + int getPosition() { exists(Function f | f.getArg(result) = this) } - /** Gets the name of this parameter */ - string getName() { result = this.asName().getId() } + /** Gets the name of this parameter */ + string getName() { result = this.asName().getId() } - /** Holds if this parameter is the first parameter of a method. It is not necessarily called "self" */ - predicate isSelf() { - exists(Function f | - f.getArg(0) = this and - f.isMethod() - ) - } + /** Holds if this parameter is the first parameter of a method. It is not necessarily called "self" */ + predicate isSelf() { + exists(Function f | + f.getArg(0) = this and + f.isMethod() + ) + } - /** - * Holds if this parameter is a "varargs" parameter. - * The `varargs` in `f(a, b, *varargs)`. - */ - predicate isVarargs() { exists(Function func | func.getVararg() = this) } + /** + * Holds if this parameter is a "varargs" parameter. + * The `varargs` in `f(a, b, *varargs)`. + */ + predicate isVarargs() { exists(Function func | func.getVararg() = this) } - /** - * Holds if this parameter is a "kwargs" parameter. - * The `kwargs` in `f(a, b, **kwargs)`. - */ - predicate isKwargs() { exists(Function func | func.getKwarg() = this) } + /** + * Holds if this parameter is a "kwargs" parameter. + * The `kwargs` in `f(a, b, **kwargs)`. + */ + predicate isKwargs() { exists(Function func | func.getKwarg() = this) } } /** An expression that generates a callable object, either a function expression or a lambda */ abstract class CallableExpr extends Expr { - /** - * Gets The default values and annotations (type-hints) for the arguments of this callable. - * - * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. - */ - abstract Arguments getArgs(); + /** + * Gets The default values and annotations (type-hints) for the arguments of this callable. + * + * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. + */ + abstract Arguments getArgs(); - /** Gets the function scope of this code expression. */ - abstract Function getInnerScope(); + /** Gets the function scope of this code expression. */ + abstract Function getInnerScope(); } /** An (artificial) expression corresponding to a function definition. */ class FunctionExpr extends FunctionExpr_, CallableExpr { - override Expr getASubExpression() { - result = this.getArgs().getASubExpression() or - result = this.getReturns() - } + override Expr getASubExpression() { + result = this.getArgs().getASubExpression() or + result = this.getReturns() + } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - Call getADecoratorCall() { - result.getArg(0) = this or - result.getArg(0) = this.getADecoratorCall() - } + Call getADecoratorCall() { + result.getArg(0) = this or + result.getArg(0) = this.getADecoratorCall() + } - /** Gets a decorator of this function expression */ - Expr getADecorator() { result = this.getADecoratorCall().getFunc() } + /** Gets a decorator of this function expression */ + Expr getADecorator() { result = this.getADecoratorCall().getFunc() } - override AstNode getAChildNode() { - result = this.getASubExpression() - or - result = this.getInnerScope() - } + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getInnerScope() + } - override Function getInnerScope() { result = FunctionExpr_.super.getInnerScope() } + override Function getInnerScope() { result = FunctionExpr_.super.getInnerScope() } - override Arguments getArgs() { result = FunctionExpr_.super.getArgs() } + override Arguments getArgs() { result = FunctionExpr_.super.getArgs() } } /** A lambda expression, such as `lambda x: x+1` */ class Lambda extends Lambda_, CallableExpr { - /** Gets the expression to the right of the colon in this lambda expression */ - Expr getExpression() { - exists(Return ret | ret = this.getInnerScope().getStmt(0) | result = ret.getValue()) - } + /** Gets the expression to the right of the colon in this lambda expression */ + Expr getExpression() { + exists(Return ret | ret = this.getInnerScope().getStmt(0) | result = ret.getValue()) + } - override Expr getASubExpression() { result = this.getArgs().getASubExpression() } + override Expr getASubExpression() { result = this.getArgs().getASubExpression() } - override AstNode getAChildNode() { - result = this.getASubExpression() or - result = this.getInnerScope() - } + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getInnerScope() + } - override Function getInnerScope() { result = Lambda_.super.getInnerScope() } + override Function getInnerScope() { result = Lambda_.super.getInnerScope() } - override Arguments getArgs() { result = Lambda_.super.getArgs() } + override Arguments getArgs() { result = Lambda_.super.getArgs() } } /** @@ -344,29 +344,27 @@ class Lambda extends Lambda_, CallableExpr { * that is generally only used for type hints today (PEP 484). */ class Arguments extends Arguments_ { + Expr getASubExpression() { + result = this.getADefault() or + result = this.getAKwDefault() or + // + result = this.getAnAnnotation() or + result = this.getVarargannotation() or + result = this.getAKwAnnotation() or + result = this.getKwargannotation() + } - Expr getASubExpression() { - result = this.getADefault() or - result = this.getAKwDefault() or - // - result = this.getAnAnnotation() or - result = this.getVarargannotation() or - result = this.getAKwAnnotation() or - result = this.getKwargannotation() - } + // The following 4 methods are overwritten to provide better QLdoc. Since the + // Arguments_ is auto-generated, we can't change the poor auto-generated docs there :( + /** Gets the default value for the `index`'th positional parameter. */ + override Expr getDefault(int index) { result = super.getDefault(index) } - // The following 4 methods are overwritten to provide better QLdoc. Since the - // Arguments_ is auto-generated, we can't change the poor auto-generated docs there :( + /** Gets the default value for the `index`'th keyword-only parameter. */ + override Expr getKwDefault(int index) { result = super.getKwDefault(index) } - /** Gets the default value for the `index`'th positional parameter. */ - override Expr getDefault(int index) { result = super.getDefault(index) } + /** Gets the annotation for the `index`'th positional parameter. */ + override Expr getAnnotation(int index) { result = super.getAnnotation(index) } - /** Gets the default value for the `index`'th keyword-only parameter. */ - override Expr getKwDefault(int index) { result = super.getKwDefault(index) } - - /** Gets the annotation for the `index`'th positional parameter. */ - override Expr getAnnotation(int index) { result = super.getAnnotation(index) } - - /** Gets the annotation for the `index`'th keyword-only parameter. */ - override Expr getKwAnnotation(int index) { result = super.getKwAnnotation(index) } + /** Gets the annotation for the `index`'th keyword-only parameter. */ + override Expr getKwAnnotation(int index) { result = super.getKwAnnotation(index) } } diff --git a/python/ql/src/semmle/python/GuardedControlFlow.qll b/python/ql/src/semmle/python/GuardedControlFlow.qll index 0675f336828..37ecfee37d5 100644 --- a/python/ql/src/semmle/python/GuardedControlFlow.qll +++ b/python/ql/src/semmle/python/GuardedControlFlow.qll @@ -2,67 +2,67 @@ import python /** A basic block which terminates in a condition, splitting the subsequent control flow */ class ConditionBlock extends BasicBlock { - ConditionBlock() { - exists(ControlFlowNode succ | - succ = this.getATrueSuccessor() or succ = this.getAFalseSuccessor() - ) - } + ConditionBlock() { + exists(ControlFlowNode succ | + succ = this.getATrueSuccessor() or succ = this.getAFalseSuccessor() + ) + } - /** Basic blocks controlled by this condition, i.e. those BBs for which the condition is testIsTrue */ - predicate controls(BasicBlock controlled, boolean testIsTrue) { - /* - * For this block to control the block 'controlled' with 'testIsTrue' the following must be true: - * Execution must have passed through the test i.e. 'this' must strictly dominate 'controlled'. - * Execution must have passed through the 'testIsTrue' edge leaving 'this'. - * - * Although "passed through the true edge" implies that this.getATrueSuccessor() dominates 'controlled', - * the reverse is not true, as flow may have passed through another edge to get to this.getATrueSuccessor() - * so we need to assert that this.getATrueSuccessor() dominates 'controlled' *and* that - * all predecessors of this.getATrueSuccessor() are either this or dominated by this.getATrueSuccessor(). - * - * For example, in the following python snippet: - * - * if x: - * controlled - * false_successor - * uncontrolled - * - * false_successor dominates uncontrolled, but not all of its predecessors are this (if x) - * or dominated by itself. Whereas in the following code: - * - * if x: - * while controlled: - * also_controlled - * false_successor - * uncontrolled - * - * the block 'while controlled' is controlled because all of its predecessors are this (if x) - * or (in the case of 'also_controlled') dominated by itself. - * - * The additional constraint on the predecessors of the test successor implies - * that `this` strictly dominates `controlled` so that isn't necessary to check - * directly. - */ + /** Basic blocks controlled by this condition, i.e. those BBs for which the condition is testIsTrue */ + predicate controls(BasicBlock controlled, boolean testIsTrue) { + /* + * For this block to control the block 'controlled' with 'testIsTrue' the following must be true: + * Execution must have passed through the test i.e. 'this' must strictly dominate 'controlled'. + * Execution must have passed through the 'testIsTrue' edge leaving 'this'. + * + * Although "passed through the true edge" implies that this.getATrueSuccessor() dominates 'controlled', + * the reverse is not true, as flow may have passed through another edge to get to this.getATrueSuccessor() + * so we need to assert that this.getATrueSuccessor() dominates 'controlled' *and* that + * all predecessors of this.getATrueSuccessor() are either this or dominated by this.getATrueSuccessor(). + * + * For example, in the following python snippet: + * + * if x: + * controlled + * false_successor + * uncontrolled + * + * false_successor dominates uncontrolled, but not all of its predecessors are this (if x) + * or dominated by itself. Whereas in the following code: + * + * if x: + * while controlled: + * also_controlled + * false_successor + * uncontrolled + * + * the block 'while controlled' is controlled because all of its predecessors are this (if x) + * or (in the case of 'also_controlled') dominated by itself. + * + * The additional constraint on the predecessors of the test successor implies + * that `this` strictly dominates `controlled` so that isn't necessary to check + * directly. + */ - exists(BasicBlock succ | - testIsTrue = true and succ = this.getATrueSuccessor() - or - testIsTrue = false and succ = this.getAFalseSuccessor() - | - succ.dominates(controlled) and - forall(BasicBlock pred | pred.getASuccessor() = succ | pred = this or succ.dominates(pred)) - ) - } + exists(BasicBlock succ | + testIsTrue = true and succ = this.getATrueSuccessor() + or + testIsTrue = false and succ = this.getAFalseSuccessor() + | + succ.dominates(controlled) and + forall(BasicBlock pred | pred.getASuccessor() = succ | pred = this or succ.dominates(pred)) + ) + } - /** Holds if this condition controls the edge `pred->succ`, i.e. those edges for which the condition is `testIsTrue`. */ - predicate controlsEdge(BasicBlock pred, BasicBlock succ, boolean testIsTrue) { - this.controls(pred, testIsTrue) and succ = pred.getASuccessor() - or - pred = this and - ( - testIsTrue = true and succ = this.getATrueSuccessor() - or - testIsTrue = false and succ = this.getAFalseSuccessor() - ) - } + /** Holds if this condition controls the edge `pred->succ`, i.e. those edges for which the condition is `testIsTrue`. */ + predicate controlsEdge(BasicBlock pred, BasicBlock succ, boolean testIsTrue) { + this.controls(pred, testIsTrue) and succ = pred.getASuccessor() + or + pred = this and + ( + testIsTrue = true and succ = this.getATrueSuccessor() + or + testIsTrue = false and succ = this.getAFalseSuccessor() + ) + } } diff --git a/python/ql/src/semmle/python/Import.qll b/python/ql/src/semmle/python/Import.qll index 4e52d08dc68..40c1c27a851 100644 --- a/python/ql/src/semmle/python/Import.qll +++ b/python/ql/src/semmle/python/Import.qll @@ -6,229 +6,229 @@ private import semmle.python.types.Builtins * `import x` is transformed into `import x as x` */ class Alias extends Alias_ { - Location getLocation() { result = this.getValue().getLocation() } + Location getLocation() { result = this.getValue().getLocation() } } private predicate valid_module_name(string name) { - exists(Module m | m.getName() = name) - or - exists(Builtin cmod | cmod.getClass() = Builtin::special("ModuleType") and cmod.getName() = name) + exists(Module m | m.getName() = name) + or + exists(Builtin cmod | cmod.getClass() = Builtin::special("ModuleType") and cmod.getName() = name) } /** An artificial expression representing an import */ class ImportExpr extends ImportExpr_ { - private string basePackageName(int n) { - n = 1 and result = this.getEnclosingModule().getPackageName() - or - exists(string bpnm1 | - bpnm1 = this.basePackageName(n - 1) and - bpnm1.matches("%.%") and - result = bpnm1.regexpReplaceAll("\\.[^.]*$", "") - ) - } + private string basePackageName(int n) { + n = 1 and result = this.getEnclosingModule().getPackageName() + or + exists(string bpnm1 | + bpnm1 = this.basePackageName(n - 1) and + bpnm1.matches("%.%") and + result = bpnm1.regexpReplaceAll("\\.[^.]*$", "") + ) + } - private predicate implicitRelativeImportsAllowed() { - // relative imports are no longer allowed in Python 3 - major_version() < 3 and - // and can be explicitly turned off in later versions of Python 2 - not getEnclosingModule().hasFromFuture("absolute_import") - } + private predicate implicitRelativeImportsAllowed() { + // relative imports are no longer allowed in Python 3 + major_version() < 3 and + // and can be explicitly turned off in later versions of Python 2 + not getEnclosingModule().hasFromFuture("absolute_import") + } - /** - * The language specifies level as -1 if relative imports are to be tried first, 0 for absolute imports, - * and level > 0 for explicit relative imports. - */ - override int getLevel() { - exists(int l | l = super.getLevel() | - l > 0 and result = l - or - /* The extractor may set level to 0 even though relative imports apply */ - l = 0 and - (if this.implicitRelativeImportsAllowed() then result = -1 else result = 0) - ) - } + /** + * The language specifies level as -1 if relative imports are to be tried first, 0 for absolute imports, + * and level > 0 for explicit relative imports. + */ + override int getLevel() { + exists(int l | l = super.getLevel() | + l > 0 and result = l + or + /* The extractor may set level to 0 even though relative imports apply */ + l = 0 and + (if this.implicitRelativeImportsAllowed() then result = -1 else result = 0) + ) + } - /** - * If this import is relative, and relative imports are allowed, compute - * the name of the topmost module that will be imported. - */ - private string relativeTopName() { - getLevel() = -1 and - result = basePackageName(1) + "." + this.getTopName() and - valid_module_name(result) - } + /** + * If this import is relative, and relative imports are allowed, compute + * the name of the topmost module that will be imported. + */ + private string relativeTopName() { + getLevel() = -1 and + result = basePackageName(1) + "." + this.getTopName() and + valid_module_name(result) + } - private string qualifiedTopName() { - if this.getLevel() <= 0 - then result = this.getTopName() - else ( - result = basePackageName(this.getLevel()) and - valid_module_name(result) - ) - } + private string qualifiedTopName() { + if this.getLevel() <= 0 + then result = this.getTopName() + else ( + result = basePackageName(this.getLevel()) and + valid_module_name(result) + ) + } - /** - * Gets the name by which the lowest level module or package is imported. - * NOTE: This is the name that used to import the module, - * which may not be the name of the module. - */ - string bottomModuleName() { - result = relativeTopName() + this.remainderOfName() - or - not exists(relativeTopName()) and - result = this.qualifiedTopName() + this.remainderOfName() - } + /** + * Gets the name by which the lowest level module or package is imported. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. + */ + string bottomModuleName() { + result = relativeTopName() + this.remainderOfName() + or + not exists(relativeTopName()) and + result = this.qualifiedTopName() + this.remainderOfName() + } - /** Gets the name of topmost module or package being imported */ - string topModuleName() { - result = relativeTopName() - or - not exists(relativeTopName()) and - result = this.qualifiedTopName() - } + /** Gets the name of topmost module or package being imported */ + string topModuleName() { + result = relativeTopName() + or + not exists(relativeTopName()) and + result = this.qualifiedTopName() + } - /** - * Gets the full name of the module resulting from evaluating this import. - * NOTE: This is the name that used to import the module, - * which may not be the name of the module. - */ - string getImportedModuleName() { - exists(string bottomName | bottomName = this.bottomModuleName() | - if this.isTop() then result = topModuleName() else result = bottomName - ) - } + /** + * Gets the full name of the module resulting from evaluating this import. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. + */ + string getImportedModuleName() { + exists(string bottomName | bottomName = this.bottomModuleName() | + if this.isTop() then result = topModuleName() else result = bottomName + ) + } - /** - * Gets the names of the modules that may be imported by this import. - * For example this predicate would return 'x' and 'x.y' for `import x.y` - */ - string getAnImportedModuleName() { - result = this.bottomModuleName() - or - result = this.getAnImportedModuleName().regexpReplaceAll("\\.[^.]*$", "") - } + /** + * Gets the names of the modules that may be imported by this import. + * For example this predicate would return 'x' and 'x.y' for `import x.y` + */ + string getAnImportedModuleName() { + result = this.bottomModuleName() + or + result = this.getAnImportedModuleName().regexpReplaceAll("\\.[^.]*$", "") + } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - override predicate hasSideEffects() { any() } + override predicate hasSideEffects() { any() } - private string getTopName() { result = this.getName().regexpReplaceAll("\\..*", "") } + private string getTopName() { result = this.getName().regexpReplaceAll("\\..*", "") } - private string remainderOfName() { - not exists(this.getName()) and result = "" - or - this.getLevel() <= 0 and result = this.getName().regexpReplaceAll("^[^\\.]*", "") - or - this.getLevel() > 0 and result = "." + this.getName() - } + private string remainderOfName() { + not exists(this.getName()) and result = "" + or + this.getLevel() <= 0 and result = this.getName().regexpReplaceAll("^[^\\.]*", "") + or + this.getLevel() > 0 and result = "." + this.getName() + } - /** - * Whether this import is relative, that is not absolute. - * See https://www.python.org/dev/peps/pep-0328/ - */ - predicate isRelative() { - /* Implicit */ - exists(this.relativeTopName()) - or - /* Explicit */ - this.getLevel() > 0 - } + /** + * Whether this import is relative, that is not absolute. + * See https://www.python.org/dev/peps/pep-0328/ + */ + predicate isRelative() { + /* Implicit */ + exists(this.relativeTopName()) + or + /* Explicit */ + this.getLevel() > 0 + } } /** A `from ... import ...` expression */ class ImportMember extends ImportMember_ { - override Expr getASubExpression() { result = this.getModule() } + override Expr getASubExpression() { result = this.getModule() } - override predicate hasSideEffects() { - /* Strictly this only has side-effects if the module is a package */ - any() - } + override predicate hasSideEffects() { + /* Strictly this only has side-effects if the module is a package */ + any() + } - /** - * Gets the full name of the module resulting from evaluating this import. - * NOTE: This is the name that used to import the module, - * which may not be the name of the module. - */ - string getImportedModuleName() { - result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName() - } + /** + * Gets the full name of the module resulting from evaluating this import. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. + */ + string getImportedModuleName() { + result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName() + } - override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() } + override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() } } /** An import statement */ class Import extends Import_ { - /* syntax: import modname */ - private ImportExpr getAModuleExpr() { - result = this.getAName().getValue() - or - result = this.getAName().getValue().(ImportMember).getModule() - } + /* syntax: import modname */ + private ImportExpr getAModuleExpr() { + result = this.getAName().getValue() + or + result = this.getAName().getValue().(ImportMember).getModule() + } - /** - * Use getAnImportedModuleName(), - * possibly combined with ModuleObject.importedAs() - * Gets a module imported by this import statement - */ - deprecated Module getAModule() { result.getName() = this.getAnImportedModuleName() } + /** + * Use getAnImportedModuleName(), + * possibly combined with ModuleObject.importedAs() + * Gets a module imported by this import statement + */ + deprecated Module getAModule() { result.getName() = this.getAnImportedModuleName() } - /** Whether this a `from ... import ...` statement */ - predicate isFromImport() { this.getAName().getValue() instanceof ImportMember } + /** Whether this a `from ... import ...` statement */ + predicate isFromImport() { this.getAName().getValue() instanceof ImportMember } - override Expr getASubExpression() { - result = this.getAModuleExpr() or - result = this.getAName().getAsname() or - result = this.getAName().getValue() - } + override Expr getASubExpression() { + result = this.getAModuleExpr() or + result = this.getAName().getAsname() or + result = this.getAName().getValue() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } - /** - * Gets the name of an imported module. - * For example, for the import statement `import bar` which - * is a relative import in package "foo", this would return - * "foo.bar". - * The import statment `from foo import bar` would return - * `foo` and `foo.bar` - */ - string getAnImportedModuleName() { - result = this.getAModuleExpr().getAnImportedModuleName() - or - exists(ImportMember m, string modname | - m = this.getAName().getValue() and - modname = m.getModule().(ImportExpr).getImportedModuleName() - | - result = modname - or - result = modname + "." + m.getName() - ) - } + /** + * Gets the name of an imported module. + * For example, for the import statement `import bar` which + * is a relative import in package "foo", this would return + * "foo.bar". + * The import statment `from foo import bar` would return + * `foo` and `foo.bar` + */ + string getAnImportedModuleName() { + result = this.getAModuleExpr().getAnImportedModuleName() + or + exists(ImportMember m, string modname | + m = this.getAName().getValue() and + modname = m.getModule().(ImportExpr).getImportedModuleName() + | + result = modname + or + result = modname + "." + m.getName() + ) + } } /** An import * statement */ class ImportStar extends ImportStar_ { - /* syntax: from modname import * */ - ImportExpr getModuleExpr() { - result = this.getModule() - or - result = this.getModule().(ImportMember).getModule() - } + /* syntax: from modname import * */ + ImportExpr getModuleExpr() { + result = this.getModule() + or + result = this.getModule().(ImportMember).getModule() + } - override string toString() { result = "from " + this.getModuleExpr().getName() + " import *" } + override string toString() { result = "from " + this.getModuleExpr().getName() + " import *" } - /** - * Use getAnImportedModuleName(), - * possibly combined with ModuleObject.importedAs() - * Gets the module imported by this import * statement - */ - deprecated Module getTheModule() { result.getName() = this.getImportedModuleName() } + /** + * Use getAnImportedModuleName(), + * possibly combined with ModuleObject.importedAs() + * Gets the module imported by this import * statement + */ + deprecated Module getTheModule() { result.getName() = this.getImportedModuleName() } - override Expr getASubExpression() { result = this.getModule() } + override Expr getASubExpression() { result = this.getModule() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } - /** Gets the name of the imported module. */ - string getImportedModuleName() { result = this.getModuleExpr().getImportedModuleName() } + /** Gets the name of the imported module. */ + string getImportedModuleName() { result = this.getModuleExpr().getImportedModuleName() } } /** @@ -236,16 +236,16 @@ class ImportStar extends ImportStar_ { * such as `import sys`, `from sys import version` or `from sys import *`. */ class ImportingStmt extends Stmt { - ImportingStmt() { - this instanceof Import - or - this instanceof ImportStar - } + ImportingStmt() { + this instanceof Import + or + this instanceof ImportStar + } - /** Gets the name of an imported module. */ - string getAnImportedModuleName() { - result = this.(Import).getAnImportedModuleName() - or - result = this.(ImportStar).getImportedModuleName() - } + /** Gets the name of an imported module. */ + string getAnImportedModuleName() { + result = this.(Import).getAnImportedModuleName() + or + result = this.(ImportStar).getImportedModuleName() + } } diff --git a/python/ql/src/semmle/python/Keywords.qll b/python/ql/src/semmle/python/Keywords.qll index 7d8b687f83c..406ba833fb8 100644 --- a/python/ql/src/semmle/python/Keywords.qll +++ b/python/ql/src/semmle/python/Keywords.qll @@ -1,60 +1,60 @@ import python class KeyValuePair extends KeyValuePair_, DictDisplayItem { - /* syntax: Expr : Expr */ - override Location getLocation() { result = KeyValuePair_.super.getLocation() } + /* syntax: Expr : Expr */ + override Location getLocation() { result = KeyValuePair_.super.getLocation() } - override string toString() { result = KeyValuePair_.super.toString() } + override string toString() { result = KeyValuePair_.super.toString() } - /** Gets the value of this dictionary unpacking. */ - override Expr getValue() { result = KeyValuePair_.super.getValue() } + /** Gets the value of this dictionary unpacking. */ + override Expr getValue() { result = KeyValuePair_.super.getValue() } - override Scope getScope() { result = this.getValue().getScope() } + override Scope getScope() { result = this.getValue().getScope() } - override AstNode getAChildNode() { - result = this.getKey() - or - result = this.getValue() - } + override AstNode getAChildNode() { + result = this.getKey() + or + result = this.getValue() + } } /** A double-starred expression in a call or dict literal. */ class DictUnpacking extends DictUnpacking_, DictUnpackingOrKeyword, DictDisplayItem { - override Location getLocation() { result = DictUnpacking_.super.getLocation() } + override Location getLocation() { result = DictUnpacking_.super.getLocation() } - override string toString() { result = DictUnpacking_.super.toString() } + override string toString() { result = DictUnpacking_.super.toString() } - /** Gets the value of this dictionary unpacking. */ - override Expr getValue() { result = DictUnpacking_.super.getValue() } + /** Gets the value of this dictionary unpacking. */ + override Expr getValue() { result = DictUnpacking_.super.getValue() } - override Scope getScope() { result = this.getValue().getScope() } + override Scope getScope() { result = this.getValue().getScope() } - override AstNode getAChildNode() { result = this.getValue() } + override AstNode getAChildNode() { result = this.getValue() } } abstract class DictUnpackingOrKeyword extends DictItem { - abstract Expr getValue(); + abstract Expr getValue(); - override string toString() { result = "DictUnpackingOrKeyword with missing toString" } + override string toString() { result = "DictUnpackingOrKeyword with missing toString" } } abstract class DictDisplayItem extends DictItem { - abstract Expr getValue(); + abstract Expr getValue(); - override string toString() { result = "DictDisplayItem with missing toString" } + override string toString() { result = "DictDisplayItem with missing toString" } } /** A keyword argument in a call. For example `arg=expr` in `foo(0, arg=expr)` */ class Keyword extends Keyword_, DictUnpackingOrKeyword { - /* syntax: name = Expr */ - override Location getLocation() { result = Keyword_.super.getLocation() } + /* syntax: name = Expr */ + override Location getLocation() { result = Keyword_.super.getLocation() } - override string toString() { result = Keyword_.super.toString() } + override string toString() { result = Keyword_.super.toString() } - /** Gets the value of this keyword argument. */ - override Expr getValue() { result = Keyword_.super.getValue() } + /** Gets the value of this keyword argument. */ + override Expr getValue() { result = Keyword_.super.getValue() } - override Scope getScope() { result = this.getValue().getScope() } + override Scope getScope() { result = this.getValue().getScope() } - override AstNode getAChildNode() { result = this.getValue() } + override AstNode getAChildNode() { result = this.getValue() } } diff --git a/python/ql/src/semmle/python/Metrics.qll b/python/ql/src/semmle/python/Metrics.qll index 694843600bd..1526578f6c2 100644 --- a/python/ql/src/semmle/python/Metrics.qll +++ b/python/ql/src/semmle/python/Metrics.qll @@ -2,332 +2,332 @@ import python /** The metrics for a function */ class FunctionMetrics extends Function { - /** - * Gets the total number of lines (including blank lines) - * from the definition to the end of the function - */ - int getNumberOfLines() { py_alllines(this, result) } + /** + * Gets the total number of lines (including blank lines) + * from the definition to the end of the function + */ + int getNumberOfLines() { py_alllines(this, result) } - /** Gets the number of lines of code in the function */ - int getNumberOfLinesOfCode() { py_codelines(this, result) } + /** Gets the number of lines of code in the function */ + int getNumberOfLinesOfCode() { py_codelines(this, result) } - /** Gets the number of lines of comments in the function */ - int getNumberOfLinesOfComments() { py_commentlines(this, result) } + /** Gets the number of lines of comments in the function */ + int getNumberOfLinesOfComments() { py_commentlines(this, result) } - /** Gets the number of lines of docstring in the function */ - int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } + /** Gets the number of lines of docstring in the function */ + int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } - /** - * Cyclomatic complexity: - * The number of linearly independent paths through the source code. - * Computed as E - N + 2P, - * where - * E = the number of edges of the graph. - * N = the number of nodes of the graph. - * P = the number of connected components, which for a single function is 1. - */ - int getCyclomaticComplexity() { - exists(int E, int N | - N = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) and - E = - count(BasicBlock b1, BasicBlock b2 | - b1 = this.getABasicBlock() and - b1.likelyReachable() and - b2 = this.getABasicBlock() and - b2.likelyReachable() and - b2 = b1.getASuccessor() and - not b1.unlikelySuccessor(b2) - ) - | - result = E - N + 2 + /** + * Cyclomatic complexity: + * The number of linearly independent paths through the source code. + * Computed as E - N + 2P, + * where + * E = the number of edges of the graph. + * N = the number of nodes of the graph. + * P = the number of connected components, which for a single function is 1. + */ + int getCyclomaticComplexity() { + exists(int E, int N | + N = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) and + E = + count(BasicBlock b1, BasicBlock b2 | + b1 = this.getABasicBlock() and + b1.likelyReachable() and + b2 = this.getABasicBlock() and + b2.likelyReachable() and + b2 = b1.getASuccessor() and + not b1.unlikelySuccessor(b2) ) - } + | + result = E - N + 2 + ) + } - private BasicBlock getABasicBlock() { - result = this.getEntryNode().getBasicBlock() + private BasicBlock getABasicBlock() { + result = this.getEntryNode().getBasicBlock() + or + exists(BasicBlock mid | mid = this.getABasicBlock() and result = mid.getASuccessor()) + } + + /** + * Dependency of Callables + * One callable "this" depends on another callable "result" + * if "this" makes some call to a method that may end up being "result". + */ + FunctionMetrics getADependency() { + result != this and + not non_coupling_method(result) and + exists(Call call | call.getScope() = this | + exists(FunctionObject callee | callee.getFunction() = result | + call.getAFlowNode().getFunction().refersTo(callee) + ) + or + exists(Attribute a | call.getFunc() = a | + unique_root_method(result, a.getName()) or - exists(BasicBlock mid | mid = this.getABasicBlock() and result = mid.getASuccessor()) - } - - /** - * Dependency of Callables - * One callable "this" depends on another callable "result" - * if "this" makes some call to a method that may end up being "result". - */ - FunctionMetrics getADependency() { - result != this and - not non_coupling_method(result) and - exists(Call call | call.getScope() = this | - exists(FunctionObject callee | callee.getFunction() = result | - call.getAFlowNode().getFunction().refersTo(callee) - ) - or - exists(Attribute a | call.getFunc() = a | - unique_root_method(result, a.getName()) - or - exists(Name n | a.getObject() = n and n.getId() = "self" | - result.getScope() = this.getScope() and - result.getName() = a.getName() - ) - ) + exists(Name n | a.getObject() = n and n.getId() = "self" | + result.getScope() = this.getScope() and + result.getName() = a.getName() ) - } + ) + ) + } - /** - * Afferent Coupling - * the number of callables that depend on this method. - * This is sometimes called the "fan-in" of a method. - */ - int getAfferentCoupling() { result = count(FunctionMetrics m | m.getADependency() = this) } + /** + * Afferent Coupling + * the number of callables that depend on this method. + * This is sometimes called the "fan-in" of a method. + */ + int getAfferentCoupling() { result = count(FunctionMetrics m | m.getADependency() = this) } - /** - * Efferent Coupling - * the number of methods that this method depends on - * This is sometimes called the "fan-out" of a method. - */ - int getEfferentCoupling() { result = count(FunctionMetrics m | this.getADependency() = m) } + /** + * Efferent Coupling + * the number of methods that this method depends on + * This is sometimes called the "fan-out" of a method. + */ + int getEfferentCoupling() { result = count(FunctionMetrics m | this.getADependency() = m) } - int getNumberOfParametersWithoutDefault() { - result = - this.getPositionalParameterCount() - - count(this.getDefinition().(FunctionExpr).getArgs().getADefault()) - } + int getNumberOfParametersWithoutDefault() { + result = + this.getPositionalParameterCount() - + count(this.getDefinition().(FunctionExpr).getArgs().getADefault()) + } - int getStatementNestingDepth() { result = max(Stmt s | s.getScope() = this | getNestingDepth(s)) } + int getStatementNestingDepth() { result = max(Stmt s | s.getScope() = this | getNestingDepth(s)) } - int getNumberOfCalls() { result = count(Call c | c.getScope() = this) } + int getNumberOfCalls() { result = count(Call c | c.getScope() = this) } } /** The metrics for a class */ class ClassMetrics extends Class { - /** - * Gets the total number of lines (including blank lines) - * from the definition to the end of the class - */ - int getNumberOfLines() { py_alllines(this, result) } + /** + * Gets the total number of lines (including blank lines) + * from the definition to the end of the class + */ + int getNumberOfLines() { py_alllines(this, result) } - /** Gets the number of lines of code in the class */ - int getNumberOfLinesOfCode() { py_codelines(this, result) } + /** Gets the number of lines of code in the class */ + int getNumberOfLinesOfCode() { py_codelines(this, result) } - /** Gets the number of lines of comments in the class */ - int getNumberOfLinesOfComments() { py_commentlines(this, result) } + /** Gets the number of lines of comments in the class */ + int getNumberOfLinesOfComments() { py_commentlines(this, result) } - /** Gets the number of lines of docstrings in the class */ - int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } + /** Gets the number of lines of docstrings in the class */ + int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } - private predicate dependsOn(Class other) { - other != this and - ( - exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | - f1.getScope() = this and f2.getScope() = other - ) - or - exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | - c.getFunc().refersTo(cls) and - cls.getPyClass() = other - ) - ) - } + private predicate dependsOn(Class other) { + other != this and + ( + exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | + f1.getScope() = this and f2.getScope() = other + ) + or + exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | + c.getFunc().refersTo(cls) and + cls.getPyClass() = other + ) + ) + } - /** - * The afferent coupling of a class is the number of classes that - * directly depend on it. - */ - int getAfferentCoupling() { result = count(ClassMetrics t | t.dependsOn(this)) } + /** + * The afferent coupling of a class is the number of classes that + * directly depend on it. + */ + int getAfferentCoupling() { result = count(ClassMetrics t | t.dependsOn(this)) } - /** - * The efferent coupling of a class is the number of classes that - * it directly depends on. - */ - int getEfferentCoupling() { result = count(ClassMetrics t | this.dependsOn(t)) } + /** + * The efferent coupling of a class is the number of classes that + * it directly depends on. + */ + int getEfferentCoupling() { result = count(ClassMetrics t | this.dependsOn(t)) } - int getInheritanceDepth() { - exists(ClassObject cls | cls.getPyClass() = this | result = max(classInheritanceDepth(cls))) - } + int getInheritanceDepth() { + exists(ClassObject cls | cls.getPyClass() = this | result = max(classInheritanceDepth(cls))) + } - /* -------- CHIDAMBER AND KEMERER LACK OF COHESION IN METHODS ------------ */ - /* - * The aim of this metric is to try and determine whether a class - * represents one abstraction (good) or multiple abstractions (bad). - * If a class represents multiple abstractions, it should be split - * up into multiple classes. - * - * In the Chidamber and Kemerer method, this is measured as follows: - * n1 = number of pairs of distinct methods in a class that do *not* - * have at least one commonly accessed field - * n2 = number of pairs of distinct methods in a class that do - * have at least one commonly accessed field - * lcom = ((n1 - n2)/2 max 0) - * - * We divide by 2 because each pair (m1,m2) is counted twice in n1 and n2. - */ + /* -------- CHIDAMBER AND KEMERER LACK OF COHESION IN METHODS ------------ */ + /* + * The aim of this metric is to try and determine whether a class + * represents one abstraction (good) or multiple abstractions (bad). + * If a class represents multiple abstractions, it should be split + * up into multiple classes. + * + * In the Chidamber and Kemerer method, this is measured as follows: + * n1 = number of pairs of distinct methods in a class that do *not* + * have at least one commonly accessed field + * n2 = number of pairs of distinct methods in a class that do + * have at least one commonly accessed field + * lcom = ((n1 - n2)/2 max 0) + * + * We divide by 2 because each pair (m1,m2) is counted twice in n1 and n2. + */ - /** should function f be excluded from the cohesion computation? */ - predicate ignoreLackOfCohesion(Function f) { f.isInitMethod() or f.isSpecialMethod() } + /** should function f be excluded from the cohesion computation? */ + predicate ignoreLackOfCohesion(Function f) { f.isInitMethod() or f.isSpecialMethod() } - private predicate methodPair(Function m1, Function m2) { - m1.getScope() = this and - m2.getScope() = this and - not this.ignoreLackOfCohesion(m1) and - not this.ignoreLackOfCohesion(m2) and - m1 != m2 - } + private predicate methodPair(Function m1, Function m2) { + m1.getScope() = this and + m2.getScope() = this and + not this.ignoreLackOfCohesion(m1) and + not this.ignoreLackOfCohesion(m2) and + m1 != m2 + } - private predicate one_accesses_other(Function m1, Function m2) { - this.methodPair(m1, m2) and - ( - exists(SelfAttributeRead sa | - sa.getName() = m1.getName() and - sa.getScope() = m2 - ) - or - exists(SelfAttributeRead sa | - sa.getName() = m2.getName() and - sa.getScope() = m1 - ) - ) - } + private predicate one_accesses_other(Function m1, Function m2) { + this.methodPair(m1, m2) and + ( + exists(SelfAttributeRead sa | + sa.getName() = m1.getName() and + sa.getScope() = m2 + ) + or + exists(SelfAttributeRead sa | + sa.getName() = m2.getName() and + sa.getScope() = m1 + ) + ) + } - /** do m1 and m2 access a common field or one calls the other? */ - private predicate shareField(Function m1, Function m2) { - this.methodPair(m1, m2) and - exists(string name | - exists(SelfAttributeRead sa | - sa.getName() = name and - sa.getScope() = m1 - ) and - exists(SelfAttributeRead sa | - sa.getName() = name and - sa.getScope() = m2 - ) - ) - } + /** do m1 and m2 access a common field or one calls the other? */ + private predicate shareField(Function m1, Function m2) { + this.methodPair(m1, m2) and + exists(string name | + exists(SelfAttributeRead sa | + sa.getName() = name and + sa.getScope() = m1 + ) and + exists(SelfAttributeRead sa | + sa.getName() = name and + sa.getScope() = m2 + ) + ) + } - private int similarMethodPairs() { - result = - count(Function m1, Function m2 | - this.methodPair(m1, m2) and - (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) - ) / 2 - } + private int similarMethodPairs() { + result = + count(Function m1, Function m2 | + this.methodPair(m1, m2) and + (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) + ) / 2 + } - private int methodPairs() { - result = count(Function m1, Function m2 | this.methodPair(m1, m2)) / 2 - } + private int methodPairs() { + result = count(Function m1, Function m2 | this.methodPair(m1, m2)) / 2 + } - /** return Chidamber and Kemerer Lack of Cohesion */ - int getLackOfCohesionCK() { - exists(int n | - n = this.methodPairs() - 2 * this.similarMethodPairs() and - result = n.maximum(0) - ) - } + /** return Chidamber and Kemerer Lack of Cohesion */ + int getLackOfCohesionCK() { + exists(int n | + n = this.methodPairs() - 2 * this.similarMethodPairs() and + result = n.maximum(0) + ) + } - private predicate similarMethodPairDag(Function m1, Function m2, int line) { - (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) and - line = m1.getLocation().getStartLine() and - line < m2.getLocation().getStartLine() - } + private predicate similarMethodPairDag(Function m1, Function m2, int line) { + (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) and + line = m1.getLocation().getStartLine() and + line < m2.getLocation().getStartLine() + } - private predicate subgraph(Function m, int line) { - this.similarMethodPairDag(m, _, line) and not this.similarMethodPairDag(_, m, _) - or - exists(Function other | this.subgraph(other, line) | - this.similarMethodPairDag(other, m, _) or - this.similarMethodPairDag(m, other, _) - ) - } + private predicate subgraph(Function m, int line) { + this.similarMethodPairDag(m, _, line) and not this.similarMethodPairDag(_, m, _) + or + exists(Function other | this.subgraph(other, line) | + this.similarMethodPairDag(other, m, _) or + this.similarMethodPairDag(m, other, _) + ) + } - predicate unionSubgraph(Function m, int line) { line = min(int l | this.subgraph(m, l)) } + predicate unionSubgraph(Function m, int line) { line = min(int l | this.subgraph(m, l)) } - /** return Hitz and Montazeri Lack of Cohesion */ - int getLackOfCohesionHM() { result = count(int line | this.unionSubgraph(_, line)) } + /** return Hitz and Montazeri Lack of Cohesion */ + int getLackOfCohesionHM() { result = count(int line | this.unionSubgraph(_, line)) } } private int classInheritanceDepth(ClassObject cls) { - /* Prevent run-away recursion in case of circular inheritance */ - not cls.getASuperType() = cls and + /* Prevent run-away recursion in case of circular inheritance */ + not cls.getASuperType() = cls and + ( + exists(ClassObject sup | cls.getABaseType() = sup | result = classInheritanceDepth(sup) + 1) + or + not exists(cls.getABaseType()) and ( - exists(ClassObject sup | cls.getABaseType() = sup | result = classInheritanceDepth(sup) + 1) - or - not exists(cls.getABaseType()) and - ( - major_version() = 2 and result = 0 - or - major_version() > 2 and result = 1 - ) + major_version() = 2 and result = 0 + or + major_version() > 2 and result = 1 ) + ) } class ModuleMetrics extends Module { - /** Gets the total number of lines (including blank lines) in the module */ - int getNumberOfLines() { py_alllines(this, result) } + /** Gets the total number of lines (including blank lines) in the module */ + int getNumberOfLines() { py_alllines(this, result) } - /** Gets the number of lines of code in the module */ - int getNumberOfLinesOfCode() { py_codelines(this, result) } + /** Gets the number of lines of code in the module */ + int getNumberOfLinesOfCode() { py_codelines(this, result) } - /** Gets the number of lines of comments in the module */ - int getNumberOfLinesOfComments() { py_commentlines(this, result) } + /** Gets the number of lines of comments in the module */ + int getNumberOfLinesOfComments() { py_commentlines(this, result) } - /** Gets the number of lines of docstrings in the module */ - int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } + /** Gets the number of lines of docstrings in the module */ + int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) } - /** - * The afferent coupling of a class is the number of classes that - * directly depend on it. - */ - int getAfferentCoupling() { result = count(ModuleMetrics t | t.dependsOn(this)) } + /** + * The afferent coupling of a class is the number of classes that + * directly depend on it. + */ + int getAfferentCoupling() { result = count(ModuleMetrics t | t.dependsOn(this)) } - /** - * The efferent coupling of a class is the number of classes that - * it directly depends on. - */ - int getEfferentCoupling() { result = count(ModuleMetrics t | this.dependsOn(t)) } + /** + * The efferent coupling of a class is the number of classes that + * it directly depends on. + */ + int getEfferentCoupling() { result = count(ModuleMetrics t | this.dependsOn(t)) } - private predicate dependsOn(Module other) { - other != this and - ( - exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | - f1.getEnclosingModule() = this and f2.getEnclosingModule() = other - ) - or - exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | - c.getFunc().refersTo(cls) and - cls.getPyClass().getEnclosingModule() = other - ) - ) - } + private predicate dependsOn(Module other) { + other != this and + ( + exists(FunctionMetrics f1, FunctionMetrics f2 | f1.getADependency() = f2 | + f1.getEnclosingModule() = this and f2.getEnclosingModule() = other + ) + or + exists(Function f, Call c, ClassObject cls | c.getScope() = f and f.getScope() = this | + c.getFunc().refersTo(cls) and + cls.getPyClass().getEnclosingModule() = other + ) + ) + } } /** Helpers for coupling */ predicate unique_root_method(Function func, string name) { - name = func.getName() and - not exists(FunctionObject f, FunctionObject other | - f.getFunction() = func and - other.getName() = name - | - not other.overrides(f) - ) + name = func.getName() and + not exists(FunctionObject f, FunctionObject other | + f.getFunction() = func and + other.getName() = name + | + not other.overrides(f) + ) } predicate non_coupling_method(Function f) { - f.isSpecialMethod() or - f.isInitMethod() or - f.getName() = "close" or - f.getName() = "write" or - f.getName() = "read" or - f.getName() = "get" or - f.getName() = "set" + f.isSpecialMethod() or + f.isInitMethod() or + f.getName() = "close" or + f.getName() = "write" or + f.getName() = "read" or + f.getName() = "get" or + f.getName() = "set" } private int getNestingDepth(Stmt s) { - not exists(Stmt outer | outer.getASubStatement() = s) and result = 1 - or - exists(Stmt outer | outer.getASubStatement() = s | - if s.(If).isElif() or s instanceof ExceptStmt - then - /* If statement is an `elif` or `except` then it is not indented relative to its parent */ - result = getNestingDepth(outer) - else result = getNestingDepth(outer) + 1 - ) + not exists(Stmt outer | outer.getASubStatement() = s) and result = 1 + or + exists(Stmt outer | outer.getASubStatement() = s | + if s.(If).isElif() or s instanceof ExceptStmt + then + /* If statement is an `elif` or `except` then it is not indented relative to its parent */ + result = getNestingDepth(outer) + else result = getNestingDepth(outer) + 1 + ) } diff --git a/python/ql/src/semmle/python/Module.qll b/python/ql/src/semmle/python/Module.qll index 4d428636893..fcf1c0b2925 100644 --- a/python/ql/src/semmle/python/Module.qll +++ b/python/ql/src/semmle/python/Module.qll @@ -7,174 +7,174 @@ private import semmle.python.objects.Modules * It is also a Scope; the scope of global variables. */ class Module extends Module_, Scope, AstNode { - override string toString() { - result = this.getKind() + " " + this.getName() - or - /* No name is defined, which means that this module is not on an import path. So it must be a script */ - not exists(this.getName()) and - not this.isPackage() and - result = "Script " + this.getFile().getShortName() - or - /* Package missing name, so just use the path instead */ - not exists(this.getName()) and - this.isPackage() and - result = "Package at " + this.getPath().getAbsolutePath() - } + override string toString() { + result = this.getKind() + " " + this.getName() + or + /* No name is defined, which means that this module is not on an import path. So it must be a script */ + not exists(this.getName()) and + not this.isPackage() and + result = "Script " + this.getFile().getShortName() + or + /* Package missing name, so just use the path instead */ + not exists(this.getName()) and + this.isPackage() and + result = "Package at " + this.getPath().getAbsolutePath() + } - /** - * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. - * The enclosing scope of this module (always none) - */ - override Scope getScope() { none() } + /** + * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. + * The enclosing scope of this module (always none) + */ + override Scope getScope() { none() } - /** The enclosing scope of this module (always none) */ - override Scope getEnclosingScope() { none() } + /** The enclosing scope of this module (always none) */ + override Scope getEnclosingScope() { none() } - /** Gets the statements forming the body of this module */ - override StmtList getBody() { result = Module_.super.getBody() } + /** Gets the statements forming the body of this module */ + override StmtList getBody() { result = Module_.super.getBody() } - /** Gets the nth statement of this module */ - override Stmt getStmt(int n) { result = Module_.super.getStmt(n) } + /** Gets the nth statement of this module */ + override Stmt getStmt(int n) { result = Module_.super.getStmt(n) } - /** Gets a top-level statement in this module */ - override Stmt getAStmt() { result = Module_.super.getAStmt() } + /** Gets a top-level statement in this module */ + override Stmt getAStmt() { result = Module_.super.getAStmt() } - /** Gets the name of this module */ - override string getName() { - result = Module_.super.getName() and legalDottedName(result) - or - not exists(Module_.super.getName()) and - result = moduleNameFromFile(this.getPath()) - } + /** Gets the name of this module */ + override string getName() { + result = Module_.super.getName() and legalDottedName(result) + or + not exists(Module_.super.getName()) and + result = moduleNameFromFile(this.getPath()) + } - /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ - string getShortName() { - result = this.getName().suffix(this.getPackage().getName().length() + 1) - or - result = this.getName() and not exists(this.getPackage()) - } + /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ + string getShortName() { + result = this.getName().suffix(this.getPackage().getName().length() + 1) + or + result = this.getName() and not exists(this.getPackage()) + } - /** Gets this module */ - override Module getEnclosingModule() { result = this } + /** Gets this module */ + override Module getEnclosingModule() { result = this } - /** Gets the __init__ module of this module if the module is a package and it has an __init__ module */ - Module getInitModule() { - /* this.isPackage() and */ result.getName() = this.getName() + ".__init__" - } + /** Gets the __init__ module of this module if the module is a package and it has an __init__ module */ + Module getInitModule() { + /* this.isPackage() and */ result.getName() = this.getName() + ".__init__" + } - /** Whether this module is a package initializer */ - predicate isPackageInit() { this.getName().matches("%\\_\\_init\\_\\_") and not this.isPackage() } + /** Whether this module is a package initializer */ + predicate isPackageInit() { this.getName().matches("%\\_\\_init\\_\\_") and not this.isPackage() } - /** Gets a name exported by this module, that is the names that will be added to a namespace by 'from this-module import *' */ - string getAnExport() { - py_exports(this, result) - or - exists(ModuleObjectInternal mod | mod.getSource() = this.getEntryNode() | - mod.(ModuleValue).exports(result) - ) - } + /** Gets a name exported by this module, that is the names that will be added to a namespace by 'from this-module import *' */ + string getAnExport() { + py_exports(this, result) + or + exists(ModuleObjectInternal mod | mod.getSource() = this.getEntryNode() | + mod.(ModuleValue).exports(result) + ) + } - /** Gets the source file for this module */ - File getFile() { py_module_path(this, result) } + /** Gets the source file for this module */ + File getFile() { py_module_path(this, result) } - /** Gets the source file or folder for this module or package */ - Container getPath() { py_module_path(this, result) } + /** Gets the source file or folder for this module or package */ + Container getPath() { py_module_path(this, result) } - /** Whether this is a package */ - predicate isPackage() { this.getPath() instanceof Folder } + /** Whether this is a package */ + predicate isPackage() { this.getPath() instanceof Folder } - /** Gets the package containing this module (or parent package if this is a package) */ - Module getPackage() { - this.getName().matches("%.%") and - result.getName() = getName().regexpReplaceAll("\\.[^.]*$", "") - } + /** Gets the package containing this module (or parent package if this is a package) */ + Module getPackage() { + this.getName().matches("%.%") and + result.getName() = getName().regexpReplaceAll("\\.[^.]*$", "") + } - /** Gets the name of the package containing this module */ - string getPackageName() { - this.getName().matches("%.%") and - result = getName().regexpReplaceAll("\\.[^.]*$", "") - } + /** Gets the name of the package containing this module */ + string getPackageName() { + this.getName().matches("%.%") and + result = getName().regexpReplaceAll("\\.[^.]*$", "") + } - /** Gets the metrics for this module */ - ModuleMetrics getMetrics() { result = this } + /** Gets the metrics for this module */ + ModuleMetrics getMetrics() { result = this } - /** - * Use ModuleObject.getAnImportedModule() instead. - * Gets a module imported by this module - */ - deprecated Module getAnImportedModule() { result.getName() = this.getAnImportedModuleName() } + /** + * Use ModuleObject.getAnImportedModule() instead. + * Gets a module imported by this module + */ + deprecated Module getAnImportedModule() { result.getName() = this.getAnImportedModuleName() } - string getAnImportedModuleName() { - exists(Import i | i.getEnclosingModule() = this | result = i.getAnImportedModuleName()) - or - exists(ImportStar i | i.getEnclosingModule() = this | result = i.getImportedModuleName()) - } + string getAnImportedModuleName() { + exists(Import i | i.getEnclosingModule() = this | result = i.getAnImportedModuleName()) + or + exists(ImportStar i | i.getEnclosingModule() = this | result = i.getImportedModuleName()) + } - override Location getLocation() { - py_scope_location(result, this) - or - not py_scope_location(_, this) and - locations_ast(result, this, 0, 0, 0, 0) - } + override Location getLocation() { + py_scope_location(result, this) + or + not py_scope_location(_, this) and + locations_ast(result, this, 0, 0, 0, 0) + } - /** Gets a child module or package of this package */ - Module getSubModule(string name) { - result.getPackage() = this and - name = result.getName().regexpReplaceAll(".*\\.", "") - } + /** Gets a child module or package of this package */ + Module getSubModule(string name) { + result.getPackage() = this and + name = result.getName().regexpReplaceAll(".*\\.", "") + } - /** Whether name is declared in the __all__ list of this module */ - predicate declaredInAll(string name) { - exists(AssignStmt a, GlobalVariable all | - a.defines(all) and - a.getScope() = this and - all.getId() = "__all__" and - a.getValue().(List).getAnElt().(StrConst).getText() = name - ) - } + /** Whether name is declared in the __all__ list of this module */ + predicate declaredInAll(string name) { + exists(AssignStmt a, GlobalVariable all | + a.defines(all) and + a.getScope() = this and + all.getId() = "__all__" and + a.getValue().(List).getAnElt().(StrConst).getText() = name + ) + } - override AstNode getAChildNode() { result = this.getAStmt() } + override AstNode getAChildNode() { result = this.getAStmt() } - predicate hasFromFuture(string attr) { - exists(Import i, ImportMember im, ImportExpr ie, Alias a, Name name | - im.getModule() = ie and - ie.getName() = "__future__" and - a.getAsname() = name and - name.getId() = attr and - i.getASubExpression() = im and - i.getAName() = a and - i.getEnclosingModule() = this - ) - } + predicate hasFromFuture(string attr) { + exists(Import i, ImportMember im, ImportExpr ie, Alias a, Name name | + im.getModule() = ie and + ie.getName() = "__future__" and + a.getAsname() = name and + name.getId() = attr and + i.getASubExpression() = im and + i.getAName() = a and + i.getEnclosingModule() = this + ) + } - /** Gets the path element from which this module was loaded. */ - Container getLoadPath() { result = this.getPath().getImportRoot() } + /** Gets the path element from which this module was loaded. */ + Container getLoadPath() { result = this.getPath().getImportRoot() } - /** Holds if this module is in the standard library for version `major.minor` */ - predicate inStdLib(int major, int minor) { this.getLoadPath().isStdLibRoot(major, minor) } + /** Holds if this module is in the standard library for version `major.minor` */ + predicate inStdLib(int major, int minor) { this.getLoadPath().isStdLibRoot(major, minor) } - /** Holds if this module is in the standard library */ - predicate inStdLib() { this.getLoadPath().isStdLibRoot() } + /** Holds if this module is in the standard library */ + predicate inStdLib() { this.getLoadPath().isStdLibRoot() } - override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } + override predicate containsInScope(AstNode inner) { Scope.super.containsInScope(inner) } - override predicate contains(AstNode inner) { Scope.super.contains(inner) } + override predicate contains(AstNode inner) { Scope.super.contains(inner) } - /** Gets the kind of this module. */ - override string getKind() { - if this.isPackage() - then result = "Package" - else ( - not exists(Module_.super.getKind()) and result = "Module" - or - result = Module_.super.getKind() - ) - } + /** Gets the kind of this module. */ + override string getKind() { + if this.isPackage() + then result = "Package" + else ( + not exists(Module_.super.getKind()) and result = "Module" + or + result = Module_.super.getKind() + ) + } } bindingset[name] private predicate legalDottedName(string name) { - name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*(\\.(\\p{L}|_)(\\p{L}|\\d|_)*)*") + name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*(\\.(\\p{L}|_)(\\p{L}|\\d|_)*)*") } bindingset[name] @@ -185,44 +185,44 @@ private predicate legalShortName(string name) { name.regexpMatch("(\\p{L}|_)(\\p * Does it have an __init__.py file (or --respect-init=False for Python 2) and is it within the source archive? */ private predicate isPotentialSourcePackage(Folder f) { - f.getRelativePath() != "" and - isPotentialPackage(f) + f.getRelativePath() != "" and + isPotentialPackage(f) } private predicate isPotentialPackage(Folder f) { - exists(f.getFile("__init__.py")) - or - py_flags_versioned("options.respect_init", "False", _) and major_version() = 2 + exists(f.getFile("__init__.py")) + or + py_flags_versioned("options.respect_init", "False", _) and major_version() = 2 } private string moduleNameFromBase(Container file) { - isPotentialPackage(file) and result = file.getBaseName() - or - file instanceof File and result = file.getStem() + isPotentialPackage(file) and result = file.getBaseName() + or + file instanceof File and result = file.getStem() } string moduleNameFromFile(Container file) { - exists(string basename | - basename = moduleNameFromBase(file) and - legalShortName(basename) and - result = moduleNameFromFile(file.getParent()) + "." + basename - ) - or - isPotentialSourcePackage(file) and - result = file.getStem() and - ( - not isPotentialSourcePackage(file.getParent()) or - not legalShortName(file.getParent().getBaseName()) - ) - or - result = file.getStem() and file.getParent() = file.getImportRoot() - or - result = file.getStem() and isStubRoot(file.getParent()) + exists(string basename | + basename = moduleNameFromBase(file) and + legalShortName(basename) and + result = moduleNameFromFile(file.getParent()) + "." + basename + ) + or + isPotentialSourcePackage(file) and + result = file.getStem() and + ( + not isPotentialSourcePackage(file.getParent()) or + not legalShortName(file.getParent().getBaseName()) + ) + or + result = file.getStem() and file.getParent() = file.getImportRoot() + or + result = file.getStem() and isStubRoot(file.getParent()) } private predicate isStubRoot(Folder f) { - not f.getParent*().isImportRoot() and - f.getAbsolutePath().matches("%/data/python/stubs") + not f.getParent*().isImportRoot() and + f.getAbsolutePath().matches("%/data/python/stubs") } /** @@ -233,22 +233,22 @@ private predicate isStubRoot(Folder f) { * this is the module most likely to be imported under that name. */ predicate isPreferredModuleForName(Container c, string name) { - exists(int p | - p = min(int x | x = priorityForName(_, name)) and - p = priorityForName(c, name) - ) + exists(int p | + p = min(int x | x = priorityForName(_, name)) and + p = priorityForName(c, name) + ) } private int priorityForName(Container c, string name) { - name = moduleNameFromFile(c) and - ( - // In the source - exists(c.getRelativePath()) and result = -1 - or - // On an import path - exists(c.getImportRoot(result)) - or - // Otherwise - result = 10000 - ) + name = moduleNameFromFile(c) and + ( + // In the source + exists(c.getRelativePath()) and result = -1 + or + // On an import path + exists(c.getImportRoot(result)) + or + // Otherwise + result = 10000 + ) } diff --git a/python/ql/src/semmle/python/Operations.qll b/python/ql/src/semmle/python/Operations.qll index c6dd102350c..b057fde5155 100644 --- a/python/ql/src/semmle/python/Operations.qll +++ b/python/ql/src/semmle/python/Operations.qll @@ -2,133 +2,133 @@ import python /** Base class for operators */ class Operator extends Operator_ { - /** Gets the name of the special method used to implement this operator */ - string getSpecialMethodName() { none() } + /** Gets the name of the special method used to implement this operator */ + string getSpecialMethodName() { none() } } /* Unary Expression and its operators */ /** A unary expression: (`+x`), (`-x`) or (`~x`) */ class UnaryExpr extends UnaryExpr_ { - override Expr getASubExpression() { result = this.getOperand() } + override Expr getASubExpression() { result = this.getOperand() } } /** A unary operator: `+`, `-`, `~` or `not` */ class Unaryop extends Unaryop_ { - /** Gets the name of the special method used to implement this operator */ - string getSpecialMethodName() { none() } + /** Gets the name of the special method used to implement this operator */ + string getSpecialMethodName() { none() } } /** An invert (`~`) unary operator */ class Invert extends Invert_ { - override string getSpecialMethodName() { result = "__invert__" } + override string getSpecialMethodName() { result = "__invert__" } } /** A positive (`+`) unary operator */ class UAdd extends UAdd_ { - override string getSpecialMethodName() { result = "__pos__" } + override string getSpecialMethodName() { result = "__pos__" } } /** A negation (`-`) unary operator */ class USub extends USub_ { - override string getSpecialMethodName() { result = "__neg__" } + override string getSpecialMethodName() { result = "__neg__" } } /** A `not` unary operator */ class Not extends Not_ { - override string getSpecialMethodName() { none() } + override string getSpecialMethodName() { none() } } /* Binary Operation and its operators */ /** A binary expression, such as `x + y` */ class BinaryExpr extends BinaryExpr_ { - override Expr getASubExpression() { result = this.getLeft() or result = this.getRight() } + override Expr getASubExpression() { result = this.getLeft() or result = this.getRight() } } /** A power (`**`) binary operator */ class Pow extends Pow_ { - override string getSpecialMethodName() { result = "__pow__" } + override string getSpecialMethodName() { result = "__pow__" } } /** A right shift (`>>`) binary operator */ class RShift extends RShift_ { - override string getSpecialMethodName() { result = "__rshift__" } + override string getSpecialMethodName() { result = "__rshift__" } } /** A subtract (`-`) binary operator */ class Sub extends Sub_ { - override string getSpecialMethodName() { result = "__sub__" } + override string getSpecialMethodName() { result = "__sub__" } } /** A bitwise and (`&`) binary operator */ class BitAnd extends BitAnd_ { - override string getSpecialMethodName() { result = "__and__" } + override string getSpecialMethodName() { result = "__and__" } } /** A bitwise or (`|`) binary operator */ class BitOr extends BitOr_ { - override string getSpecialMethodName() { result = "__or__" } + override string getSpecialMethodName() { result = "__or__" } } /** A bitwise exclusive-or (`^`) binary operator */ class BitXor extends BitXor_ { - override string getSpecialMethodName() { result = "__xor__" } + override string getSpecialMethodName() { result = "__xor__" } } /** An add (`+`) binary operator */ class Add extends Add_ { - override string getSpecialMethodName() { result = "__add__" } + override string getSpecialMethodName() { result = "__add__" } } /** An (true) divide (`/`) binary operator */ class Div extends Div_ { - override string getSpecialMethodName() { - result = "__truediv__" - or - major_version() = 2 and result = "__div__" - } + override string getSpecialMethodName() { + result = "__truediv__" + or + major_version() = 2 and result = "__div__" + } } /** An floor divide (`//`) binary operator */ class FloorDiv extends FloorDiv_ { - override string getSpecialMethodName() { result = "__floordiv__" } + override string getSpecialMethodName() { result = "__floordiv__" } } /** A left shift (`<<`) binary operator */ class LShift extends LShift_ { - override string getSpecialMethodName() { result = "__lshift__" } + override string getSpecialMethodName() { result = "__lshift__" } } /** A modulo (`%`) binary operator, which includes string formatting */ class Mod extends Mod_ { - override string getSpecialMethodName() { result = "__mod__" } + override string getSpecialMethodName() { result = "__mod__" } } /** A multiplication (`*`) binary operator */ class Mult extends Mult_ { - override string getSpecialMethodName() { result = "__mul__" } + override string getSpecialMethodName() { result = "__mul__" } } /** A matrix multiplication (`@`) binary operator */ class MatMult extends MatMult_ { - override string getSpecialMethodName() { result = "__matmul__" } + override string getSpecialMethodName() { result = "__matmul__" } } /* Comparison Operation and its operators */ /** A comparison operation, such as `x`) comparison operator */ class Gt extends Gt_ { - override string getSymbol() { result = ">" } + override string getSymbol() { result = ">" } - override string getSpecialMethodName() { result = "__gt__" } + override string getSpecialMethodName() { result = "__gt__" } } /** A greater than or equals (`>=`) comparison operator */ class GtE extends GtE_ { - override string getSymbol() { result = ">=" } + override string getSymbol() { result = ">=" } - override string getSpecialMethodName() { result = "__ge__" } + override string getSpecialMethodName() { result = "__ge__" } } /** An `in` comparison operator */ class In extends In_ { - override string getSymbol() { result = "in" } + override string getSymbol() { result = "in" } } /** An `is` comparison operator */ class Is extends Is_ { - override string getSymbol() { result = "is" } + override string getSymbol() { result = "is" } } /** An `is not` comparison operator */ class IsNot extends IsNot_ { - override string getSymbol() { result = "is not" } + override string getSymbol() { result = "is not" } } /** An equals (`==`) comparison operator */ class Eq extends Eq_ { - override string getSymbol() { result = "==" } + override string getSymbol() { result = "==" } - override string getSpecialMethodName() { result = "__eq__" } + override string getSpecialMethodName() { result = "__eq__" } } /** A less than (`<`) comparison operator */ class Lt extends Lt_ { - override string getSymbol() { result = "<" } + override string getSymbol() { result = "<" } - override string getSpecialMethodName() { result = "__lt__" } + override string getSpecialMethodName() { result = "__lt__" } } /** A less than or equals (`<=`) comparison operator */ class LtE extends LtE_ { - override string getSymbol() { result = "<=" } + override string getSymbol() { result = "<=" } - override string getSpecialMethodName() { result = "__le__" } + override string getSpecialMethodName() { result = "__le__" } } /** A not equals (`!=`) comparison operator */ class NotEq extends NotEq_ { - override string getSymbol() { result = "!=" } + override string getSymbol() { result = "!=" } - override string getSpecialMethodName() { result = "__ne__" } + override string getSpecialMethodName() { result = "__ne__" } } /** An `not in` comparison operator */ class NotIn extends NotIn_ { - override string getSymbol() { result = "not in" } + override string getSymbol() { result = "not in" } } /* Boolean Operation (and/or) and its operators */ /** A boolean shortcut (and/or) operation */ class BoolExpr extends BoolExpr_ { - override Expr getASubExpression() { result = this.getAValue() } + override Expr getASubExpression() { result = this.getAValue() } - string getOperator() { - this.getOp() instanceof And and result = "and" - or - this.getOp() instanceof Or and result = "or" - } + string getOperator() { + this.getOp() instanceof And and result = "and" + or + this.getOp() instanceof Or and result = "or" + } - /** Whether part evaluates to partIsTrue if this evaluates to wholeIsTrue */ - predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue) { - if this.getOp() instanceof And - then ( - wholeIsTrue = true and partIsTrue = true and part = this.getAValue() - or - wholeIsTrue = true and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, true) - ) else ( - wholeIsTrue = false and partIsTrue = false and part = this.getAValue() - or - wholeIsTrue = false and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, false) - ) - } + /** Whether part evaluates to partIsTrue if this evaluates to wholeIsTrue */ + predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue) { + if this.getOp() instanceof And + then ( + wholeIsTrue = true and partIsTrue = true and part = this.getAValue() + or + wholeIsTrue = true and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, true) + ) else ( + wholeIsTrue = false and partIsTrue = false and part = this.getAValue() + or + wholeIsTrue = false and this.getAValue().(BoolExpr).impliesValue(part, partIsTrue, false) + ) + } } /** A short circuit boolean operator, and/or */ diff --git a/python/ql/src/semmle/python/SSA.qll b/python/ql/src/semmle/python/SSA.qll index ac7ab8e7d32..deaa0db914e 100644 --- a/python/ql/src/semmle/python/SSA.qll +++ b/python/ql/src/semmle/python/SSA.qll @@ -9,202 +9,202 @@ import python * Definitions without uses do not have a SSA variable. */ class SsaVariable extends @py_ssa_var { - SsaVariable() { py_ssa_var(this, _) } + SsaVariable() { py_ssa_var(this, _) } - /** Gets the source variable */ - Variable getVariable() { py_ssa_var(this, result) } + /** Gets the source variable */ + Variable getVariable() { py_ssa_var(this, result) } - /** Gets a use of this variable */ - ControlFlowNode getAUse() { py_ssa_use(result, this) } + /** Gets a use of this variable */ + ControlFlowNode getAUse() { py_ssa_use(result, this) } - /** Gets the definition (which may be a deletion) of this SSA variable */ - ControlFlowNode getDefinition() { py_ssa_defn(this, result) } + /** Gets the definition (which may be a deletion) of this SSA variable */ + ControlFlowNode getDefinition() { py_ssa_defn(this, result) } - /** - * Gets an argument of the phi function defining this variable. - * This predicate uses the raw SSA form produced by the extractor. - * In general, you should use `getAPrunedPhiInput()` instead. - */ - SsaVariable getAPhiInput() { py_ssa_phi(this, result) } - - /** - * Gets the edge(s) (result->this.getDefinition()) on which the SSA variable 'input' defines this SSA variable. - * For each incoming edge `X->B`, where `B` is the basic block containing this phi-node, only one of the input SSA variables - * for this phi-node is live. This predicate returns the predecessor block such that the variable 'input' - * is the live variable on the edge result->B. - */ - BasicBlock getPredecessorBlockForPhiArgument(SsaVariable input) { - input = this.getAPhiInput() and - result = this.getAPredecessorBlockForPhi() and - input.getDefinition().getBasicBlock().dominates(result) and - /* - * Beware the case where an SSA variable that is an input on one edge dominates another edge. - * Consider (in SSA form): - * x0 = 0 - * if cond: - * x1 = 1 - * x2 = phi(x0, x1) - * use(x2) - * - * The definition of x0 dominates the exit from the block x1=1, even though it does not reach it. - * Hence we need to check that no other definition dominates the edge and actually reaches it. - * Note that if a dominates c and b dominates c, then either a dominates b or vice-versa. - */ - - not exists(SsaVariable other, BasicBlock other_def | - not other = input and - other = this.getAPhiInput() and - other_def = other.getDefinition().getBasicBlock() - | - other_def.dominates(result) and - input.getDefinition().getBasicBlock().strictlyDominates(other_def) - ) - } - - /** Gets an argument of the phi function defining this variable, pruned of unlikely edges. */ - SsaVariable getAPrunedPhiInput() { - result = this.getAPhiInput() and - exists(BasicBlock incoming | incoming = this.getPredecessorBlockForPhiArgument(result) | - not incoming.getLastNode().(RaisingNode).unlikelySuccessor(this.getDefinition()) - ) - } - - /** Gets a variable that ultimately defines this variable and is not itself defined by another variable */ - SsaVariable getAnUltimateDefinition() { - result = this and not exists(this.getAPhiInput()) - or - result = this.getAPhiInput().getAnUltimateDefinition() - } - - /** Gets a textual representation of this element. */ - string toString() { result = "SSA Variable " + this.getId() } - - Location getLocation() { result = this.getDefinition().getLocation() } - - /** Gets the id (name) of this variable */ - string getId() { result = this.getVariable().getId() } - - /** Gets the incoming edges for a Phi node. */ - private BasicBlock getAPredecessorBlockForPhi() { - exists(getAPhiInput()) and - result.getASuccessor() = this.getDefinition().getBasicBlock() - } - - /** Gets the incoming edges for a Phi node, pruned of unlikely edges. */ - private BasicBlock getAPrunedPredecessorBlockForPhi() { - result = this.getAPredecessorBlockForPhi() and - not result.unlikelySuccessor(this.getDefinition().getBasicBlock()) - } - - /** Whether it is possible to reach a use of this variable without passing a definition */ - predicate reachableWithoutDefinition() { - not exists(this.getDefinition()) and not py_ssa_phi(this, _) - or - exists(SsaVariable var | var = this.getAPhiInput() | var.reachableWithoutDefinition()) - or - /* - * For phi-nodes, there must be a corresponding phi-input for each control-flow - * predecessor. Otherwise, the variable will be undefined on that incoming edge. - * WARNING: the same phi-input may cover multiple predecessors, so this check - * cannot be done by counting. - */ - - exists(BasicBlock incoming | - incoming = this.getAPredecessorBlockForPhi() and - not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) - ) - } - - /** Whether this variable may be undefined */ - predicate maybeUndefined() { - not exists(this.getDefinition()) and not py_ssa_phi(this, _) and not this.implicitlyDefined() - or - this.getDefinition().isDelete() - or - exists(SsaVariable var | var = this.getAPrunedPhiInput() | var.maybeUndefined()) - or - /* - * For phi-nodes, there must be a corresponding phi-input for each control-flow - * predecessor. Otherwise, the variable will be undefined on that incoming edge. - * WARNING: the same phi-input may cover multiple predecessors, so this check - * cannot be done by counting. - */ - - exists(BasicBlock incoming | - reaches_end(incoming) and - incoming = this.getAPrunedPredecessorBlockForPhi() and - not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) - ) - } - - private predicate implicitlyDefined() { - not exists(this.getDefinition()) and - not py_ssa_phi(this, _) and - exists(GlobalVariable var | this.getVariable() = var | - globallyDefinedName(var.getId()) - or - var.getId() = "__path__" and var.getScope().(Module).isPackageInit() - ) - } - - /** - * Gets the global variable that is accessed if this local is undefined. - * Only applies to local variables in class scopes. - */ - GlobalVariable getFallbackGlobal() { - exists(LocalVariable local, Class cls | this.getVariable() = local | - local.getScope() = cls and - result.getScope() = cls.getScope() and - result.getId() = local.getId() and - not exists(this.getDefinition()) - ) - } + /** + * Gets an argument of the phi function defining this variable. + * This predicate uses the raw SSA form produced by the extractor. + * In general, you should use `getAPrunedPhiInput()` instead. + */ + SsaVariable getAPhiInput() { py_ssa_phi(this, result) } + /** + * Gets the edge(s) (result->this.getDefinition()) on which the SSA variable 'input' defines this SSA variable. + * For each incoming edge `X->B`, where `B` is the basic block containing this phi-node, only one of the input SSA variables + * for this phi-node is live. This predicate returns the predecessor block such that the variable 'input' + * is the live variable on the edge result->B. + */ + BasicBlock getPredecessorBlockForPhiArgument(SsaVariable input) { + input = this.getAPhiInput() and + result = this.getAPredecessorBlockForPhi() and + input.getDefinition().getBasicBlock().dominates(result) and /* - * Whether this SSA variable is the first parameter of a method - * (regardless of whether it is actually called self or not) + * Beware the case where an SSA variable that is an input on one edge dominates another edge. + * Consider (in SSA form): + * x0 = 0 + * if cond: + * x1 = 1 + * x2 = phi(x0, x1) + * use(x2) + * + * The definition of x0 dominates the exit from the block x1=1, even though it does not reach it. + * Hence we need to check that no other definition dominates the edge and actually reaches it. + * Note that if a dominates c and b dominates c, then either a dominates b or vice-versa. */ - predicate isSelf() { - exists(Function func | - func.isMethod() and - this.getDefinition().getNode() = func.getArg(0) - ) - } + not exists(SsaVariable other, BasicBlock other_def | + not other = input and + other = this.getAPhiInput() and + other_def = other.getDefinition().getBasicBlock() + | + other_def.dominates(result) and + input.getDefinition().getBasicBlock().strictlyDominates(other_def) + ) + } + + /** Gets an argument of the phi function defining this variable, pruned of unlikely edges. */ + SsaVariable getAPrunedPhiInput() { + result = this.getAPhiInput() and + exists(BasicBlock incoming | incoming = this.getPredecessorBlockForPhiArgument(result) | + not incoming.getLastNode().(RaisingNode).unlikelySuccessor(this.getDefinition()) + ) + } + + /** Gets a variable that ultimately defines this variable and is not itself defined by another variable */ + SsaVariable getAnUltimateDefinition() { + result = this and not exists(this.getAPhiInput()) + or + result = this.getAPhiInput().getAnUltimateDefinition() + } + + /** Gets a textual representation of this element. */ + string toString() { result = "SSA Variable " + this.getId() } + + Location getLocation() { result = this.getDefinition().getLocation() } + + /** Gets the id (name) of this variable */ + string getId() { result = this.getVariable().getId() } + + /** Gets the incoming edges for a Phi node. */ + private BasicBlock getAPredecessorBlockForPhi() { + exists(getAPhiInput()) and + result.getASuccessor() = this.getDefinition().getBasicBlock() + } + + /** Gets the incoming edges for a Phi node, pruned of unlikely edges. */ + private BasicBlock getAPrunedPredecessorBlockForPhi() { + result = this.getAPredecessorBlockForPhi() and + not result.unlikelySuccessor(this.getDefinition().getBasicBlock()) + } + + /** Whether it is possible to reach a use of this variable without passing a definition */ + predicate reachableWithoutDefinition() { + not exists(this.getDefinition()) and not py_ssa_phi(this, _) + or + exists(SsaVariable var | var = this.getAPhiInput() | var.reachableWithoutDefinition()) + or + /* + * For phi-nodes, there must be a corresponding phi-input for each control-flow + * predecessor. Otherwise, the variable will be undefined on that incoming edge. + * WARNING: the same phi-input may cover multiple predecessors, so this check + * cannot be done by counting. + */ + + exists(BasicBlock incoming | + incoming = this.getAPredecessorBlockForPhi() and + not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) + } + + /** Whether this variable may be undefined */ + predicate maybeUndefined() { + not exists(this.getDefinition()) and not py_ssa_phi(this, _) and not this.implicitlyDefined() + or + this.getDefinition().isDelete() + or + exists(SsaVariable var | var = this.getAPrunedPhiInput() | var.maybeUndefined()) + or + /* + * For phi-nodes, there must be a corresponding phi-input for each control-flow + * predecessor. Otherwise, the variable will be undefined on that incoming edge. + * WARNING: the same phi-input may cover multiple predecessors, so this check + * cannot be done by counting. + */ + + exists(BasicBlock incoming | + reaches_end(incoming) and + incoming = this.getAPrunedPredecessorBlockForPhi() and + not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) + } + + private predicate implicitlyDefined() { + not exists(this.getDefinition()) and + not py_ssa_phi(this, _) and + exists(GlobalVariable var | this.getVariable() = var | + globallyDefinedName(var.getId()) + or + var.getId() = "__path__" and var.getScope().(Module).isPackageInit() + ) + } + + /** + * Gets the global variable that is accessed if this local is undefined. + * Only applies to local variables in class scopes. + */ + GlobalVariable getFallbackGlobal() { + exists(LocalVariable local, Class cls | this.getVariable() = local | + local.getScope() = cls and + result.getScope() = cls.getScope() and + result.getId() = local.getId() and + not exists(this.getDefinition()) + ) + } + + /* + * Whether this SSA variable is the first parameter of a method + * (regardless of whether it is actually called self or not) + */ + + predicate isSelf() { + exists(Function func | + func.isMethod() and + this.getDefinition().getNode() = func.getArg(0) + ) + } } private predicate reaches_end(BasicBlock b) { - not exits_early(b) and - ( - /* Entry point */ - not exists(BasicBlock prev | prev.getASuccessor() = b) - or - exists(BasicBlock prev | prev.getASuccessor() = b | reaches_end(prev)) - ) + not exits_early(b) and + ( + /* Entry point */ + not exists(BasicBlock prev | prev.getASuccessor() = b) + or + exists(BasicBlock prev | prev.getASuccessor() = b | reaches_end(prev)) + ) } private predicate exits_early(BasicBlock b) { - exists(FunctionObject f | - f.neverReturns() and - f.getACall().getBasicBlock() = b - ) + exists(FunctionObject f | + f.neverReturns() and + f.getACall().getBasicBlock() = b + ) } private predicate gettext_installed() { - // Good enough (and fast) approximation - exists(Module m | m.getName() = "gettext") + // Good enough (and fast) approximation + exists(Module m | m.getName() = "gettext") } private predicate builtin_constant(string name) { - exists(Object::builtin(name)) - or - name = "WindowsError" - or - name = "_" and gettext_installed() + exists(Object::builtin(name)) + or + name = "WindowsError" + or + name = "_" and gettext_installed() } private predicate auto_name(string name) { - name = "__file__" or name = "__builtins__" or name = "__name__" + name = "__file__" or name = "__builtins__" or name = "__name__" } /** Whether this name is (almost) always defined, ie. it is a builtin or VM defined name */ @@ -212,11 +212,11 @@ predicate globallyDefinedName(string name) { builtin_constant(name) or auto_name /** An SSA variable that is backed by a global variable */ class GlobalSsaVariable extends EssaVariable { - GlobalSsaVariable() { this.getSourceVariable() instanceof GlobalVariable } + GlobalSsaVariable() { this.getSourceVariable() instanceof GlobalVariable } - GlobalVariable getVariable() { result = this.getSourceVariable() } + GlobalVariable getVariable() { result = this.getSourceVariable() } - string getId() { result = this.getVariable().getId() } + string getId() { result = this.getVariable().getId() } - override string toString() { result = "GSSA Variable " + this.getId() } + override string toString() { result = "GSSA Variable " + this.getId() } } diff --git a/python/ql/src/semmle/python/Scope.qll b/python/ql/src/semmle/python/Scope.qll index 1fab7f77013..60e69673bbe 100755 --- a/python/ql/src/semmle/python/Scope.qll +++ b/python/ql/src/semmle/python/Scope.qll @@ -6,148 +6,148 @@ import python * The scopes for expressions that create new scopes, lambdas and comprehensions, are handled by creating an anonymous Function. */ class Scope extends Scope_ { - Module getEnclosingModule() { result = this.getEnclosingScope().getEnclosingModule() } + Module getEnclosingModule() { result = this.getEnclosingScope().getEnclosingModule() } - /** - * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. - * The reason for this is to avoid confusion around use of `x.getScope+()` where `x` might be an - * `AstNode` or a `Variable`. Forcing the users to write `x.getScope().getEnclosingScope*()` ensures that - * the apparent semantics and the actual semantics coincide. - * [ Gets the scope enclosing this scope (modules have no enclosing scope) ] - */ - Scope getScope() { none() } + /** + * This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. + * The reason for this is to avoid confusion around use of `x.getScope+()` where `x` might be an + * `AstNode` or a `Variable`. Forcing the users to write `x.getScope().getEnclosingScope*()` ensures that + * the apparent semantics and the actual semantics coincide. + * [ Gets the scope enclosing this scope (modules have no enclosing scope) ] + */ + Scope getScope() { none() } - /** Gets the scope enclosing this scope (modules have no enclosing scope) */ - Scope getEnclosingScope() { none() } + /** Gets the scope enclosing this scope (modules have no enclosing scope) */ + Scope getEnclosingScope() { none() } - /** Gets the statements forming the body of this scope */ - StmtList getBody() { none() } + /** Gets the statements forming the body of this scope */ + StmtList getBody() { none() } - /** Gets the nth statement of this scope */ - Stmt getStmt(int n) { none() } + /** Gets the nth statement of this scope */ + Stmt getStmt(int n) { none() } - /** Gets a top-level statement in this scope */ - Stmt getAStmt() { none() } + /** Gets a top-level statement in this scope */ + Stmt getAStmt() { none() } - Location getLocation() { none() } + Location getLocation() { none() } - /** Gets the name of this scope */ - string getName() { py_strs(result, this, 0) } + /** Gets the name of this scope */ + string getName() { py_strs(result, this, 0) } - /** Gets the docstring for this scope */ - StrConst getDocString() { result = this.getStmt(0).(ExprStmt).getValue() } + /** Gets the docstring for this scope */ + StrConst getDocString() { result = this.getStmt(0).(ExprStmt).getValue() } - /** Gets the entry point into this Scope's control flow graph */ - ControlFlowNode getEntryNode() { py_scope_flow(result, this, -1) } + /** Gets the entry point into this Scope's control flow graph */ + ControlFlowNode getEntryNode() { py_scope_flow(result, this, -1) } - /** Gets the non-explicit exit from this Scope's control flow graph */ - ControlFlowNode getFallthroughNode() { py_scope_flow(result, this, 0) } + /** Gets the non-explicit exit from this Scope's control flow graph */ + ControlFlowNode getFallthroughNode() { py_scope_flow(result, this, 0) } - /** Gets the exit of this scope following from a return statement */ - ControlFlowNode getReturnNode() { py_scope_flow(result, this, 2) } + /** Gets the exit of this scope following from a return statement */ + ControlFlowNode getReturnNode() { py_scope_flow(result, this, 2) } - /** Gets an exit from this Scope's control flow graph */ - ControlFlowNode getAnExitNode() { exists(int i | py_scope_flow(result, this, i) and i >= 0) } + /** Gets an exit from this Scope's control flow graph */ + ControlFlowNode getAnExitNode() { exists(int i | py_scope_flow(result, this, i) and i >= 0) } - /** - * Gets an exit from this Scope's control flow graph, - * that does not result from an exception - */ - ControlFlowNode getANormalExit() { - result = this.getFallthroughNode() + /** + * Gets an exit from this Scope's control flow graph, + * that does not result from an exception + */ + ControlFlowNode getANormalExit() { + result = this.getFallthroughNode() + or + result = this.getReturnNode() + } + + /** Holds if this a top-level (non-nested) class or function */ + predicate isTopLevel() { this.getEnclosingModule() = this.getEnclosingScope() } + + /** Holds if this scope is deemed to be public */ + predicate isPublic() { + /* Not inside a function */ + not this.getEnclosingScope() instanceof Function and + /* Not implicitly private */ + this.getName().charAt(0) != "_" and + ( + this instanceof Module + or + exists(Module m | m = this.getEnclosingScope() and m.isPublic() | + /* If the module has an __all__, is this in it */ + not exists(m.getAnExport()) or - result = this.getReturnNode() - } + m.getAnExport() = this.getName() + ) + or + exists(Class c | c = this.getEnclosingScope() | + this instanceof Function and + c.isPublic() + ) + ) + } - /** Holds if this a top-level (non-nested) class or function */ - predicate isTopLevel() { this.getEnclosingModule() = this.getEnclosingScope() } + predicate contains(AstNode a) { + this.getBody().contains(a) + or + exists(Scope inner | inner.getEnclosingScope() = this | inner.contains(a)) + } - /** Holds if this scope is deemed to be public */ - predicate isPublic() { - /* Not inside a function */ - not this.getEnclosingScope() instanceof Function and - /* Not implicitly private */ - this.getName().charAt(0) != "_" and - ( - this instanceof Module - or - exists(Module m | m = this.getEnclosingScope() and m.isPublic() | - /* If the module has an __all__, is this in it */ - not exists(m.getAnExport()) - or - m.getAnExport() = this.getName() - ) - or - exists(Class c | c = this.getEnclosingScope() | - this instanceof Function and - c.isPublic() - ) - ) - } - - predicate contains(AstNode a) { - this.getBody().contains(a) + /** + * Holds if this scope can be expected to execute before `other`. + * Modules precede functions and methods in those modules + * `__init__` precedes other methods. `__enter__` precedes `__exit__`. + * NOTE that this is context-insensitive, so a module "precedes" a function + * in that module, even if that function is called from the module scope. + */ + predicate precedes(Scope other) { + exists(Function f, string name | f = other and name = f.getName() | + if f.isMethod() + then + // The __init__ method is preceded by the enclosing module + this = f.getEnclosingModule() and name = "__init__" or - exists(Scope inner | inner.getEnclosingScope() = this | inner.contains(a)) - } - - /** - * Holds if this scope can be expected to execute before `other`. - * Modules precede functions and methods in those modules - * `__init__` precedes other methods. `__enter__` precedes `__exit__`. - * NOTE that this is context-insensitive, so a module "precedes" a function - * in that module, even if that function is called from the module scope. - */ - predicate precedes(Scope other) { - exists(Function f, string name | f = other and name = f.getName() | - if f.isMethod() - then - // The __init__ method is preceded by the enclosing module - this = f.getEnclosingModule() and name = "__init__" - or - exists(Class c, string pred_name | - // __init__ -> __enter__ -> __exit__ - // __init__ -> other-methods - f.getScope() = c and - ( - pred_name = "__init__" and not name = "__init__" and not name = "__exit__" - or - pred_name = "__enter__" and name = "__exit__" - ) - | - this.getScope() = c and - pred_name = this.(Function).getName() - or - not exists(Function pre_func | - pre_func.getName() = pred_name and - pre_func.getScope() = c - ) and - this = other.getEnclosingModule() - ) - else - // Normal functions are preceded by the enclosing module - this = f.getEnclosingModule() + exists(Class c, string pred_name | + // __init__ -> __enter__ -> __exit__ + // __init__ -> other-methods + f.getScope() = c and + ( + pred_name = "__init__" and not name = "__init__" and not name = "__exit__" + or + pred_name = "__enter__" and name = "__exit__" + ) + | + this.getScope() = c and + pred_name = this.(Function).getName() + or + not exists(Function pre_func | + pre_func.getName() = pred_name and + pre_func.getScope() = c + ) and + this = other.getEnclosingModule() ) - } + else + // Normal functions are preceded by the enclosing module + this = f.getEnclosingModule() + ) + } - /** - * Gets the evaluation scope for code in this (lexical) scope. - * This is usually the scope itself, but may be an enclosing scope. - * Notably, for list comprehensions in Python 2. - */ - Scope getEvaluatingScope() { result = this } + /** + * Gets the evaluation scope for code in this (lexical) scope. + * This is usually the scope itself, but may be an enclosing scope. + * Notably, for list comprehensions in Python 2. + */ + Scope getEvaluatingScope() { result = this } - /** - * Holds if this scope is in the source archive, - * that is it is part of the code specified, not library code - */ - predicate inSource() { exists(this.getEnclosingModule().getFile().getRelativePath()) } + /** + * Holds if this scope is in the source archive, + * that is it is part of the code specified, not library code + */ + predicate inSource() { exists(this.getEnclosingModule().getFile().getRelativePath()) } - Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } - /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ - predicate containsInScope(AstNode inner) { - this.getBody().contains(inner) and - this = inner.getScope() - } + /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ + predicate containsInScope(AstNode inner) { + this.getBody().contains(inner) and + this = inner.getScope() + } } diff --git a/python/ql/src/semmle/python/SelfAttribute.qll b/python/ql/src/semmle/python/SelfAttribute.qll index c40b3b3c0be..6fe5942a128 100644 --- a/python/ql/src/semmle/python/SelfAttribute.qll +++ b/python/ql/src/semmle/python/SelfAttribute.qll @@ -11,80 +11,80 @@ private import semmle.python.pointsto.Filters * is `self`. */ class SelfAttribute extends Attribute { - SelfAttribute() { self_attribute(this, _) } + SelfAttribute() { self_attribute(this, _) } - Class getClass() { self_attribute(this, result) } + Class getClass() { self_attribute(this, result) } } /** Whether variable 'self' is the self variable in method 'method' */ private predicate self_variable(Function method, Variable self) { - self.isParameter() and - method.isMethod() and - method.getArg(0).asName() = self.getAnAccess() + self.isParameter() and + method.isMethod() and + method.getArg(0).asName() = self.getAnAccess() } /** Whether attribute is an access of the form `self.attr` in the body of the class 'cls' */ private predicate self_attribute(Attribute attr, Class cls) { - exists(Function f, Variable self | self_variable(f, self) | - self.getAnAccess() = attr.getObject() and - cls = f.getScope+() - ) + exists(Function f, Variable self | self_variable(f, self) | + self.getAnAccess() = attr.getObject() and + cls = f.getScope+() + ) } /** Helper class for UndefinedClassAttribute.ql & MaybeUndefinedClassAttribute.ql */ class SelfAttributeRead extends SelfAttribute { - SelfAttributeRead() { - this.getCtx() instanceof Load and - // Be stricter for loads. - // We want to generous as to what is defined (i.e. stores), - // but strict as to what needs to be defined (i.e. loads). - exists(ClassObject cls, FunctionObject func | cls.declaredAttribute(_) = func | - func.getFunction() = this.getScope() and - cls.getPyClass() = this.getClass() - ) - } + SelfAttributeRead() { + this.getCtx() instanceof Load and + // Be stricter for loads. + // We want to generous as to what is defined (i.e. stores), + // but strict as to what needs to be defined (i.e. loads). + exists(ClassObject cls, FunctionObject func | cls.declaredAttribute(_) = func | + func.getFunction() = this.getScope() and + cls.getPyClass() = this.getClass() + ) + } - predicate guardedByHasattr() { - exists(Variable var, ControlFlowNode n | - var.getAUse() = this.getObject().getAFlowNode() and - hasattr(n, var.getAUse(), this.getName()) and - n.strictlyDominates(this.getAFlowNode()) - ) - } + predicate guardedByHasattr() { + exists(Variable var, ControlFlowNode n | + var.getAUse() = this.getObject().getAFlowNode() and + hasattr(n, var.getAUse(), this.getName()) and + n.strictlyDominates(this.getAFlowNode()) + ) + } - pragma[noinline] - predicate locallyDefined() { - exists(SelfAttributeStore store | - this.getName() = store.getName() and - this.getScope() = store.getScope() - | - store.getAFlowNode().strictlyDominates(this.getAFlowNode()) - ) - } + pragma[noinline] + predicate locallyDefined() { + exists(SelfAttributeStore store | + this.getName() = store.getName() and + this.getScope() = store.getScope() + | + store.getAFlowNode().strictlyDominates(this.getAFlowNode()) + ) + } } class SelfAttributeStore extends SelfAttribute { - SelfAttributeStore() { this.getCtx() instanceof Store } + SelfAttributeStore() { this.getCtx() instanceof Store } - Expr getAssignedValue() { exists(Assign a | a.getATarget() = this | result = a.getValue()) } + Expr getAssignedValue() { exists(Assign a | a.getATarget() = this | result = a.getValue()) } } private predicate attr_assigned_in_method_arg_n(FunctionObject method, string name, int n) { - exists(SsaVariable param | - method.getFunction().getArg(n).asName() = param.getDefinition().getNode() - | - exists(AttrNode attr | - attr.getObject(name) = param.getAUse() and - attr.isStore() - ) - or - exists(CallNode call, FunctionObject callee, int m | - callee.getArgumentForCall(call, m) = param.getAUse() and - attr_assigned_in_method_arg_n(callee, name, m) - ) + exists(SsaVariable param | + method.getFunction().getArg(n).asName() = param.getDefinition().getNode() + | + exists(AttrNode attr | + attr.getObject(name) = param.getAUse() and + attr.isStore() ) + or + exists(CallNode call, FunctionObject callee, int m | + callee.getArgumentForCall(call, m) = param.getAUse() and + attr_assigned_in_method_arg_n(callee, name, m) + ) + ) } predicate attribute_assigned_in_method(FunctionObject method, string name) { - attr_assigned_in_method_arg_n(method, name, 0) + attr_assigned_in_method_arg_n(method, name, 0) } diff --git a/python/ql/src/semmle/python/Stmts.qll b/python/ql/src/semmle/python/Stmts.qll index 660ecc5982e..0aa34c2a3fe 100644 --- a/python/ql/src/semmle/python/Stmts.qll +++ b/python/ql/src/semmle/python/Stmts.qll @@ -2,436 +2,436 @@ import python /** A statement */ class Stmt extends Stmt_, AstNode { - /** Gets the scope immediately enclosing this statement */ - override Scope getScope() { py_scopes(this, result) } + /** Gets the scope immediately enclosing this statement */ + override Scope getScope() { py_scopes(this, result) } - override string toString() { result = "Stmt" } + override string toString() { result = "Stmt" } - /** Gets the module enclosing this statement */ - Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } + /** Gets the module enclosing this statement */ + Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } - override Location getLocation() { result = Stmt_.super.getLocation() } + override Location getLocation() { result = Stmt_.super.getLocation() } - /** Gets an immediate (non-nested) sub-expression of this statement */ - Expr getASubExpression() { none() } + /** Gets an immediate (non-nested) sub-expression of this statement */ + Expr getASubExpression() { none() } - /** Gets an immediate (non-nested) sub-statement of this statement */ - Stmt getASubStatement() { none() } + /** Gets an immediate (non-nested) sub-statement of this statement */ + Stmt getASubStatement() { none() } - override AstNode getAChildNode() { - result = this.getASubExpression() - or - result = this.getASubStatement() - } + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getASubStatement() + } - private ControlFlowNode possibleEntryNode() { - result.getNode() = this or - this.containsInScope(result.getNode()) - } + private ControlFlowNode possibleEntryNode() { + result.getNode() = this or + this.containsInScope(result.getNode()) + } - /** - * Gets a control flow node for an entry into this statement. - */ - ControlFlowNode getAnEntryNode() { - result = this.possibleEntryNode() and - exists(ControlFlowNode pred | - pred.getASuccessor() = result and - not pred = this.possibleEntryNode() - ) - } + /** + * Gets a control flow node for an entry into this statement. + */ + ControlFlowNode getAnEntryNode() { + result = this.possibleEntryNode() and + exists(ControlFlowNode pred | + pred.getASuccessor() = result and + not pred = this.possibleEntryNode() + ) + } - /** Holds if this statement cannot be reached */ - predicate isUnreachable() { - not exists(this.getAnEntryNode()) - or - exists(If ifstmt | - ifstmt.getTest().(ImmutableLiteral).booleanValue() = false and ifstmt.getBody().contains(this) - or - ifstmt.getTest().(ImmutableLiteral).booleanValue() = true and - ifstmt.getOrelse().contains(this) - ) - or - exists(While whilestmt | - whilestmt.getTest().(ImmutableLiteral).booleanValue() = false and - whilestmt.getBody().contains(this) - ) - } + /** Holds if this statement cannot be reached */ + predicate isUnreachable() { + not exists(this.getAnEntryNode()) + or + exists(If ifstmt | + ifstmt.getTest().(ImmutableLiteral).booleanValue() = false and ifstmt.getBody().contains(this) + or + ifstmt.getTest().(ImmutableLiteral).booleanValue() = true and + ifstmt.getOrelse().contains(this) + ) + or + exists(While whilestmt | + whilestmt.getTest().(ImmutableLiteral).booleanValue() = false and + whilestmt.getBody().contains(this) + ) + } - /** - * Gets the final statement in this statement, ordered by location. - * Will be this statement if not a compound statement. - */ - Stmt getLastStatement() { result = this } + /** + * Gets the final statement in this statement, ordered by location. + * Will be this statement if not a compound statement. + */ + Stmt getLastStatement() { result = this } } /** A statement that includes a binding (except imports) */ class Assign extends Assign_ { - /** Use ControlFlowNodes and SsaVariables for data-flow analysis. */ - predicate defines(Variable v) { this.getATarget().defines(v) } + /** Use ControlFlowNodes and SsaVariables for data-flow analysis. */ + predicate defines(Variable v) { this.getATarget().defines(v) } - override Expr getASubExpression() { - result = this.getATarget() or - result = this.getValue() - } + override Expr getASubExpression() { + result = this.getATarget() or + result = this.getValue() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An assignment statement */ class AssignStmt extends Assign { - /* syntax: Expr, ... = Expr */ - AssignStmt() { not this instanceof FunctionDef and not this instanceof ClassDef } + /* syntax: Expr, ... = Expr */ + AssignStmt() { not this instanceof FunctionDef and not this instanceof ClassDef } - override string toString() { result = "AssignStmt" } + override string toString() { result = "AssignStmt" } } /** An augmented assignment statement, such as `x += y` */ class AugAssign extends AugAssign_ { - /* syntax: Expr += Expr */ - override Expr getASubExpression() { result = this.getOperation() } + /* syntax: Expr += Expr */ + override Expr getASubExpression() { result = this.getOperation() } - /** - * Gets the target of this augmented assignment statement. - * That is, the `a` in `a += b`. - */ - Expr getTarget() { result = this.getOperation().(BinaryExpr).getLeft() } + /** + * Gets the target of this augmented assignment statement. + * That is, the `a` in `a += b`. + */ + Expr getTarget() { result = this.getOperation().(BinaryExpr).getLeft() } - /** - * Gets the value of this augmented assignment statement. - * That is, the `b` in `a += b`. - */ - Expr getValue() { result = this.getOperation().(BinaryExpr).getRight() } + /** + * Gets the value of this augmented assignment statement. + * That is, the `b` in `a += b`. + */ + Expr getValue() { result = this.getOperation().(BinaryExpr).getRight() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An annotated assignment statement, such as `x: int = 0` */ class AnnAssign extends AnnAssign_ { - /* syntax: Expr: Expr = Expr */ - override Expr getASubExpression() { - result = this.getAnnotation() or - result = this.getTarget() or - result = this.getValue() - } + /* syntax: Expr: Expr = Expr */ + override Expr getASubExpression() { + result = this.getAnnotation() or + result = this.getTarget() or + result = this.getValue() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } - /** Holds if the value of the annotation of this assignment is stored at runtime. */ - predicate isStored() { - not this.getScope() instanceof Function and - exists(Name n | - n = this.getTarget() and - not n.isParenthesized() - ) - } + /** Holds if the value of the annotation of this assignment is stored at runtime. */ + predicate isStored() { + not this.getScope() instanceof Function and + exists(Name n | + n = this.getTarget() and + not n.isParenthesized() + ) + } } /** An exec statement */ class Exec extends Exec_ { - /* syntax: exec Expr */ - override Expr getASubExpression() { - result = this.getBody() or - result = this.getGlobals() or - result = this.getLocals() - } + /* syntax: exec Expr */ + override Expr getASubExpression() { + result = this.getBody() or + result = this.getGlobals() or + result = this.getLocals() + } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An except statement (part of a `try` statement), such as `except IOError as err:` */ class ExceptStmt extends ExceptStmt_ { - /* syntax: except Expr [ as Expr ]: */ - /** Gets the immediately enclosing try statement */ - Try getTry() { result.getAHandler() = this } + /* syntax: except Expr [ as Expr ]: */ + /** Gets the immediately enclosing try statement */ + Try getTry() { result.getAHandler() = this } - override Expr getASubExpression() { - result = this.getName() - or - result = this.getType() - } + override Expr getASubExpression() { + result = this.getName() + or + result = this.getType() + } - override Stmt getASubStatement() { result = this.getAStmt() } + override Stmt getASubStatement() { result = this.getAStmt() } - override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } } /** An assert statement, such as `assert a == b, "A is not equal to b"` */ class Assert extends Assert_ { - /* syntax: assert Expr [, Expr] */ - override Expr getASubExpression() { result = this.getMsg() or result = this.getTest() } + /* syntax: assert Expr [, Expr] */ + override Expr getASubExpression() { result = this.getMsg() or result = this.getTest() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A break statement */ class Break extends Break_ { - /* syntax: assert Expr [, Expr] */ - override Expr getASubExpression() { none() } + /* syntax: assert Expr [, Expr] */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A continue statement */ class Continue extends Continue_ { - /* syntax: continue */ - override Expr getASubExpression() { none() } + /* syntax: continue */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A delete statement, such as `del x[-1]` */ class Delete extends Delete_ { - /* syntax: del Expr, ... */ - override Expr getASubExpression() { result = this.getATarget() } + /* syntax: del Expr, ... */ + override Expr getASubExpression() { result = this.getATarget() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An expression statement, such as `len(x)` or `yield y` */ class ExprStmt extends ExprStmt_ { - /* syntax: Expr */ - override Expr getASubExpression() { result = this.getValue() } + /* syntax: Expr */ + override Expr getASubExpression() { result = this.getValue() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** A for statement, such as `for x in y: print(x)` */ class For extends For_ { - /* syntax: for varname in Expr: ... */ - override Stmt getASubStatement() { - result = this.getAStmt() or - result = this.getAnOrelse() - } + /* syntax: for varname in Expr: ... */ + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } - override Expr getASubExpression() { - result = this.getTarget() or - result = this.getIter() - } + override Expr getASubExpression() { + result = this.getTarget() or + result = this.getIter() + } - override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } } /** A global statement, such as `global var` */ class Global extends Global_ { - /* syntax: global varname */ - override Expr getASubExpression() { none() } + /* syntax: global varname */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An if statement, such as `if eggs: print("spam")` */ class If extends If_ { - /* syntax: if Expr: ... */ - override Stmt getASubStatement() { - result = this.getAStmt() or - result = this.getAnOrelse() - } + /* syntax: if Expr: ... */ + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } - override Expr getASubExpression() { result = this.getTest() } + override Expr getASubExpression() { result = this.getTest() } - /** Whether this if statement takes the form `if __name__ == "__main__":` */ - predicate isNameEqMain() { - exists(StrConst m, Name n, Compare c | - this.getTest() = c and - c.getOp(0) instanceof Eq and - ( - c.getLeft() = n and c.getComparator(0) = m - or - c.getLeft() = m and c.getComparator(0) = n - ) and - n.getId() = "__name__" and - m.getText() = "__main__" - ) - } - - /** Whether this if statement starts with the keyword `elif` */ - predicate isElif() { - /* - * The Python parser turns all elif chains into nested if-else statements. - * An `elif` can be identified as it is the first statement in an `else` block - * and it is not indented relative to its parent `if`. - */ - - exists(If i | - i.getOrelse(0) = this and - this.getLocation().getStartColumn() = i.getLocation().getStartColumn() - ) - } - - /** Gets the `elif` branch of this `if`-statement, if present */ - If getElif() { - result = this.getOrelse(0) and - result.isElif() - } - - override Stmt getLastStatement() { - result = this.getOrelse().getLastItem().getLastStatement() + /** Whether this if statement takes the form `if __name__ == "__main__":` */ + predicate isNameEqMain() { + exists(StrConst m, Name n, Compare c | + this.getTest() = c and + c.getOp(0) instanceof Eq and + ( + c.getLeft() = n and c.getComparator(0) = m or - not exists(this.getOrelse()) and - result = this.getBody().getLastItem().getLastStatement() - } + c.getLeft() = m and c.getComparator(0) = n + ) and + n.getId() = "__name__" and + m.getText() = "__main__" + ) + } + + /** Whether this if statement starts with the keyword `elif` */ + predicate isElif() { + /* + * The Python parser turns all elif chains into nested if-else statements. + * An `elif` can be identified as it is the first statement in an `else` block + * and it is not indented relative to its parent `if`. + */ + + exists(If i | + i.getOrelse(0) = this and + this.getLocation().getStartColumn() = i.getLocation().getStartColumn() + ) + } + + /** Gets the `elif` branch of this `if`-statement, if present */ + If getElif() { + result = this.getOrelse(0) and + result.isElif() + } + + override Stmt getLastStatement() { + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getOrelse()) and + result = this.getBody().getLastItem().getLastStatement() + } } /** A nonlocal statement, such as `nonlocal var` */ class Nonlocal extends Nonlocal_ { - /* syntax: nonlocal varname */ - override Stmt getASubStatement() { none() } + /* syntax: nonlocal varname */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } - Variable getAVariable() { - result.getScope() = this.getScope() and - result.getId() = this.getAName() - } + Variable getAVariable() { + result.getScope() = this.getScope() and + result.getId() = this.getAName() + } } /** A pass statement */ class Pass extends Pass_ { - /* syntax: pass */ - override Stmt getASubStatement() { none() } + /* syntax: pass */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { none() } + override Expr getASubExpression() { none() } } /** A print statement (Python 2 only), such as `print 0` */ class Print extends Print_ { - /* syntax: print Expr, ... */ - override Stmt getASubStatement() { none() } + /* syntax: print Expr, ... */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { - result = this.getAValue() or - result = this.getDest() - } + override Expr getASubExpression() { + result = this.getAValue() or + result = this.getDest() + } } /** A raise statement, such as `raise CompletelyDifferentException()` */ class Raise extends Raise_ { - /* syntax: raise Expr */ - override Stmt getASubStatement() { none() } + /* syntax: raise Expr */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { py_exprs(result, _, this, _) } + override Expr getASubExpression() { py_exprs(result, _, this, _) } - /** - * The expression immediately following the `raise`, this is the - * exception raised, but not accounting for tuples in Python 2. - */ - Expr getException() { - result = this.getType() - or - result = this.getExc() - } + /** + * The expression immediately following the `raise`, this is the + * exception raised, but not accounting for tuples in Python 2. + */ + Expr getException() { + result = this.getType() + or + result = this.getExc() + } - /** The exception raised, accounting for tuples in Python 2. */ - Expr getRaised() { - exists(Expr raw | raw = this.getException() | - if not major_version() = 2 or not exists(raw.(Tuple).getAnElt()) - then result = raw - else - /* In Python 2 raising a tuple will result in the first element of the tuple being raised. */ - result = raw.(Tuple).getElt(0) - ) - } + /** The exception raised, accounting for tuples in Python 2. */ + Expr getRaised() { + exists(Expr raw | raw = this.getException() | + if not major_version() = 2 or not exists(raw.(Tuple).getAnElt()) + then result = raw + else + /* In Python 2 raising a tuple will result in the first element of the tuple being raised. */ + result = raw.(Tuple).getElt(0) + ) + } } /** A return statement, such as return None */ class Return extends Return_ { - /* syntax: return Expr */ - override Stmt getASubStatement() { none() } + /* syntax: return Expr */ + override Stmt getASubStatement() { none() } - override Expr getASubExpression() { result = this.getValue() } + override Expr getASubExpression() { result = this.getValue() } } /** A try statement */ class Try extends Try_ { - /* syntax: try: ... */ - override Expr getASubExpression() { none() } + /* syntax: try: ... */ + override Expr getASubExpression() { none() } - override Stmt getASubStatement() { - result = this.getAHandler() or - result = this.getAStmt() or - result = this.getAFinalstmt() or - result = this.getAnOrelse() - } + override Stmt getASubStatement() { + result = this.getAHandler() or + result = this.getAStmt() or + result = this.getAFinalstmt() or + result = this.getAnOrelse() + } - override ExceptStmt getHandler(int i) { result = Try_.super.getHandler(i) } + override ExceptStmt getHandler(int i) { result = Try_.super.getHandler(i) } - /** Gets an exception handler of this try statement. */ - override ExceptStmt getAHandler() { result = Try_.super.getAHandler() } + /** Gets an exception handler of this try statement. */ + override ExceptStmt getAHandler() { result = Try_.super.getAHandler() } - override Stmt getLastStatement() { - result = this.getFinalbody().getLastItem().getLastStatement() - or - not exists(this.getFinalbody()) and - result = this.getOrelse().getLastItem().getLastStatement() - or - not exists(this.getFinalbody()) and - not exists(this.getOrelse()) and - result = this.getHandlers().getLastItem().getLastStatement() - or - not exists(this.getFinalbody()) and - not exists(this.getOrelse()) and - not exists(this.getHandlers()) and - result = this.getBody().getLastItem().getLastStatement() - } + override Stmt getLastStatement() { + result = this.getFinalbody().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and + not exists(this.getOrelse()) and + result = this.getHandlers().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and + not exists(this.getOrelse()) and + not exists(this.getHandlers()) and + result = this.getBody().getLastItem().getLastStatement() + } } /** A while statement, such as `while parrot_resting():` */ class While extends While_ { - /* syntax: while Expr: ... */ - override Expr getASubExpression() { result = this.getTest() } + /* syntax: while Expr: ... */ + override Expr getASubExpression() { result = this.getTest() } - override Stmt getASubStatement() { - result = this.getAStmt() or - result = this.getAnOrelse() - } + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } - override Stmt getLastStatement() { - result = this.getOrelse().getLastItem().getLastStatement() - or - not exists(this.getOrelse()) and - result = this.getBody().getLastItem().getLastStatement() - } + override Stmt getLastStatement() { + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getOrelse()) and + result = this.getBody().getLastItem().getLastStatement() + } } /** A with statement such as `with f as open("file"): text = f.read()` */ class With extends With_ { - /* syntax: with Expr as varname: ... */ - override Expr getASubExpression() { - result = this.getContextExpr() or - result = this.getOptionalVars() - } + /* syntax: with Expr as varname: ... */ + override Expr getASubExpression() { + result = this.getContextExpr() or + result = this.getOptionalVars() + } - override Stmt getASubStatement() { result = this.getAStmt() } + override Stmt getASubStatement() { result = this.getAStmt() } - override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } + override Stmt getLastStatement() { result = this.getBody().getLastItem().getLastStatement() } } /** A plain text used in a template is wrapped in a TemplateWrite statement */ class TemplateWrite extends TemplateWrite_ { - override Expr getASubExpression() { result = this.getValue() } + override Expr getASubExpression() { result = this.getValue() } - override Stmt getASubStatement() { none() } + override Stmt getASubStatement() { none() } } /** An asynchronous `for` statement, such as `async for varname in Expr: ...` */ class AsyncFor extends For { - /* syntax: async for varname in Expr: ... */ - AsyncFor() { this.isAsync() } + /* syntax: async for varname in Expr: ... */ + AsyncFor() { this.isAsync() } } /** An asynchronous `with` statement, such as `async with varname as Expr: ...` */ class AsyncWith extends With { - /* syntax: async with Expr as varname: ... */ - AsyncWith() { this.isAsync() } + /* syntax: async with Expr as varname: ... */ + AsyncWith() { this.isAsync() } } /** A list of statements */ class StmtList extends StmtList_ { - /** Holds if this list of statements contains the AST node `a` */ - predicate contains(AstNode a) { - exists(Stmt item | item = this.getAnItem() | item = a or item.contains(a)) - } + /** Holds if this list of statements contains the AST node `a` */ + predicate contains(AstNode a) { + exists(Stmt item | item = this.getAnItem() | item = a or item.contains(a)) + } - /** Gets the last item in this list of statements, if any. */ - Stmt getLastItem() { result = this.getItem(max(int i | exists(this.getItem(i)))) } + /** Gets the last item in this list of statements, if any. */ + Stmt getLastItem() { result = this.getItem(max(int i | exists(this.getItem(i)))) } } diff --git a/python/ql/src/semmle/python/TestUtils.qll b/python/ql/src/semmle/python/TestUtils.qll index a51d68dcf73..d7b8fc24676 100644 --- a/python/ql/src/semmle/python/TestUtils.qll +++ b/python/ql/src/semmle/python/TestUtils.qll @@ -4,13 +4,13 @@ import python /** Removes everything up to the occurrence of `sub` in the string `str` */ bindingset[str, sub] string remove_prefix_before_substring(string str, string sub) { - exists(int index | - index = str.indexOf(sub) and - result = str.suffix(index) - ) - or - not exists(str.indexOf(sub)) and - result = str + exists(int index | + index = str.indexOf(sub) and + result = str.suffix(index) + ) + or + not exists(str.indexOf(sub)) and + result = str } /** @@ -18,12 +18,12 @@ string remove_prefix_before_substring(string str, string sub) { * from machine to machine. */ string remove_library_prefix(Location loc) { - result = remove_prefix_before_substring(loc.toString(), "resources/lib") + result = remove_prefix_before_substring(loc.toString(), "resources/lib") } /** Returns the location of an AST node in compact form: `basename:line:column` */ string compact_location(AstNode a) { - exists(Location l | l = a.getLocation() | - result = l.getFile().getBaseName() + ":" + l.getStartLine() + ":" + l.getStartColumn() - ) + exists(Location l | l = a.getLocation() | + result = l.getFile().getBaseName() + ":" + l.getStartLine() + ":" + l.getStartColumn() + ) } diff --git a/python/ql/src/semmle/python/Variables.qll b/python/ql/src/semmle/python/Variables.qll index 896057fa2df..9740d658572 100644 --- a/python/ql/src/semmle/python/Variables.qll +++ b/python/ql/src/semmle/python/Variables.qll @@ -2,71 +2,71 @@ import python /** A variable, either a global or local variable (including parameters) */ class Variable extends @py_variable { - Variable() { - exists(string name | - variable(this, _, name) and - not name = "*" and - not name = "$" - ) - } + Variable() { + exists(string name | + variable(this, _, name) and + not name = "*" and + not name = "$" + ) + } - /** Gets the identifier (name) of this variable */ - string getId() { variable(this, _, result) } + /** Gets the identifier (name) of this variable */ + string getId() { variable(this, _, result) } - /** Gets a textual representation of this element. */ - string toString() { result = "Variable " + this.getId() } + /** Gets a textual representation of this element. */ + string toString() { result = "Variable " + this.getId() } - /** Gets an access (load or store) of this variable */ - Name getAnAccess() { - result = this.getALoad() - or - result = this.getAStore() - } + /** Gets an access (load or store) of this variable */ + Name getAnAccess() { + result = this.getALoad() + or + result = this.getAStore() + } - /** Gets a load of this variable */ - Name getALoad() { result.uses(this) } + /** Gets a load of this variable */ + Name getALoad() { result.uses(this) } - /** Gets a store of this variable */ - Name getAStore() { result.defines(this) } + /** Gets a store of this variable */ + Name getAStore() { result.defines(this) } - /** Gets a use of this variable */ - NameNode getAUse() { result.uses(this) } + /** Gets a use of this variable */ + NameNode getAUse() { result.uses(this) } - /** Gets the scope of this variable */ - Scope getScope() { variable(this, result, _) } + /** Gets the scope of this variable */ + Scope getScope() { variable(this, result, _) } - /** - * Whether there is an access to this variable outside - * of its own scope. Usually occurs in nested functions - * or for global variables. - */ - predicate escapes() { exists(Name n | n = this.getAnAccess() | n.getScope() != this.getScope()) } + /** + * Whether there is an access to this variable outside + * of its own scope. Usually occurs in nested functions + * or for global variables. + */ + predicate escapes() { exists(Name n | n = this.getAnAccess() | n.getScope() != this.getScope()) } - /** Whether this variable is a parameter */ - predicate isParameter() { none() } + /** Whether this variable is a parameter */ + predicate isParameter() { none() } - predicate isSelf() { none() } + predicate isSelf() { none() } } /** A local (function or class) variable */ class LocalVariable extends Variable { - LocalVariable() { - exists(Scope s | s = this.getScope() | s instanceof Function or s instanceof Class) - } + LocalVariable() { + exists(Scope s | s = this.getScope() | s instanceof Function or s instanceof Class) + } - override string toString() { result = "Local Variable " + this.getId() } + override string toString() { result = "Local Variable " + this.getId() } - /** Whether this variable is a parameter */ - override predicate isParameter() { exists(Parameter p | this.getAnAccess() = p) } + /** Whether this variable is a parameter */ + override predicate isParameter() { exists(Parameter p | this.getAnAccess() = p) } - /** Holds if this variable is the first parameter of a method. It is not necessarily called "self" */ - override predicate isSelf() { - exists(Function f, Parameter self | - this.getAnAccess() = self and - f.isMethod() and - f.getArg(0) = self - ) - } + /** Holds if this variable is the first parameter of a method. It is not necessarily called "self" */ + override predicate isSelf() { + exists(Function f, Parameter self | + this.getAnAccess() = self and + f.isMethod() and + f.getArg(0) = self + ) + } } /** @@ -74,7 +74,7 @@ class LocalVariable extends Variable { * If the variable is undefined, then raise an exception. */ class FastLocalVariable extends LocalVariable { - FastLocalVariable() { this.getScope() instanceof FastLocalsFunction } + FastLocalVariable() { this.getScope() instanceof FastLocalsFunction } } /** @@ -82,12 +82,12 @@ class FastLocalVariable extends LocalVariable { * If the variable is undefined, then lookup the value in globals(). */ class NameLocalVariable extends LocalVariable { - NameLocalVariable() { not this instanceof FastLocalVariable } + NameLocalVariable() { not this instanceof FastLocalVariable } } /** A global (module-level) variable */ class GlobalVariable extends Variable { - GlobalVariable() { exists(Module m | m = this.getScope()) } + GlobalVariable() { exists(Module m | m = this.getScope()) } - override string toString() { result = "Global Variable " + this.getId() } + override string toString() { result = "Global Variable " + this.getId() } } diff --git a/python/ql/src/semmle/python/dataflow/Configuration.qll b/python/ql/src/semmle/python/dataflow/Configuration.qll index 91a9971c97d..04d89265f2a 100644 --- a/python/ql/src/semmle/python/dataflow/Configuration.qll +++ b/python/ql/src/semmle/python/dataflow/Configuration.qll @@ -4,138 +4,138 @@ private import semmle.python.objects.ObjectInternal private import semmle.python.dataflow.Implementation module TaintTracking { - class Source = TaintSource; + class Source = TaintSource; - class Sink = TaintSink; + class Sink = TaintSink; - class Extension = DataFlowExtension::DataFlowNode; + class Extension = DataFlowExtension::DataFlowNode; - class PathSource = TaintTrackingNode; + class PathSource = TaintTrackingNode; - class PathSink = TaintTrackingNode; + class PathSink = TaintTrackingNode; - abstract class Configuration extends string { - /* Required to prevent compiler warning */ - bindingset[this] - Configuration() { this = this } + abstract class Configuration extends string { + /* Required to prevent compiler warning */ + bindingset[this] + Configuration() { this = this } - /* Old implementation API */ - predicate isSource(Source src) { none() } + /* Old implementation API */ + predicate isSource(Source src) { none() } - predicate isSink(Sink sink) { none() } + predicate isSink(Sink sink) { none() } - predicate isSanitizer(Sanitizer sanitizer) { none() } + predicate isSanitizer(Sanitizer sanitizer) { none() } - predicate isExtension(Extension extension) { none() } + predicate isExtension(Extension extension) { none() } - /* New implementation API */ - /** - * Holds if `src` is a source of taint of `kind` that is relevant - * for this configuration. - */ - predicate isSource(DataFlow::Node src, TaintKind kind) { - exists(TaintSource taintSrc | - this.isSource(taintSrc) and - src.asCfgNode() = taintSrc and - taintSrc.isSourceOf(kind) - ) - } - - /** - * Holds if `sink` is a sink of taint of `kind` that is relevant - * for this configuration. - */ - predicate isSink(DataFlow::Node sink, TaintKind kind) { - exists(TaintSink taintSink | - this.isSink(taintSink) and - sink.asCfgNode() = taintSink and - taintSink.sinks(kind) - ) - } - - /** - * Holds if `src -> dest` should be considered as a flow edge - * in addition to standard data flow edges. - */ - predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { none() } - - /** - * Holds if `src -> dest` is a flow edge converting taint from `srckind` to `destkind`. - */ - predicate isAdditionalFlowStep( - DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind - ) { - none() - } - - /** - * Holds if `node` should be considered as a barrier to flow of any kind. - */ - predicate isBarrier(DataFlow::Node node) { none() } - - /** - * Holds if `node` should be considered as a barrier to flow of `kind`. - */ - predicate isBarrier(DataFlow::Node node, TaintKind kind) { - exists(Sanitizer sanitizer | this.isSanitizer(sanitizer) | - sanitizer.sanitizingNode(kind, node.asCfgNode()) - or - sanitizer.sanitizingEdge(kind, node.asVariable()) - or - sanitizer.sanitizingSingleEdge(kind, node.asVariable()) - or - sanitizer.sanitizingDefinition(kind, node.asVariable()) - or - exists(MethodCallsiteRefinement call, FunctionObject callee | - call = node.asVariable().getDefinition() and - callee.getACall() = call.getCall() and - sanitizer.sanitizingCall(kind, callee) - ) - ) - } - - /** - * Holds if flow from `src` to `dest` is prohibited. - */ - predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { none() } - - /** - * Holds if control flow from `test` along the `isTrue` edge is prohibited. - */ - predicate isBarrierTest(ControlFlowNode test, boolean isTrue) { none() } - - /** - * Holds if flow from `src` to `dest` is prohibited when the incoming taint is `srckind` and the outgoing taint is `destkind`. - * Note that `srckind` and `destkind` can be the same. - */ - predicate isBarrierEdge( - DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind - ) { - none() - } - - /* Common query API */ - predicate hasFlowPath(PathSource src, PathSink sink) { - this.(TaintTrackingImplementation).hasFlowPath(src, sink) - } - - /* Old query API */ - /* deprecated */ - deprecated predicate hasFlow(Source src, Sink sink) { - exists(PathSource psrc, PathSink psink | - this.hasFlowPath(psrc, psink) and - src = psrc.getNode().asCfgNode() and - sink = psink.getNode().asCfgNode() - ) - } - - /* New query API */ - predicate hasSimpleFlow(DataFlow::Node src, DataFlow::Node sink) { - exists(PathSource psrc, PathSink psink | - this.hasFlowPath(psrc, psink) and - src = psrc.getNode() and - sink = psink.getNode() - ) - } + /* New implementation API */ + /** + * Holds if `src` is a source of taint of `kind` that is relevant + * for this configuration. + */ + predicate isSource(DataFlow::Node src, TaintKind kind) { + exists(TaintSource taintSrc | + this.isSource(taintSrc) and + src.asCfgNode() = taintSrc and + taintSrc.isSourceOf(kind) + ) } + + /** + * Holds if `sink` is a sink of taint of `kind` that is relevant + * for this configuration. + */ + predicate isSink(DataFlow::Node sink, TaintKind kind) { + exists(TaintSink taintSink | + this.isSink(taintSink) and + sink.asCfgNode() = taintSink and + taintSink.sinks(kind) + ) + } + + /** + * Holds if `src -> dest` should be considered as a flow edge + * in addition to standard data flow edges. + */ + predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { none() } + + /** + * Holds if `src -> dest` is a flow edge converting taint from `srckind` to `destkind`. + */ + predicate isAdditionalFlowStep( + DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind + ) { + none() + } + + /** + * Holds if `node` should be considered as a barrier to flow of any kind. + */ + predicate isBarrier(DataFlow::Node node) { none() } + + /** + * Holds if `node` should be considered as a barrier to flow of `kind`. + */ + predicate isBarrier(DataFlow::Node node, TaintKind kind) { + exists(Sanitizer sanitizer | this.isSanitizer(sanitizer) | + sanitizer.sanitizingNode(kind, node.asCfgNode()) + or + sanitizer.sanitizingEdge(kind, node.asVariable()) + or + sanitizer.sanitizingSingleEdge(kind, node.asVariable()) + or + sanitizer.sanitizingDefinition(kind, node.asVariable()) + or + exists(MethodCallsiteRefinement call, FunctionObject callee | + call = node.asVariable().getDefinition() and + callee.getACall() = call.getCall() and + sanitizer.sanitizingCall(kind, callee) + ) + ) + } + + /** + * Holds if flow from `src` to `dest` is prohibited. + */ + predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { none() } + + /** + * Holds if control flow from `test` along the `isTrue` edge is prohibited. + */ + predicate isBarrierTest(ControlFlowNode test, boolean isTrue) { none() } + + /** + * Holds if flow from `src` to `dest` is prohibited when the incoming taint is `srckind` and the outgoing taint is `destkind`. + * Note that `srckind` and `destkind` can be the same. + */ + predicate isBarrierEdge( + DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind + ) { + none() + } + + /* Common query API */ + predicate hasFlowPath(PathSource src, PathSink sink) { + this.(TaintTrackingImplementation).hasFlowPath(src, sink) + } + + /* Old query API */ + /* deprecated */ + deprecated predicate hasFlow(Source src, Sink sink) { + exists(PathSource psrc, PathSink psink | + this.hasFlowPath(psrc, psink) and + src = psrc.getNode().asCfgNode() and + sink = psink.getNode().asCfgNode() + ) + } + + /* New query API */ + predicate hasSimpleFlow(DataFlow::Node src, DataFlow::Node sink) { + exists(PathSource psrc, PathSink psink | + this.hasFlowPath(psrc, psink) and + src = psrc.getNode() and + sink = psink.getNode() + ) + } + } } diff --git a/python/ql/src/semmle/python/dataflow/Files.qll b/python/ql/src/semmle/python/dataflow/Files.qll index a0cd1753f9d..3439e559efa 100644 --- a/python/ql/src/semmle/python/dataflow/Files.qll +++ b/python/ql/src/semmle/python/dataflow/Files.qll @@ -2,18 +2,18 @@ import python import semmle.python.dataflow.TaintTracking class OpenFile extends TaintKind { - OpenFile() { this = "file.open" } + OpenFile() { this = "file.open" } - override string repr() { result = "an open file" } + override string repr() { result = "an open file" } } class OpenFileConfiguration extends TaintTracking::Configuration { - OpenFileConfiguration() { this = "Open file configuration" } + OpenFileConfiguration() { this = "Open file configuration" } - override predicate isSource(DataFlow::Node src, TaintKind kind) { - src.asCfgNode() = Value::named("open").getACall() and - kind instanceof OpenFile - } + override predicate isSource(DataFlow::Node src, TaintKind kind) { + src.asCfgNode() = Value::named("open").getACall() and + kind instanceof OpenFile + } - override predicate isSink(DataFlow::Node sink, TaintKind kind) { none() } + override predicate isSink(DataFlow::Node sink, TaintKind kind) { none() } } diff --git a/python/ql/src/semmle/python/dataflow/Implementation.qll b/python/ql/src/semmle/python/dataflow/Implementation.qll index c62641e497e..5a2a0e7eeee 100644 --- a/python/ql/src/semmle/python/dataflow/Implementation.qll +++ b/python/ql/src/semmle/python/dataflow/Implementation.qll @@ -11,10 +11,10 @@ import semmle.python.dataflow.Legacy */ newtype TTaintTrackingContext = - TNoParam() or - TParamContext(TaintKind param, AttributePath path, int n) { - any(TaintTrackingImplementation impl).callWithTaintedArgument(_, _, _, _, n, path, param) - } + TNoParam() or + TParamContext(TaintKind param, AttributePath path, int n) { + any(TaintTrackingImplementation impl).callWithTaintedArgument(_, _, _, _, n, path, param) + } /** * The context for taint-tracking. @@ -24,42 +24,42 @@ newtype TTaintTrackingContext = * Used to track taint through calls accurately and reasonably efficiently. */ class TaintTrackingContext extends TTaintTrackingContext { - /** Gets a textual representation of this element. */ - string toString() { - this = TNoParam() and result = "" - or - exists(TaintKind param, AttributePath path, int n | - this = TParamContext(param, path, n) and - result = "p" + n.toString() + path.extension() + " = " + param - ) - } + /** Gets a textual representation of this element. */ + string toString() { + this = TNoParam() and result = "" + or + exists(TaintKind param, AttributePath path, int n | + this = TParamContext(param, path, n) and + result = "p" + n.toString() + path.extension() + " = " + param + ) + } - TaintKind getParameterTaint(int n) { this = TParamContext(result, _, n) } + TaintKind getParameterTaint(int n) { this = TParamContext(result, _, n) } - AttributePath getAttributePath() { this = TParamContext(_, result, _) } + AttributePath getAttributePath() { this = TParamContext(_, result, _) } - TaintTrackingContext getCaller() { result = this.getCaller(_) } + TaintTrackingContext getCaller() { result = this.getCaller(_) } - TaintTrackingContext getCaller(CallNode call) { - exists(TaintKind param, AttributePath path, int n | - this = TParamContext(param, path, n) and - exists(TaintTrackingImplementation impl | - impl.callWithTaintedArgument(_, call, result, _, n, path, param) - ) - ) - } + TaintTrackingContext getCaller(CallNode call) { + exists(TaintKind param, AttributePath path, int n | + this = TParamContext(param, path, n) and + exists(TaintTrackingImplementation impl | + impl.callWithTaintedArgument(_, call, result, _, n, path, param) + ) + ) + } - predicate isTop() { this = TNoParam() } + predicate isTop() { this = TNoParam() } } private newtype TAttributePath = - TNoAttribute() or - /* - * It might make sense to add another level, attribute of attribute. - * But some experimentation would be needed. - */ + TNoAttribute() or + /* + * It might make sense to add another level, attribute of attribute. + * But some experimentation would be needed. + */ - TAttribute(string name) { exists(Attribute a | a.getName() = name) } + TAttribute(string name) { exists(Attribute a | a.getName() = name) } /** * The attribute of the tracked value holding the taint. @@ -67,46 +67,46 @@ private newtype TAttributePath = * Used for tracking tainted attributes of objects. */ abstract class AttributePath extends TAttributePath { - /** Gets a textual representation of this element. */ - abstract string toString(); + /** Gets a textual representation of this element. */ + abstract string toString(); - abstract string extension(); + abstract string extension(); - abstract AttributePath fromAttribute(string name); + abstract AttributePath fromAttribute(string name); - AttributePath getAttribute(string name) { this = result.fromAttribute(name) } + AttributePath getAttribute(string name) { this = result.fromAttribute(name) } - predicate noAttribute() { this = TNoAttribute() } + predicate noAttribute() { this = TNoAttribute() } } /** AttributePath for no attribute. */ class NoAttribute extends TNoAttribute, AttributePath { - override string toString() { result = "no attribute" } + override string toString() { result = "no attribute" } - override string extension() { result = "" } + override string extension() { result = "" } - override AttributePath fromAttribute(string name) { none() } + override AttributePath fromAttribute(string name) { none() } } /** AttributePath for an attribute. */ class NamedAttributePath extends TAttribute, AttributePath { - override string toString() { - exists(string attr | - this = TAttribute(attr) and - result = "attribute " + attr - ) - } + override string toString() { + exists(string attr | + this = TAttribute(attr) and + result = "attribute " + attr + ) + } - override string extension() { - exists(string attr | - this = TAttribute(attr) and - result = "." + attr - ) - } + override string extension() { + exists(string attr | + this = TAttribute(attr) and + result = "." + attr + ) + } - override AttributePath fromAttribute(string name) { - this = TAttribute(name) and result = TNoAttribute() - } + override AttributePath fromAttribute(string name) { + this = TAttribute(name) and result = TNoAttribute() + } } /** @@ -114,81 +114,81 @@ class NamedAttributePath extends TAttribute, AttributePath { * Construction of this type is mutually recursive with `TaintTrackingImplementation.flowStep(...)` */ newtype TTaintTrackingNode = - TTaintTrackingNode_( - DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind, - TaintTracking::Configuration config - ) { - config.(TaintTrackingImplementation).flowSource(node, context, path, kind) - or - config.(TaintTrackingImplementation).flowStep(_, node, context, path, kind, _) - } + TTaintTrackingNode_( + DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind, + TaintTracking::Configuration config + ) { + config.(TaintTrackingImplementation).flowSource(node, context, path, kind) + or + config.(TaintTrackingImplementation).flowStep(_, node, context, path, kind, _) + } /** * Class representing the (node, context, path, kind) tuple. * Used for context-sensitive path-aware taint-tracking. */ class TaintTrackingNode extends TTaintTrackingNode { - /** Gets a textual representation of this element. */ - string toString() { - if this.getPath() instanceof NoAttribute - then result = this.getTaintKind().repr() - else result = this.getPath().extension() + " = " + this.getTaintKind().repr() - } + /** Gets a textual representation of this element. */ + string toString() { + if this.getPath() instanceof NoAttribute + then result = this.getTaintKind().repr() + else result = this.getPath().extension() + " = " + this.getTaintKind().repr() + } - /** Gets the data flow node for this taint-tracking node */ - DataFlow::Node getNode() { this = TTaintTrackingNode_(result, _, _, _, _) } + /** Gets the data flow node for this taint-tracking node */ + DataFlow::Node getNode() { this = TTaintTrackingNode_(result, _, _, _, _) } - /** Gets the taint kind for this taint-tracking node */ - TaintKind getTaintKind() { this = TTaintTrackingNode_(_, _, _, result, _) } + /** Gets the taint kind for this taint-tracking node */ + TaintKind getTaintKind() { this = TTaintTrackingNode_(_, _, _, result, _) } - /** Gets the taint-tracking context for this taint-tracking node */ - TaintTrackingContext getContext() { this = TTaintTrackingNode_(_, result, _, _, _) } + /** Gets the taint-tracking context for this taint-tracking node */ + TaintTrackingContext getContext() { this = TTaintTrackingNode_(_, result, _, _, _) } - /** Gets the attribute path context for this taint-tracking node */ - AttributePath getPath() { this = TTaintTrackingNode_(_, _, result, _, _) } + /** Gets the attribute path context for this taint-tracking node */ + AttributePath getPath() { this = TTaintTrackingNode_(_, _, result, _, _) } - TaintTracking::Configuration getConfiguration() { this = TTaintTrackingNode_(_, _, _, _, result) } + TaintTracking::Configuration getConfiguration() { this = TTaintTrackingNode_(_, _, _, _, result) } - Location getLocation() { result = this.getNode().getLocation() } + Location getLocation() { result = this.getNode().getLocation() } - predicate isSource() { this.getConfiguration().(TaintTrackingImplementation).isPathSource(this) } + predicate isSource() { this.getConfiguration().(TaintTrackingImplementation).isPathSource(this) } - predicate isSink() { this.getConfiguration().(TaintTrackingImplementation).isPathSink(this) } + predicate isSink() { this.getConfiguration().(TaintTrackingImplementation).isPathSink(this) } - ControlFlowNode getCfgNode() { result = this.getNode().asCfgNode() } + ControlFlowNode getCfgNode() { result = this.getNode().asCfgNode() } - /** Get the AST node for this node. */ - AstNode getAstNode() { result = this.getCfgNode().getNode() } + /** Get the AST node for this node. */ + AstNode getAstNode() { result = this.getCfgNode().getNode() } - TaintTrackingNode getASuccessor(string edgeLabel) { - this.isVisible() and - result = this.unlabeledSuccessor*().labeledSuccessor(edgeLabel) - } + TaintTrackingNode getASuccessor(string edgeLabel) { + this.isVisible() and + result = this.unlabeledSuccessor*().labeledSuccessor(edgeLabel) + } - TaintTrackingNode getASuccessor() { - result = this.getASuccessor(_) - or - this.isVisible() and - result = this.unlabeledSuccessor+() and - result.isSink() - } + TaintTrackingNode getASuccessor() { + result = this.getASuccessor(_) + or + this.isVisible() and + result = this.unlabeledSuccessor+() and + result.isSink() + } - private TaintTrackingNode unlabeledSuccessor() { - this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, "") - } + private TaintTrackingNode unlabeledSuccessor() { + this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, "") + } - private TaintTrackingNode labeledSuccessor(string label) { - not label = "" and - this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, label) - } + private TaintTrackingNode labeledSuccessor(string label) { + not label = "" and + this.getConfiguration().(TaintTrackingImplementation).flowStep(this, result, label) + } - private predicate isVisible() { - any(TaintTrackingNode pred).labeledSuccessor(_) = this - or - this.isSource() - } + private predicate isVisible() { + any(TaintTrackingNode pred).labeledSuccessor(_) = this + or + this.isSource() + } - predicate flowsTo(TaintTrackingNode other) { this.getASuccessor*() = other } + predicate flowsTo(TaintTrackingNode other) { this.getASuccessor*() = other } } /** @@ -198,449 +198,449 @@ class TaintTrackingNode extends TTaintTrackingNode { * in `TaintTracking::Configuration` simpler. */ class TaintTrackingImplementation extends string { - TaintTrackingImplementation() { this instanceof TaintTracking::Configuration } + TaintTrackingImplementation() { this instanceof TaintTracking::Configuration } - /** - * Hold if there is a flow from `source`, which is a taint source, to - * `sink`, which is a taint sink, with this configuration. - */ - predicate hasFlowPath(TaintTrackingNode source, TaintTrackingNode sink) { - this.isPathSource(source) and - this.isPathSink(sink) and - source.flowsTo(sink) - } + /** + * Hold if there is a flow from `source`, which is a taint source, to + * `sink`, which is a taint sink, with this configuration. + */ + predicate hasFlowPath(TaintTrackingNode source, TaintTrackingNode sink) { + this.isPathSource(source) and + this.isPathSink(sink) and + source.flowsTo(sink) + } - /** - * Hold if `node` is a source of taint `kind` with context `context` and attribute path `path`. - */ - predicate flowSource( - DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind - ) { - context = TNoParam() and - path = TNoAttribute() and - this.(TaintTracking::Configuration).isSource(node, kind) - } + /** + * Hold if `node` is a source of taint `kind` with context `context` and attribute path `path`. + */ + predicate flowSource( + DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind + ) { + context = TNoParam() and + path = TNoAttribute() and + this.(TaintTracking::Configuration).isSource(node, kind) + } - /** Hold if `source` is a source of taint. */ - predicate isPathSource(TaintTrackingNode source) { - exists(DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind | - source = TTaintTrackingNode_(node, context, path, kind, this) and - this.flowSource(node, context, path, kind) + /** Hold if `source` is a source of taint. */ + predicate isPathSource(TaintTrackingNode source) { + exists(DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind | + source = TTaintTrackingNode_(node, context, path, kind, this) and + this.flowSource(node, context, path, kind) + ) + } + + /** Hold if `sink` is a taint sink. */ + predicate isPathSink(TaintTrackingNode sink) { + exists(DataFlow::Node node, AttributePath path, TaintKind kind | + sink = TTaintTrackingNode_(node, _, path, kind, this) and + path = TNoAttribute() and + this.(TaintTracking::Configuration).isSink(node, kind) + ) + } + + /** + * Hold if taint flows to `src` to `dest` in a single step, labeled with `edgeLabel` + * `edgeLabel` is purely informative. + */ + predicate flowStep(TaintTrackingNode src, TaintTrackingNode dest, string edgeLabel) { + exists(DataFlow::Node node, TaintTrackingContext ctx, AttributePath path, TaintKind kind | + dest = TTaintTrackingNode_(node, ctx, path, kind, this) and + this.flowStep(src, node, ctx, path, kind, edgeLabel) + ) + } + + /** + * Hold if taint flows to `src` to `(node, context, path, kind)` in a single step, labelled with `egdeLabel` with this configuration. + * `edgeLabel` is purely informative. + */ + predicate flowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + this.unprunedStep(src, node, context, path, kind, edgeLabel) and + node.getBasicBlock().likelyReachable() and + not this.(TaintTracking::Configuration).isBarrier(node) and + ( + not path = TNoAttribute() + or + not this.(TaintTracking::Configuration).isBarrier(node, kind) and + exists(DataFlow::Node srcnode, TaintKind srckind | + src = TTaintTrackingNode_(srcnode, _, _, srckind, this) and + not this.prunedEdge(srcnode, node, srckind, kind) + ) + ) + } + + private predicate prunedEdge( + DataFlow::Node srcnode, DataFlow::Node destnode, TaintKind srckind, TaintKind destkind + ) { + this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode, srckind, destkind) + or + srckind = destkind and this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode) + } + + private predicate unprunedStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + this.importStep(src, node, context, path, kind, edgeLabel) + or + this.fromImportStep(src, node, context, path, kind, edgeLabel) + or + this.attributeLoadStep(src, node, context, path, kind, edgeLabel) + or + this.getattrStep(src, node, context, path, kind, edgeLabel) + or + this.useStep(src, node, context, path, kind, edgeLabel) + or + this.callTaintStep(src, node, context, path, kind, edgeLabel) + or + this.returnFlowStep(src, node, context, path, kind, edgeLabel) + or + this.callFlowStep(src, node, context, path, kind, edgeLabel) + or + this.iterationStep(src, node, context, path, kind, edgeLabel) + or + this.yieldStep(src, node, context, path, kind, edgeLabel) + or + this.parameterStep(src, node, context, path, kind, edgeLabel) + or + this.ifExpStep(src, node, context, path, kind, edgeLabel) + or + this.essaFlowStep(src, node, context, path, kind, edgeLabel) + or + this.instantiationStep(src, node, context, path, kind, edgeLabel) + or + this.legacyExtensionStep(src, node, context, path, kind, edgeLabel) + or + exists(DataFlow::Node srcnode, TaintKind srckind | + this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node, srckind, kind) and + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + path.noAttribute() and + edgeLabel = "additional with kind" + ) + or + exists(DataFlow::Node srcnode | + this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node) and + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + path.noAttribute() and + edgeLabel = "additional" + ) + or + exists(DataFlow::Node srcnode, TaintKind srckind | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + path.noAttribute() + | + kind = srckind.getTaintForFlowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) + or + kind = srckind.(CollectionKind).getMember() and + srckind.(CollectionKind).flowToMember(srcnode, node) and + edgeLabel = "to member" + or + srckind = kind.(CollectionKind).getMember() and + kind.(CollectionKind).flowFromMember(srcnode, node) and + edgeLabel = "from member" + or + kind = srckind and srckind.flowStep(srcnode, node, edgeLabel) + or + kind = srckind and + srckind instanceof DictKind and + DictKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) + or + kind = srckind and + srckind instanceof SequenceKind and + SequenceKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) + ) + } + + pragma[noinline] + predicate importStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + edgeLabel = "import" and + exists(ModuleValue m, string name, AttributePath srcpath | + src = TTaintTrackingNode_(_, context, srcpath, kind, this) and + this.moduleAttributeTainted(m, name, src) and + node.asCfgNode().pointsTo(m) and + path = srcpath.getAttribute(name) + ) + } + + pragma[noinline] + predicate fromImportStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + edgeLabel = "from import" and + exists(ModuleValue m, string name | + src = TTaintTrackingNode_(_, context, path, kind, this) and + this.moduleAttributeTainted(m, name, src) and + node.asCfgNode().(ImportMemberNode).getModule(name).pointsTo(m) + ) + } + + pragma[noinline] + predicate attributeLoadStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | + src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and + srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and + path = srcpath.fromAttribute(attrname) and + edgeLabel = "from path attribute" + ) + or + exists(DataFlow::Node srcnode, TaintKind srckind, string attrname | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and + kind = srckind.getTaintOfAttribute(attrname) and + edgeLabel = "from taint attribute" and + path instanceof NoAttribute + ) + } + + pragma[noinline] + predicate getattrStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, AttributePath srcpath, TaintKind srckind, string attrname | + src = TTaintTrackingNode_(srcnode, context, srcpath, srckind, this) and + exists(CallNode call, ControlFlowNode arg | + call = node.asCfgNode() and + call.getFunction().pointsTo(ObjectInternal::builtin("getattr")) and + arg = call.getArg(0) and + attrname = call.getArg(1).getNode().(StrConst).getText() and + arg = srcnode.asCfgNode() + | + path = srcpath.fromAttribute(attrname) and + kind = srckind + or + path = srcpath and + kind = srckind.getTaintOfAttribute(attrname) + ) + ) and + edgeLabel = "getattr" + } + + pragma[noinline] + predicate useStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + node.asCfgNode() = srcnode.asVariable().getASourceUse() + ) and + edgeLabel = "use" + } + + /* If the return value is tainted without context, then it always flows back to the caller */ + pragma[noinline] + predicate returnFlowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(CallNode call, PythonFunctionObjectInternal pyfunc, DataFlow::Node retval | + pyfunc.getACall() = call and + context = TNoParam() and + src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and + node.asCfgNode() = call and + retval.asCfgNode() = + any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() + ) and + edgeLabel = "return" + } + + /* + * Avoid taint flow from return value to caller as it can produce imprecise flow graphs + * Step directly from tainted argument to call result. + */ + + pragma[noinline] + predicate callFlowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists( + CallNode call, PythonFunctionObjectInternal pyfunc, TaintTrackingContext callee, + DataFlow::Node retval, TaintTrackingNode retnode + | + this.callContexts(call, src, pyfunc, context, callee) and + retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and + node.asCfgNode() = call and + retval.asCfgNode() = + any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() + ) and + edgeLabel = "call" + } + + pragma[noinline] + predicate callContexts( + CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal pyfunc, + TaintTrackingContext caller, TaintTrackingContext callee + ) { + exists(int arg, TaintKind callerKind, AttributePath callerPath | + this.callWithTaintedArgument(argnode, call, caller, pyfunc, arg, callerPath, callerKind) and + callee = TParamContext(callerKind, callerPath, arg) + ) + } + + predicate callWithTaintedArgument( + TaintTrackingNode src, CallNode call, TaintTrackingContext caller, CallableValue pyfunc, + int arg, AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, caller, path, kind, this) and + srcnode.asCfgNode() = pyfunc.getArgumentForCall(call, arg) + ) + } + + predicate instantiationStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(PythonFunctionValue init, EssaVariable self, TaintTrackingContext callee | + instantiationCall(node.asCfgNode(), src, init, context, callee) and + this.(EssaTaintTracking).taintedDefinition(_, self.getDefinition(), callee, path, kind) and + self.getSourceVariable().(Variable).isSelf() and + BaseFlow::reaches_exit(self) and + self.getScope() = init.getScope() + ) and + edgeLabel = "instantiation" + } + + predicate instantiationCall( + CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal init, + TaintTrackingContext caller, TaintTrackingContext callee + ) { + exists(ClassValue cls | + call.getFunction().pointsTo(cls) and + cls.lookup("__init__") = init + | + exists(int arg, TaintKind callerKind, AttributePath callerPath, DataFlow::Node argument | + argnode = TTaintTrackingNode_(argument, caller, callerPath, callerKind, this) and + call.getArg(arg - 1) = argument.asCfgNode() and + callee = TParamContext(callerKind, callerPath, arg) + ) + ) + } + + pragma[noinline] + predicate callTaintStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, CallNode call, TaintKind srckind, string name | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + call.getFunction().(AttrNode).getObject(name) = src.getNode().asCfgNode() and + kind = srckind.getTaintOfMethodResult(name) and + node.asCfgNode() = call + ) and + edgeLabel = "call" + } + + pragma[noinline] + predicate iterationStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(ForNode for, DataFlow::Node sequence, TaintKind seqkind | + src = TTaintTrackingNode_(sequence, context, path, seqkind, this) and + for.iterates(_, sequence.asCfgNode()) and + node.asCfgNode() = for and + path.noAttribute() and + kind = seqkind.getTaintForIteration() + ) and + edgeLabel = "iteration" + } + + pragma[noinline] + predicate parameterStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(CallNode call, PythonFunctionObjectInternal pyfunc, int arg | + this.callWithTaintedArgument(src, call, _, pyfunc, arg, path, kind) and + node.asCfgNode() = pyfunc.getParameter(arg) and + context = TParamContext(kind, path, arg) + ) and + edgeLabel = "parameter" + } + + pragma[noinline] + predicate yieldStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode, TaintKind itemkind | + src = TTaintTrackingNode_(srcnode, context, path, itemkind, this) and + itemkind = kind.getTaintForIteration() and + exists(PyFunctionObject func | + func.getFunction().isGenerator() and + func.getACall() = node.asCfgNode() and + exists(Yield yield | + yield.getScope() = func.getFunction() and + yield.getValue() = srcnode.asCfgNode().getNode() ) - } + ) + ) and + edgeLabel = "yield" + } - /** Hold if `sink` is a taint sink. */ - predicate isPathSink(TaintTrackingNode sink) { - exists(DataFlow::Node node, AttributePath path, TaintKind kind | - sink = TTaintTrackingNode_(node, _, path, kind, this) and - path = TNoAttribute() and - this.(TaintTracking::Configuration).isSink(node, kind) - ) - } + pragma[noinline] + predicate ifExpStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asCfgNode() = node.asCfgNode().(IfExprNode).getAnOperand() + ) and + edgeLabel = "if exp" + } - /** - * Hold if taint flows to `src` to `dest` in a single step, labeled with `edgeLabel` - * `edgeLabel` is purely informative. - */ - predicate flowStep(TaintTrackingNode src, TaintTrackingNode dest, string edgeLabel) { - exists(DataFlow::Node node, TaintTrackingContext ctx, AttributePath path, TaintKind kind | - dest = TTaintTrackingNode_(node, ctx, path, kind, this) and - this.flowStep(src, node, ctx, path, kind, edgeLabel) - ) - } + pragma[noinline] + predicate essaFlowStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + this + .(EssaTaintTracking) + .taintedDefinition(src, node.asVariable().getDefinition(), context, path, kind) and + edgeLabel = "" + } - /** - * Hold if taint flows to `src` to `(node, context, path, kind)` in a single step, labelled with `egdeLabel` with this configuration. - * `edgeLabel` is purely informative. - */ - predicate flowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - this.unprunedStep(src, node, context, path, kind, edgeLabel) and - node.getBasicBlock().likelyReachable() and - not this.(TaintTracking::Configuration).isBarrier(node) and - ( - not path = TNoAttribute() - or - not this.(TaintTracking::Configuration).isBarrier(node, kind) and - exists(DataFlow::Node srcnode, TaintKind srckind | - src = TTaintTrackingNode_(srcnode, _, _, srckind, this) and - not this.prunedEdge(srcnode, node, srckind, kind) - ) - ) - } + pragma[noinline] + predicate legacyExtensionStep( + TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, + TaintKind kind, string edgeLabel + ) { + exists(TaintTracking::Extension extension, DataFlow::Node srcnode, TaintKind srckind | + this.(TaintTracking::Configuration).isExtension(extension) and + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + srcnode.asCfgNode() = extension + | + extension.getASuccessorNode() = node.asCfgNode() and kind = srckind + or + extension.getASuccessorNode(srckind, kind) = node.asCfgNode() + or + extension.getASuccessorVariable() = node.asVariable() and kind = srckind + ) and + edgeLabel = "legacy extension" + } - private predicate prunedEdge( - DataFlow::Node srcnode, DataFlow::Node destnode, TaintKind srckind, TaintKind destkind - ) { - this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode, srckind, destkind) - or - srckind = destkind and this.(TaintTracking::Configuration).isBarrierEdge(srcnode, destnode) - } - - private predicate unprunedStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - this.importStep(src, node, context, path, kind, edgeLabel) - or - this.fromImportStep(src, node, context, path, kind, edgeLabel) - or - this.attributeLoadStep(src, node, context, path, kind, edgeLabel) - or - this.getattrStep(src, node, context, path, kind, edgeLabel) - or - this.useStep(src, node, context, path, kind, edgeLabel) - or - this.callTaintStep(src, node, context, path, kind, edgeLabel) - or - this.returnFlowStep(src, node, context, path, kind, edgeLabel) - or - this.callFlowStep(src, node, context, path, kind, edgeLabel) - or - this.iterationStep(src, node, context, path, kind, edgeLabel) - or - this.yieldStep(src, node, context, path, kind, edgeLabel) - or - this.parameterStep(src, node, context, path, kind, edgeLabel) - or - this.ifExpStep(src, node, context, path, kind, edgeLabel) - or - this.essaFlowStep(src, node, context, path, kind, edgeLabel) - or - this.instantiationStep(src, node, context, path, kind, edgeLabel) - or - this.legacyExtensionStep(src, node, context, path, kind, edgeLabel) - or - exists(DataFlow::Node srcnode, TaintKind srckind | - this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node, srckind, kind) and - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - path.noAttribute() and - edgeLabel = "additional with kind" - ) - or - exists(DataFlow::Node srcnode | - this.(TaintTracking::Configuration).isAdditionalFlowStep(srcnode, node) and - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - path.noAttribute() and - edgeLabel = "additional" - ) - or - exists(DataFlow::Node srcnode, TaintKind srckind | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - path.noAttribute() - | - kind = srckind.getTaintForFlowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) - or - kind = srckind.(CollectionKind).getMember() and - srckind.(CollectionKind).flowToMember(srcnode, node) and - edgeLabel = "to member" - or - srckind = kind.(CollectionKind).getMember() and - kind.(CollectionKind).flowFromMember(srcnode, node) and - edgeLabel = "from member" - or - kind = srckind and srckind.flowStep(srcnode, node, edgeLabel) - or - kind = srckind and - srckind instanceof DictKind and - DictKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) - or - kind = srckind and - srckind instanceof SequenceKind and - SequenceKind::flowStep(srcnode.asCfgNode(), node.asCfgNode(), edgeLabel) - ) - } - - pragma[noinline] - predicate importStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - edgeLabel = "import" and - exists(ModuleValue m, string name, AttributePath srcpath | - src = TTaintTrackingNode_(_, context, srcpath, kind, this) and - this.moduleAttributeTainted(m, name, src) and - node.asCfgNode().pointsTo(m) and - path = srcpath.getAttribute(name) - ) - } - - pragma[noinline] - predicate fromImportStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - edgeLabel = "from import" and - exists(ModuleValue m, string name | - src = TTaintTrackingNode_(_, context, path, kind, this) and - this.moduleAttributeTainted(m, name, src) and - node.asCfgNode().(ImportMemberNode).getModule(name).pointsTo(m) - ) - } - - pragma[noinline] - predicate attributeLoadStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | - src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and - srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and - path = srcpath.fromAttribute(attrname) and - edgeLabel = "from path attribute" - ) - or - exists(DataFlow::Node srcnode, TaintKind srckind, string attrname | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - srcnode.asCfgNode() = node.asCfgNode().(AttrNode).getObject(attrname) and - kind = srckind.getTaintOfAttribute(attrname) and - edgeLabel = "from taint attribute" and - path instanceof NoAttribute - ) - } - - pragma[noinline] - predicate getattrStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, AttributePath srcpath, TaintKind srckind, string attrname | - src = TTaintTrackingNode_(srcnode, context, srcpath, srckind, this) and - exists(CallNode call, ControlFlowNode arg | - call = node.asCfgNode() and - call.getFunction().pointsTo(ObjectInternal::builtin("getattr")) and - arg = call.getArg(0) and - attrname = call.getArg(1).getNode().(StrConst).getText() and - arg = srcnode.asCfgNode() - | - path = srcpath.fromAttribute(attrname) and - kind = srckind - or - path = srcpath and - kind = srckind.getTaintOfAttribute(attrname) - ) - ) and - edgeLabel = "getattr" - } - - pragma[noinline] - predicate useStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - node.asCfgNode() = srcnode.asVariable().getASourceUse() - ) and - edgeLabel = "use" - } - - /* If the return value is tainted without context, then it always flows back to the caller */ - pragma[noinline] - predicate returnFlowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(CallNode call, PythonFunctionObjectInternal pyfunc, DataFlow::Node retval | - pyfunc.getACall() = call and - context = TNoParam() and - src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and - node.asCfgNode() = call and - retval.asCfgNode() = - any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() - ) and - edgeLabel = "return" - } - - /* - * Avoid taint flow from return value to caller as it can produce imprecise flow graphs - * Step directly from tainted argument to call result. - */ - - pragma[noinline] - predicate callFlowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists( - CallNode call, PythonFunctionObjectInternal pyfunc, TaintTrackingContext callee, - DataFlow::Node retval, TaintTrackingNode retnode - | - this.callContexts(call, src, pyfunc, context, callee) and - retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and - node.asCfgNode() = call and - retval.asCfgNode() = - any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode() - ) and - edgeLabel = "call" - } - - pragma[noinline] - predicate callContexts( - CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal pyfunc, - TaintTrackingContext caller, TaintTrackingContext callee - ) { - exists(int arg, TaintKind callerKind, AttributePath callerPath | - this.callWithTaintedArgument(argnode, call, caller, pyfunc, arg, callerPath, callerKind) and - callee = TParamContext(callerKind, callerPath, arg) - ) - } - - predicate callWithTaintedArgument( - TaintTrackingNode src, CallNode call, TaintTrackingContext caller, CallableValue pyfunc, - int arg, AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, caller, path, kind, this) and - srcnode.asCfgNode() = pyfunc.getArgumentForCall(call, arg) - ) - } - - predicate instantiationStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(PythonFunctionValue init, EssaVariable self, TaintTrackingContext callee | - instantiationCall(node.asCfgNode(), src, init, context, callee) and - this.(EssaTaintTracking).taintedDefinition(_, self.getDefinition(), callee, path, kind) and - self.getSourceVariable().(Variable).isSelf() and - BaseFlow::reaches_exit(self) and - self.getScope() = init.getScope() - ) and - edgeLabel = "instantiation" - } - - predicate instantiationCall( - CallNode call, TaintTrackingNode argnode, PythonFunctionObjectInternal init, - TaintTrackingContext caller, TaintTrackingContext callee - ) { - exists(ClassValue cls | - call.getFunction().pointsTo(cls) and - cls.lookup("__init__") = init - | - exists(int arg, TaintKind callerKind, AttributePath callerPath, DataFlow::Node argument | - argnode = TTaintTrackingNode_(argument, caller, callerPath, callerKind, this) and - call.getArg(arg - 1) = argument.asCfgNode() and - callee = TParamContext(callerKind, callerPath, arg) - ) - ) - } - - pragma[noinline] - predicate callTaintStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, CallNode call, TaintKind srckind, string name | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - call.getFunction().(AttrNode).getObject(name) = src.getNode().asCfgNode() and - kind = srckind.getTaintOfMethodResult(name) and - node.asCfgNode() = call - ) and - edgeLabel = "call" - } - - pragma[noinline] - predicate iterationStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(ForNode for, DataFlow::Node sequence, TaintKind seqkind | - src = TTaintTrackingNode_(sequence, context, path, seqkind, this) and - for.iterates(_, sequence.asCfgNode()) and - node.asCfgNode() = for and - path.noAttribute() and - kind = seqkind.getTaintForIteration() - ) and - edgeLabel = "iteration" - } - - pragma[noinline] - predicate parameterStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(CallNode call, PythonFunctionObjectInternal pyfunc, int arg | - this.callWithTaintedArgument(src, call, _, pyfunc, arg, path, kind) and - node.asCfgNode() = pyfunc.getParameter(arg) and - context = TParamContext(kind, path, arg) - ) and - edgeLabel = "parameter" - } - - pragma[noinline] - predicate yieldStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode, TaintKind itemkind | - src = TTaintTrackingNode_(srcnode, context, path, itemkind, this) and - itemkind = kind.getTaintForIteration() and - exists(PyFunctionObject func | - func.getFunction().isGenerator() and - func.getACall() = node.asCfgNode() and - exists(Yield yield | - yield.getScope() = func.getFunction() and - yield.getValue() = srcnode.asCfgNode().getNode() - ) - ) - ) and - edgeLabel = "yield" - } - - pragma[noinline] - predicate ifExpStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asCfgNode() = node.asCfgNode().(IfExprNode).getAnOperand() - ) and - edgeLabel = "if exp" - } - - pragma[noinline] - predicate essaFlowStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - this - .(EssaTaintTracking) - .taintedDefinition(src, node.asVariable().getDefinition(), context, path, kind) and - edgeLabel = "" - } - - pragma[noinline] - predicate legacyExtensionStep( - TaintTrackingNode src, DataFlow::Node node, TaintTrackingContext context, AttributePath path, - TaintKind kind, string edgeLabel - ) { - exists(TaintTracking::Extension extension, DataFlow::Node srcnode, TaintKind srckind | - this.(TaintTracking::Configuration).isExtension(extension) and - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - srcnode.asCfgNode() = extension - | - extension.getASuccessorNode() = node.asCfgNode() and kind = srckind - or - extension.getASuccessorNode(srckind, kind) = node.asCfgNode() - or - extension.getASuccessorVariable() = node.asVariable() and kind = srckind - ) and - edgeLabel = "legacy extension" - } - - predicate moduleAttributeTainted(ModuleValue m, string name, TaintTrackingNode taint) { - exists(DataFlow::Node srcnode, EssaVariable var | - taint = TTaintTrackingNode_(srcnode, TNoParam(), _, _, this) and - var = srcnode.asVariable() and - var.getName() = name and - BaseFlow::reaches_exit(var) and - var.getScope() = m.getScope() - ) - } + predicate moduleAttributeTainted(ModuleValue m, string name, TaintTrackingNode taint) { + exists(DataFlow::Node srcnode, EssaVariable var | + taint = TTaintTrackingNode_(srcnode, TNoParam(), _, _, this) and + var = srcnode.asVariable() and + var.getName() = name and + BaseFlow::reaches_exit(var) and + var.getScope() = m.getScope() + ) + } } /** @@ -648,326 +648,326 @@ class TaintTrackingImplementation extends string { * This class handle tracking of ESSA variables. */ private class EssaTaintTracking extends string { - EssaTaintTracking() { this instanceof TaintTracking::Configuration } + EssaTaintTracking() { this instanceof TaintTracking::Configuration } - pragma[noinline] - predicate taintedDefinition( - TaintTrackingNode src, EssaDefinition defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - this.taintedPhi(src, defn, context, path, kind) - or - this.taintedAssignment(src, defn, context, path, kind) - or - this.taintedMultiAssignment(src, defn, context, path, kind) - or - this.taintedAttributeAssignment(src, defn, context, path, kind) - or - this.taintedParameterDefinition(src, defn, context, path, kind) - or - this.taintedCallsite(src, defn, context, path, kind) - or - this.taintedMethodCallsite(src, defn, context, path, kind) - or - this.taintedUniEdge(src, defn, context, path, kind) - or - this.taintedPiNode(src, defn, context, path, kind) - or - this.taintedArgument(src, defn, context, path, kind) - or - this.taintedExceptionCapture(src, defn, context, path, kind) - or - this.taintedScopeEntryDefinition(src, defn, context, path, kind) - or - this.taintedWith(src, defn, context, path, kind) - } + pragma[noinline] + predicate taintedDefinition( + TaintTrackingNode src, EssaDefinition defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + this.taintedPhi(src, defn, context, path, kind) + or + this.taintedAssignment(src, defn, context, path, kind) + or + this.taintedMultiAssignment(src, defn, context, path, kind) + or + this.taintedAttributeAssignment(src, defn, context, path, kind) + or + this.taintedParameterDefinition(src, defn, context, path, kind) + or + this.taintedCallsite(src, defn, context, path, kind) + or + this.taintedMethodCallsite(src, defn, context, path, kind) + or + this.taintedUniEdge(src, defn, context, path, kind) + or + this.taintedPiNode(src, defn, context, path, kind) + or + this.taintedArgument(src, defn, context, path, kind) + or + this.taintedExceptionCapture(src, defn, context, path, kind) + or + this.taintedScopeEntryDefinition(src, defn, context, path, kind) + or + this.taintedWith(src, defn, context, path, kind) + } - pragma[noinline] - private predicate taintedPhi( - TaintTrackingNode src, PhiFunction defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode, BasicBlock pred, EssaVariable predvar, DataFlow::Node phi | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - defn = phi.asVariable().getDefinition() and - predvar = defn.getInput(pred) and - not pred.unlikelySuccessor(defn.getBasicBlock()) and - not this.(TaintTracking::Configuration).isBarrierEdge(srcnode, phi) and - srcnode.asVariable() = predvar - ) - } + pragma[noinline] + private predicate taintedPhi( + TaintTrackingNode src, PhiFunction defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode, BasicBlock pred, EssaVariable predvar, DataFlow::Node phi | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + defn = phi.asVariable().getDefinition() and + predvar = defn.getInput(pred) and + not pred.unlikelySuccessor(defn.getBasicBlock()) and + not this.(TaintTracking::Configuration).isBarrierEdge(srcnode, phi) and + srcnode.asVariable() = predvar + ) + } - pragma[noinline] - private predicate taintedAssignment( - TaintTrackingNode src, AssignmentDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - defn.getValue() = srcnode.asCfgNode() - ) - } + pragma[noinline] + private predicate taintedAssignment( + TaintTrackingNode src, AssignmentDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + defn.getValue() = srcnode.asCfgNode() + ) + } - pragma[noinline] - private predicate taintedMultiAssignment( - TaintTrackingNode src, MultiAssignmentDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode, TaintKind srckind, Assign assign, int depth | - src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and - path.noAttribute() - | - assign.getValue().getAFlowNode() = srcnode.asCfgNode() and - depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and - kind = taint_at_depth(srckind, depth) - ) - } + pragma[noinline] + private predicate taintedMultiAssignment( + TaintTrackingNode src, MultiAssignmentDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode, TaintKind srckind, Assign assign, int depth | + src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and + path.noAttribute() + | + assign.getValue().getAFlowNode() = srcnode.asCfgNode() and + depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and + kind = taint_at_depth(srckind, depth) + ) + } - pragma[noinline] - private predicate taintedAttributeAssignment( - TaintTrackingNode src, AttributeAssignment defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | - src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and - defn.getValue() = srcnode.asCfgNode() and - defn.getName() = attrname and - path = srcpath.getAttribute(attrname) - ) - } + pragma[noinline] + private predicate taintedAttributeAssignment( + TaintTrackingNode src, AttributeAssignment defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode, AttributePath srcpath, string attrname | + src = TTaintTrackingNode_(srcnode, context, srcpath, kind, this) and + defn.getValue() = srcnode.asCfgNode() and + defn.getName() = attrname and + path = srcpath.getAttribute(attrname) + ) + } - pragma[noinline] - private predicate taintedParameterDefinition( - TaintTrackingNode src, ParameterDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asCfgNode() = defn.getDefiningNode() - ) - } + pragma[noinline] + private predicate taintedParameterDefinition( + TaintTrackingNode src, ParameterDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asCfgNode() = defn.getDefiningNode() + ) + } - pragma[noinline] - private predicate taintedCallsite( - TaintTrackingNode src, CallsiteRefinement defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - /* - * In the interest of simplicity and performance we assume that tainted escaping variables remain tainted across calls. - * In the cases were this assumption is false, it is easy enough to add an additional barrier. - */ - - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asVariable() = defn.getInput() - ) - } - - pragma[noinline] - private predicate taintedMethodCallsite( - TaintTrackingNode src, MethodCallsiteRefinement defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asVariable() = defn.getInput() - ) - } - - pragma[noinline] - private predicate taintedUniEdge( - TaintTrackingNode src, SingleSuccessorGuard defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asVariable() = defn.getInput() and - not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) - ) - } - - private predicate taintedPiNode( - TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - taintedPiNodeOneway(src, defn, context, path, kind) - or - taintedPiNodeBothways(src, defn, context, path, kind) - } - - pragma[noinline] - private predicate taintedPiNodeOneway( - TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode, ControlFlowNode use | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) and - defn.getSense() = testEvaluates(defn, defn.getTest(), use, src) - ) - } - - pragma[noinline] - private predicate taintedPiNodeBothways( - TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode, ControlFlowNode test, ControlFlowNode use | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - piNodeTestAndUse(defn, test, use) and - srcnode.asVariable() = defn.getInput() and - not this.(TaintTracking::Configuration).isBarrierTest(test, defn.getSense()) and - testEvaluatesMaybe(test, use) - ) - } - - pragma[noinline] - private predicate taintedArgument( - TaintTrackingNode src, ArgumentRefinement defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - defn.getInput() = srcnode.asVariable() - ) - } - - pragma[noinline] - private predicate taintedExceptionCapture( - TaintTrackingNode src, ExceptionCapture defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - srcnode.asCfgNode() = defn.getDefiningNode() - ) - } - - pragma[noinline] - private predicate taintedScopeEntryDefinition( - TaintTrackingNode src, ScopeEntryDefinition defn, TaintTrackingContext context, - AttributePath path, TaintKind kind - ) { - exists(EssaVariable var | - BaseFlow::scope_entry_value_transfer_from_earlier(var, _, defn, _) and - this.taintedDefinition(src, var.getDefinition(), context, path, kind) - ) - } - - pragma[noinline] - private predicate taintedWith( - TaintTrackingNode src, WithDefinition defn, TaintTrackingContext context, AttributePath path, - TaintKind kind - ) { - exists(DataFlow::Node srcnode | - src = TTaintTrackingNode_(srcnode, context, path, kind, this) and - with_flow(_, srcnode.asCfgNode(), defn.getDefiningNode()) - ) - } - - /** - * Gets the boolean value that `test` evaluates to when `use` is tainted with `kind` - * and `test` and `use` are part of a test in a branch. + pragma[noinline] + private predicate taintedCallsite( + TaintTrackingNode src, CallsiteRefinement defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + /* + * In the interest of simplicity and performance we assume that tainted escaping variables remain tainted across calls. + * In the cases were this assumption is false, it is easy enough to add an additional barrier. */ - private boolean testEvaluates( - PyEdgeRefinement defn, ControlFlowNode test, ControlFlowNode use, TaintTrackingNode src - ) { - defn.getTest().getAChild*() = use and - exists(DataFlow::Node srcnode, TaintKind kind | - srcnode.asVariable() = defn.getInput() and - srcnode.asVariable().getASourceUse() = use and - src = TTaintTrackingNode_(srcnode, _, TNoAttribute(), kind, this) - | - test = use and result = kind.booleanValue() - or - exists(ControlFlowNode const | - Filters::equality_test(test, use, result.booleanNot(), const) and - const.getNode() instanceof ImmutableLiteral - ) - or - exists(ControlFlowNode c, ClassValue cls | - Filters::isinstance(test, c, use) and - c.pointsTo(cls) - | - exists(ClassValue scls | scls = kind.getType() | - scls.getASuperType() = cls and result = true - or - not scls.getASuperType() = cls and result = false - ) - or - not exists(kind.getType()) and result = maybe() - ) + + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asVariable() = defn.getInput() + ) + } + + pragma[noinline] + private predicate taintedMethodCallsite( + TaintTrackingNode src, MethodCallsiteRefinement defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asVariable() = defn.getInput() + ) + } + + pragma[noinline] + private predicate taintedUniEdge( + TaintTrackingNode src, SingleSuccessorGuard defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asVariable() = defn.getInput() and + not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) + ) + } + + private predicate taintedPiNode( + TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + taintedPiNodeOneway(src, defn, context, path, kind) + or + taintedPiNodeBothways(src, defn, context, path, kind) + } + + pragma[noinline] + private predicate taintedPiNodeOneway( + TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode, ControlFlowNode use | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + not this.(TaintTracking::Configuration).isBarrierTest(defn.getTest(), defn.getSense()) and + defn.getSense() = testEvaluates(defn, defn.getTest(), use, src) + ) + } + + pragma[noinline] + private predicate taintedPiNodeBothways( + TaintTrackingNode src, PyEdgeRefinement defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode, ControlFlowNode test, ControlFlowNode use | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + piNodeTestAndUse(defn, test, use) and + srcnode.asVariable() = defn.getInput() and + not this.(TaintTracking::Configuration).isBarrierTest(test, defn.getSense()) and + testEvaluatesMaybe(test, use) + ) + } + + pragma[noinline] + private predicate taintedArgument( + TaintTrackingNode src, ArgumentRefinement defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + defn.getInput() = srcnode.asVariable() + ) + } + + pragma[noinline] + private predicate taintedExceptionCapture( + TaintTrackingNode src, ExceptionCapture defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + srcnode.asCfgNode() = defn.getDefiningNode() + ) + } + + pragma[noinline] + private predicate taintedScopeEntryDefinition( + TaintTrackingNode src, ScopeEntryDefinition defn, TaintTrackingContext context, + AttributePath path, TaintKind kind + ) { + exists(EssaVariable var | + BaseFlow::scope_entry_value_transfer_from_earlier(var, _, defn, _) and + this.taintedDefinition(src, var.getDefinition(), context, path, kind) + ) + } + + pragma[noinline] + private predicate taintedWith( + TaintTrackingNode src, WithDefinition defn, TaintTrackingContext context, AttributePath path, + TaintKind kind + ) { + exists(DataFlow::Node srcnode | + src = TTaintTrackingNode_(srcnode, context, path, kind, this) and + with_flow(_, srcnode.asCfgNode(), defn.getDefiningNode()) + ) + } + + /** + * Gets the boolean value that `test` evaluates to when `use` is tainted with `kind` + * and `test` and `use` are part of a test in a branch. + */ + private boolean testEvaluates( + PyEdgeRefinement defn, ControlFlowNode test, ControlFlowNode use, TaintTrackingNode src + ) { + defn.getTest().getAChild*() = use and + exists(DataFlow::Node srcnode, TaintKind kind | + srcnode.asVariable() = defn.getInput() and + srcnode.asVariable().getASourceUse() = use and + src = TTaintTrackingNode_(srcnode, _, TNoAttribute(), kind, this) + | + test = use and result = kind.booleanValue() + or + exists(ControlFlowNode const | + Filters::equality_test(test, use, result.booleanNot(), const) and + const.getNode() instanceof ImmutableLiteral + ) + or + exists(ControlFlowNode c, ClassValue cls | + Filters::isinstance(test, c, use) and + c.pointsTo(cls) + | + exists(ClassValue scls | scls = kind.getType() | + scls.getASuperType() = cls and result = true + or + not scls.getASuperType() = cls and result = false ) or - result = testEvaluates(defn, not_operand(test), use, src).booleanNot() - } + not exists(kind.getType()) and result = maybe() + ) + ) + or + result = testEvaluates(defn, not_operand(test), use, src).booleanNot() + } - /** - * Holds if `test` is the test in a branch and `use` is that test - * with all the `not` prefixes removed. - */ - private predicate boolean_filter(ControlFlowNode test, ControlFlowNode use) { - any(PyEdgeRefinement ref).getTest() = test and - ( - use = test - or - exists(ControlFlowNode notuse | - boolean_filter(test, notuse) and - use = not_operand(notuse) - ) - ) - } + /** + * Holds if `test` is the test in a branch and `use` is that test + * with all the `not` prefixes removed. + */ + private predicate boolean_filter(ControlFlowNode test, ControlFlowNode use) { + any(PyEdgeRefinement ref).getTest() = test and + ( + use = test + or + exists(ControlFlowNode notuse | + boolean_filter(test, notuse) and + use = not_operand(notuse) + ) + ) + } } private predicate testEvaluatesMaybe(ControlFlowNode test, ControlFlowNode use) { - any(PyEdgeRefinement ref).getTest().getAChild*() = test and - test.getAChild*() = use and - not test.(UnaryExprNode).getNode().getOp() instanceof Not and - not exists(ControlFlowNode const | - Filters::equality_test(test, use, _, const) and - const.getNode() instanceof ImmutableLiteral - ) and - not Filters::isinstance(test, _, use) and - not test = use - or - testEvaluatesMaybe(not_operand(test), use) + any(PyEdgeRefinement ref).getTest().getAChild*() = test and + test.getAChild*() = use and + not test.(UnaryExprNode).getNode().getOp() instanceof Not and + not exists(ControlFlowNode const | + Filters::equality_test(test, use, _, const) and + const.getNode() instanceof ImmutableLiteral + ) and + not Filters::isinstance(test, _, use) and + not test = use + or + testEvaluatesMaybe(not_operand(test), use) } /** Gets the operand of a unary `not` expression. */ private ControlFlowNode not_operand(ControlFlowNode expr) { - expr.(UnaryExprNode).getNode().getOp() instanceof Not and - result = expr.(UnaryExprNode).getOperand() + expr.(UnaryExprNode).getNode().getOp() instanceof Not and + result = expr.(UnaryExprNode).getOperand() } /* Helper predicate for tainted_with */ private predicate with_flow(With with, ControlFlowNode contextManager, ControlFlowNode var) { - with.getContextExpr() = contextManager.getNode() and - with.getOptionalVars() = var.getNode() and - contextManager.strictlyDominates(var) + with.getContextExpr() = contextManager.getNode() and + with.getOptionalVars() = var.getNode() and + contextManager.strictlyDominates(var) } /* Helper predicate for taintedPiNode */ pragma[noinline] private predicate piNodeTestAndUse(PyEdgeRefinement defn, ControlFlowNode test, ControlFlowNode use) { - test = defn.getTest() and use = defn.getInput().getASourceUse() and test.getAChild*() = use + test = defn.getTest() and use = defn.getInput().getASourceUse() and test.getAChild*() = use } /** Helper predicate for taintedMultiAssignment */ private TaintKind taint_at_depth(SequenceKind parent_kind, int depth) { - depth >= 0 and - ( - // base-case #0 - depth = 0 and - result = parent_kind - or - // base-case #1 - depth = 1 and - result = parent_kind.getMember() - or - // recursive case - depth > 1 and - result = taint_at_depth(parent_kind.getMember(), depth - 1) - ) + depth >= 0 and + ( + // base-case #0 + depth = 0 and + result = parent_kind + or + // base-case #1 + depth = 1 and + result = parent_kind.getMember() + or + // recursive case + depth > 1 and + result = taint_at_depth(parent_kind.getMember(), depth - 1) + ) } /** @@ -983,25 +983,25 @@ private TaintKind taint_at_depth(SequenceKind parent_kind, int depth) { * - with `left_defn` = `*y`, `left_parent` = `((x, *y), ...)`, result = 1 */ int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_defn) { - exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and - left_parent.getAnElement() = left_defn and - // Handle `a, *b = some_iterable` - if left_defn instanceof StarredNode then result = 0 else result = 1 - or - result = 1 + iterable_unpacking_descent(left_parent.getAnElement(), left_defn) + exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and + left_parent.getAnElement() = left_defn and + // Handle `a, *b = some_iterable` + if left_defn instanceof StarredNode then result = 0 else result = 1 + or + result = 1 + iterable_unpacking_descent(left_parent.getAnElement(), left_defn) } module Implementation { - /* A call that returns a copy (or similar) of the argument */ - predicate copyCall(ControlFlowNode fromnode, CallNode tonode) { - tonode.getFunction().(AttrNode).getObject("copy") = fromnode - or - exists(ModuleObject copy, string name | name = "copy" or name = "deepcopy" | - copy.attr(name).(FunctionObject).getACall() = tonode and - tonode.getArg(0) = fromnode - ) - or - tonode.getFunction().pointsTo(ObjectInternal::builtin("reversed")) and - tonode.getArg(0) = fromnode - } + /* A call that returns a copy (or similar) of the argument */ + predicate copyCall(ControlFlowNode fromnode, CallNode tonode) { + tonode.getFunction().(AttrNode).getObject("copy") = fromnode + or + exists(ModuleObject copy, string name | name = "copy" or name = "deepcopy" | + copy.attr(name).(FunctionObject).getACall() = tonode and + tonode.getArg(0) = fromnode + ) + or + tonode.getFunction().pointsTo(ObjectInternal::builtin("reversed")) and + tonode.getArg(0) = fromnode + } } diff --git a/python/ql/src/semmle/python/dataflow/Legacy.qll b/python/ql/src/semmle/python/dataflow/Legacy.qll index ffdb7aee869..df0649963d0 100644 --- a/python/ql/src/semmle/python/dataflow/Legacy.qll +++ b/python/ql/src/semmle/python/dataflow/Legacy.qll @@ -4,65 +4,65 @@ import semmle.python.dataflow.Implementation /* For backwards compatibility -- Use `TaintTrackingContext` instead. */ deprecated class CallContext extends TaintTrackingContext { - TaintTrackingContext getCallee(CallNode call) { result.getCaller(call) = this } + TaintTrackingContext getCallee(CallNode call) { result.getCaller(call) = this } - predicate appliesToScope(Scope s) { - exists(PythonFunctionObjectInternal func, TaintKind param, AttributePath path, int n | - this = TParamContext(param, path, n) and - exists(TaintTrackingImplementation impl | - impl.callWithTaintedArgument(_, _, _, func, n, path, param) and - s = func.getScope() - ) - ) - or - this.isTop() - } + predicate appliesToScope(Scope s) { + exists(PythonFunctionObjectInternal func, TaintKind param, AttributePath path, int n | + this = TParamContext(param, path, n) and + exists(TaintTrackingImplementation impl | + impl.callWithTaintedArgument(_, _, _, func, n, path, param) and + s = func.getScope() + ) + ) + or + this.isTop() + } } /* Backwards compatibility with config-less taint-tracking */ private class LegacyConfiguration extends TaintTracking::Configuration { - LegacyConfiguration() { - /* A name that won't be accidentally chosen by users */ - this = "Semmle: Internal legacy configuration" - } + LegacyConfiguration() { + /* A name that won't be accidentally chosen by users */ + this = "Semmle: Internal legacy configuration" + } - override predicate isSource(TaintSource src) { src = src } + override predicate isSource(TaintSource src) { src = src } - override predicate isSink(TaintSink sink) { sink = sink } + override predicate isSink(TaintSink sink) { sink = sink } - override predicate isSanitizer(Sanitizer sanitizer) { sanitizer = sanitizer } + override predicate isSanitizer(Sanitizer sanitizer) { sanitizer = sanitizer } - override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { - exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | - dest.asCfgNode() = legacyExtension.getASuccessorNode() - or - dest.asVariable() = legacyExtension.getASuccessorVariable() - or - dest.asCfgNode() = legacyExtension.getAReturnSuccessorNode(_) - or - dest.asCfgNode() = legacyExtension.getACalleeSuccessorNode(_) - ) - } + override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) { + exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | + dest.asCfgNode() = legacyExtension.getASuccessorNode() + or + dest.asVariable() = legacyExtension.getASuccessorVariable() + or + dest.asCfgNode() = legacyExtension.getAReturnSuccessorNode(_) + or + dest.asCfgNode() = legacyExtension.getACalleeSuccessorNode(_) + ) + } - override predicate isAdditionalFlowStep( - DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind - ) { - exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | - dest.asCfgNode() = legacyExtension.getASuccessorNode(srckind, destkind) - ) - } + override predicate isAdditionalFlowStep( + DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind + ) { + exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension | + dest.asCfgNode() = legacyExtension.getASuccessorNode(srckind, destkind) + ) + } - override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { - ( - exists(DataFlowExtension::DataFlowVariable legacyExtension | - src.asVariable() = legacyExtension and - legacyExtension.prunedSuccessor(dest.asVariable()) - ) - or - exists(DataFlowExtension::DataFlowNode legacyExtension | - src.asCfgNode() = legacyExtension and - legacyExtension.prunedSuccessor(dest.asCfgNode()) - ) - ) - } + override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { + ( + exists(DataFlowExtension::DataFlowVariable legacyExtension | + src.asVariable() = legacyExtension and + legacyExtension.prunedSuccessor(dest.asVariable()) + ) + or + exists(DataFlowExtension::DataFlowNode legacyExtension | + src.asCfgNode() = legacyExtension and + legacyExtension.prunedSuccessor(dest.asCfgNode()) + ) + ) + } } diff --git a/python/ql/src/semmle/python/dataflow/StateTracking.qll b/python/ql/src/semmle/python/dataflow/StateTracking.qll index 55bed5bbfff..d302ea5c1ba 100644 --- a/python/ql/src/semmle/python/dataflow/StateTracking.qll +++ b/python/ql/src/semmle/python/dataflow/StateTracking.qll @@ -16,159 +16,159 @@ private import semmle.python.objects.ObjectInternal /** A state that should be tracked. */ abstract class TrackableState extends string { - bindingset[this] - TrackableState() { this = this } + bindingset[this] + TrackableState() { this = this } - /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ - final predicate appliesTo(ControlFlowNode f) { this.appliesTo(f, _) } + /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ + final predicate appliesTo(ControlFlowNode f) { this.appliesTo(f, _) } - /** Holds if this state may not apply to the control flow node `f`, given the context `ctx`. */ - final predicate appliesTo(ControlFlowNode f, Context ctx) { - StateTracking::appliesToNode(this, f, ctx, true) - } + /** Holds if this state may not apply to the control flow node `f`, given the context `ctx`. */ + final predicate appliesTo(ControlFlowNode f, Context ctx) { + StateTracking::appliesToNode(this, f, ctx, true) + } - /** Holds if this state may apply to the control flow node `f`, given the context `ctx`. */ - final predicate mayNotApplyTo(ControlFlowNode f, Context ctx) { - StateTracking::appliesToNode(this, f, ctx, false) - } + /** Holds if this state may apply to the control flow node `f`, given the context `ctx`. */ + final predicate mayNotApplyTo(ControlFlowNode f, Context ctx) { + StateTracking::appliesToNode(this, f, ctx, false) + } - /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ - final predicate mayNotApplyTo(ControlFlowNode f) { this.mayNotApplyTo(f, _) } + /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ + final predicate mayNotApplyTo(ControlFlowNode f) { this.mayNotApplyTo(f, _) } - /** Holds if `test` shows value to be untainted with `taint`, given the context `ctx`. */ - predicate testsFor(PyEdgeRefinement test, Context ctx, boolean sense) { - ctx.appliesToScope(test.getScope()) and this.testsFor(test, sense) - } + /** Holds if `test` shows value to be untainted with `taint`, given the context `ctx`. */ + predicate testsFor(PyEdgeRefinement test, Context ctx, boolean sense) { + ctx.appliesToScope(test.getScope()) and this.testsFor(test, sense) + } - /** Holds if `test` shows value to be untainted with `taint` */ - predicate testsFor(PyEdgeRefinement test, boolean sense) { none() } + /** Holds if `test` shows value to be untainted with `taint` */ + predicate testsFor(PyEdgeRefinement test, boolean sense) { none() } - /** - * Holds if state starts at `f`. - * Either this predicate or `startsAt(ControlFlowNode f, Context ctx)` - * should be overriden by sub-classes. - */ - predicate startsAt(ControlFlowNode f) { none() } + /** + * Holds if state starts at `f`. + * Either this predicate or `startsAt(ControlFlowNode f, Context ctx)` + * should be overriden by sub-classes. + */ + predicate startsAt(ControlFlowNode f) { none() } - /** - * Holds if state starts at `f` given context `ctx`. - * Either this predicate or `startsAt(ControlFlowNode f)` - * should be overriden by sub-classes. - */ - pragma[noinline] - predicate startsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.startsAt(f) } + /** + * Holds if state starts at `f` given context `ctx`. + * Either this predicate or `startsAt(ControlFlowNode f)` + * should be overriden by sub-classes. + */ + pragma[noinline] + predicate startsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.startsAt(f) } - /** - * Holds if state ends at `f`. - * Either this predicate or `endsAt(ControlFlowNode f, Context ctx)` - * may be overriden by sub-classes. - */ - predicate endsAt(ControlFlowNode f) { none() } + /** + * Holds if state ends at `f`. + * Either this predicate or `endsAt(ControlFlowNode f, Context ctx)` + * may be overriden by sub-classes. + */ + predicate endsAt(ControlFlowNode f) { none() } - /** - * Holds if state ends at `f` given context `ctx`. - * Either this predicate or `endsAt(ControlFlowNode f)` - * may be overriden by sub-classes. - */ - pragma[noinline] - predicate endsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.endsAt(f) } + /** + * Holds if state ends at `f` given context `ctx`. + * Either this predicate or `endsAt(ControlFlowNode f)` + * may be overriden by sub-classes. + */ + pragma[noinline] + predicate endsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.endsAt(f) } } module StateTracking { - private predicate not_allowed(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { - state.endsAt(f, ctx) and sense = true - or - state.startsAt(f, ctx) and sense = false - } + private predicate not_allowed(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { + state.endsAt(f, ctx) and sense = true + or + state.startsAt(f, ctx) and sense = false + } - /** - * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) to - * control flow node `f` given the context `ctx`. - */ - predicate appliesToNode(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { - state.endsAt(f, ctx) and sense = false + /** + * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) to + * control flow node `f` given the context `ctx`. + */ + predicate appliesToNode(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { + state.endsAt(f, ctx) and sense = false + or + state.startsAt(f, ctx) and sense = true + or + not not_allowed(state, f, ctx, sense) and + ( + exists(BasicBlock b | + /* First node in a block */ + f = b.getNode(0) and appliesAtBlockStart(state, b, ctx, sense) or - state.startsAt(f, ctx) and sense = true - or - not not_allowed(state, f, ctx, sense) and - ( - exists(BasicBlock b | - /* First node in a block */ - f = b.getNode(0) and appliesAtBlockStart(state, b, ctx, sense) - or - /* Other nodes in block, except trackable calls */ - exists(int n | - f = b.getNode(n) and - appliesToNode(state, b.getNode(n - 1), ctx, sense) and - not exists(PythonFunctionObjectInternal func, Context callee | - callee.fromCall(f, func, ctx) - ) - ) - ) - or - /* Function entry via call */ - exists(PythonFunctionObjectInternal func, CallNode call, Context caller | - ctx.fromCall(call, func, caller) and - func.getScope().getEntryNode() = f and - appliesToNode(state, call.getAPredecessor(), caller, sense) - ) - or - /* Function return */ - exists(PythonFunctionObjectInternal func, Context callee | - callee.fromCall(f, func, ctx) and - appliesToNode(state, func.getScope().getANormalExit(), callee, sense) - ) - or - /* Other scope entries */ - exists(Scope s | - s.getEntryNode() = f and - ctx.appliesToScope(s) - | - not exists(Scope pred | pred.precedes(s)) and - (ctx.isImport() or ctx.isRuntime()) and - sense = false - or - exists(Scope pred, Context pred_ctx | - appliesToNode(state, pred.getANormalExit(), pred_ctx, sense) and - pred.precedes(s) and - ctx.isRuntime() - | - pred_ctx.isRuntime() or pred_ctx.isImport() - ) - ) + /* Other nodes in block, except trackable calls */ + exists(int n | + f = b.getNode(n) and + appliesToNode(state, b.getNode(n - 1), ctx, sense) and + not exists(PythonFunctionObjectInternal func, Context callee | + callee.fromCall(f, func, ctx) + ) ) - } + ) + or + /* Function entry via call */ + exists(PythonFunctionObjectInternal func, CallNode call, Context caller | + ctx.fromCall(call, func, caller) and + func.getScope().getEntryNode() = f and + appliesToNode(state, call.getAPredecessor(), caller, sense) + ) + or + /* Function return */ + exists(PythonFunctionObjectInternal func, Context callee | + callee.fromCall(f, func, ctx) and + appliesToNode(state, func.getScope().getANormalExit(), callee, sense) + ) + or + /* Other scope entries */ + exists(Scope s | + s.getEntryNode() = f and + ctx.appliesToScope(s) + | + not exists(Scope pred | pred.precedes(s)) and + (ctx.isImport() or ctx.isRuntime()) and + sense = false + or + exists(Scope pred, Context pred_ctx | + appliesToNode(state, pred.getANormalExit(), pred_ctx, sense) and + pred.precedes(s) and + ctx.isRuntime() + | + pred_ctx.isRuntime() or pred_ctx.isImport() + ) + ) + ) + } - /** - * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the - * start of basic block `block` given the context `ctx`. - */ - private predicate appliesAtBlockStart( - TrackableState state, BasicBlock block, Context ctx, boolean sense - ) { - exists(PyEdgeRefinement test | - test.getSuccessor() = block and - state.testsFor(test, ctx, sense) - ) - or - exists(BasicBlock pred | - pred.getASuccessor() = block and - appliesAtBlockEnd(state, pred, ctx, sense) and - not exists(PyEdgeRefinement test | - test.getPredecessor() = pred and - test.getSuccessor() = block and - state.testsFor(test, sense.booleanNot()) - ) - ) - } + /** + * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the + * start of basic block `block` given the context `ctx`. + */ + private predicate appliesAtBlockStart( + TrackableState state, BasicBlock block, Context ctx, boolean sense + ) { + exists(PyEdgeRefinement test | + test.getSuccessor() = block and + state.testsFor(test, ctx, sense) + ) + or + exists(BasicBlock pred | + pred.getASuccessor() = block and + appliesAtBlockEnd(state, pred, ctx, sense) and + not exists(PyEdgeRefinement test | + test.getPredecessor() = pred and + test.getSuccessor() = block and + state.testsFor(test, sense.booleanNot()) + ) + ) + } - /** - * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the - * end of basic block `block` given the context `ctx`. - */ - private predicate appliesAtBlockEnd( - TrackableState state, BasicBlock block, Context ctx, boolean sense - ) { - appliesToNode(state, block.getLastNode(), ctx, sense) - } + /** + * Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the + * end of basic block `block` given the context `ctx`. + */ + private predicate appliesAtBlockEnd( + TrackableState state, BasicBlock block, Context ctx, boolean sense + ) { + appliesToNode(state, block.getLastNode(), ctx, sense) + } } diff --git a/python/ql/src/semmle/python/dataflow/TaintTracking.qll b/python/ql/src/semmle/python/dataflow/TaintTracking.qll index f264e2dd600..d6d1f9388c4 100755 --- a/python/ql/src/semmle/python/dataflow/TaintTracking.qll +++ b/python/ql/src/semmle/python/dataflow/TaintTracking.qll @@ -101,82 +101,82 @@ import semmle.python.dataflow.Configuration * the local file system. */ abstract class TaintKind extends string { - bindingset[this] - TaintKind() { any() } + bindingset[this] + TaintKind() { any() } - /** - * Gets the kind of taint that the named attribute will have if an object is tainted with this taint. - * In other words, if `x` has this kind of taint then it implies that `x.name` - * has `result` kind of taint. + /** + * Gets the kind of taint that the named attribute will have if an object is tainted with this taint. + * In other words, if `x` has this kind of taint then it implies that `x.name` + * has `result` kind of taint. + */ + TaintKind getTaintOfAttribute(string name) { none() } + + /** + * Gets the kind of taint results from calling the named method if an object is tainted with this taint. + * In other words, if `x` has this kind of taint then it implies that `x.name()` + * has `result` kind of taint. + */ + TaintKind getTaintOfMethodResult(string name) { none() } + + /** + * Gets the taint resulting from the flow step `fromnode` -> `tonode`. + */ + TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { none() } + + /** + * Gets the taint resulting from the flow step `fromnode` -> `tonode`, with `edgeLabel` + */ + TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { + result = this.getTaintForFlowStep(fromnode, tonode) and + edgeLabel = "custom taint flow step for " + this + } + + /** + * Holds if this kind of taint "taints" `expr`. + */ + final predicate taints(ControlFlowNode expr) { + exists(TaintedNode n | n.getTaintKind() = this and n.getCfgNode() = expr) + } + + /** DEPRECATED -- Use getType() instead */ + deprecated ClassObject getClass() { none() } + + /** + * Gets the class of this kind of taint. + * For example, if this were a kind of string taint + * the `result` would be `theStrType()`. + */ + ClassValue getType() { none() } + + /** + * Gets the boolean values (may be one, neither, or both) that + * may result from the Python expression `bool(this)` + */ + boolean booleanValue() { + /* + * Default to true as the vast majority of taint is strings and + * the empty string is almost always benign. */ - TaintKind getTaintOfAttribute(string name) { none() } - /** - * Gets the kind of taint results from calling the named method if an object is tainted with this taint. - * In other words, if `x` has this kind of taint then it implies that `x.name()` - * has `result` kind of taint. - */ - TaintKind getTaintOfMethodResult(string name) { none() } + result = true + } - /** - * Gets the taint resulting from the flow step `fromnode` -> `tonode`. - */ - TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { none() } + string repr() { result = this } - /** - * Gets the taint resulting from the flow step `fromnode` -> `tonode`, with `edgeLabel` - */ - TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { - result = this.getTaintForFlowStep(fromnode, tonode) and - edgeLabel = "custom taint flow step for " + this - } + /** + * Gets the taint resulting from iterating over this kind of taint. + * For example iterating over a text file produces lines. So iterating + * over a tainted file would result in tainted strings + */ + TaintKind getTaintForIteration() { none() } - /** - * Holds if this kind of taint "taints" `expr`. - */ - final predicate taints(ControlFlowNode expr) { - exists(TaintedNode n | n.getTaintKind() = this and n.getCfgNode() = expr) - } - - /** DEPRECATED -- Use getType() instead */ - deprecated ClassObject getClass() { none() } - - /** - * Gets the class of this kind of taint. - * For example, if this were a kind of string taint - * the `result` would be `theStrType()`. - */ - ClassValue getType() { none() } - - /** - * Gets the boolean values (may be one, neither, or both) that - * may result from the Python expression `bool(this)` - */ - boolean booleanValue() { - /* - * Default to true as the vast majority of taint is strings and - * the empty string is almost always benign. - */ - - result = true - } - - string repr() { result = this } - - /** - * Gets the taint resulting from iterating over this kind of taint. - * For example iterating over a text file produces lines. So iterating - * over a tainted file would result in tainted strings - */ - TaintKind getTaintForIteration() { none() } - - predicate flowStep(DataFlow::Node fromnode, DataFlow::Node tonode, string edgeLabel) { - exists(DataFlowExtension::DataFlowVariable v | - v = fromnode.asVariable() and - v.getASuccessorVariable() = tonode.asVariable() - ) and - edgeLabel = "custom taint variable step" - } + predicate flowStep(DataFlow::Node fromnode, DataFlow::Node tonode, string edgeLabel) { + exists(DataFlowExtension::DataFlowVariable v | + v = fromnode.asVariable() and + v.getASuccessorVariable() = tonode.asVariable() + ) and + edgeLabel = "custom taint variable step" + } } /** @@ -193,19 +193,19 @@ class FlowLabel = TaintKind; * and ease of preventing infinite recursion. */ abstract class CollectionKind extends TaintKind { - bindingset[this] - CollectionKind() { - (this.charAt(0) = "[" or this.charAt(0) = "{") and - /* Prevent any collection kinds more than 2 deep */ - not this.charAt(2) = "[" and - not this.charAt(2) = "{" - } + bindingset[this] + CollectionKind() { + (this.charAt(0) = "[" or this.charAt(0) = "{") and + /* Prevent any collection kinds more than 2 deep */ + not this.charAt(2) = "[" and + not this.charAt(2) = "{" + } - abstract TaintKind getMember(); + abstract TaintKind getMember(); - abstract predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode); + abstract predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode); - abstract predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode); + abstract predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode); } /** @@ -213,82 +213,82 @@ abstract class CollectionKind extends TaintKind { * Typically a sequence, but can include sets. */ class SequenceKind extends CollectionKind { - TaintKind itemKind; + TaintKind itemKind; - SequenceKind() { this = "[" + itemKind + "]" } + SequenceKind() { this = "[" + itemKind + "]" } - TaintKind getItem() { result = itemKind } + TaintKind getItem() { result = itemKind } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - exists(BinaryExprNode mod | - mod = tonode and - mod.getOp() instanceof Mod and - mod.getAnOperand() = fromnode and - result = this.getItem() and - result.getType() = ObjectInternal::builtin("str") - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + exists(BinaryExprNode mod | + mod = tonode and + mod.getOp() instanceof Mod and + mod.getAnOperand() = fromnode and + result = this.getItem() and + result.getType() = ObjectInternal::builtin("str") + ) + } - override TaintKind getTaintOfMethodResult(string name) { - name = "pop" and result = this.getItem() - } + override TaintKind getTaintOfMethodResult(string name) { + name = "pop" and result = this.getItem() + } - override string repr() { result = "sequence of " + itemKind } + override string repr() { result = "sequence of " + itemKind } - override TaintKind getTaintForIteration() { result = itemKind } + override TaintKind getTaintForIteration() { result = itemKind } - override TaintKind getMember() { result = itemKind } + override TaintKind getMember() { result = itemKind } - override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - sequence_construct(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + sequence_construct(fromnode.asCfgNode(), tonode.asCfgNode()) + } - override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - SequenceKind::itemFlowStep(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + SequenceKind::itemFlowStep(fromnode.asCfgNode(), tonode.asCfgNode()) + } } module SequenceKind { - predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { - tonode.(BinaryExprNode).getAnOperand() = fromnode and edgeLabel = "binary operation" - or - Implementation::copyCall(fromnode, tonode) and - edgeLabel = "dict copy" - or - sequence_call(fromnode, tonode) and edgeLabel = "sequence construction" - or - subscript_slice(fromnode, tonode) and edgeLabel = "slicing" - } + predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { + tonode.(BinaryExprNode).getAnOperand() = fromnode and edgeLabel = "binary operation" + or + Implementation::copyCall(fromnode, tonode) and + edgeLabel = "dict copy" + or + sequence_call(fromnode, tonode) and edgeLabel = "sequence construction" + or + subscript_slice(fromnode, tonode) and edgeLabel = "slicing" + } - predicate itemFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - subscript_index(fromnode, tonode) - } + predicate itemFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + subscript_index(fromnode, tonode) + } } module DictKind { - predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { - Implementation::copyCall(fromnode, tonode) and - edgeLabel = "dict copy" - or - tonode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and - tonode.(CallNode).getArg(0) = fromnode and - edgeLabel = "dict() call" - } + predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) { + Implementation::copyCall(fromnode, tonode) and + edgeLabel = "dict copy" + or + tonode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and + tonode.(CallNode).getArg(0) = fromnode and + edgeLabel = "dict() call" + } } /* Helper for sequence flow steps */ pragma[noinline] private predicate subscript_index(ControlFlowNode obj, SubscriptNode sub) { - sub.isLoad() and - sub.getObject() = obj and - not sub.getNode().getIndex() instanceof Slice + sub.isLoad() and + sub.getObject() = obj and + not sub.getNode().getIndex() instanceof Slice } pragma[noinline] private predicate subscript_slice(ControlFlowNode obj, SubscriptNode sub) { - sub.isLoad() and - sub.getObject() = obj and - sub.getNode().getIndex() instanceof Slice + sub.isLoad() and + sub.getObject() = obj and + sub.getNode().getIndex() instanceof Slice } /** @@ -296,31 +296,31 @@ private predicate subscript_slice(ControlFlowNode obj, SubscriptNode sub) { * Typically a dict, but can include other mappings. */ class DictKind extends CollectionKind { - TaintKind valueKind; + TaintKind valueKind; - DictKind() { this = "{" + valueKind + "}" } + DictKind() { this = "{" + valueKind + "}" } - TaintKind getValue() { result = valueKind } + TaintKind getValue() { result = valueKind } - override TaintKind getTaintOfMethodResult(string name) { - name = "get" and result = valueKind - or - name = "values" and result.(SequenceKind).getItem() = valueKind - or - name = "itervalues" and result.(SequenceKind).getItem() = valueKind - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result = valueKind + or + name = "values" and result.(SequenceKind).getItem() = valueKind + or + name = "itervalues" and result.(SequenceKind).getItem() = valueKind + } - override string repr() { result = "dict of " + valueKind } + override string repr() { result = "dict of " + valueKind } - override TaintKind getMember() { result = valueKind } + override TaintKind getMember() { result = valueKind } - override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - dict_construct(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + dict_construct(fromnode.asCfgNode(), tonode.asCfgNode()) + } - override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { - subscript_index(fromnode.asCfgNode(), tonode.asCfgNode()) - } + override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) { + subscript_index(fromnode.asCfgNode(), tonode.asCfgNode()) + } } /** @@ -330,23 +330,23 @@ class DictKind extends CollectionKind { * For example, a sanitizer for DB commands would not be safe to use for http responses. */ abstract class Sanitizer extends string { - bindingset[this] - Sanitizer() { any() } + bindingset[this] + Sanitizer() { any() } - /** Holds if `taint` cannot flow through `node`. */ - predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { none() } + /** Holds if `taint` cannot flow through `node`. */ + predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { none() } - /** Holds if `call` removes removes the `taint` */ - predicate sanitizingCall(TaintKind taint, FunctionObject callee) { none() } + /** Holds if `call` removes removes the `taint` */ + predicate sanitizingCall(TaintKind taint, FunctionObject callee) { none() } - /** Holds if `test` shows value to be untainted with `taint` */ - predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { none() } + /** Holds if `test` shows value to be untainted with `taint` */ + predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { none() } - /** Holds if `test` shows value to be untainted with `taint` */ - predicate sanitizingSingleEdge(TaintKind taint, SingleSuccessorGuard test) { none() } + /** Holds if `test` shows value to be untainted with `taint` */ + predicate sanitizingSingleEdge(TaintKind taint, SingleSuccessorGuard test) { none() } - /** Holds if `def` shows value to be untainted with `taint` */ - predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { none() } + /** Holds if `def` shows value to be untainted with `taint` */ + predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { none() } } /** @@ -355,65 +355,66 @@ abstract class Sanitizer extends string { * class to provide their own sources. */ abstract class TaintSource extends @py_flow_node { - /** Gets a textual representation of this element. */ - string toString() { result = "Taint source" } + /** Gets a textual representation of this element. */ + string toString() { result = "Taint source" } - /** - * Holds if `this` is a source of taint kind `kind` - * - * This must be overridden by subclasses to specify sources of taint. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - abstract predicate isSourceOf(TaintKind kind); + /** + * Holds if `this` is a source of taint kind `kind` + * + * This must be overridden by subclasses to specify sources of taint. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + abstract predicate isSourceOf(TaintKind kind); - /** - * Holds if `this` is a source of taint kind `kind` for the given context. - * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { - context.isTop() and this.isSourceOf(kind) - } + /** + * Holds if `this` is a source of taint kind `kind` for the given context. + * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { + context.isTop() and this.isSourceOf(kind) + } - Location getLocation() { result = this.(ControlFlowNode).getLocation() } + Location getLocation() { result = this.(ControlFlowNode).getLocation() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } - /** Gets a TaintedNode for this taint source */ - TaintedNode getATaintNode() { - result.getCfgNode() = this and - this.isSourceOf(result.getTaintKind(), result.getContext()) and - result.getPath().noAttribute() - } + /** Gets a TaintedNode for this taint source */ + TaintedNode getATaintNode() { + result.getCfgNode() = this and + this.isSourceOf(result.getTaintKind(), result.getContext()) and + result.getPath().noAttribute() + } - /** Holds if taint can flow from this source to sink `sink` */ - final predicate flowsToSink(TaintKind srckind, TaintSink sink) { - exists(TaintedNode src, TaintedNode tsink | - src = this.getATaintNode() and - src.getTaintKind() = srckind and - src.flowsTo(tsink) and - this.isSourceOf(srckind, _) and - sink = tsink.getCfgNode() and - sink.sinks(tsink.getTaintKind()) and - tsink.getPath().noAttribute() and - tsink.isSink() - ) - } + /** Holds if taint can flow from this source to sink `sink` */ + final predicate flowsToSink(TaintKind srckind, TaintSink sink) { + exists(TaintedNode src, TaintedNode tsink | + src = this.getATaintNode() and + src.getTaintKind() = srckind and + src.flowsTo(tsink) and + this.isSourceOf(srckind, _) and + sink = tsink.getCfgNode() and + sink.sinks(tsink.getTaintKind()) and + tsink.getPath().noAttribute() and + tsink.isSink() + ) + } - /** Holds if taint can flow from this source to taint sink `sink` */ - final predicate flowsToSink(TaintSink sink) { this.flowsToSink(_, sink) } + /** Holds if taint can flow from this source to taint sink `sink` */ + final predicate flowsToSink(TaintSink sink) { this.flowsToSink(_, sink) } } /** @@ -423,50 +424,50 @@ abstract class TaintSource extends @py_flow_node { * class to provide their own sources on the ESSA graph. */ abstract class TaintedDefinition extends EssaNodeDefinition { - /** - * Holds if `this` is a source of taint kind `kind` - * - * This should be overridden by subclasses to specify sources of taint. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - abstract predicate isSourceOf(TaintKind kind); + /** + * Holds if `this` is a source of taint kind `kind` + * + * This should be overridden by subclasses to specify sources of taint. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + abstract predicate isSourceOf(TaintKind kind); - /** - * Holds if `this` is a source of taint kind `kind` for the given context. - * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. - * - * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. - */ - predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { - context.isTop() and this.isSourceOf(kind) - } + /** + * Holds if `this` is a source of taint kind `kind` for the given context. + * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + predicate isSourceOf(TaintKind kind, TaintTrackingContext context) { + context.isTop() and this.isSourceOf(kind) + } } private class DictUpdate extends DataFlowExtension::DataFlowNode { - MethodCallsiteRefinement call; + MethodCallsiteRefinement call; - DictUpdate() { - exists(CallNode c | c = call.getCall() | - c.getFunction().(AttrNode).getName() = "update" and - c.getArg(0) = this - ) - } + DictUpdate() { + exists(CallNode c | c = call.getCall() | + c.getFunction().(AttrNode).getName() = "update" and + c.getArg(0) = this + ) + } - override EssaVariable getASuccessorVariable() { call.getVariable() = result } + override EssaVariable getASuccessorVariable() { call.getVariable() = result } } private class SequenceExtends extends DataFlowExtension::DataFlowNode { - MethodCallsiteRefinement call; + MethodCallsiteRefinement call; - SequenceExtends() { - exists(CallNode c | c = call.getCall() | - c.getFunction().(AttrNode).getName() = "extend" and - c.getArg(0) = this - ) - } + SequenceExtends() { + exists(CallNode c | c = call.getCall() | + c.getFunction().(AttrNode).getName() = "extend" and + c.getArg(0) = this + ) + } - override EssaVariable getASuccessorVariable() { call.getVariable() = result } + override EssaVariable getASuccessorVariable() { call.getVariable() = result } } /** @@ -479,30 +480,31 @@ private class SequenceExtends extends DataFlowExtension::DataFlowNode { * class to provide their own sink nodes. */ abstract class TaintSink extends @py_flow_node { - /** Gets a textual representation of this element. */ - string toString() { result = "Taint sink" } + /** Gets a textual representation of this element. */ + string toString() { result = "Taint sink" } - /** - * Holds if `this` "sinks" taint kind `kind` - * Typically this means that `this` is vulnerable to taint kind `kind`. - * - * This must be overridden by subclasses to specify vulnerabilities or other sinks of taint. - */ - abstract predicate sinks(TaintKind taint); + /** + * Holds if `this` "sinks" taint kind `kind` + * Typically this means that `this` is vulnerable to taint kind `kind`. + * + * This must be overridden by subclasses to specify vulnerabilities or other sinks of taint. + */ + abstract predicate sinks(TaintKind taint); - Location getLocation() { result = this.(ControlFlowNode).getLocation() } + Location getLocation() { result = this.(ControlFlowNode).getLocation() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } } /** @@ -511,87 +513,87 @@ abstract class TaintSink extends @py_flow_node { * data-flow machinery. */ module DataFlowExtension { - /** A control flow node that modifies the basic data-flow. */ - abstract class DataFlowNode extends @py_flow_node { - /** Gets a textual representation of this element. */ - string toString() { result = "Dataflow extension node" } + /** A control flow node that modifies the basic data-flow. */ + abstract class DataFlowNode extends @py_flow_node { + /** Gets a textual representation of this element. */ + string toString() { result = "Dataflow extension node" } - /** - * Gets a successor node for data-flow. - * Data (all forms) is assumed to flow from `this` to `result` - */ - ControlFlowNode getASuccessorNode() { none() } + /** + * Gets a successor node for data-flow. + * Data (all forms) is assumed to flow from `this` to `result` + */ + ControlFlowNode getASuccessorNode() { none() } - /** - * Gets a successor variable for data-flow. - * Data (all forms) is assumed to flow from `this` to `result`. - * Note: This is an unlikely form of flow. See `DataFlowVariable.getASuccessorVariable()` - */ - EssaVariable getASuccessorVariable() { none() } + /** + * Gets a successor variable for data-flow. + * Data (all forms) is assumed to flow from `this` to `result`. + * Note: This is an unlikely form of flow. See `DataFlowVariable.getASuccessorVariable()` + */ + EssaVariable getASuccessorVariable() { none() } - /** - * Holds if data cannot flow from `this` to `succ`, - * even though it would normally do so. - */ - predicate prunedSuccessor(ControlFlowNode succ) { none() } + /** + * Holds if data cannot flow from `this` to `succ`, + * even though it would normally do so. + */ + predicate prunedSuccessor(ControlFlowNode succ) { none() } - /** - * Gets a successor node, where the successor node will be tainted with `tokind` - * when `this` is tainted with `fromkind`. - * Extensions to `DataFlowNode` should override this to provide additional taint steps. - */ - ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { none() } + /** + * Gets a successor node, where the successor node will be tainted with `tokind` + * when `this` is tainted with `fromkind`. + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { none() } - /** - * Gets a successor node for data-flow with a change of context from callee to caller - * (going *up* the call-stack) across call-site `call`. - * Data (all forms) is assumed to flow from `this` to `result` - * Extensions to `DataFlowNode` should override this to provide additional taint steps. - */ - ControlFlowNode getAReturnSuccessorNode(CallNode call) { none() } + /** + * Gets a successor node for data-flow with a change of context from callee to caller + * (going *up* the call-stack) across call-site `call`. + * Data (all forms) is assumed to flow from `this` to `result` + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getAReturnSuccessorNode(CallNode call) { none() } - /** - * Gets a successor node for data-flow with a change of context from caller to callee - * (going *down* the call-stack) across call-site `call`. - * Data (all forms) is assumed to flow from `this` to `result` - * Extensions to `DataFlowNode` should override this to provide additional taint steps. - */ - ControlFlowNode getACalleeSuccessorNode(CallNode call) { none() } - } + /** + * Gets a successor node for data-flow with a change of context from caller to callee + * (going *down* the call-stack) across call-site `call`. + * Data (all forms) is assumed to flow from `this` to `result` + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getACalleeSuccessorNode(CallNode call) { none() } + } - /** Data flow variable that modifies the basic data-flow. */ - class DataFlowVariable extends EssaVariable { - /** - * Gets a successor node for data-flow. - * Data (all forms) is assumed to flow from `this` to `result` - * Note: This is an unlikely form of flow. See `DataFlowNode.getASuccessorNode()` - */ - ControlFlowNode getASuccessorNode() { none() } + /** Data flow variable that modifies the basic data-flow. */ + class DataFlowVariable extends EssaVariable { + /** + * Gets a successor node for data-flow. + * Data (all forms) is assumed to flow from `this` to `result` + * Note: This is an unlikely form of flow. See `DataFlowNode.getASuccessorNode()` + */ + ControlFlowNode getASuccessorNode() { none() } - /** - * Gets a successor variable for data-flow. - * Data (all forms) is assumed to flow from `this` to `result`. - */ - EssaVariable getASuccessorVariable() { none() } + /** + * Gets a successor variable for data-flow. + * Data (all forms) is assumed to flow from `this` to `result`. + */ + EssaVariable getASuccessorVariable() { none() } - /** - * Holds if data cannot flow from `this` to `succ`, - * even though it would normally do so. - */ - predicate prunedSuccessor(EssaVariable succ) { none() } - } + /** + * Holds if data cannot flow from `this` to `succ`, + * even though it would normally do so. + */ + predicate prunedSuccessor(EssaVariable succ) { none() } + } } class TaintedPathSource extends TaintTrackingNode { - TaintedPathSource() { this.isSource() } + TaintedPathSource() { this.isSource() } - DataFlow::Node getSource() { result = this.getNode() } + DataFlow::Node getSource() { result = this.getNode() } } class TaintedPathSink extends TaintTrackingNode { - TaintedPathSink() { this.isSink() } + TaintedPathSink() { this.isSink() } - DataFlow::Node getSink() { result = this.getNode() } + DataFlow::Node getSink() { result = this.getNode() } } /* Backwards compatible name */ @@ -605,145 +607,145 @@ private import semmle.python.pointsto.PointsTo * the other language implementations. */ module DataFlow { - /** - * Generic taint kind, source and sink classes for convenience and - * compatibility with other language libraries - */ - class Extension = DataFlowExtension::DataFlowNode; + /** + * Generic taint kind, source and sink classes for convenience and + * compatibility with other language libraries + */ + class Extension = DataFlowExtension::DataFlowNode; - abstract deprecated class Configuration extends string { - bindingset[this] - Configuration() { this = this } + abstract deprecated class Configuration extends string { + bindingset[this] + Configuration() { this = this } - abstract predicate isSource(ControlFlowNode source); + abstract predicate isSource(ControlFlowNode source); - abstract predicate isSink(ControlFlowNode sink); + abstract predicate isSink(ControlFlowNode sink); - private predicate hasFlowPath(TaintedNode source, TaintedNode sink) { - source.getConfiguration() = this and - this.isSource(source.getCfgNode()) and - this.isSink(sink.getCfgNode()) and - source.flowsTo(sink) - } - - predicate hasFlow(ControlFlowNode source, ControlFlowNode sink) { - exists(TaintedNode psource, TaintedNode psink | - psource.getCfgNode() = source and - psink.getCfgNode() = sink and - this.isSource(source) and - this.isSink(sink) and - this.hasFlowPath(psource, psink) - ) - } + private predicate hasFlowPath(TaintedNode source, TaintedNode sink) { + source.getConfiguration() = this and + this.isSource(source.getCfgNode()) and + this.isSink(sink.getCfgNode()) and + source.flowsTo(sink) } - deprecated private class ConfigurationAdapter extends TaintTracking::Configuration { - ConfigurationAdapter() { this instanceof Configuration } + predicate hasFlow(ControlFlowNode source, ControlFlowNode sink) { + exists(TaintedNode psource, TaintedNode psink | + psource.getCfgNode() = source and + psink.getCfgNode() = sink and + this.isSource(source) and + this.isSink(sink) and + this.hasFlowPath(psource, psink) + ) + } + } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - this.(Configuration).isSource(node.asCfgNode()) and - kind instanceof DataFlowType - } + deprecated private class ConfigurationAdapter extends TaintTracking::Configuration { + ConfigurationAdapter() { this instanceof Configuration } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - this.(Configuration).isSink(node.asCfgNode()) and - kind instanceof DataFlowType - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + this.(Configuration).isSource(node.asCfgNode()) and + kind instanceof DataFlowType } - private newtype TDataFlowNode = - TEssaNode(EssaVariable var) or - TCfgNode(ControlFlowNode node) + override predicate isSink(DataFlow::Node node, TaintKind kind) { + this.(Configuration).isSink(node.asCfgNode()) and + kind instanceof DataFlowType + } + } - abstract class Node extends TDataFlowNode { - abstract ControlFlowNode asCfgNode(); + private newtype TDataFlowNode = + TEssaNode(EssaVariable var) or + TCfgNode(ControlFlowNode node) - abstract EssaVariable asVariable(); + abstract class Node extends TDataFlowNode { + abstract ControlFlowNode asCfgNode(); - /** Gets a textual representation of this element. */ - abstract string toString(); + abstract EssaVariable asVariable(); - abstract Scope getScope(); + /** Gets a textual representation of this element. */ + abstract string toString(); - abstract BasicBlock getBasicBlock(); + abstract Scope getScope(); - abstract Location getLocation(); + abstract BasicBlock getBasicBlock(); - AstNode asAstNode() { result = this.asCfgNode().getNode() } + abstract Location getLocation(); - /** For backwards compatibility -- Use asAstNode() instead */ - deprecated AstNode getNode() { result = this.asAstNode() } + AstNode asAstNode() { result = this.asCfgNode().getNode() } + + /** For backwards compatibility -- Use asAstNode() instead */ + deprecated AstNode getNode() { result = this.asAstNode() } + } + + class CfgNode extends Node, TCfgNode { + override ControlFlowNode asCfgNode() { this = TCfgNode(result) } + + override EssaVariable asVariable() { none() } + + /** Gets a textual representation of this element. */ + override string toString() { result = this.asAstNode().toString() } + + override Scope getScope() { result = this.asCfgNode().getScope() } + + override BasicBlock getBasicBlock() { result = this.asCfgNode().getBasicBlock() } + + override Location getLocation() { result = this.asCfgNode().getLocation() } + } + + class EssaNode extends Node, TEssaNode { + override ControlFlowNode asCfgNode() { none() } + + override EssaVariable asVariable() { this = TEssaNode(result) } + + /** Gets a textual representation of this element. */ + override string toString() { result = this.asVariable().toString() } + + override Scope getScope() { result = this.asVariable().getScope() } + + override BasicBlock getBasicBlock() { + result = this.asVariable().getDefinition().getBasicBlock() } - class CfgNode extends Node, TCfgNode { - override ControlFlowNode asCfgNode() { this = TCfgNode(result) } - - override EssaVariable asVariable() { none() } - - /** Gets a textual representation of this element. */ - override string toString() { result = this.asAstNode().toString() } - - override Scope getScope() { result = this.asCfgNode().getScope() } - - override BasicBlock getBasicBlock() { result = this.asCfgNode().getBasicBlock() } - - override Location getLocation() { result = this.asCfgNode().getLocation() } - } - - class EssaNode extends Node, TEssaNode { - override ControlFlowNode asCfgNode() { none() } - - override EssaVariable asVariable() { this = TEssaNode(result) } - - /** Gets a textual representation of this element. */ - override string toString() { result = this.asVariable().toString() } - - override Scope getScope() { result = this.asVariable().getScope() } - - override BasicBlock getBasicBlock() { - result = this.asVariable().getDefinition().getBasicBlock() - } - - override Location getLocation() { result = this.asVariable().getDefinition().getLocation() } - } + override Location getLocation() { result = this.asVariable().getDefinition().getLocation() } + } } deprecated private class DataFlowType extends TaintKind { - DataFlowType() { - this = "Data flow" and - exists(DataFlow::Configuration c) - } + DataFlowType() { + this = "Data flow" and + exists(DataFlow::Configuration c) + } } pragma[noinline] private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictnode) { - dictnode.(DictNode).getAValue() = itemnode - or - dictnode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and - dictnode.(CallNode).getArgByName(_) = itemnode + dictnode.(DictNode).getAValue() = itemnode + or + dictnode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and + dictnode.(CallNode).getArgByName(_) = itemnode } pragma[noinline] private predicate sequence_construct(ControlFlowNode itemnode, ControlFlowNode seqnode) { - seqnode.isLoad() and - ( - seqnode.(ListNode).getElement(_) = itemnode - or - seqnode.(TupleNode).getElement(_) = itemnode - or - seqnode.(SetNode).getAnElement() = itemnode - ) + seqnode.isLoad() and + ( + seqnode.(ListNode).getElement(_) = itemnode + or + seqnode.(TupleNode).getElement(_) = itemnode + or + seqnode.(SetNode).getAnElement() = itemnode + ) } /* A call to construct a sequence from a sequence or iterator*/ pragma[noinline] private predicate sequence_call(ControlFlowNode fromnode, CallNode tonode) { - tonode.getArg(0) = fromnode and - exists(ControlFlowNode cls | cls = tonode.getFunction() | - cls.pointsTo(ObjectInternal::builtin("list")) - or - cls.pointsTo(ObjectInternal::builtin("tuple")) - or - cls.pointsTo(ObjectInternal::builtin("set")) - ) + tonode.getArg(0) = fromnode and + exists(ControlFlowNode cls | cls = tonode.getFunction() | + cls.pointsTo(ObjectInternal::builtin("list")) + or + cls.pointsTo(ObjectInternal::builtin("tuple")) + or + cls.pointsTo(ObjectInternal::builtin("set")) + ) } diff --git a/python/ql/src/semmle/python/dependencies/Dependencies.qll b/python/ql/src/semmle/python/dependencies/Dependencies.qll index 6d2052f0dfb..3c018a13837 100644 --- a/python/ql/src/semmle/python/dependencies/Dependencies.qll +++ b/python/ql/src/semmle/python/dependencies/Dependencies.qll @@ -2,72 +2,72 @@ import python import semmle.python.dependencies.DependencyKind private predicate importDependency(Object target, AstNode source) { - source.getScope() != target.getOrigin() and - /* Imports of own module are ignored */ - ( - exists(ModuleObject importee, ImportingStmt imp_stmt | - source = imp_stmt and - importee = target - | - exists(ImportMember im | imp_stmt.contains(im) | - importee.importedAs(im.getImportedModuleName()) - ) - or - exists(ImportExpr im | imp_stmt.contains(im) | - importee.importedAs(im.getImportedModuleName()) - ) - or - exists(ModuleObject mod | - importDependency(mod, source) and - target = mod.getPackage+() - ) - ) - or - /* from m import name, where m.name is not a submodule */ - exists(PythonModuleObject importee, ImportingStmt imp_stmt | source = imp_stmt | - exists(ImportMember im | imp_stmt.contains(im) | - importee.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and - defn_of_module_attribute(target, importee.getModule(), im.getName()) - ) - ) + source.getScope() != target.getOrigin() and + /* Imports of own module are ignored */ + ( + exists(ModuleObject importee, ImportingStmt imp_stmt | + source = imp_stmt and + importee = target + | + exists(ImportMember im | imp_stmt.contains(im) | + importee.importedAs(im.getImportedModuleName()) + ) + or + exists(ImportExpr im | imp_stmt.contains(im) | + importee.importedAs(im.getImportedModuleName()) + ) + or + exists(ModuleObject mod | + importDependency(mod, source) and + target = mod.getPackage+() + ) ) + or + /* from m import name, where m.name is not a submodule */ + exists(PythonModuleObject importee, ImportingStmt imp_stmt | source = imp_stmt | + exists(ImportMember im | imp_stmt.contains(im) | + importee.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) and + defn_of_module_attribute(target, importee.getModule(), im.getName()) + ) + ) + ) } class PythonImport extends DependencyKind { - PythonImport() { this = "import" } + PythonImport() { this = "import" } - override predicate isADependency(AstNode source, Object target) { - this = this and - importDependency(target, source) - } + override predicate isADependency(AstNode source, Object target) { + this = this and + importDependency(target, source) + } } private predicate interesting(Object target) { - target.(ControlFlowNode).getNode() instanceof Scope - or - target instanceof FunctionObject - or - target instanceof ClassObject - or - target instanceof ModuleObject + target.(ControlFlowNode).getNode() instanceof Scope + or + target instanceof FunctionObject + or + target instanceof ClassObject + or + target instanceof ModuleObject } class PythonUse extends DependencyKind { - PythonUse() { this = "use" } + PythonUse() { this = "use" } - override predicate isADependency(AstNode source, Object target) { - interesting(target) and - this = this and - source != target.(ControlFlowNode).getNode() and - exists(ControlFlowNode use, Object obj | - use.getNode() = source and - use.refersTo(obj) and - use.isLoad() - | - interesting(obj) and target = obj - ) and - not has_more_specific_dependency_source(source) - } + override predicate isADependency(AstNode source, Object target) { + interesting(target) and + this = this and + source != target.(ControlFlowNode).getNode() and + exists(ControlFlowNode use, Object obj | + use.getNode() = source and + use.refersTo(obj) and + use.isLoad() + | + interesting(obj) and target = obj + ) and + not has_more_specific_dependency_source(source) + } } /** @@ -76,66 +76,66 @@ class PythonUse extends DependencyKind { * don't make pack.mod depend on the module 'pack.mod' */ private predicate has_more_specific_dependency_source(Expr e) { - exists(Attribute member | member.getObject() = e | - attribute_access_dependency(_, member) - or - has_more_specific_dependency_source(member) - ) + exists(Attribute member | member.getObject() = e | + attribute_access_dependency(_, member) + or + has_more_specific_dependency_source(member) + ) } class PythonInheritance extends DependencyKind { - PythonInheritance() { this = "inheritance" } + PythonInheritance() { this = "inheritance" } - override predicate isADependency(AstNode source, Object target) { - this = this and - exists(ClassObject cls | source = cls.getOrigin() | - target = cls.getASuperType() - or - target = cls.getAnInferredType() - ) - } + override predicate isADependency(AstNode source, Object target) { + this = this and + exists(ClassObject cls | source = cls.getOrigin() | + target = cls.getASuperType() + or + target = cls.getAnInferredType() + ) + } } class PythonAttribute extends DependencyKind { - PythonAttribute() { this = "attribute" } + PythonAttribute() { this = "attribute" } - override predicate isADependency(AstNode source, Object target) { - this = this and - attribute_access_dependency(target, source) - } + override predicate isADependency(AstNode source, Object target) { + this = this and + attribute_access_dependency(target, source) + } } private predicate attribute_access_dependency(Object target, AstNode source) { - exists(Scope s, string name | - use_of_attribute(source, s, name) and - defn_of_attribute(target, s, name) - ) + exists(Scope s, string name | + use_of_attribute(source, s, name) and + defn_of_attribute(target, s, name) + ) } private predicate use_of_attribute(Attribute attr, Scope s, string name) { - exists(AttrNode cfg | cfg.isLoad() and cfg.getNode() = attr | - exists(Object obj | cfg.getObject(name).refersTo(obj) | - s = obj.(PythonModuleObject).getModule() or - s = obj.(ClassObject).getPyClass() - ) - or - exists(ClassObject cls | cfg.getObject(name).refersTo(_, cls, _) | s = cls.getPyClass()) + exists(AttrNode cfg | cfg.isLoad() and cfg.getNode() = attr | + exists(Object obj | cfg.getObject(name).refersTo(obj) | + s = obj.(PythonModuleObject).getModule() or + s = obj.(ClassObject).getPyClass() ) or - exists(SelfAttributeRead sar | sar = attr | - sar.getClass() = s and - sar.getName() = name - ) + exists(ClassObject cls | cfg.getObject(name).refersTo(_, cls, _) | s = cls.getPyClass()) + ) + or + exists(SelfAttributeRead sar | sar = attr | + sar.getClass() = s and + sar.getName() = name + ) } private predicate defn_of_attribute(Object target, Scope s, string name) { - exists(Assign asgn | target.(ControlFlowNode).getNode() = asgn | - defn_of_instance_attribute(asgn, s, name) - or - defn_of_class_attribute(asgn, s, name) - ) + exists(Assign asgn | target.(ControlFlowNode).getNode() = asgn | + defn_of_instance_attribute(asgn, s, name) or - defn_of_module_attribute(target, s, name) + defn_of_class_attribute(asgn, s, name) + ) + or + defn_of_module_attribute(target, s, name) } /* @@ -145,30 +145,30 @@ private predicate defn_of_attribute(Object target, Scope s, string name) { */ private predicate defn_of_instance_attribute(Assign asgn, Class c, string name) { - exists(SelfAttributeStore sas | asgn.getATarget() = sas | - sas.getClass() = c and - sas.getName() = name and - not exists(SelfAttributeStore in_init | - not sas.getScope().(Function).isInitMethod() and - not sas = in_init and - in_init.getClass() = c and - in_init.getName() = name and - in_init.getScope().(Function).isInitMethod() - ) + exists(SelfAttributeStore sas | asgn.getATarget() = sas | + sas.getClass() = c and + sas.getName() = name and + not exists(SelfAttributeStore in_init | + not sas.getScope().(Function).isInitMethod() and + not sas = in_init and + in_init.getClass() = c and + in_init.getName() = name and + in_init.getScope().(Function).isInitMethod() ) + ) } /* Whether asgn defines an attribute of a class */ private predicate defn_of_class_attribute(Assign asgn, Class c, string name) { - asgn.getScope() = c and - asgn.getATarget().(Name).getId() = name + asgn.getScope() = c and + asgn.getATarget().(Name).getId() = name } /* Holds if `value` is a value assigned to the `name`d attribute of module `m`. */ private predicate defn_of_module_attribute(ControlFlowNode value, Module m, string name) { - exists(DefinitionNode def | - def.getScope() = m and - def.getValue() = value and - def.(NameNode).getId() = name - ) + exists(DefinitionNode def | + def.getScope() = m and + def.getValue() = value and + def.(NameNode).getId() = name + ) } diff --git a/python/ql/src/semmle/python/dependencies/DependencyKind.qll b/python/ql/src/semmle/python/dependencies/DependencyKind.qll index ae91593d251..2e4fab1af0b 100644 --- a/python/ql/src/semmle/python/dependencies/DependencyKind.qll +++ b/python/ql/src/semmle/python/dependencies/DependencyKind.qll @@ -13,15 +13,15 @@ import semmle.python.dependencies.Dependencies */ abstract class DependencyKind extends string { - bindingset[this] - DependencyKind() { this = this } + bindingset[this] + DependencyKind() { this = this } - /* Tech inventory interface */ - /** - * Identify dependencies associated with this category. - *

- * The source element is the source of the dependency. - *

- */ - abstract predicate isADependency(AstNode source, Object target); + /* Tech inventory interface */ + /** + * Identify dependencies associated with this category. + *

+ * The source element is the source of the dependency. + *

+ */ + abstract predicate isADependency(AstNode source, Object target); } diff --git a/python/ql/src/semmle/python/dependencies/TechInventory.qll b/python/ql/src/semmle/python/dependencies/TechInventory.qll index 43a7cf55ec2..0c48b5ecfbb 100644 --- a/python/ql/src/semmle/python/dependencies/TechInventory.qll +++ b/python/ql/src/semmle/python/dependencies/TechInventory.qll @@ -7,23 +7,23 @@ import semmle.python.dependencies.DependencyKind * /path/to/file.py<|>package-name-and-version */ string munge(File sourceFile, ExternalPackage package) { - result = - "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>" + package.getVersion() - or - not exists(package.getVersion()) and - result = "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>unknown" + result = + "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>" + package.getVersion() + or + not exists(package.getVersion()) and + result = "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>unknown" } abstract class ExternalPackage extends Object { - ExternalPackage() { this instanceof ModuleObject } + ExternalPackage() { this instanceof ModuleObject } - abstract string getName(); + abstract string getName(); - abstract string getVersion(); + abstract string getVersion(); - Object getAttribute(string name) { result = this.(ModuleObject).attr(name) } + Object getAttribute(string name) { result = this.(ModuleObject).attr(name) } - PackageObject getPackage() { result = this.(ModuleObject).getPackage() } + PackageObject getPackage() { result = this.(ModuleObject).getPackage() } } bindingset[text] @@ -31,80 +31,80 @@ private predicate is_version(string text) { text.regexpMatch("\\d+\\.\\d+(\\.\\d bindingset[v] private string version_format(float v) { - exists(int i, int f | i = (v + 0.05).floor() and f = ((v + 0.05 - i) * 10).floor() | - result = i + "." + f - ) + exists(int i, int f | i = (v + 0.05).floor() and f = ((v + 0.05 - i) * 10).floor() | + result = i + "." + f + ) } class DistPackage extends ExternalPackage { - DistPackage() { - exists(Folder parent | - parent = this.(ModuleObject).getPath().getParent() and - parent.isImportRoot() and - /* Not in standard library */ - not parent.isStdLibRoot() and - /* Not in the source */ - not exists(parent.getRelativePath()) - ) - } + DistPackage() { + exists(Folder parent | + parent = this.(ModuleObject).getPath().getParent() and + parent.isImportRoot() and + /* Not in standard library */ + not parent.isStdLibRoot() and + /* Not in the source */ + not exists(parent.getRelativePath()) + ) + } - /* - * We don't extract the meta-data for dependencies (yet), so make a best guess from the source - * https://www.python.org/dev/peps/pep-0396/ - */ + /* + * We don't extract the meta-data for dependencies (yet), so make a best guess from the source + * https://www.python.org/dev/peps/pep-0396/ + */ - private predicate possibleVersion(string version, int priority) { - exists(Object v | v = this.getAttribute("__version__") and priority = 3 | - version = v.(StringObject).getText() and is_version(version) - or - version = version_format(v.(NumericObject).floatValue()) - or - version = version_format(v.(NumericObject).intValue()) - ) - or - exists(SequenceObject tuple, NumericObject major, NumericObject minor, string base_version | - this.getAttribute("version_info") = tuple and - major = tuple.getInferredElement(0) and - minor = tuple.getInferredElement(1) and - base_version = major.intValue() + "." + minor.intValue() - | - version = base_version + "." + tuple.getBuiltinElement(2).(NumericObject).intValue() - or - not exists(tuple.getBuiltinElement(2)) and version = base_version - ) and - priority = 2 - or - exists(string v | v.toLowerCase() = "version" | - is_version(version) and - version = this.getAttribute(v).(StringObject).getText() - ) and - priority = 1 - } + private predicate possibleVersion(string version, int priority) { + exists(Object v | v = this.getAttribute("__version__") and priority = 3 | + version = v.(StringObject).getText() and is_version(version) + or + version = version_format(v.(NumericObject).floatValue()) + or + version = version_format(v.(NumericObject).intValue()) + ) + or + exists(SequenceObject tuple, NumericObject major, NumericObject minor, string base_version | + this.getAttribute("version_info") = tuple and + major = tuple.getInferredElement(0) and + minor = tuple.getInferredElement(1) and + base_version = major.intValue() + "." + minor.intValue() + | + version = base_version + "." + tuple.getBuiltinElement(2).(NumericObject).intValue() + or + not exists(tuple.getBuiltinElement(2)) and version = base_version + ) and + priority = 2 + or + exists(string v | v.toLowerCase() = "version" | + is_version(version) and + version = this.getAttribute(v).(StringObject).getText() + ) and + priority = 1 + } - override string getVersion() { - this.possibleVersion(result, max(int priority | this.possibleVersion(_, priority))) - } + override string getVersion() { + this.possibleVersion(result, max(int priority | this.possibleVersion(_, priority))) + } - override string getName() { result = this.(ModuleObject).getShortName() } + override string getName() { result = this.(ModuleObject).getShortName() } - predicate fromSource(Object src) { - exists(ModuleObject m | - m.getModule() = src.(ControlFlowNode).getEnclosingModule() or - src = m - | - m = this - or - m.getPackage+() = this and - not exists(DistPackage inter | - m.getPackage*() = inter and - inter.getPackage+() = this - ) - ) - } + predicate fromSource(Object src) { + exists(ModuleObject m | + m.getModule() = src.(ControlFlowNode).getEnclosingModule() or + src = m + | + m = this + or + m.getPackage+() = this and + not exists(DistPackage inter | + m.getPackage*() = inter and + inter.getPackage+() = this + ) + ) + } } predicate dependency(AstNode src, DistPackage package) { - exists(DependencyKind cat, Object target | cat.isADependency(src, target) | - package.fromSource(target) - ) + exists(DependencyKind cat, Object target | cat.isADependency(src, target) | + package.fromSource(target) + ) } diff --git a/python/ql/src/semmle/python/essa/Definitions.qll b/python/ql/src/semmle/python/essa/Definitions.qll index 9e203f36ec6..752ff9da329 100644 --- a/python/ql/src/semmle/python/essa/Definitions.qll +++ b/python/ql/src/semmle/python/essa/Definitions.qll @@ -14,345 +14,345 @@ import python /** A source language variable, to be converted into a set of SSA variables. */ abstract class SsaSourceVariable extends @py_variable { - SsaSourceVariable() { - /* Exclude `True`, `False` and `None` */ - not this.(Variable).getALoad() instanceof NameConstant - } + SsaSourceVariable() { + /* Exclude `True`, `False` and `None` */ + not this.(Variable).getALoad() instanceof NameConstant + } - /** Gets the name of this variable */ - string getName() { variable(this, _, result) } + /** Gets the name of this variable */ + string getName() { variable(this, _, result) } - Scope getScope() { variable(this, result, _) } + Scope getScope() { variable(this, result, _) } - /** Gets an implicit use of this variable */ - abstract ControlFlowNode getAnImplicitUse(); + /** Gets an implicit use of this variable */ + abstract ControlFlowNode getAnImplicitUse(); - abstract ControlFlowNode getScopeEntryDefinition(); + abstract ControlFlowNode getScopeEntryDefinition(); - /** Gets a textual representation of this element. */ - string toString() { result = "SsaSourceVariable " + this.getName() } + /** Gets a textual representation of this element. */ + string toString() { result = "SsaSourceVariable " + this.getName() } - /** Gets a use of this variable, either explicit or implicit. */ - ControlFlowNode getAUse() { - result = this.getASourceUse() - or - result = this.getAnImplicitUse() - or - /* - * `import *` is a definition of *all* variables, so must be a use as well, for pass-through - * once we have established that a variable is not redefined. - */ - - SsaSource::import_star_refinement(this, result, _) - or - /* - * Add a use at the end of scope for all variables to keep them live - * This is necessary for taint-tracking. - */ - - result = this.getScope().getANormalExit() - } - - /** Holds if `def` defines an ESSA variable for this variable. */ - predicate hasDefiningNode(ControlFlowNode def) { - def = this.getScopeEntryDefinition() - or - SsaSource::assignment_definition(this, def, _) - or - SsaSource::multi_assignment_definition(this, def, _, _) - or - SsaSource::deletion_definition(this, def) - or - SsaSource::init_module_submodule_defn(this, def) - or - SsaSource::parameter_definition(this, def) - or - SsaSource::exception_capture(this, def) - or - SsaSource::with_definition(this, def) - } - - /** - * Holds if `def` defines an ESSA variable for this variable in such a way - * that the new variable is a refinement in some way of the variable used at `use`. + /** Gets a use of this variable, either explicit or implicit. */ + ControlFlowNode getAUse() { + result = this.getASourceUse() + or + result = this.getAnImplicitUse() + or + /* + * `import *` is a definition of *all* variables, so must be a use as well, for pass-through + * once we have established that a variable is not redefined. */ - predicate hasRefinement(ControlFlowNode use, ControlFlowNode def) { - this.hasDefiningNode(_) and - /* Can't have a refinement unless there is a definition */ - refinement(this, use, def) - } - /** - * Holds if the edge `pred`->`succ` defines an ESSA variable for this variable in such a way - * that the new variable is a refinement in some way of the variable used at `use`. + SsaSource::import_star_refinement(this, result, _) + or + /* + * Add a use at the end of scope for all variables to keep them live + * This is necessary for taint-tracking. */ - predicate hasRefinementEdge(ControlFlowNode use, BasicBlock pred, BasicBlock succ) { - test_contains(pred.getLastNode(), use) and - use.(NameNode).uses(this) and - (pred.getAFalseSuccessor() = succ or pred.getATrueSuccessor() = succ) and - /* There is a store to this variable -- We don't want to refine builtins */ - exists(this.(Variable).getAStore()) - } - /** Gets a use of this variable that corresponds to an explicit use in the source. */ - ControlFlowNode getASourceUse() { - result.(NameNode).uses(this) - or - result.(NameNode).deletes(this) - } + result = this.getScope().getANormalExit() + } - abstract CallNode redefinedAtCallSite(); + /** Holds if `def` defines an ESSA variable for this variable. */ + predicate hasDefiningNode(ControlFlowNode def) { + def = this.getScopeEntryDefinition() + or + SsaSource::assignment_definition(this, def, _) + or + SsaSource::multi_assignment_definition(this, def, _, _) + or + SsaSource::deletion_definition(this, def) + or + SsaSource::init_module_submodule_defn(this, def) + or + SsaSource::parameter_definition(this, def) + or + SsaSource::exception_capture(this, def) + or + SsaSource::with_definition(this, def) + } + + /** + * Holds if `def` defines an ESSA variable for this variable in such a way + * that the new variable is a refinement in some way of the variable used at `use`. + */ + predicate hasRefinement(ControlFlowNode use, ControlFlowNode def) { + this.hasDefiningNode(_) and + /* Can't have a refinement unless there is a definition */ + refinement(this, use, def) + } + + /** + * Holds if the edge `pred`->`succ` defines an ESSA variable for this variable in such a way + * that the new variable is a refinement in some way of the variable used at `use`. + */ + predicate hasRefinementEdge(ControlFlowNode use, BasicBlock pred, BasicBlock succ) { + test_contains(pred.getLastNode(), use) and + use.(NameNode).uses(this) and + (pred.getAFalseSuccessor() = succ or pred.getATrueSuccessor() = succ) and + /* There is a store to this variable -- We don't want to refine builtins */ + exists(this.(Variable).getAStore()) + } + + /** Gets a use of this variable that corresponds to an explicit use in the source. */ + ControlFlowNode getASourceUse() { + result.(NameNode).uses(this) + or + result.(NameNode).deletes(this) + } + + abstract CallNode redefinedAtCallSite(); } private predicate refinement(SsaSourceVariable v, ControlFlowNode use, ControlFlowNode def) { - SsaSource::import_star_refinement(v, use, def) - or - SsaSource::attribute_assignment_refinement(v, use, def) - or - SsaSource::argument_refinement(v, use, def) - or - SsaSource::attribute_deletion_refinement(v, use, def) - or - SsaSource::test_refinement(v, use, def) - or - SsaSource::method_call_refinement(v, use, def) - or - def = v.redefinedAtCallSite() and def = use + SsaSource::import_star_refinement(v, use, def) + or + SsaSource::attribute_assignment_refinement(v, use, def) + or + SsaSource::argument_refinement(v, use, def) + or + SsaSource::attribute_deletion_refinement(v, use, def) + or + SsaSource::test_refinement(v, use, def) + or + SsaSource::method_call_refinement(v, use, def) + or + def = v.redefinedAtCallSite() and def = use } class FunctionLocalVariable extends SsaSourceVariable { - FunctionLocalVariable() { - this.(LocalVariable).getScope() instanceof Function and - not this instanceof NonLocalVariable - } + FunctionLocalVariable() { + this.(LocalVariable).getScope() instanceof Function and + not this instanceof NonLocalVariable + } - override ControlFlowNode getAnImplicitUse() { - this.(Variable).isSelf() and this.(Variable).getScope().getANormalExit() = result - } + override ControlFlowNode getAnImplicitUse() { + this.(Variable).isSelf() and this.(Variable).getScope().getANormalExit() = result + } - override ControlFlowNode getScopeEntryDefinition() { - exists(Scope s | s.getEntryNode() = result | - s = this.(LocalVariable).getScope() and - not this.(LocalVariable).isParameter() - or - s != this.(LocalVariable).getScope() and - s = this.(LocalVariable).getALoad().getScope() - ) - } + override ControlFlowNode getScopeEntryDefinition() { + exists(Scope s | s.getEntryNode() = result | + s = this.(LocalVariable).getScope() and + not this.(LocalVariable).isParameter() + or + s != this.(LocalVariable).getScope() and + s = this.(LocalVariable).getALoad().getScope() + ) + } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class NonLocalVariable extends SsaSourceVariable { - NonLocalVariable() { - exists(Function f | - this.(LocalVariable).getScope() = f and - this.(LocalVariable).getAStore().getScope() != f - ) - } + NonLocalVariable() { + exists(Function f | + this.(LocalVariable).getScope() = f and + this.(LocalVariable).getAStore().getScope() != f + ) + } - override ControlFlowNode getAnImplicitUse() { - result.(CallNode).getScope().getScope*() = this.(LocalVariable).getScope() - } + override ControlFlowNode getAnImplicitUse() { + result.(CallNode).getScope().getScope*() = this.(LocalVariable).getScope() + } - override ControlFlowNode getScopeEntryDefinition() { - exists(Function f | - f.getScope+() = this.(LocalVariable).getScope() and - f.getEntryNode() = result - ) - or - not this.(LocalVariable).isParameter() and - this.(LocalVariable).getScope().getEntryNode() = result - } + override ControlFlowNode getScopeEntryDefinition() { + exists(Function f | + f.getScope+() = this.(LocalVariable).getScope() and + f.getEntryNode() = result + ) + or + not this.(LocalVariable).isParameter() and + this.(LocalVariable).getScope().getEntryNode() = result + } - pragma[noinline] - Scope scope_as_local_variable() { result = this.(LocalVariable).getScope() } + pragma[noinline] + Scope scope_as_local_variable() { result = this.(LocalVariable).getScope() } - override CallNode redefinedAtCallSite() { - result.getScope().getScope*() = this.scope_as_local_variable() - } + override CallNode redefinedAtCallSite() { + result.getScope().getScope*() = this.scope_as_local_variable() + } } class ClassLocalVariable extends SsaSourceVariable { - ClassLocalVariable() { this.(LocalVariable).getScope() instanceof Class } + ClassLocalVariable() { this.(LocalVariable).getScope() instanceof Class } - override ControlFlowNode getAnImplicitUse() { none() } + override ControlFlowNode getAnImplicitUse() { none() } - override ControlFlowNode getScopeEntryDefinition() { - result = this.(LocalVariable).getScope().getEntryNode() - } + override ControlFlowNode getScopeEntryDefinition() { + result = this.(LocalVariable).getScope().getEntryNode() + } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class BuiltinVariable extends SsaSourceVariable { - BuiltinVariable() { - this instanceof GlobalVariable and - not exists(this.(Variable).getAStore()) and - not this.(Variable).getId() = "__name__" and - not this.(Variable).getId() = "__package__" and - not exists(ImportStar is | is.getScope() = this.(Variable).getScope()) - } + BuiltinVariable() { + this instanceof GlobalVariable and + not exists(this.(Variable).getAStore()) and + not this.(Variable).getId() = "__name__" and + not this.(Variable).getId() = "__package__" and + not exists(ImportStar is | is.getScope() = this.(Variable).getScope()) + } - override ControlFlowNode getAnImplicitUse() { none() } + override ControlFlowNode getAnImplicitUse() { none() } - override ControlFlowNode getScopeEntryDefinition() { none() } + override ControlFlowNode getScopeEntryDefinition() { none() } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class ModuleVariable extends SsaSourceVariable { - ModuleVariable() { - this instanceof GlobalVariable and - ( - exists(this.(Variable).getAStore()) - or - this.(Variable).getId() = "__name__" - or - this.(Variable).getId() = "__package__" - or - exists(ImportStar is | is.getScope() = this.(Variable).getScope()) - ) - } + ModuleVariable() { + this instanceof GlobalVariable and + ( + exists(this.(Variable).getAStore()) + or + this.(Variable).getId() = "__name__" + or + this.(Variable).getId() = "__package__" + or + exists(ImportStar is | is.getScope() = this.(Variable).getScope()) + ) + } - pragma[noinline] - CallNode global_variable_callnode() { result.getScope() = this.(GlobalVariable).getScope() } + pragma[noinline] + CallNode global_variable_callnode() { result.getScope() = this.(GlobalVariable).getScope() } - pragma[noinline] - ImportMemberNode global_variable_import() { - result.getScope() = this.(GlobalVariable).getScope() and - import_from_dot_in_init(result.(ImportMemberNode).getModule(this.getName())) - } + pragma[noinline] + ImportMemberNode global_variable_import() { + result.getScope() = this.(GlobalVariable).getScope() and + import_from_dot_in_init(result.(ImportMemberNode).getModule(this.getName())) + } - override ControlFlowNode getAnImplicitUse() { - result = global_variable_callnode() - or - result = global_variable_import() - or - exists(ImportTimeScope scope | scope.entryEdge(result, _) | - this = scope.getOuterVariable(_) or - this.(Variable).getAUse().getScope() = scope - ) - or - /* For implicit use of __metaclass__ when constructing class */ - exists(Class c | - class_with_global_metaclass(c, this) and - c.(ImportTimeScope).entryEdge(result, _) - ) - or - exists(ImportTimeScope s | - result = s.getANormalExit() and - this.(Variable).getScope() = s and - implicit_definition(this) - ) - } + override ControlFlowNode getAnImplicitUse() { + result = global_variable_callnode() + or + result = global_variable_import() + or + exists(ImportTimeScope scope | scope.entryEdge(result, _) | + this = scope.getOuterVariable(_) or + this.(Variable).getAUse().getScope() = scope + ) + or + /* For implicit use of __metaclass__ when constructing class */ + exists(Class c | + class_with_global_metaclass(c, this) and + c.(ImportTimeScope).entryEdge(result, _) + ) + or + exists(ImportTimeScope s | + result = s.getANormalExit() and + this.(Variable).getScope() = s and + implicit_definition(this) + ) + } - override ControlFlowNode getScopeEntryDefinition() { - exists(Scope s | s.getEntryNode() = result | - /* Module entry point */ - this.(GlobalVariable).getScope() = s - or - /* For implicit use of __metaclass__ when constructing class */ - class_with_global_metaclass(s, this) - or - /* Variable is used in scope */ - this.(GlobalVariable).getAUse().getScope() = s - ) - or - exists(ImportTimeScope scope | scope.entryEdge(_, result) | - this = scope.getOuterVariable(_) or - this.(Variable).getAUse().getScope() = scope - ) - } + override ControlFlowNode getScopeEntryDefinition() { + exists(Scope s | s.getEntryNode() = result | + /* Module entry point */ + this.(GlobalVariable).getScope() = s + or + /* For implicit use of __metaclass__ when constructing class */ + class_with_global_metaclass(s, this) + or + /* Variable is used in scope */ + this.(GlobalVariable).getAUse().getScope() = s + ) + or + exists(ImportTimeScope scope | scope.entryEdge(_, result) | + this = scope.getOuterVariable(_) or + this.(Variable).getAUse().getScope() = scope + ) + } - override CallNode redefinedAtCallSite() { none() } + override CallNode redefinedAtCallSite() { none() } } class NonEscapingGlobalVariable extends ModuleVariable { - NonEscapingGlobalVariable() { - this instanceof GlobalVariable and - exists(this.(Variable).getAStore()) and - not variable_or_attribute_defined_out_of_scope(this) - } + NonEscapingGlobalVariable() { + this instanceof GlobalVariable and + exists(this.(Variable).getAStore()) and + not variable_or_attribute_defined_out_of_scope(this) + } } class EscapingGlobalVariable extends ModuleVariable { - EscapingGlobalVariable() { - this instanceof GlobalVariable and - exists(this.(Variable).getAStore()) and - variable_or_attribute_defined_out_of_scope(this) - } + EscapingGlobalVariable() { + this instanceof GlobalVariable and + exists(this.(Variable).getAStore()) and + variable_or_attribute_defined_out_of_scope(this) + } - override ControlFlowNode getAnImplicitUse() { - result = ModuleVariable.super.getAnImplicitUse() - or - result.(CallNode).getScope().getScope+() = this.(GlobalVariable).getScope() - or - result = this.innerScope().getANormalExit() - } + override ControlFlowNode getAnImplicitUse() { + result = ModuleVariable.super.getAnImplicitUse() + or + result.(CallNode).getScope().getScope+() = this.(GlobalVariable).getScope() + or + result = this.innerScope().getANormalExit() + } - private Scope innerScope() { - result.getScope+() = this.(GlobalVariable).getScope() and - not result instanceof ImportTimeScope - } + private Scope innerScope() { + result.getScope+() = this.(GlobalVariable).getScope() and + not result instanceof ImportTimeScope + } - override ControlFlowNode getScopeEntryDefinition() { - result = ModuleVariable.super.getScopeEntryDefinition() - or - result = this.innerScope().getEntryNode() - } + override ControlFlowNode getScopeEntryDefinition() { + result = ModuleVariable.super.getScopeEntryDefinition() + or + result = this.innerScope().getEntryNode() + } - pragma[noinline] - Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } + pragma[noinline] + Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } - override CallNode redefinedAtCallSite() { - result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() - } + override CallNode redefinedAtCallSite() { + result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() + } } class EscapingAssignmentGlobalVariable extends EscapingGlobalVariable { - EscapingAssignmentGlobalVariable() { - exists(NameNode n | n.defines(this) and not n.getScope() = this.getScope()) - } + EscapingAssignmentGlobalVariable() { + exists(NameNode n | n.defines(this) and not n.getScope() = this.getScope()) + } } class SpecialSsaSourceVariable extends SsaSourceVariable { - SpecialSsaSourceVariable() { variable(this, _, "*") or variable(this, _, "$") } + SpecialSsaSourceVariable() { variable(this, _, "*") or variable(this, _, "$") } - override ControlFlowNode getAnImplicitUse() { - exists(ImportTimeScope s | result = s.getANormalExit() and this.getScope() = s) - } + override ControlFlowNode getAnImplicitUse() { + exists(ImportTimeScope s | result = s.getANormalExit() and this.getScope() = s) + } - override ControlFlowNode getScopeEntryDefinition() { - /* Module entry point */ - this.getScope().getEntryNode() = result - } + override ControlFlowNode getScopeEntryDefinition() { + /* Module entry point */ + this.getScope().getEntryNode() = result + } - pragma[noinline] - Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } + pragma[noinline] + Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() } - override CallNode redefinedAtCallSite() { - result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() - } + override CallNode redefinedAtCallSite() { + result.(CallNode).getScope().getScope*() = this.scope_as_global_variable() + } } /** Holds if this variable is implicitly defined */ private predicate implicit_definition(Variable v) { - v.getId() = "*" or - v.getId() = "$" or - exists(ImportStar is | is.getScope() = v.getScope()) + v.getId() = "*" or + v.getId() = "$" or + exists(ImportStar is | is.getScope() = v.getScope()) } private predicate variable_or_attribute_defined_out_of_scope(Variable v) { - exists(NameNode n | n.defines(v) and not n.getScope() = v.getScope()) - or - exists(AttrNode a | - a.isStore() and a.getObject() = v.getAUse() and not a.getScope() = v.getScope() - ) + exists(NameNode n | n.defines(v) and not n.getScope() = v.getScope()) + or + exists(AttrNode a | + a.isStore() and a.getObject() = v.getAUse() and not a.getScope() = v.getScope() + ) } private predicate class_with_global_metaclass(Class cls, GlobalVariable metaclass) { - metaclass.getId() = "__metaclass__" and - major_version() = 2 and - cls.getEnclosingModule() = metaclass.getScope() + metaclass.getId() = "__metaclass__" and + major_version() = 2 and + cls.getEnclosingModule() = metaclass.getScope() } diff --git a/python/ql/src/semmle/python/essa/Essa.qll b/python/ql/src/semmle/python/essa/Essa.qll index ef169d736a8..b44b784b48d 100644 --- a/python/ql/src/semmle/python/essa/Essa.qll +++ b/python/ql/src/semmle/python/essa/Essa.qll @@ -8,59 +8,59 @@ import semmle.python.essa.Definitions /** An (enhanced) SSA variable derived from `SsaSourceVariable`. */ class EssaVariable extends TEssaDefinition { - /** Gets the (unique) definition of this variable. */ - EssaDefinition getDefinition() { this = result } + /** Gets the (unique) definition of this variable. */ + EssaDefinition getDefinition() { this = result } - /** - * Gets a use of this variable, where a "use" is defined by - * `SsaSourceVariable.getAUse()`. - * Note that this differs from `EssaVariable.getASourceUse()`. - */ - ControlFlowNode getAUse() { result = this.getDefinition().getAUse() } + /** + * Gets a use of this variable, where a "use" is defined by + * `SsaSourceVariable.getAUse()`. + * Note that this differs from `EssaVariable.getASourceUse()`. + */ + ControlFlowNode getAUse() { result = this.getDefinition().getAUse() } - /** Gets the source variable from which this variable is derived. */ - SsaSourceVariable getSourceVariable() { result = this.getDefinition().getSourceVariable() } + /** Gets the source variable from which this variable is derived. */ + SsaSourceVariable getSourceVariable() { result = this.getDefinition().getSourceVariable() } - /** Gets the name of this variable. */ - string getName() { result = this.getSourceVariable().getName() } + /** Gets the name of this variable. */ + string getName() { result = this.getSourceVariable().getName() } - /** Gets a textual representation of this element. */ - string toString() { result = "SSA variable " + this.getName() } + /** Gets a textual representation of this element. */ + string toString() { result = "SSA variable " + this.getName() } - /** - * Gets a string representation of this variable. - * WARNING: The format of this may change and it may be very inefficient to compute. - * To used for debugging and testing only. - */ - string getRepresentation() { result = this.getSourceVariable().getName() + "_" + var_rank(this) } + /** + * Gets a string representation of this variable. + * WARNING: The format of this may change and it may be very inefficient to compute. + * To used for debugging and testing only. + */ + string getRepresentation() { result = this.getSourceVariable().getName() + "_" + var_rank(this) } - /** - * Gets a use of this variable, where a "use" is defined by - * `SsaSourceVariable.getASourceUse()`. - * Note that this differs from `EssaVariable.getAUse()`. - */ - ControlFlowNode getASourceUse() { - exists(SsaSourceVariable var | - result = use_for_var(var) and - result = var.getASourceUse() - ) - } + /** + * Gets a use of this variable, where a "use" is defined by + * `SsaSourceVariable.getASourceUse()`. + * Note that this differs from `EssaVariable.getAUse()`. + */ + ControlFlowNode getASourceUse() { + exists(SsaSourceVariable var | + result = use_for_var(var) and + result = var.getASourceUse() + ) + } - pragma[nomagic] - private ControlFlowNode use_for_var(SsaSourceVariable var) { - result = this.getAUse() and - var = this.getSourceVariable() - } + pragma[nomagic] + private ControlFlowNode use_for_var(SsaSourceVariable var) { + result = this.getAUse() and + var = this.getSourceVariable() + } - /** Gets the scope of this variable. */ - Scope getScope() { result = this.getDefinition().getScope() } + /** Gets the scope of this variable. */ + Scope getScope() { result = this.getDefinition().getScope() } - /** - * Holds if this the meta-variable for a scope. - * This is used to attach attributes for undeclared variables implicitly - * defined by `from ... import *` and the like. - */ - predicate isMetaVariable() { this.getName() = "$" } + /** + * Holds if this the meta-variable for a scope. + * This is used to attach attributes for undeclared variables implicitly + * defined by `from ... import *` and the like. + */ + predicate isMetaVariable() { this.getName() = "$" } } /* @@ -69,62 +69,62 @@ class EssaVariable extends TEssaDefinition { */ private int exception_handling(BasicBlock b) { - b.reachesExit() and result = 0 - or - not b.reachesExit() and result = 1 + b.reachesExit() and result = 0 + or + not b.reachesExit() and result = 1 } /* Helper for var_index. Come up with a (probably) unique string per location. */ pragma[noinline] private string location_string(EssaVariable v) { - exists(EssaDefinition def, BasicBlock b, int index, int line, int col | - def = v.getDefinition() and - ( - if b.getNode(0).isNormalExit() - then line = 100000 and col = 0 - else b.hasLocationInfo(_, line, col, _, _) - ) and - /* Add large numbers to values to prevent 1000 sorting before 99 */ - result = - (line + 100000) + ":" + (col * 2 + 10000 + exception_handling(b)) + ":" + (index + 100003) - | - def = TEssaNodeDefinition(_, b, index) - or - def = TEssaNodeRefinement(_, b, index) - or - def = TEssaEdgeDefinition(_, _, b) and index = piIndex() - or - def = TPhiFunction(_, b) and index = phiIndex() - ) + exists(EssaDefinition def, BasicBlock b, int index, int line, int col | + def = v.getDefinition() and + ( + if b.getNode(0).isNormalExit() + then line = 100000 and col = 0 + else b.hasLocationInfo(_, line, col, _, _) + ) and + /* Add large numbers to values to prevent 1000 sorting before 99 */ + result = + (line + 100000) + ":" + (col * 2 + 10000 + exception_handling(b)) + ":" + (index + 100003) + | + def = TEssaNodeDefinition(_, b, index) + or + def = TEssaNodeRefinement(_, b, index) + or + def = TEssaEdgeDefinition(_, _, b) and index = piIndex() + or + def = TPhiFunction(_, b) and index = phiIndex() + ) } /* Helper to compute an index for this SSA variable. */ private int var_index(EssaVariable v) { - location_string(v) = rank[result](string s | exists(EssaVariable x | location_string(x) = s) | s) + location_string(v) = rank[result](string s | exists(EssaVariable x | location_string(x) = s) | s) } /* Helper for `v.getRepresentation()` */ private int var_rank(EssaVariable v) { - exists(int r, SsaSourceVariable var | - var = v.getSourceVariable() and - var_index(v) = rank[r](EssaVariable x | x.getSourceVariable() = var | var_index(x)) and - result = r - 1 - ) + exists(int r, SsaSourceVariable var | + var = v.getSourceVariable() and + var_index(v) = rank[r](EssaVariable x | x.getSourceVariable() = var | var_index(x)) and + result = r - 1 + ) } /** Underlying IPA type for EssaDefinition and EssaVariable. */ cached private newtype TEssaDefinition = - TEssaNodeDefinition(SsaSourceVariable v, BasicBlock b, int i) { - EssaDefinitions::variableDefinition(v, _, b, _, i) - } or - TEssaNodeRefinement(SsaSourceVariable v, BasicBlock b, int i) { - EssaDefinitions::variableRefinement(v, _, b, _, i) - } or - TEssaEdgeDefinition(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { - EssaDefinitions::piNode(v, pred, succ) - } or - TPhiFunction(SsaSourceVariable v, BasicBlock b) { EssaDefinitions::phiNode(v, b) } + TEssaNodeDefinition(SsaSourceVariable v, BasicBlock b, int i) { + EssaDefinitions::variableDefinition(v, _, b, _, i) + } or + TEssaNodeRefinement(SsaSourceVariable v, BasicBlock b, int i) { + EssaDefinitions::variableRefinement(v, _, b, _, i) + } or + TEssaEdgeDefinition(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { + EssaDefinitions::piNode(v, pred, succ) + } or + TPhiFunction(SsaSourceVariable v, BasicBlock b) { EssaDefinitions::phiNode(v, b) } /** * Definition of an extended-SSA (ESSA) variable. @@ -132,38 +132,38 @@ private newtype TEssaDefinition = * and exactly one variable for each definition. */ abstract class EssaDefinition extends TEssaDefinition { - /** Gets a textual representation of this element. */ - string toString() { result = "EssaDefinition" } + /** Gets a textual representation of this element. */ + string toString() { result = "EssaDefinition" } - /** Gets the source variable for which this a definition, either explicit or implicit. */ - abstract SsaSourceVariable getSourceVariable(); + /** Gets the source variable for which this a definition, either explicit or implicit. */ + abstract SsaSourceVariable getSourceVariable(); - /** Gets a use of this definition as defined by the `SsaSourceVariable` class. */ - abstract ControlFlowNode getAUse(); + /** Gets a use of this definition as defined by the `SsaSourceVariable` class. */ + abstract ControlFlowNode getAUse(); - /** Holds if this definition reaches the end of `b`. */ - abstract predicate reachesEndOfBlock(BasicBlock b); + /** Holds if this definition reaches the end of `b`. */ + abstract predicate reachesEndOfBlock(BasicBlock b); - /** - * Gets the location of a control flow node that is indicative of this definition. - * Since definitions may occur on edges of the control flow graph, the given location may - * be imprecise. - * Distinct `EssaDefinitions` may return the same ControlFlowNode even for - * the same variable. - */ - abstract Location getLocation(); + /** + * Gets the location of a control flow node that is indicative of this definition. + * Since definitions may occur on edges of the control flow graph, the given location may + * be imprecise. + * Distinct `EssaDefinitions` may return the same ControlFlowNode even for + * the same variable. + */ + abstract Location getLocation(); - /** - * Gets a representation of this SSA definition for debugging purposes. - * Since this is primarily for debugging and testing, performance may be poor. - */ - abstract string getRepresentation(); + /** + * Gets a representation of this SSA definition for debugging purposes. + * Since this is primarily for debugging and testing, performance may be poor. + */ + abstract string getRepresentation(); - abstract Scope getScope(); + abstract Scope getScope(); - EssaVariable getVariable() { result.getDefinition() = this } + EssaVariable getVariable() { result.getDefinition() = this } - abstract BasicBlock getBasicBlock(); + abstract BasicBlock getBasicBlock(); } /** @@ -172,193 +172,193 @@ abstract class EssaDefinition extends TEssaDefinition { * variable. On one edge the test is true, on the other it is false. */ class EssaEdgeRefinement extends EssaDefinition, TEssaEdgeDefinition { - override string toString() { result = "SSA filter definition" } + override string toString() { result = "SSA filter definition" } - boolean getSense() { - this.getPredecessor().getATrueSuccessor() = this.getSuccessor() and result = true - or - this.getPredecessor().getAFalseSuccessor() = this.getSuccessor() and result = false - } + boolean getSense() { + this.getPredecessor().getATrueSuccessor() = this.getSuccessor() and result = true + or + this.getPredecessor().getAFalseSuccessor() = this.getSuccessor() and result = false + } - override SsaSourceVariable getSourceVariable() { this = TEssaEdgeDefinition(result, _, _) } + override SsaSourceVariable getSourceVariable() { this = TEssaEdgeDefinition(result, _, _) } - /** Gets the basic block preceding the edge on which this refinement occurs. */ - BasicBlock getPredecessor() { this = TEssaEdgeDefinition(_, result, _) } + /** Gets the basic block preceding the edge on which this refinement occurs. */ + BasicBlock getPredecessor() { this = TEssaEdgeDefinition(_, result, _) } - /** Gets the basic block succeeding the edge on which this refinement occurs. */ - BasicBlock getSuccessor() { this = TEssaEdgeDefinition(_, _, result) } + /** Gets the basic block succeeding the edge on which this refinement occurs. */ + BasicBlock getSuccessor() { this = TEssaEdgeDefinition(_, _, result) } - override ControlFlowNode getAUse() { - SsaDefinitions::reachesUse(this.getSourceVariable(), this.getSuccessor(), piIndex(), result) - } + override ControlFlowNode getAUse() { + SsaDefinitions::reachesUse(this.getSourceVariable(), this.getSuccessor(), piIndex(), result) + } - override predicate reachesEndOfBlock(BasicBlock b) { - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getSuccessor(), piIndex(), b) - } + override predicate reachesEndOfBlock(BasicBlock b) { + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getSuccessor(), piIndex(), b) + } - override Location getLocation() { result = this.getSuccessor().getNode(0).getLocation() } + override Location getLocation() { result = this.getSuccessor().getNode(0).getLocation() } - /** Gets the SSA variable to which this refinement applies. */ - EssaVariable getInput() { - exists(SsaSourceVariable var, EssaDefinition def | - var = this.getSourceVariable() and - var = def.getSourceVariable() and - def.reachesEndOfBlock(this.getPredecessor()) and - result.getDefinition() = def - ) - } + /** Gets the SSA variable to which this refinement applies. */ + EssaVariable getInput() { + exists(SsaSourceVariable var, EssaDefinition def | + var = this.getSourceVariable() and + var = def.getSourceVariable() and + def.reachesEndOfBlock(this.getPredecessor()) and + result.getDefinition() = def + ) + } - override string getRepresentation() { - result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" - } + override string getRepresentation() { + result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" + } - /** Gets the scope of the variable defined by this definition. */ - override Scope getScope() { result = this.getPredecessor().getScope() } + /** Gets the scope of the variable defined by this definition. */ + override Scope getScope() { result = this.getPredecessor().getScope() } - override BasicBlock getBasicBlock() { result = this.getSuccessor() } + override BasicBlock getBasicBlock() { result = this.getSuccessor() } } /** A Phi-function as specified in classic SSA form. */ class PhiFunction extends EssaDefinition, TPhiFunction { - override ControlFlowNode getAUse() { - SsaDefinitions::reachesUse(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), result) - } + override ControlFlowNode getAUse() { + SsaDefinitions::reachesUse(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), result) + } - override predicate reachesEndOfBlock(BasicBlock b) { - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), b) - } + override predicate reachesEndOfBlock(BasicBlock b) { + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), b) + } - override SsaSourceVariable getSourceVariable() { this = TPhiFunction(result, _) } + override SsaSourceVariable getSourceVariable() { this = TPhiFunction(result, _) } - /** Gets an input refinement that exists on one of the incoming edges to this phi node. */ - private EssaEdgeRefinement inputEdgeRefinement(BasicBlock pred) { - result.getSourceVariable() = this.getSourceVariable() and - result.getSuccessor() = this.getBasicBlock() and - result.getPredecessor() = pred - } + /** Gets an input refinement that exists on one of the incoming edges to this phi node. */ + private EssaEdgeRefinement inputEdgeRefinement(BasicBlock pred) { + result.getSourceVariable() = this.getSourceVariable() and + result.getSuccessor() = this.getBasicBlock() and + result.getPredecessor() = pred + } - private BasicBlock nonPiInput() { - result = this.getBasicBlock().getAPredecessor() and - not exists(this.inputEdgeRefinement(result)) - } + private BasicBlock nonPiInput() { + result = this.getBasicBlock().getAPredecessor() and + not exists(this.inputEdgeRefinement(result)) + } - pragma[noinline] - private SsaSourceVariable pred_var(BasicBlock pred) { - result = this.getSourceVariable() and - pred = this.nonPiInput() - } + pragma[noinline] + private SsaSourceVariable pred_var(BasicBlock pred) { + result = this.getSourceVariable() and + pred = this.nonPiInput() + } - /** Gets another definition of the same source variable that reaches this definition. */ - private EssaDefinition reachingDefinition(BasicBlock pred) { - result.getScope() = this.getScope() and - result.getSourceVariable() = pred_var(pred) and - result.reachesEndOfBlock(pred) - } + /** Gets another definition of the same source variable that reaches this definition. */ + private EssaDefinition reachingDefinition(BasicBlock pred) { + result.getScope() = this.getScope() and + result.getSourceVariable() = pred_var(pred) and + result.reachesEndOfBlock(pred) + } - /** Gets the input variable for this phi node on the edge `pred` -> `this.getBasicBlock()`, if any. */ - cached - EssaVariable getInput(BasicBlock pred) { - result.getDefinition() = this.reachingDefinition(pred) - or - result.getDefinition() = this.inputEdgeRefinement(pred) - } + /** Gets the input variable for this phi node on the edge `pred` -> `this.getBasicBlock()`, if any. */ + cached + EssaVariable getInput(BasicBlock pred) { + result.getDefinition() = this.reachingDefinition(pred) + or + result.getDefinition() = this.inputEdgeRefinement(pred) + } - /** Gets an input variable for this phi node. */ - EssaVariable getAnInput() { result = this.getInput(_) } + /** Gets an input variable for this phi node. */ + EssaVariable getAnInput() { result = this.getInput(_) } - /** Holds if forall incoming edges in the flow graph, there is an input variable */ - predicate isComplete() { - forall(BasicBlock pred | pred = this.getBasicBlock().getAPredecessor() | - exists(this.getInput(pred)) - ) - } + /** Holds if forall incoming edges in the flow graph, there is an input variable */ + predicate isComplete() { + forall(BasicBlock pred | pred = this.getBasicBlock().getAPredecessor() | + exists(this.getInput(pred)) + ) + } - override string toString() { result = "SSA Phi Function" } + override string toString() { result = "SSA Phi Function" } - /** Gets the basic block that succeeds this phi node. */ - override BasicBlock getBasicBlock() { this = TPhiFunction(_, result) } + /** Gets the basic block that succeeds this phi node. */ + override BasicBlock getBasicBlock() { this = TPhiFunction(_, result) } - override Location getLocation() { result = this.getBasicBlock().getNode(0).getLocation() } + override Location getLocation() { result = this.getBasicBlock().getNode(0).getLocation() } - /** Helper for `argList(n)`. */ - private int rankInput(EssaVariable input) { - input = this.getAnInput() and - var_index(input) = rank[result](EssaVariable v | v = this.getAnInput() | var_index(v)) - } + /** Helper for `argList(n)`. */ + private int rankInput(EssaVariable input) { + input = this.getAnInput() and + var_index(input) = rank[result](EssaVariable v | v = this.getAnInput() | var_index(v)) + } - /** Helper for `argList()`. */ - private string argList(int n) { - exists(EssaVariable input | n = this.rankInput(input) | - n = 1 and result = input.getRepresentation() - or - n > 1 and result = this.argList(n - 1) + ", " + input.getRepresentation() - ) - } + /** Helper for `argList()`. */ + private string argList(int n) { + exists(EssaVariable input | n = this.rankInput(input) | + n = 1 and result = input.getRepresentation() + or + n > 1 and result = this.argList(n - 1) + ", " + input.getRepresentation() + ) + } - /** Helper for `getRepresentation()`. */ - private string argList() { - exists(int last | - last = (max(int x | x = this.rankInput(_))) and - result = this.argList(last) - ) - } + /** Helper for `getRepresentation()`. */ + private string argList() { + exists(int last | + last = (max(int x | x = this.rankInput(_))) and + result = this.argList(last) + ) + } - override string getRepresentation() { - not exists(this.getAnInput()) and result = "phi()" - or - result = "phi(" + this.argList() + ")" - or - exists(this.getAnInput()) and - not exists(this.argList()) and - result = "phi(" + this.getSourceVariable().getName() + "??)" - } + override string getRepresentation() { + not exists(this.getAnInput()) and result = "phi()" + or + result = "phi(" + this.argList() + ")" + or + exists(this.getAnInput()) and + not exists(this.argList()) and + result = "phi(" + this.getSourceVariable().getName() + "??)" + } - override Scope getScope() { result = this.getBasicBlock().getScope() } + override Scope getScope() { result = this.getBasicBlock().getScope() } - private EssaEdgeRefinement piInputDefinition(EssaVariable input) { - input = this.getAnInput() and - result = input.getDefinition() - or - input = this.getAnInput() and result = input.getDefinition().(PhiFunction).piInputDefinition(_) - } + private EssaEdgeRefinement piInputDefinition(EssaVariable input) { + input = this.getAnInput() and + result = input.getDefinition() + or + input = this.getAnInput() and result = input.getDefinition().(PhiFunction).piInputDefinition(_) + } - /** - * Gets the variable which is the common and complete input to all pi-nodes that are themselves - * inputs to this phi-node. - * For example: - * ``` - * x = y() - * if complicated_test(x): - * do_a() - * else: - * do_b() - * phi - * ``` - * Which gives us the ESSA form: - * x0 = y() - * x1 = pi(x0, complicated_test(x0)) - * x2 = pi(x0, not complicated_test(x0)) - * x3 = phi(x1, x2) - * However we may not be able to track the value of `x` through `compilated_test` - * meaning that we cannot track `x` from `x0` to `x3`. - * By using `getShortCircuitInput()` we can do so, since the short-circuit input of `x3` is `x0`. - */ - pragma[noinline] - EssaVariable getShortCircuitInput() { - exists(BasicBlock common | - forall(EssaVariable input | input = this.getAnInput() | - common = this.piInputDefinition(input).getPredecessor() - ) and - forall(BasicBlock succ | succ = common.getASuccessor() | - succ = this.piInputDefinition(_).getSuccessor() - ) and - exists(EssaEdgeRefinement ref | - ref = this.piInputDefinition(_) and - ref.getPredecessor() = common and - ref.getInput() = result - ) - ) - } + /** + * Gets the variable which is the common and complete input to all pi-nodes that are themselves + * inputs to this phi-node. + * For example: + * ``` + * x = y() + * if complicated_test(x): + * do_a() + * else: + * do_b() + * phi + * ``` + * Which gives us the ESSA form: + * x0 = y() + * x1 = pi(x0, complicated_test(x0)) + * x2 = pi(x0, not complicated_test(x0)) + * x3 = phi(x1, x2) + * However we may not be able to track the value of `x` through `compilated_test` + * meaning that we cannot track `x` from `x0` to `x3`. + * By using `getShortCircuitInput()` we can do so, since the short-circuit input of `x3` is `x0`. + */ + pragma[noinline] + EssaVariable getShortCircuitInput() { + exists(BasicBlock common | + forall(EssaVariable input | input = this.getAnInput() | + common = this.piInputDefinition(input).getPredecessor() + ) and + forall(BasicBlock succ | succ = common.getASuccessor() | + succ = this.piInputDefinition(_).getSuccessor() + ) and + exists(EssaEdgeRefinement ref | + ref = this.piInputDefinition(_) and + ref.getPredecessor() = common and + ref.getInput() = result + ) + ) + } } /** @@ -366,114 +366,114 @@ class PhiFunction extends EssaDefinition, TPhiFunction { * another ESSA variable. */ class EssaNodeDefinition extends EssaDefinition, TEssaNodeDefinition { - override string toString() { result = "Essa node definition" } + override string toString() { result = "Essa node definition" } - override ControlFlowNode getAUse() { - exists(SsaSourceVariable v, BasicBlock b, int i | - this = TEssaNodeDefinition(v, b, i) and - SsaDefinitions::reachesUse(v, b, i, result) - ) - } + override ControlFlowNode getAUse() { + exists(SsaSourceVariable v, BasicBlock b, int i | + this = TEssaNodeDefinition(v, b, i) and + SsaDefinitions::reachesUse(v, b, i, result) + ) + } - override predicate reachesEndOfBlock(BasicBlock b) { - exists(BasicBlock defb, int i | - this = TEssaNodeDefinition(_, defb, i) and - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) - ) - } + override predicate reachesEndOfBlock(BasicBlock b) { + exists(BasicBlock defb, int i | + this = TEssaNodeDefinition(_, defb, i) and + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) + ) + } - override SsaSourceVariable getSourceVariable() { this = TEssaNodeDefinition(result, _, _) } + override SsaSourceVariable getSourceVariable() { this = TEssaNodeDefinition(result, _, _) } - /** Gets the ControlFlowNode corresponding to this definition */ - ControlFlowNode getDefiningNode() { this.definedBy(_, result) } + /** Gets the ControlFlowNode corresponding to this definition */ + ControlFlowNode getDefiningNode() { this.definedBy(_, result) } - override Location getLocation() { result = this.getDefiningNode().getLocation() } + override Location getLocation() { result = this.getDefiningNode().getLocation() } - override string getRepresentation() { result = this.getAQlClass() } + override string getRepresentation() { result = this.getAQlClass() } - override Scope getScope() { - exists(BasicBlock defb | - this = TEssaNodeDefinition(_, defb, _) and - result = defb.getScope() - ) - } + override Scope getScope() { + exists(BasicBlock defb | + this = TEssaNodeDefinition(_, defb, _) and + result = defb.getScope() + ) + } - predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { - exists(BasicBlock b, int i | def = b.getNode(i) | - this = TEssaNodeDefinition(v, b, i + i) - or - this = TEssaNodeDefinition(v, b, i + i + 1) - ) - } + predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { + exists(BasicBlock b, int i | def = b.getNode(i) | + this = TEssaNodeDefinition(v, b, i + i) + or + this = TEssaNodeDefinition(v, b, i + i + 1) + ) + } - override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } + override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } } /** A definition of an ESSA variable that takes another ESSA variable as an input. */ class EssaNodeRefinement extends EssaDefinition, TEssaNodeRefinement { - override string toString() { result = "SSA filter definition" } + override string toString() { result = "SSA filter definition" } - /** Gets the SSA variable to which this refinement applies. */ - EssaVariable getInput() { - result = potential_input(this) and - not result = potential_input(potential_input(this).getDefinition()) - } + /** Gets the SSA variable to which this refinement applies. */ + EssaVariable getInput() { + result = potential_input(this) and + not result = potential_input(potential_input(this).getDefinition()) + } - override ControlFlowNode getAUse() { - exists(SsaSourceVariable v, BasicBlock b, int i | - this = TEssaNodeRefinement(v, b, i) and - SsaDefinitions::reachesUse(v, b, i, result) - ) - } + override ControlFlowNode getAUse() { + exists(SsaSourceVariable v, BasicBlock b, int i | + this = TEssaNodeRefinement(v, b, i) and + SsaDefinitions::reachesUse(v, b, i, result) + ) + } - override predicate reachesEndOfBlock(BasicBlock b) { - exists(BasicBlock defb, int i | - this = TEssaNodeRefinement(_, defb, i) and - SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) - ) - } + override predicate reachesEndOfBlock(BasicBlock b) { + exists(BasicBlock defb, int i | + this = TEssaNodeRefinement(_, defb, i) and + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) + ) + } - override SsaSourceVariable getSourceVariable() { this = TEssaNodeRefinement(result, _, _) } + override SsaSourceVariable getSourceVariable() { this = TEssaNodeRefinement(result, _, _) } - /** Gets the ControlFlowNode corresponding to this definition */ - ControlFlowNode getDefiningNode() { this.definedBy(_, result) } + /** Gets the ControlFlowNode corresponding to this definition */ + ControlFlowNode getDefiningNode() { this.definedBy(_, result) } - override Location getLocation() { result = this.getDefiningNode().getLocation() } + override Location getLocation() { result = this.getDefiningNode().getLocation() } - override string getRepresentation() { - result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" - or - not exists(this.getInput()) and - result = this.getAQlClass() + "(" + this.getSourceVariable().getName() + "??)" - } + override string getRepresentation() { + result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" + or + not exists(this.getInput()) and + result = this.getAQlClass() + "(" + this.getSourceVariable().getName() + "??)" + } - override Scope getScope() { - exists(BasicBlock defb | - this = TEssaNodeRefinement(_, defb, _) and - result = defb.getScope() - ) - } + override Scope getScope() { + exists(BasicBlock defb | + this = TEssaNodeRefinement(_, defb, _) and + result = defb.getScope() + ) + } - predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { - exists(BasicBlock b, int i | def = b.getNode(i) | - this = TEssaNodeRefinement(v, b, i + i) - or - this = TEssaNodeRefinement(v, b, i + i + 1) - ) - } + predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { + exists(BasicBlock b, int i | def = b.getNode(i) | + this = TEssaNodeRefinement(v, b, i + i) + or + this = TEssaNodeRefinement(v, b, i + i + 1) + ) + } - override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } + override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() } } pragma[noopt] private EssaVariable potential_input(EssaNodeRefinement ref) { - exists(ControlFlowNode use, SsaSourceVariable var, ControlFlowNode def | - var.hasRefinement(use, def) and - use = result.getAUse() and - var = result.getSourceVariable() and - def = ref.getDefiningNode() and - var = ref.getSourceVariable() - ) + exists(ControlFlowNode use, SsaSourceVariable var, ControlFlowNode def | + var.hasRefinement(use, def) and + use = result.getAUse() and + var = result.getSourceVariable() and + def = ref.getDefiningNode() and + var = ref.getSourceVariable() + ) } /* For backwards compatibility */ @@ -484,97 +484,97 @@ deprecated class PyNodeRefinement = EssaNodeRefinement; /** An assignment to a variable `v = val` */ class AssignmentDefinition extends EssaNodeDefinition { - AssignmentDefinition() { - SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _) - } + AssignmentDefinition() { + SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _) + } - ControlFlowNode getValue() { - SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), result) - } + ControlFlowNode getValue() { + SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), result) + } - override string getRepresentation() { result = this.getValue().getNode().toString() } + override string getRepresentation() { result = this.getValue().getNode().toString() } } /** Capture of a raised exception `except ExceptionType ex:` */ class ExceptionCapture extends EssaNodeDefinition { - ExceptionCapture() { - SsaSource::exception_capture(this.getSourceVariable(), this.getDefiningNode()) - } + ExceptionCapture() { + SsaSource::exception_capture(this.getSourceVariable(), this.getDefiningNode()) + } - ControlFlowNode getType() { - exists(ExceptFlowNode ex | - ex.getName() = this.getDefiningNode() and - result = ex.getType() - ) - } + ControlFlowNode getType() { + exists(ExceptFlowNode ex | + ex.getName() = this.getDefiningNode() and + result = ex.getType() + ) + } - override string getRepresentation() { result = "except " + this.getSourceVariable().getName() } + override string getRepresentation() { result = "except " + this.getSourceVariable().getName() } } /** An assignment to a variable as part of a multiple assignment `..., v, ... = val` */ class MultiAssignmentDefinition extends EssaNodeDefinition { - MultiAssignmentDefinition() { - SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _, _) - } + MultiAssignmentDefinition() { + SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _, _) + } - override string getRepresentation() { - exists(ControlFlowNode value, int n | - this.indexOf(n, value) and - result = value.(DefinitionNode).getValue().getNode().toString() + "[" + n + "]" - ) - } + override string getRepresentation() { + exists(ControlFlowNode value, int n | + this.indexOf(n, value) and + result = value.(DefinitionNode).getValue().getNode().toString() + "[" + n + "]" + ) + } - /** Holds if `this` has (zero-based) index `index` in `lhs`. */ - predicate indexOf(int index, SequenceNode lhs) { - SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), index, - lhs) - } + /** Holds if `this` has (zero-based) index `index` in `lhs`. */ + predicate indexOf(int index, SequenceNode lhs) { + SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), index, + lhs) + } } /** A definition of a variable in a `with` statement */ class WithDefinition extends EssaNodeDefinition { - WithDefinition() { SsaSource::with_definition(this.getSourceVariable(), this.getDefiningNode()) } + WithDefinition() { SsaSource::with_definition(this.getSourceVariable(), this.getDefiningNode()) } - override string getRepresentation() { result = "with" } + override string getRepresentation() { result = "with" } } /** A definition of a variable by declaring it as a parameter */ class ParameterDefinition extends EssaNodeDefinition { - ParameterDefinition() { - SsaSource::parameter_definition(this.getSourceVariable(), this.getDefiningNode()) - } + ParameterDefinition() { + SsaSource::parameter_definition(this.getSourceVariable(), this.getDefiningNode()) + } - predicate isSelf() { this.getDefiningNode().getNode().(Parameter).isSelf() } + predicate isSelf() { this.getDefiningNode().getNode().(Parameter).isSelf() } - /** Gets the control flow node for the default value of this parameter */ - ControlFlowNode getDefault() { result.getNode() = this.getParameter().getDefault() } + /** Gets the control flow node for the default value of this parameter */ + ControlFlowNode getDefault() { result.getNode() = this.getParameter().getDefault() } - /** Gets the annotation control flow node of this parameter */ - ControlFlowNode getAnnotation() { result.getNode() = this.getParameter().getAnnotation() } + /** Gets the annotation control flow node of this parameter */ + ControlFlowNode getAnnotation() { result.getNode() = this.getParameter().getAnnotation() } - /** Gets the name of this parameter definition */ - string getName() { result = this.getParameter().asName().getId() } + /** Gets the name of this parameter definition */ + string getName() { result = this.getParameter().asName().getId() } - predicate isVarargs() { - exists(Function func | func.getVararg() = this.getDefiningNode().getNode()) - } + predicate isVarargs() { + exists(Function func | func.getVararg() = this.getDefiningNode().getNode()) + } - /** - * Holds if this parameter is a 'kwargs' parameter. - * The `kwargs` in `f(a, b, **kwargs)`. - */ - predicate isKwargs() { - exists(Function func | func.getKwarg() = this.getDefiningNode().getNode()) - } + /** + * Holds if this parameter is a 'kwargs' parameter. + * The `kwargs` in `f(a, b, **kwargs)`. + */ + predicate isKwargs() { + exists(Function func | func.getKwarg() = this.getDefiningNode().getNode()) + } - Parameter getParameter() { result = this.getDefiningNode().getNode() } + Parameter getParameter() { result = this.getDefiningNode().getNode() } } /** A deletion of a variable `del v` */ class DeletionDefinition extends EssaNodeDefinition { - DeletionDefinition() { - SsaSource::deletion_definition(this.getSourceVariable(), this.getDefiningNode()) - } + DeletionDefinition() { + SsaSource::deletion_definition(this.getSourceVariable(), this.getDefiningNode()) + } } /** @@ -582,88 +582,88 @@ class DeletionDefinition extends EssaNodeDefinition { * a global or non-local variable from one scope to another. */ class ScopeEntryDefinition extends EssaNodeDefinition { - ScopeEntryDefinition() { - this.getDefiningNode() = this.getSourceVariable().getScopeEntryDefinition() and - not this instanceof ImplicitSubModuleDefinition - } + ScopeEntryDefinition() { + this.getDefiningNode() = this.getSourceVariable().getScopeEntryDefinition() and + not this instanceof ImplicitSubModuleDefinition + } - override Scope getScope() { result.getEntryNode() = this.getDefiningNode() } + override Scope getScope() { result.getEntryNode() = this.getDefiningNode() } } /** Possible redefinition of variable via `from ... import *` */ class ImportStarRefinement extends EssaNodeRefinement { - ImportStarRefinement() { - SsaSource::import_star_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + ImportStarRefinement() { + SsaSource::import_star_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } } /** Assignment of an attribute `obj.attr = val` */ class AttributeAssignment extends EssaNodeRefinement { - AttributeAssignment() { - SsaSource::attribute_assignment_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + AttributeAssignment() { + SsaSource::attribute_assignment_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } - string getName() { result = this.getDefiningNode().(AttrNode).getName() } + string getName() { result = this.getDefiningNode().(AttrNode).getName() } - ControlFlowNode getValue() { result = this.getDefiningNode().(DefinitionNode).getValue() } + ControlFlowNode getValue() { result = this.getDefiningNode().(DefinitionNode).getValue() } - override string getRepresentation() { - result = - this.getAQlClass() + " '" + this.getName() + "'(" + this.getInput().getRepresentation() + ")" - or - not exists(this.getInput()) and - result = - this.getAQlClass() + " '" + this.getName() + "'(" + this.getSourceVariable().getName() + "??)" - } + override string getRepresentation() { + result = + this.getAQlClass() + " '" + this.getName() + "'(" + this.getInput().getRepresentation() + ")" + or + not exists(this.getInput()) and + result = + this.getAQlClass() + " '" + this.getName() + "'(" + this.getSourceVariable().getName() + "??)" + } } /** A use of a variable as an argument, `foo(v)`, which might modify the object referred to. */ class ArgumentRefinement extends EssaNodeRefinement { - ControlFlowNode argument; + ControlFlowNode argument; - ArgumentRefinement() { - SsaSource::argument_refinement(this.getSourceVariable(), argument, this.getDefiningNode()) - } + ArgumentRefinement() { + SsaSource::argument_refinement(this.getSourceVariable(), argument, this.getDefiningNode()) + } - ControlFlowNode getArgument() { result = argument } + ControlFlowNode getArgument() { result = argument } - CallNode getCall() { result = this.getDefiningNode() } + CallNode getCall() { result = this.getDefiningNode() } } /** Deletion of an attribute `del obj.attr`. */ class EssaAttributeDeletion extends EssaNodeRefinement { - EssaAttributeDeletion() { - SsaSource::attribute_deletion_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + EssaAttributeDeletion() { + SsaSource::attribute_deletion_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } - string getName() { result = this.getDefiningNode().(AttrNode).getName() } + string getName() { result = this.getDefiningNode().(AttrNode).getName() } } /** A pi-node (guard) with only one successor. */ class SingleSuccessorGuard extends EssaNodeRefinement { - SingleSuccessorGuard() { - SsaSource::test_refinement(this.getSourceVariable(), _, this.getDefiningNode()) - } + SingleSuccessorGuard() { + SsaSource::test_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } - boolean getSense() { - exists(this.getDefiningNode().getAFalseSuccessor()) and result = false - or - exists(this.getDefiningNode().getATrueSuccessor()) and result = true - } + boolean getSense() { + exists(this.getDefiningNode().getAFalseSuccessor()) and result = false + or + exists(this.getDefiningNode().getATrueSuccessor()) and result = true + } - override string getRepresentation() { - result = EssaNodeRefinement.super.getRepresentation() + " [" + this.getSense().toString() + "]" - or - not exists(this.getSense()) and - result = EssaNodeRefinement.super.getRepresentation() + " [??]" - } + override string getRepresentation() { + result = EssaNodeRefinement.super.getRepresentation() + " [" + this.getSense().toString() + "]" + or + not exists(this.getSense()) and + result = EssaNodeRefinement.super.getRepresentation() + " [??]" + } - ControlFlowNode getTest() { result = this.getDefiningNode() } + ControlFlowNode getTest() { result = this.getDefiningNode() } - predicate useAndTest(ControlFlowNode use, ControlFlowNode test) { - test = this.getDefiningNode() and - SsaSource::test_refinement(this.getSourceVariable(), use, test) - } + predicate useAndTest(ControlFlowNode use, ControlFlowNode test) { + test = this.getDefiningNode() and + SsaSource::test_refinement(this.getSourceVariable(), use, test) + } } /** @@ -672,56 +672,56 @@ class SingleSuccessorGuard extends EssaNodeRefinement { * as they are imported, this is a good approximation for static analysis. */ class ImplicitSubModuleDefinition extends EssaNodeDefinition { - ImplicitSubModuleDefinition() { - SsaSource::init_module_submodule_defn(this.getSourceVariable(), this.getDefiningNode()) - } + ImplicitSubModuleDefinition() { + SsaSource::init_module_submodule_defn(this.getSourceVariable(), this.getDefiningNode()) + } } /** An implicit (possible) definition of an escaping variable at a call-site */ class CallsiteRefinement extends EssaNodeRefinement { - override string toString() { result = "CallsiteRefinement" } + override string toString() { result = "CallsiteRefinement" } - CallsiteRefinement() { - exists(SsaSourceVariable var, ControlFlowNode defn | - defn = var.redefinedAtCallSite() and - this.definedBy(var, defn) and - not this instanceof ArgumentRefinement and - not this instanceof MethodCallsiteRefinement and - not this instanceof SingleSuccessorGuard - ) - } + CallsiteRefinement() { + exists(SsaSourceVariable var, ControlFlowNode defn | + defn = var.redefinedAtCallSite() and + this.definedBy(var, defn) and + not this instanceof ArgumentRefinement and + not this instanceof MethodCallsiteRefinement and + not this instanceof SingleSuccessorGuard + ) + } - CallNode getCall() { this.getDefiningNode() = result } + CallNode getCall() { this.getDefiningNode() = result } } /** An implicit (possible) modification of the object referred at a method call */ class MethodCallsiteRefinement extends EssaNodeRefinement { - MethodCallsiteRefinement() { - SsaSource::method_call_refinement(this.getSourceVariable(), _, this.getDefiningNode()) and - not this instanceof SingleSuccessorGuard - } + MethodCallsiteRefinement() { + SsaSource::method_call_refinement(this.getSourceVariable(), _, this.getDefiningNode()) and + not this instanceof SingleSuccessorGuard + } - CallNode getCall() { this.getDefiningNode() = result } + CallNode getCall() { this.getDefiningNode() = result } } /** An implicit (possible) modification of `self` at a method call */ class SelfCallsiteRefinement extends MethodCallsiteRefinement { - SelfCallsiteRefinement() { this.getSourceVariable().(Variable).isSelf() } + SelfCallsiteRefinement() { this.getSourceVariable().(Variable).isSelf() } } /** Python specific sub-class of generic EssaEdgeRefinement */ class PyEdgeRefinement extends EssaEdgeRefinement { - override string getRepresentation() { - /* - * This is for testing so use capital 'P' to make it sort before 'phi' and - * be more visually distinctive. - */ + override string getRepresentation() { + /* + * This is for testing so use capital 'P' to make it sort before 'phi' and + * be more visually distinctive. + */ - result = "Pi(" + this.getInput().getRepresentation() + ") [" + this.getSense() + "]" - or - not exists(this.getInput()) and - result = "Pi(" + this.getSourceVariable().getName() + "??) [" + this.getSense() + "]" - } + result = "Pi(" + this.getInput().getRepresentation() + ") [" + this.getSense() + "]" + or + not exists(this.getInput()) and + result = "Pi(" + this.getSourceVariable().getName() + "??) [" + this.getSense() + "]" + } - ControlFlowNode getTest() { result = this.getPredecessor().getLastNode() } + ControlFlowNode getTest() { result = this.getPredecessor().getLastNode() } } diff --git a/python/ql/src/semmle/python/essa/SsaCompute.qll b/python/ql/src/semmle/python/essa/SsaCompute.qll index 5a0980ed973..a3344e036e9 100644 --- a/python/ql/src/semmle/python/essa/SsaCompute.qll +++ b/python/ql/src/semmle/python/essa/SsaCompute.qll @@ -93,261 +93,261 @@ import python cached private module SsaComputeImpl { + cached + module EssaDefinitionsImpl { + /** Whether `n` is a live update that is a definition of the variable `v`. */ cached - module EssaDefinitionsImpl { - /** Whether `n` is a live update that is a definition of the variable `v`. */ - cached - predicate variableDefinition( - SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i - ) { - SsaComputeImpl::variableDefine(v, n, b, i) and - SsaComputeImpl::defUseRank(v, b, rankix, i) and - ( - SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and - not SsaComputeImpl::defRank(v, b, rankix + 1, _) - or - not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) - ) - } - - /** Whether `n` is a live update that is a definition of the variable `v`. */ - cached - predicate variableRefinement( - SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i - ) { - SsaComputeImpl::variableRefine(v, n, b, i) and - SsaComputeImpl::defUseRank(v, b, rankix, i) and - ( - SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and - not SsaComputeImpl::defRank(v, b, rankix + 1, _) - or - not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) - ) - } - - cached - predicate variableUpdate(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i) { - variableDefinition(v, n, b, rankix, i) - or - variableRefinement(v, n, b, rankix, i) - } - - /** Holds if `def` is a pi-node for `v` on the edge `pred` -> `succ` */ - cached - predicate piNode(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { - v.hasRefinementEdge(_, pred, succ) and - Liveness::liveAtEntry(v, succ) - } - - /** A phi node for `v` at the beginning of basic block `b`. */ - cached - predicate phiNode(SsaSourceVariable v, BasicBlock b) { - ( - exists(BasicBlock def | def.dominanceFrontier(b) | SsaComputeImpl::ssaDef(v, def)) - or - piNode(v, _, b) and strictcount(b.getAPredecessor()) > 1 - ) and - Liveness::liveAtEntry(v, b) - } - } - - cached - predicate variableDefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { - v.hasDefiningNode(n) and - exists(int j | - n = b.getNode(j) and - i = j * 2 + 1 - ) - } - - cached - predicate variableRefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { - v.hasRefinement(_, n) and - exists(int j | - n = b.getNode(j) and - i = j * 2 + 1 - ) - } - - cached - predicate variableDef(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { - variableDefine(v, n, b, i) or variableRefine(v, n, b, i) - } - - /** - * A ranking of the indices `i` at which there is an SSA definition or use of - * `v` in the basic block `b`. - * - * Basic block indices are translated to rank indices in order to skip - * irrelevant indices at which there is no definition or use when traversing - * basic blocks. - */ - cached - predicate defUseRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { - i = rank[rankix](int j | variableDef(v, _, b, j) or variableUse(v, _, b, j)) - } - - /** A definition of a variable occurring at the specified rank index in basic block `b`. */ - cached - predicate defRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { - variableDef(v, _, b, i) and - defUseRank(v, b, rankix, i) - } - - /** A `VarAccess` `use` of `v` in `b` at index `i`. */ - cached - predicate variableUse(SsaSourceVariable v, ControlFlowNode use, BasicBlock b, int i) { - (v.getAUse() = use or v.hasRefinement(use, _)) and - exists(int j | - b.getNode(j) = use and - i = 2 * j - ) - } - - /** - * A definition of an SSA variable occurring at the specified position. - * This is either a phi node, a `VariableUpdate`, or a parameter. - */ - cached - predicate ssaDef(SsaSourceVariable v, BasicBlock b) { - EssaDefinitions::phiNode(v, b) - or - EssaDefinitions::variableUpdate(v, _, b, _, _) - or - EssaDefinitions::piNode(v, _, b) - } - - /* - * The construction of SSA form ensures that each use of a variable is - * dominated by its definition. A definition of an SSA variable therefore - * reaches a `ControlFlowNode` if it is the _closest_ SSA variable definition - * that dominates the node. If two definitions dominate a node then one must - * dominate the other, so therefore the definition of _closest_ is given by the - * dominator tree. Thus, reaching definitions can be calculated in terms of - * dominance. - */ - - /** The maximum rank index for the given variable and basic block. */ - cached - int lastRank(SsaSourceVariable v, BasicBlock b) { - result = max(int rankix | defUseRank(v, b, rankix, _)) - or - not defUseRank(v, b, _, _) and - (EssaDefinitions::phiNode(v, b) or EssaDefinitions::piNode(v, _, b)) and - result = 0 - } - - private predicate ssaDefRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { - EssaDefinitions::variableUpdate(v, _, b, rankix, i) - or - EssaDefinitions::phiNode(v, b) and rankix = 0 and i = phiIndex() - or - EssaDefinitions::piNode(v, _, b) and - EssaDefinitions::phiNode(v, b) and - rankix = -1 and - i = piIndex() - or - EssaDefinitions::piNode(v, _, b) and - not EssaDefinitions::phiNode(v, b) and - rankix = 0 and - i = piIndex() - } - - /** The SSA definition reaches the rank index `rankix` in its own basic block `b`. */ - cached - predicate ssaDefReachesRank(SsaSourceVariable v, BasicBlock b, int i, int rankix) { - ssaDefRank(v, b, rankix, i) - or - ssaDefReachesRank(v, b, i, rankix - 1) and - rankix <= lastRank(v, b) and - not ssaDefRank(v, b, rankix, _) - } - - /** - * The SSA definition of `v` at `def` reaches `use` in the same basic block - * without crossing another SSA definition of `v`. - */ - cached - predicate ssaDefReachesUseWithinBlock( - SsaSourceVariable v, BasicBlock b, int i, ControlFlowNode use + predicate variableDefinition( + SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i ) { - exists(int rankix, int useix | - ssaDefReachesRank(v, b, i, rankix) and - defUseRank(v, b, rankix, useix) and - variableUse(v, use, b, useix) - ) + SsaComputeImpl::variableDefine(v, n, b, i) and + SsaComputeImpl::defUseRank(v, b, rankix, i) and + ( + SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and + not SsaComputeImpl::defRank(v, b, rankix + 1, _) + or + not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) + ) + } + + /** Whether `n` is a live update that is a definition of the variable `v`. */ + cached + predicate variableRefinement( + SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i + ) { + SsaComputeImpl::variableRefine(v, n, b, i) and + SsaComputeImpl::defUseRank(v, b, rankix, i) and + ( + SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and + not SsaComputeImpl::defRank(v, b, rankix + 1, _) + or + not SsaComputeImpl::defUseRank(v, b, rankix + 1, _) and Liveness::liveAtExit(v, b) + ) } cached - module LivenessImpl { - cached - predicate liveAtExit(SsaSourceVariable v, BasicBlock b) { liveAtEntry(v, b.getASuccessor()) } - - cached - predicate liveAtEntry(SsaSourceVariable v, BasicBlock b) { - SsaComputeImpl::defUseRank(v, b, 1, _) and not SsaComputeImpl::defRank(v, b, 1, _) - or - not SsaComputeImpl::defUseRank(v, b, _, _) and liveAtExit(v, b) - } + predicate variableUpdate(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i) { + variableDefinition(v, n, b, rankix, i) + or + variableRefinement(v, n, b, rankix, i) } + /** Holds if `def` is a pi-node for `v` on the edge `pred` -> `succ` */ + cached + predicate piNode(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { + v.hasRefinementEdge(_, pred, succ) and + Liveness::liveAtEntry(v, succ) + } + + /** A phi node for `v` at the beginning of basic block `b`. */ + cached + predicate phiNode(SsaSourceVariable v, BasicBlock b) { + ( + exists(BasicBlock def | def.dominanceFrontier(b) | SsaComputeImpl::ssaDef(v, def)) + or + piNode(v, _, b) and strictcount(b.getAPredecessor()) > 1 + ) and + Liveness::liveAtEntry(v, b) + } + } + + cached + predicate variableDefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + v.hasDefiningNode(n) and + exists(int j | + n = b.getNode(j) and + i = j * 2 + 1 + ) + } + + cached + predicate variableRefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + v.hasRefinement(_, n) and + exists(int j | + n = b.getNode(j) and + i = j * 2 + 1 + ) + } + + cached + predicate variableDef(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + variableDefine(v, n, b, i) or variableRefine(v, n, b, i) + } + + /** + * A ranking of the indices `i` at which there is an SSA definition or use of + * `v` in the basic block `b`. + * + * Basic block indices are translated to rank indices in order to skip + * irrelevant indices at which there is no definition or use when traversing + * basic blocks. + */ + cached + predicate defUseRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + i = rank[rankix](int j | variableDef(v, _, b, j) or variableUse(v, _, b, j)) + } + + /** A definition of a variable occurring at the specified rank index in basic block `b`. */ + cached + predicate defRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + variableDef(v, _, b, i) and + defUseRank(v, b, rankix, i) + } + + /** A `VarAccess` `use` of `v` in `b` at index `i`. */ + cached + predicate variableUse(SsaSourceVariable v, ControlFlowNode use, BasicBlock b, int i) { + (v.getAUse() = use or v.hasRefinement(use, _)) and + exists(int j | + b.getNode(j) = use and + i = 2 * j + ) + } + + /** + * A definition of an SSA variable occurring at the specified position. + * This is either a phi node, a `VariableUpdate`, or a parameter. + */ + cached + predicate ssaDef(SsaSourceVariable v, BasicBlock b) { + EssaDefinitions::phiNode(v, b) + or + EssaDefinitions::variableUpdate(v, _, b, _, _) + or + EssaDefinitions::piNode(v, _, b) + } + + /* + * The construction of SSA form ensures that each use of a variable is + * dominated by its definition. A definition of an SSA variable therefore + * reaches a `ControlFlowNode` if it is the _closest_ SSA variable definition + * that dominates the node. If two definitions dominate a node then one must + * dominate the other, so therefore the definition of _closest_ is given by the + * dominator tree. Thus, reaching definitions can be calculated in terms of + * dominance. + */ + + /** The maximum rank index for the given variable and basic block. */ + cached + int lastRank(SsaSourceVariable v, BasicBlock b) { + result = max(int rankix | defUseRank(v, b, rankix, _)) + or + not defUseRank(v, b, _, _) and + (EssaDefinitions::phiNode(v, b) or EssaDefinitions::piNode(v, _, b)) and + result = 0 + } + + private predicate ssaDefRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + EssaDefinitions::variableUpdate(v, _, b, rankix, i) + or + EssaDefinitions::phiNode(v, b) and rankix = 0 and i = phiIndex() + or + EssaDefinitions::piNode(v, _, b) and + EssaDefinitions::phiNode(v, b) and + rankix = -1 and + i = piIndex() + or + EssaDefinitions::piNode(v, _, b) and + not EssaDefinitions::phiNode(v, b) and + rankix = 0 and + i = piIndex() + } + + /** The SSA definition reaches the rank index `rankix` in its own basic block `b`. */ + cached + predicate ssaDefReachesRank(SsaSourceVariable v, BasicBlock b, int i, int rankix) { + ssaDefRank(v, b, rankix, i) + or + ssaDefReachesRank(v, b, i, rankix - 1) and + rankix <= lastRank(v, b) and + not ssaDefRank(v, b, rankix, _) + } + + /** + * The SSA definition of `v` at `def` reaches `use` in the same basic block + * without crossing another SSA definition of `v`. + */ + cached + predicate ssaDefReachesUseWithinBlock( + SsaSourceVariable v, BasicBlock b, int i, ControlFlowNode use + ) { + exists(int rankix, int useix | + ssaDefReachesRank(v, b, i, rankix) and + defUseRank(v, b, rankix, useix) and + variableUse(v, use, b, useix) + ) + } + + cached + module LivenessImpl { + cached + predicate liveAtExit(SsaSourceVariable v, BasicBlock b) { liveAtEntry(v, b.getASuccessor()) } + cached - module SsaDefinitionsImpl { - pragma[noinline] - private predicate reachesEndOfBlockRec( - SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b - ) { - exists(BasicBlock idom | reachesEndOfBlock(v, defbb, defindex, idom) | - idom = b.getImmediateDominator() - ) - } - - /** - * The SSA definition of `v` at `def` reaches the end of a basic block `b`, at - * which point it is still live, without crossing another SSA definition of `v`. - */ - cached - predicate reachesEndOfBlock(SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b) { - Liveness::liveAtExit(v, b) and - ( - defbb = b and - SsaComputeImpl::ssaDefReachesRank(v, defbb, defindex, SsaComputeImpl::lastRank(v, b)) - or - // It is sufficient to traverse the dominator graph, cf. discussion above. - reachesEndOfBlockRec(v, defbb, defindex, b) and - not SsaComputeImpl::ssaDef(v, b) - ) - } - - /** - * The SSA definition of `v` at `(defbb, defindex)` reaches `use` without crossing another - * SSA definition of `v`. - */ - cached - predicate reachesUse(SsaSourceVariable v, BasicBlock defbb, int defindex, ControlFlowNode use) { - SsaComputeImpl::ssaDefReachesUseWithinBlock(v, defbb, defindex, use) - or - exists(BasicBlock b | - SsaComputeImpl::variableUse(v, use, b, _) and - reachesEndOfBlock(v, defbb, defindex, b.getAPredecessor()) and - not SsaComputeImpl::ssaDefReachesUseWithinBlock(v, b, _, use) - ) - } - - /** - * Holds if `(defbb, defindex)` is an SSA definition of `v` that reaches an exit without crossing another - * SSA definition of `v`. - */ - cached - predicate reachesExit(SsaSourceVariable v, BasicBlock defbb, int defindex) { - exists(BasicBlock last, ControlFlowNode use, int index | - not Liveness::liveAtExit(v, last) and - reachesUse(v, defbb, defindex, use) and - SsaComputeImpl::defUseRank(v, last, SsaComputeImpl::lastRank(v, last), index) and - SsaComputeImpl::variableUse(v, use, last, index) - ) - } + predicate liveAtEntry(SsaSourceVariable v, BasicBlock b) { + SsaComputeImpl::defUseRank(v, b, 1, _) and not SsaComputeImpl::defRank(v, b, 1, _) + or + not SsaComputeImpl::defUseRank(v, b, _, _) and liveAtExit(v, b) } + } + + cached + module SsaDefinitionsImpl { + pragma[noinline] + private predicate reachesEndOfBlockRec( + SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b + ) { + exists(BasicBlock idom | reachesEndOfBlock(v, defbb, defindex, idom) | + idom = b.getImmediateDominator() + ) + } + + /** + * The SSA definition of `v` at `def` reaches the end of a basic block `b`, at + * which point it is still live, without crossing another SSA definition of `v`. + */ + cached + predicate reachesEndOfBlock(SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b) { + Liveness::liveAtExit(v, b) and + ( + defbb = b and + SsaComputeImpl::ssaDefReachesRank(v, defbb, defindex, SsaComputeImpl::lastRank(v, b)) + or + // It is sufficient to traverse the dominator graph, cf. discussion above. + reachesEndOfBlockRec(v, defbb, defindex, b) and + not SsaComputeImpl::ssaDef(v, b) + ) + } + + /** + * The SSA definition of `v` at `(defbb, defindex)` reaches `use` without crossing another + * SSA definition of `v`. + */ + cached + predicate reachesUse(SsaSourceVariable v, BasicBlock defbb, int defindex, ControlFlowNode use) { + SsaComputeImpl::ssaDefReachesUseWithinBlock(v, defbb, defindex, use) + or + exists(BasicBlock b | + SsaComputeImpl::variableUse(v, use, b, _) and + reachesEndOfBlock(v, defbb, defindex, b.getAPredecessor()) and + not SsaComputeImpl::ssaDefReachesUseWithinBlock(v, b, _, use) + ) + } + + /** + * Holds if `(defbb, defindex)` is an SSA definition of `v` that reaches an exit without crossing another + * SSA definition of `v`. + */ + cached + predicate reachesExit(SsaSourceVariable v, BasicBlock defbb, int defindex) { + exists(BasicBlock last, ControlFlowNode use, int index | + not Liveness::liveAtExit(v, last) and + reachesUse(v, defbb, defindex, use) and + SsaComputeImpl::defUseRank(v, last, SsaComputeImpl::lastRank(v, last), index) and + SsaComputeImpl::variableUse(v, use, last, index) + ) + } + } } import SsaComputeImpl::SsaDefinitionsImpl as SsaDefinitions diff --git a/python/ql/src/semmle/python/essa/SsaDefinitions.qll b/python/ql/src/semmle/python/essa/SsaDefinitions.qll index 992a2bd96ac..0de3507f45e 100644 --- a/python/ql/src/semmle/python/essa/SsaDefinitions.qll +++ b/python/ql/src/semmle/python/essa/SsaDefinitions.qll @@ -8,142 +8,142 @@ private import semmle.python.pointsto.Base cached module SsaSource { - /** Holds if `v` is used as the receiver in a method call. */ - cached - predicate method_call_refinement(Variable v, ControlFlowNode use, CallNode call) { - use = v.getAUse() and - call.getFunction().(AttrNode).getObject() = use and - not test_contains(_, call) - } + /** Holds if `v` is used as the receiver in a method call. */ + cached + predicate method_call_refinement(Variable v, ControlFlowNode use, CallNode call) { + use = v.getAUse() and + call.getFunction().(AttrNode).getObject() = use and + not test_contains(_, call) + } - /** Holds if `v` is defined by assignment at `defn` and given `value`. */ - cached - predicate assignment_definition(Variable v, ControlFlowNode defn, ControlFlowNode value) { - defn.(NameNode).defines(v) and defn.(DefinitionNode).getValue() = value - } + /** Holds if `v` is defined by assignment at `defn` and given `value`. */ + cached + predicate assignment_definition(Variable v, ControlFlowNode defn, ControlFlowNode value) { + defn.(NameNode).defines(v) and defn.(DefinitionNode).getValue() = value + } - /** Holds if `v` is defined by assignment of the captured exception. */ - cached - predicate exception_capture(Variable v, NameNode defn) { - defn.defines(v) and - exists(ExceptFlowNode ex | ex.getName() = defn) - } + /** Holds if `v` is defined by assignment of the captured exception. */ + cached + predicate exception_capture(Variable v, NameNode defn) { + defn.defines(v) and + exists(ExceptFlowNode ex | ex.getName() = defn) + } - /** Holds if `v` is defined by a with statement. */ - cached - predicate with_definition(Variable v, ControlFlowNode defn) { - exists(With with, Name var | - with.getOptionalVars() = var and - var.getAFlowNode() = defn - | - var = v.getAStore() - ) - } + /** Holds if `v` is defined by a with statement. */ + cached + predicate with_definition(Variable v, ControlFlowNode defn) { + exists(With with, Name var | + with.getOptionalVars() = var and + var.getAFlowNode() = defn + | + var = v.getAStore() + ) + } - /** Holds if `v` is defined by multiple assignment at `defn`. */ - cached - predicate multi_assignment_definition(Variable v, ControlFlowNode defn, int n, SequenceNode lhs) { - ( - defn.(NameNode).defines(v) - or - defn.(StarredNode).getValue().(NameNode).defines(v) - ) and - not exists(defn.(DefinitionNode).getValue()) and - lhs.getElement(n) = defn and - lhs.getBasicBlock().dominates(defn.getBasicBlock()) - } + /** Holds if `v` is defined by multiple assignment at `defn`. */ + cached + predicate multi_assignment_definition(Variable v, ControlFlowNode defn, int n, SequenceNode lhs) { + ( + defn.(NameNode).defines(v) + or + defn.(StarredNode).getValue().(NameNode).defines(v) + ) and + not exists(defn.(DefinitionNode).getValue()) and + lhs.getElement(n) = defn and + lhs.getBasicBlock().dominates(defn.getBasicBlock()) + } - /** Holds if `v` is defined by a `for` statement, the definition being `defn` */ - cached - predicate iteration_defined_variable(Variable v, ControlFlowNode defn, ControlFlowNode sequence) { - exists(ForNode for | for.iterates(defn, sequence)) and - defn.(NameNode).defines(v) - } + /** Holds if `v` is defined by a `for` statement, the definition being `defn` */ + cached + predicate iteration_defined_variable(Variable v, ControlFlowNode defn, ControlFlowNode sequence) { + exists(ForNode for | for.iterates(defn, sequence)) and + defn.(NameNode).defines(v) + } - /** Holds if `v` is a parameter variable and `defn` is the CFG node for that parameter. */ - cached - predicate parameter_definition(Variable v, ControlFlowNode defn) { - exists(Function f, Name param | - f.getAnArg() = param or - f.getVararg() = param or - f.getKwarg() = param or - f.getKeywordOnlyArg(_) = param - | - defn.getNode() = param and - param.getVariable() = v - ) - } + /** Holds if `v` is a parameter variable and `defn` is the CFG node for that parameter. */ + cached + predicate parameter_definition(Variable v, ControlFlowNode defn) { + exists(Function f, Name param | + f.getAnArg() = param or + f.getVararg() = param or + f.getKwarg() = param or + f.getKeywordOnlyArg(_) = param + | + defn.getNode() = param and + param.getVariable() = v + ) + } - /** Holds if `v` is deleted at `del`. */ - cached - predicate deletion_definition(Variable v, DeletionNode del) { - del.getTarget().(NameNode).deletes(v) - } + /** Holds if `v` is deleted at `del`. */ + cached + predicate deletion_definition(Variable v, DeletionNode del) { + del.getTarget().(NameNode).deletes(v) + } - /** - * Holds if the name of `var` refers to a submodule of a package and `f` is the entry point - * to the __init__ module of that package. - */ - cached - predicate init_module_submodule_defn(SsaSourceVariable var, ControlFlowNode f) { - var instanceof GlobalVariable and - exists(Module init | - init.isPackageInit() and - exists(init.getPackage().getSubModule(var.getName())) and - init.getEntryNode() = f and - var.getScope() = init - ) - } + /** + * Holds if the name of `var` refers to a submodule of a package and `f` is the entry point + * to the __init__ module of that package. + */ + cached + predicate init_module_submodule_defn(SsaSourceVariable var, ControlFlowNode f) { + var instanceof GlobalVariable and + exists(Module init | + init.isPackageInit() and + exists(init.getPackage().getSubModule(var.getName())) and + init.getEntryNode() = f and + var.getScope() = init + ) + } - /** Holds if the `v` is in scope at a `from import ... *` and may thus be redefined by that statement */ - cached - predicate import_star_refinement(SsaSourceVariable v, ControlFlowNode use, ControlFlowNode def) { - use = def and - def instanceof ImportStarNode and - ( - v.getScope() = def.getScope() - or - exists(NameNode other | - other.uses(v) and - def.getScope() = other.getScope() - ) - ) - } + /** Holds if the `v` is in scope at a `from import ... *` and may thus be redefined by that statement */ + cached + predicate import_star_refinement(SsaSourceVariable v, ControlFlowNode use, ControlFlowNode def) { + use = def and + def instanceof ImportStarNode and + ( + v.getScope() = def.getScope() + or + exists(NameNode other | + other.uses(v) and + def.getScope() = other.getScope() + ) + ) + } - /** Holds if an attribute is assigned at `def` and `use` is the use of `v` for that assignment */ - cached - predicate attribute_assignment_refinement(Variable v, ControlFlowNode use, ControlFlowNode def) { - use.(NameNode).uses(v) and - def.isStore() and - def.(AttrNode).getObject() = use - } + /** Holds if an attribute is assigned at `def` and `use` is the use of `v` for that assignment */ + cached + predicate attribute_assignment_refinement(Variable v, ControlFlowNode use, ControlFlowNode def) { + use.(NameNode).uses(v) and + def.isStore() and + def.(AttrNode).getObject() = use + } - /** Holds if a `v` is used as an argument to `call`, which *may* modify the object referred to by `v` */ - cached - predicate argument_refinement(Variable v, ControlFlowNode use, CallNode call) { - use.(NameNode).uses(v) and - call.getArg(0) = use and - not method_call_refinement(v, _, call) and - not test_contains(_, call) - } + /** Holds if a `v` is used as an argument to `call`, which *may* modify the object referred to by `v` */ + cached + predicate argument_refinement(Variable v, ControlFlowNode use, CallNode call) { + use.(NameNode).uses(v) and + call.getArg(0) = use and + not method_call_refinement(v, _, call) and + not test_contains(_, call) + } - /** Holds if an attribute is deleted at `def` and `use` is the use of `v` for that deletion */ - cached - predicate attribute_deletion_refinement(Variable v, NameNode use, DeletionNode def) { - use.uses(v) and - def.getTarget().(AttrNode).getObject() = use - } + /** Holds if an attribute is deleted at `def` and `use` is the use of `v` for that deletion */ + cached + predicate attribute_deletion_refinement(Variable v, NameNode use, DeletionNode def) { + use.uses(v) and + def.getTarget().(AttrNode).getObject() = use + } - /** Holds if the set of possible values for `v` is refined by `test` and `use` is the use of `v` in that test. */ - cached - predicate test_refinement(Variable v, ControlFlowNode use, ControlFlowNode test) { - use.(NameNode).uses(v) and - test.getAChild*() = use and - test.isBranch() and - exists(BasicBlock block | - block = use.getBasicBlock() and - block = test.getBasicBlock() and - not block.getLastNode() = test - ) - } + /** Holds if the set of possible values for `v` is refined by `test` and `use` is the use of `v` in that test. */ + cached + predicate test_refinement(Variable v, ControlFlowNode use, ControlFlowNode test) { + use.(NameNode).uses(v) and + test.getAChild*() = use and + test.isBranch() and + exists(BasicBlock block | + block = use.getBasicBlock() and + block = test.getBasicBlock() and + not block.getLastNode() = test + ) + } } diff --git a/python/ql/src/semmle/python/filters/GeneratedCode.qll b/python/ql/src/semmle/python/filters/GeneratedCode.qll index 5b1721945da..a818f172637 100644 --- a/python/ql/src/semmle/python/filters/GeneratedCode.qll +++ b/python/ql/src/semmle/python/filters/GeneratedCode.qll @@ -5,7 +5,7 @@ import semmle.python.templates.Templates * A file that is detected as being generated. */ abstract class GeneratedFile extends File { - abstract string getTool(); + abstract string getTool(); } /* @@ -15,173 +15,173 @@ abstract class GeneratedFile extends File { */ library class GenericGeneratedFile extends GeneratedFile { - GenericGeneratedFile() { - not this instanceof SpecificGeneratedFile and - ( - (lax_generated_by(this, _) or lax_generated_from(this, _)) and - dont_modify(this) - or - strict_generated_by(this, _) - or - strict_generated_from(this, _) - or - auto_generated(this) - ) - } + GenericGeneratedFile() { + not this instanceof SpecificGeneratedFile and + ( + (lax_generated_by(this, _) or lax_generated_from(this, _)) and + dont_modify(this) + or + strict_generated_by(this, _) + or + strict_generated_from(this, _) + or + auto_generated(this) + ) + } - override string getTool() { lax_generated_by(this, result) or strict_generated_by(this, result) } + override string getTool() { lax_generated_by(this, result) or strict_generated_by(this, result) } } private string comment_or_docstring(File f, boolean before_code) { - exists(Comment c | - c.getLocation().getFile() = f and - result = c.getText() - | - if - exists(Stmt s | - s.getEnclosingModule().getFile() = f and - s.getLocation().getStartLine() < c.getLocation().getStartLine() - ) - then before_code = false - else before_code = true - ) - or - exists(Module m | m.getFile() = f | - result = m.getDocString().getText() and - before_code = true - ) + exists(Comment c | + c.getLocation().getFile() = f and + result = c.getText() + | + if + exists(Stmt s | + s.getEnclosingModule().getFile() = f and + s.getLocation().getStartLine() < c.getLocation().getStartLine() + ) + then before_code = false + else before_code = true + ) + or + exists(Module m | m.getFile() = f | + result = m.getDocString().getText() and + before_code = true + ) } private predicate lax_generated_by(File f, string tool) { - exists(string comment | comment = comment_or_docstring(f, _) | - tool = - comment - .regexpCapture("(?is).*\\b(?:(?:auto[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, _) | + tool = + comment + .regexpCapture("(?is).*\\b(?:(?:auto[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate lax_generated_from(File f, string src) { - exists(string comment | comment = comment_or_docstring(f, _) | - src = - comment - .regexpCapture("(?is).*\\b((?:auto[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, _) | + src = + comment + .regexpCapture("(?is).*\\b((?:auto[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate strict_generated_by(File f, string tool) { - exists(string comment | comment = comment_or_docstring(f, true) | - tool = - comment - .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, true) | + tool = + comment + .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate strict_generated_from(File f, string src) { - exists(string comment | comment = comment_or_docstring(f, true) | - src = - comment - .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", - 1) - ) + exists(string comment | comment = comment_or_docstring(f, true) | + src = + comment + .regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", + 1) + ) } private predicate dont_modify(File f) { - comment_or_docstring(f, _).regexpMatch("(?is).*\\b(Do not|Don't) (edit|modify|make changes)\\b.*") + comment_or_docstring(f, _).regexpMatch("(?is).*\\b(Do not|Don't) (edit|modify|make changes)\\b.*") } private predicate auto_generated(File f) { - exists(Comment c | - c.getLocation().getFile() = f and - c - .getText() - .regexpMatch("(?is)# *this +(code|file) +is +(auto(matically)?[ -]?generated|created automatically).*") - ) + exists(Comment c | + c.getLocation().getFile() = f and + c + .getText() + .regexpMatch("(?is)# *this +(code|file) +is +(auto(matically)?[ -]?generated|created automatically).*") + ) } /** * A file generated by a template engine */ abstract library class SpecificGeneratedFile extends GeneratedFile { - /* - * Currently cover Spitfire, Pyxl and Mako. - * Django templates are not compiled to Python. - * Jinja2 templates are compiled direct to bytecode via the ast. - */ + /* + * Currently cover Spitfire, Pyxl and Mako. + * Django templates are not compiled to Python. + * Jinja2 templates are compiled direct to bytecode via the ast. + */ - } + } /** File generated by the spitfire templating engine */ class SpitfireGeneratedFile extends SpecificGeneratedFile { - SpitfireGeneratedFile() { - exists(Module m | m.getFile() = this and not m instanceof SpitfireTemplate | - exists(ImportMember template_method, ImportExpr spitfire_runtime_template | - spitfire_runtime_template.getName() = "spitfire.runtime.template" and - template_method.getModule() = spitfire_runtime_template and - template_method.getName() = "template_method" - ) - ) - } + SpitfireGeneratedFile() { + exists(Module m | m.getFile() = this and not m instanceof SpitfireTemplate | + exists(ImportMember template_method, ImportExpr spitfire_runtime_template | + spitfire_runtime_template.getName() = "spitfire.runtime.template" and + template_method.getModule() = spitfire_runtime_template and + template_method.getName() = "template_method" + ) + ) + } - override string getTool() { result = "spitfire" } + override string getTool() { result = "spitfire" } } /** File generated by the pyxl templating engine */ class PyxlGeneratedFile extends SpecificGeneratedFile { - PyxlGeneratedFile() { this.getSpecifiedEncoding() = "pyxl" } + PyxlGeneratedFile() { this.getSpecifiedEncoding() = "pyxl" } - override string getTool() { result = "pyxl" } + override string getTool() { result = "pyxl" } } /** File generated by the mako templating engine */ class MakoGeneratedFile extends SpecificGeneratedFile { - MakoGeneratedFile() { - exists(Module m | m.getFile() = this | - from_mako_import(m) = "runtime" and - from_mako_import(m) = "filters" and - from_mako_import(m) = "cache" and - exists(Assign a, Name n | - a.getScope() = m and a.getATarget() = n and n.getId() = "__M_dict_builtin" - ) and - exists(Assign a, Name n | - a.getScope() = m and a.getATarget() = n and n.getId() = "__M_locals_builtin" - ) and - exists(Assign a, Name n | - a.getScope() = m and a.getATarget() = n and n.getId() = "_magic_number" - ) - ) - } + MakoGeneratedFile() { + exists(Module m | m.getFile() = this | + from_mako_import(m) = "runtime" and + from_mako_import(m) = "filters" and + from_mako_import(m) = "cache" and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "__M_dict_builtin" + ) and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "__M_locals_builtin" + ) and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "_magic_number" + ) + ) + } - override string getTool() { result = "mako" } + override string getTool() { result = "mako" } } string from_mako_import(Module m) { - exists(ImportMember member, ImportExpr mako | - member.getScope() = m and - member.getModule() = mako and - mako.getName() = "mako" - | - result = member.getName() - ) + exists(ImportMember member, ImportExpr mako | + member.getScope() = m and + member.getModule() = mako and + mako.getName() = "mako" + | + result = member.getName() + ) } /** File generated by Google's protobuf tool. */ class ProtobufGeneratedFile extends SpecificGeneratedFile { - ProtobufGeneratedFile() { - this.getAbsolutePath().regexpMatch(".*_pb2?.py") and - exists(Module m | m.getFile() = this | - exists(ImportExpr imp | imp.getEnclosingModule() = m | - imp.getImportedModuleName() = "google.net.proto2.python.public" - ) and - exists(AssignStmt a, Name n | - a.getEnclosingModule() = m and - a.getATarget() = n and - n.getId() = "DESCRIPTOR" - ) - ) - } + ProtobufGeneratedFile() { + this.getAbsolutePath().regexpMatch(".*_pb2?.py") and + exists(Module m | m.getFile() = this | + exists(ImportExpr imp | imp.getEnclosingModule() = m | + imp.getImportedModuleName() = "google.net.proto2.python.public" + ) and + exists(AssignStmt a, Name n | + a.getEnclosingModule() = m and + a.getATarget() = n and + n.getId() = "DESCRIPTOR" + ) + ) + } - override string getTool() { result = "protobuf" } + override string getTool() { result = "protobuf" } } diff --git a/python/ql/src/semmle/python/filters/Tests.qll b/python/ql/src/semmle/python/filters/Tests.qll index b4fb59fda07..b35f275aab5 100644 --- a/python/ql/src/semmle/python/filters/Tests.qll +++ b/python/ql/src/semmle/python/filters/Tests.qll @@ -4,43 +4,43 @@ abstract class TestScope extends Scope { } // don't extend Class directly to avoid ambiguous method warnings class UnitTestClass extends TestScope { - UnitTestClass() { - exists(ClassValue cls | this = cls.getScope() | - cls.getABaseType+() = Module::named("unittest").attr(_) - or - cls.getABaseType+().getName().toLowerCase() = "testcase" - ) - } + UnitTestClass() { + exists(ClassValue cls | this = cls.getScope() | + cls.getABaseType+() = Module::named("unittest").attr(_) + or + cls.getABaseType+().getName().toLowerCase() = "testcase" + ) + } } abstract class Test extends TestScope { } /** Class of test function that uses the `unittest` framework */ class UnitTestFunction extends Test { - UnitTestFunction() { - this.getScope+() instanceof UnitTestClass and - this.(Function).getName().matches("test%") - } + UnitTestFunction() { + this.getScope+() instanceof UnitTestClass and + this.(Function).getName().matches("test%") + } } class PyTestFunction extends Test { - PyTestFunction() { - exists(Module pytest | pytest.getName() = "pytest") and - this.(Function).getName().matches("test%") - } + PyTestFunction() { + exists(Module pytest | pytest.getName() = "pytest") and + this.(Function).getName().matches("test%") + } } class NoseTestFunction extends Test { - NoseTestFunction() { - exists(Module nose | nose.getName() = "nose") and - this.(Function).getName().matches("test%") - } + NoseTestFunction() { + exists(Module nose | nose.getName() = "nose") and + this.(Function).getName().matches("test%") + } } /** Class of functions that are clearly tests, but don't belong to a specific framework */ class UnknownTestFunction extends Test { - UnknownTestFunction() { - this.(Function).getName().matches("test%") and - this.getEnclosingModule().getFile().getShortName().matches("test_%.py") - } + UnknownTestFunction() { + this.(Function).getName().matches("test%") and + this.getEnclosingModule().getFile().getShortName().matches("test_%.py") + } } diff --git a/python/ql/src/semmle/python/libraries/Zope.qll b/python/ql/src/semmle/python/libraries/Zope.qll index 381e40edeab..728c334352b 100644 --- a/python/ql/src/semmle/python/libraries/Zope.qll +++ b/python/ql/src/semmle/python/libraries/Zope.qll @@ -5,43 +5,43 @@ private import semmle.python.pointsto.PointsTo /** A method that to a sub-class of `zope.interface.Interface` */ deprecated class ZopeInterfaceMethod extends PyFunctionObject { - /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ - ZopeInterfaceMethod() { - exists(Object interface, ClassObject owner | - interface = ModuleObject::named("zope.interface").attr("Interface") and - owner.declaredAttribute(_) = this and - owner.getAnImproperSuperType().getABaseType() = interface - ) - } + /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ + ZopeInterfaceMethod() { + exists(Object interface, ClassObject owner | + interface = ModuleObject::named("zope.interface").attr("Interface") and + owner.declaredAttribute(_) = this and + owner.getAnImproperSuperType().getABaseType() = interface + ) + } - override int minParameters() { result = super.minParameters() + 1 } + override int minParameters() { result = super.minParameters() + 1 } - override int maxParameters() { - if exists(this.getFunction().getVararg()) - then result = super.maxParameters() - else result = super.maxParameters() + 1 - } + override int maxParameters() { + if exists(this.getFunction().getVararg()) + then result = super.maxParameters() + else result = super.maxParameters() + 1 + } } /** A method that belongs to a sub-class of `zope.interface.Interface` */ class ZopeInterfaceMethodValue extends PythonFunctionValue { - /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ - ZopeInterfaceMethodValue() { - exists(Value interface, ClassValue owner | - interface = Module::named("zope.interface").attr("Interface") and - owner.declaredAttribute(_) = this and - // `zope.interface.Interface` will be recognized as a Value by the pointsTo analysis, - // because it is the result of instantiating a "meta" class. getASuperType only returns - // ClassValues, so we do this little trick to make things work - Types::getBase(owner.getASuperType(), _) = interface - ) - } + /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ + ZopeInterfaceMethodValue() { + exists(Value interface, ClassValue owner | + interface = Module::named("zope.interface").attr("Interface") and + owner.declaredAttribute(_) = this and + // `zope.interface.Interface` will be recognized as a Value by the pointsTo analysis, + // because it is the result of instantiating a "meta" class. getASuperType only returns + // ClassValues, so we do this little trick to make things work + Types::getBase(owner.getASuperType(), _) = interface + ) + } - override int minParameters() { result = super.minParameters() + 1 } + override int minParameters() { result = super.minParameters() + 1 } - override int maxParameters() { - if exists(this.getScope().getVararg()) - then result = super.maxParameters() - else result = super.maxParameters() + 1 - } + override int maxParameters() { + if exists(this.getScope().getVararg()) + then result = super.maxParameters() + else result = super.maxParameters() + 1 + } } diff --git a/python/ql/src/semmle/python/objects/Callables.qll b/python/ql/src/semmle/python/objects/Callables.qll index 4021f04c510..2bac030d89e 100644 --- a/python/ql/src/semmle/python/objects/Callables.qll +++ b/python/ql/src/semmle/python/objects/Callables.qll @@ -7,376 +7,376 @@ private import semmle.python.pointsto.MRO private import semmle.python.types.Builtins abstract class CallableObjectInternal extends ObjectInternal { - /** Gets the scope of this callable if it has one */ - abstract Function getScope(); + /** Gets the scope of this callable if it has one */ + abstract Function getScope(); - /** Gets a call to this callable from the given context */ - abstract CallNode getACall(PointsToContext ctx); + /** Gets a call to this callable from the given context */ + abstract CallNode getACall(PointsToContext ctx); - /** Gets a call to this callable */ - CallNode getACall() { result = this.getACall(_) } + /** Gets a call to this callable */ + CallNode getACall() { result = this.getACall(_) } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - /** Gets the `n`th parameter node of this callable. */ - abstract NameNode getParameter(int n); + /** Gets the `n`th parameter node of this callable. */ + abstract NameNode getParameter(int n); - /** Gets the `name`d parameter node of this callable. */ - abstract NameNode getParameterByName(string name); + /** Gets the `name`d parameter node of this callable. */ + abstract NameNode getParameterByName(string name); - abstract predicate neverReturns(); + abstract predicate neverReturns(); - override int length() { none() } + override int length() { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - /* Callables aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Callables aren't iterable */ + override ObjectInternal getIterNext() { none() } } /** Class representing Python functions */ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject { - override Function getScope() { - exists(CallableExpr expr | - this = TPythonFunctionObject(expr.getAFlowNode()) and - result = expr.getInnerScope() - ) - } + override Function getScope() { + exists(CallableExpr expr | + this = TPythonFunctionObject(expr.getAFlowNode()) and + result = expr.getInnerScope() + ) + } - override string toString() { result = "Function " + this.getScope().getQualifiedName() } + override string toString() { result = "Function " + this.getScope().getQualifiedName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TPythonFunctionObject(node) and context.appliesTo(node) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TPythonFunctionObject(node) and context.appliesTo(node) + } - override ObjectInternal getClass() { - result = TBuiltinClassObject(Builtin::special("FunctionType")) - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(Builtin::special("FunctionType")) + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TPythonFunctionObject(result) } + override ControlFlowNode getOrigin() { this = TPythonFunctionObject(result) } - pragma[nomagic] - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - exists(Function func, ControlFlowNode rval, ControlFlowNode forigin | - func = this.getScope() and - callee.appliesToScope(func) - | - rval = func.getAReturnValueFlowNode() and - PointsToInternal::pointsTo(rval, callee, obj, forigin) and - origin = CfgOrigin::fromCfgNode(forigin) - ) - or - procedureReturnsNone(callee, obj, origin) - } + pragma[nomagic] + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + exists(Function func, ControlFlowNode rval, ControlFlowNode forigin | + func = this.getScope() and + callee.appliesToScope(func) + | + rval = func.getAReturnValueFlowNode() and + PointsToInternal::pointsTo(rval, callee, obj, forigin) and + origin = CfgOrigin::fromCfgNode(forigin) + ) + or + procedureReturnsNone(callee, obj, origin) + } - private predicate procedureReturnsNone( - PointsToContext callee, ObjectInternal obj, CfgOrigin origin - ) { - exists(Function func | - func = this.getScope() and - callee.appliesToScope(func) - | - PointsToInternal::reachableBlock(blockReturningNone(func), callee) and - obj = ObjectInternal::none_() and - origin = CfgOrigin::unknown() - ) - } + private predicate procedureReturnsNone( + PointsToContext callee, ObjectInternal obj, CfgOrigin origin + ) { + exists(Function func | + func = this.getScope() and + callee.appliesToScope(func) + | + PointsToInternal::reachableBlock(blockReturningNone(func), callee) and + obj = ObjectInternal::none_() and + origin = CfgOrigin::unknown() + ) + } - pragma[noinline] - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - this.getScope().isProcedure() and - obj = ObjectInternal::none_() and - origin = CfgOrigin::fromCfgNode(this.getScope().getEntryNode()) - } + pragma[noinline] + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + this.getScope().isProcedure() and + obj = ObjectInternal::none_() and + origin = CfgOrigin::fromCfgNode(this.getScope().getEntryNode()) + } - override predicate calleeAndOffset(Function scope, int paramOffset) { - scope = this.getScope() and paramOffset = 0 - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + scope = this.getScope() and paramOffset = 0 + } - override string getName() { result = this.getScope().getName() } + override string getName() { result = this.getScope().getName() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this and - origin = CfgOrigin::fromCfgNode(this.getOrigin()) - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this and + origin = CfgOrigin::fromCfgNode(this.getOrigin()) + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - or - exists(BoundMethodObjectInternal bm | bm.getACall(ctx) = result and this = bm.getFunction()) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + or + exists(BoundMethodObjectInternal bm | bm.getACall(ctx) = result and this = bm.getFunction()) + } - override NameNode getParameter(int n) { result.getNode() = this.getScope().getArg(n) } + override NameNode getParameter(int n) { result.getNode() = this.getScope().getArg(n) } - override NameNode getParameterByName(string name) { - result.getNode() = this.getScope().getArgByName(name) - } + override NameNode getParameterByName(string name) { + result.getNode() = this.getScope().getArgByName(name) + } - override predicate neverReturns() { InterProceduralPointsTo::neverReturns(this.getScope()) } + override predicate neverReturns() { InterProceduralPointsTo::neverReturns(this.getScope()) } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this and offset = 0 + } - override predicate contextSensitiveCallee() { any() } + override predicate contextSensitiveCallee() { any() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private BasicBlock blockReturningNone(Function func) { - exists(Return ret | - not exists(ret.getValue()) and - ret.getScope() = func and - result = ret.getAFlowNode().getBasicBlock() - ) + exists(Return ret | + not exists(ret.getValue()) and + ret.getScope() = func and + result = ret.getAFlowNode().getBasicBlock() + ) } /** Class representing built-in functions such as `len` or `print`. */ class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunctionObject { - override Builtin getBuiltin() { this = TBuiltinFunctionObject(result) } + override Builtin getBuiltin() { this = TBuiltinFunctionObject(result) } - override string toString() { result = "Builtin-function " + this.getBuiltin().getName() } + override string toString() { result = "Builtin-function " + this.getBuiltin().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } + override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - exists(Builtin func, BuiltinClassObjectInternal cls | - func = this.getBuiltin() and - func != Builtin::builtin("isinstance") and - func != Builtin::builtin("issubclass") and - func != Builtin::builtin("callable") and - cls = ObjectInternal::fromBuiltin(this.getReturnType()) - | - obj = TUnknownInstance(cls) - or - cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() - or - cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) - ) and - origin = CfgOrigin::unknown() - or - this.returnTypeUnknown() and - obj = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + exists(Builtin func, BuiltinClassObjectInternal cls | + func = this.getBuiltin() and + func != Builtin::builtin("isinstance") and + func != Builtin::builtin("issubclass") and + func != Builtin::builtin("callable") and + cls = ObjectInternal::fromBuiltin(this.getReturnType()) + | + obj = TUnknownInstance(cls) + or + cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() + or + cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) + ) and + origin = CfgOrigin::unknown() + or + this.returnTypeUnknown() and + obj = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - Builtin getReturnType() { - exists(Builtin func | - func = this.getBuiltin() and - result = getBuiltinFunctionReturnType(func) - ) - } + Builtin getReturnType() { + exists(Builtin func | + func = this.getBuiltin() and + result = getBuiltinFunctionReturnType(func) + ) + } - private predicate returnTypeUnknown() { - exists(Builtin func | - func = this.getBuiltin() and - not exists(getBuiltinFunctionReturnType(func)) - ) - } + private predicate returnTypeUnknown() { + exists(Builtin func | + func = this.getBuiltin() and + not exists(getBuiltinFunctionReturnType(func)) + ) + } - override Function getScope() { none() } + override Function getScope() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + } - override NameNode getParameter(int n) { none() } + override NameNode getParameter(int n) { none() } - override NameNode getParameterByName(string name) { none() } + override NameNode getParameterByName(string name) { none() } - override predicate neverReturns() { - exists(ModuleObjectInternal sys | - sys.getName() = "sys" and - sys.attribute("exit", this, _) - ) - } + override predicate neverReturns() { + exists(ModuleObjectInternal sys | + sys.getName() = "sys" and + sys.attribute("exit", this, _) + ) + } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this and offset = 0 + } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private Builtin getBuiltinFunctionReturnType(Builtin func) { - /* Enumerate the types of a few builtin functions, that the CPython analysis misses. */ - func = Builtin::builtin("hex") and result = Builtin::special("str") + /* Enumerate the types of a few builtin functions, that the CPython analysis misses. */ + func = Builtin::builtin("hex") and result = Builtin::special("str") + or + func = Builtin::builtin("oct") and result = Builtin::special("str") + or + func = Builtin::builtin("intern") and result = Builtin::special("str") + or + func = Builtin::builtin("__import__") and result = Builtin::special("ModuleType") + or + /* Fix a few minor inaccuracies in the CPython analysis */ + ext_rettype(func, result) and + not ( + func = Builtin::builtin("__import__") or - func = Builtin::builtin("oct") and result = Builtin::special("str") + func = Builtin::builtin("compile") and result = Builtin::special("NoneType") or - func = Builtin::builtin("intern") and result = Builtin::special("str") + func = Builtin::builtin("sum") or - func = Builtin::builtin("__import__") and result = Builtin::special("ModuleType") - or - /* Fix a few minor inaccuracies in the CPython analysis */ - ext_rettype(func, result) and - not ( - func = Builtin::builtin("__import__") - or - func = Builtin::builtin("compile") and result = Builtin::special("NoneType") - or - func = Builtin::builtin("sum") - or - func = Builtin::builtin("filter") - ) + func = Builtin::builtin("filter") + ) } /** Class representing methods of built-in classes (otherwise known as method-descriptors) such as `list.append`. */ class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethodObject { - override Builtin getBuiltin() { this = TBuiltinMethodObject(result) } + override Builtin getBuiltin() { this = TBuiltinMethodObject(result) } - override string toString() { result = "builtin method " + this.getBuiltin().getName() } + override string toString() { result = "builtin method " + this.getBuiltin().getName() } - override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } + override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - exists(Builtin func, BuiltinClassObjectInternal cls | - func = this.getBuiltin() and - cls = ObjectInternal::fromBuiltin(this.getReturnType()) - | - obj = TUnknownInstance(cls) - or - cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() - or - cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) - ) and - origin = CfgOrigin::unknown() - or - this.returnTypeUnknown() and - obj = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + exists(Builtin func, BuiltinClassObjectInternal cls | + func = this.getBuiltin() and + cls = ObjectInternal::fromBuiltin(this.getReturnType()) + | + obj = TUnknownInstance(cls) + or + cls = ObjectInternal::noneType() and obj = ObjectInternal::none_() + or + cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_) + ) and + origin = CfgOrigin::unknown() + or + this.returnTypeUnknown() and + obj = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + } - Builtin getReturnType() { - /* If we have a record of the return type in our stubs, use that. */ - exists(Builtin func | func = this.getBuiltin() | ext_rettype(func, result)) - } + Builtin getReturnType() { + /* If we have a record of the return type in our stubs, use that. */ + exists(Builtin func | func = this.getBuiltin() | ext_rettype(func, result)) + } - private predicate returnTypeUnknown() { - exists(Builtin func | func = this.getBuiltin() | not ext_rettype(func, _)) - } + private predicate returnTypeUnknown() { + exists(Builtin func | func = this.getBuiltin() | not ext_rettype(func, _)) + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - override Function getScope() { none() } + override Function getScope() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + } - override NameNode getParameter(int n) { none() } + override NameNode getParameter(int n) { none() } - override NameNode getParameterByName(string name) { none() } + override NameNode getParameterByName(string name) { none() } - override predicate neverReturns() { none() } + override predicate neverReturns() { none() } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this and offset = 0 + } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** @@ -386,89 +386,89 @@ class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethod * is the same and we treat them identically. */ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - CallableObjectInternal getFunction() { this = TBoundMethod(_, result) } + CallableObjectInternal getFunction() { this = TBoundMethod(_, result) } - ObjectInternal getSelf() { this = TBoundMethod(result, _) } + ObjectInternal getSelf() { this = TBoundMethod(result, _) } - override string toString() { - result = "Method(" + this.getFunction() + ", " + this.getSelf() + ")" - } + override string toString() { + result = "Method(" + this.getFunction() + ", " + this.getSelf() + ")" + } - override ObjectInternal getClass() { - result = TBuiltinClassObject(Builtin::special("MethodType")) - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(Builtin::special("MethodType")) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - this.getFunction().callResult(callee, obj, origin) - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + this.getFunction().callResult(callee, obj, origin) + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - this.getFunction().callResult(obj, origin) - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + this.getFunction().callResult(obj, origin) + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { - this.getFunction().calleeAndOffset(scope, paramOffset - 1) - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + this.getFunction().calleeAndOffset(scope, paramOffset - 1) + } - override string getName() { result = this.getFunction().getName() } + override string getName() { result = this.getFunction().getName() } - override Function getScope() { result = this.getFunction().getScope() } + override Function getScope() { result = this.getFunction().getScope() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override CallNode getACall(PointsToContext ctx) { - PointsTo::pointsTo(result.getFunction(), ctx, this, _) - } + override CallNode getACall(PointsToContext ctx) { + PointsTo::pointsTo(result.getFunction(), ctx, this, _) + } - /** Gets the parameter node that will be used for `self`. */ - NameNode getSelfParameter() { result = this.getFunction().getParameter(0) } + /** Gets the parameter node that will be used for `self`. */ + NameNode getSelfParameter() { result = this.getFunction().getParameter(0) } - override NameNode getParameter(int n) { - result = this.getFunction().getParameter(n + 1) and - // don't return the parameter for `self` at `n = -1` - n >= 0 - } + override NameNode getParameter(int n) { + result = this.getFunction().getParameter(n + 1) and + // don't return the parameter for `self` at `n = -1` + n >= 0 + } - /** - * Gets the `name`d parameter node of this callable. - * Will not return the parameter node for `self`, instead use `getSelfParameter`. - */ - override NameNode getParameterByName(string name) { - result = this.getFunction().getParameterByName(name) and - not result = this.getSelfParameter() - } + /** + * Gets the `name`d parameter node of this callable. + * Will not return the parameter node for `self`, instead use `getSelfParameter`. + */ + override NameNode getParameterByName(string name) { + result = this.getFunction().getParameterByName(name) and + not result = this.getSelfParameter() + } - override predicate neverReturns() { this.getFunction().neverReturns() } + override predicate neverReturns() { this.getFunction().neverReturns() } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - function = this.getFunction() and offset = 1 - or - function = this and offset = 0 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + function = this.getFunction() and offset = 1 + or + function = this and offset = 0 + } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override predicate contextSensitiveCallee() { this.getFunction().contextSensitiveCallee() } + override predicate contextSensitiveCallee() { this.getFunction().contextSensitiveCallee() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/Classes.qll b/python/ql/src/semmle/python/objects/Classes.qll index b509268a80a..ec45772bccc 100644 --- a/python/ql/src/semmle/python/objects/Classes.qll +++ b/python/ql/src/semmle/python/objects/Classes.qll @@ -8,377 +8,377 @@ private import semmle.python.types.Builtins /** Class representing classes */ abstract class ClassObjectInternal extends ObjectInternal { - override string getName() { result = this.getClassDeclaration().getName() } + override string getName() { result = this.getClassDeclaration().getName() } - /** - * Holds if this is a class whose instances we treat specially, rather than as a generic instance. - * For example, `type` or `int`. - */ - boolean isSpecial() { result = Types::getMro(this).containsSpecial() } + /** + * Holds if this is a class whose instances we treat specially, rather than as a generic instance. + * For example, `type` or `int`. + */ + boolean isSpecial() { result = Types::getMro(this).containsSpecial() } - /** - * Looks up the attribute `name` on this class. - * Note that this may be different from `this.attr(name)`. - * For example given the class: - * ```class C: - * @classmethod - * def f(cls): pass - * ``` - * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method - * whereas - * `this.attr("f") is equivalent to `C.f`, which is a bound-method. - */ - abstract predicate lookup(string name, ObjectInternal value, CfgOrigin origin); + /** + * Looks up the attribute `name` on this class. + * Note that this may be different from `this.attr(name)`. + * For example given the class: + * ```class C: + * @classmethod + * def f(cls): pass + * ``` + * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method + * whereas + * `this.attr("f") is equivalent to `C.f`, which is a bound-method. + */ + abstract predicate lookup(string name, ObjectInternal value, CfgOrigin origin); - /** Holds if this is a subclass of the `Iterable` abstract base class. */ - boolean isIterableSubclass() { - this = ObjectInternal::builtin("list") and result = true - or - this = ObjectInternal::builtin("set") and result = true - or - this = ObjectInternal::builtin("dict") and result = true - or - this != ObjectInternal::builtin("list") and - this != ObjectInternal::builtin("set") and - this != ObjectInternal::builtin("dict") and - result = false - } + /** Holds if this is a subclass of the `Iterable` abstract base class. */ + boolean isIterableSubclass() { + this = ObjectInternal::builtin("list") and result = true + or + this = ObjectInternal::builtin("set") and result = true + or + this = ObjectInternal::builtin("dict") and result = true + or + this != ObjectInternal::builtin("list") and + this != ObjectInternal::builtin("set") and + this != ObjectInternal::builtin("dict") and + result = false + } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - instance = this and - PointsToInternal::attributeRequired(this, name) and - this.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + instance = this and + PointsToInternal::attributeRequired(this, name) and + this.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true + } - /** Approximation to descriptor protocol, skipping meta-descriptor protocol */ - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - exists(ObjectInternal descriptor, CfgOrigin desc_origin | - this.lookup(name, descriptor, desc_origin) - | - descriptor.isDescriptor() = false and - value = descriptor and - origin = desc_origin - or - descriptor.isDescriptor() = true and - descriptor.descriptorGetClass(this, value, origin) - ) - } + /** Approximation to descriptor protocol, skipping meta-descriptor protocol */ + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + exists(ObjectInternal descriptor, CfgOrigin desc_origin | + this.lookup(name, descriptor, desc_origin) + | + descriptor.isDescriptor() = false and + value = descriptor and + origin = desc_origin + or + descriptor.isDescriptor() = true and + descriptor.descriptorGetClass(this, value, origin) + ) + } - override int length() { none() } + override int length() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override boolean isClass() { result = true } + override boolean isClass() { result = true } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Classes aren't usually iterable, but can e.g. Enums */ - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + /* Classes aren't usually iterable, but can e.g. Enums */ + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate hasAttribute(string name) { - this.getClassDeclaration().declaresAttribute(name) - or - Types::getBase(this, _).hasAttribute(name) - } + override predicate hasAttribute(string name) { + this.getClassDeclaration().declaresAttribute(name) + or + Types::getBase(this, _).hasAttribute(name) + } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** Class representing Python source classes */ class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject { - /** Gets the scope for this Python class */ - Class getScope() { - exists(ClassExpr expr | - this = TPythonClassObject(expr.getAFlowNode()) and - result = expr.getInnerScope() - ) - } + /** Gets the scope for this Python class */ + Class getScope() { + exists(ClassExpr expr | + this = TPythonClassObject(expr.getAFlowNode()) and + result = expr.getInnerScope() + ) + } - override string toString() { result = "class " + this.getScope().getName() } + override string toString() { result = "class " + this.getScope().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TPythonClassObject(node) and context.appliesTo(node) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TPythonClassObject(node) and context.appliesTo(node) + } - override ClassDecl getClassDeclaration() { this = TPythonClassObject(result) } + override ClassDecl getClassDeclaration() { this = TPythonClassObject(result) } - override ObjectInternal getClass() { result = Types::getMetaClass(this) } + override ObjectInternal getClass() { result = Types::getMetaClass(this) } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TPythonClassObject(result) } + override ControlFlowNode getOrigin() { this = TPythonClassObject(result) } - override predicate calleeAndOffset(Function scope, int paramOffset) { - exists(PythonFunctionObjectInternal init | - this.lookup("__init__", init, _) and - init.calleeAndOffset(scope, paramOffset - 1) - ) - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + exists(PythonFunctionObjectInternal init | + this.lookup("__init__", init, _) and + init.calleeAndOffset(scope, paramOffset - 1) + ) + } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this).lookup(name, value, origin) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this).lookup(name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Handled by Instance classes. - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Handled by Instance classes. + none() + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { - this.lookup("__init__", function, _) and offset = 1 - } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { + this.lookup("__init__", function, _) and offset = 1 + } } /** Class representing built-in classes, except `type` */ class BuiltinClassObjectInternal extends ClassObjectInternal, TBuiltinClassObject { - override Builtin getBuiltin() { this = TBuiltinClassObject(result) } + override Builtin getBuiltin() { this = TBuiltinClassObject(result) } - override string toString() { result = "builtin-class " + this.getBuiltin().getName() } + override string toString() { result = "builtin-class " + this.getBuiltin().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { this = TBuiltinClassObject(result) } + override ClassDecl getClassDeclaration() { this = TBuiltinClassObject(result) } - override ObjectInternal getClass() { - result = TBuiltinClassObject(this.getBuiltin().getClass()) - or - this.getBuiltin().getClass() = Builtin::special("type") and - result = TType() - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(this.getBuiltin().getClass()) + or + this.getBuiltin().getClass() = Builtin::special("type") and + result = TType() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this).lookup(name, value, origin) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this).lookup(name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Handled by Instance classes. - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Handled by Instance classes. + none() + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } } /** A class representing an unknown class */ class UnknownClassInternal extends ClassObjectInternal, TUnknownClass { - override string toString() { result = "Unknown class" } + override string toString() { result = "Unknown class" } - override ClassDecl getClassDeclaration() { result = Builtin::unknownType() } + override ClassDecl getClassDeclaration() { result = Builtin::unknownType() } - override ObjectInternal getClass() { result = this } + override ObjectInternal getClass() { result = this } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override Builtin getBuiltin() { result = Builtin::unknownType() } + override Builtin getBuiltin() { result = Builtin::unknownType() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } } /** A class representing the built-in class `type`. */ class TypeInternal extends ClassObjectInternal, TType { - override string toString() { result = "builtin-class type" } + override string toString() { result = "builtin-class type" } - override ClassDecl getClassDeclaration() { result = Builtin::special("type") } + override ClassDecl getClassDeclaration() { result = Builtin::special("type") } - override ObjectInternal getClass() { result = this } + override ObjectInternal getClass() { result = this } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { result = Builtin::special("type") } + override Builtin getBuiltin() { result = Builtin::special("type") } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this).lookup(name, value, origin) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this).lookup(name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } } /** A class representing a dynamically created class `type(name, *args, **kwargs)`. */ class DynamicallyCreatedClass extends ClassObjectInternal, TDynamicClass { - override string toString() { result = this.getOrigin().getNode().toString() } + override string toString() { result = this.getOrigin().getNode().toString() } - override ObjectInternal getClass() { this = TDynamicClass(_, result, _) } + override ObjectInternal getClass() { this = TDynamicClass(_, result, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - exists(ClassObjectInternal decl | decl = Types::getMro(this).findDeclaringClass(name) | - Types::declaredAttribute(decl, name, value, origin) - ) - } + override predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + exists(ClassObjectInternal decl | decl = Types::getMro(this).findDeclaringClass(name) | + Types::declaredAttribute(decl, name, value, origin) + ) + } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TDynamicClass(result, _, _) } + override ControlFlowNode getOrigin() { this = TDynamicClass(result, _, _) } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TDynamicClass(node, _, context) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TDynamicClass(node, _, context) + } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } } class SubscriptedTypeInternal extends ObjectInternal, TSubscriptedType { - ObjectInternal getGeneric() { this = TSubscriptedType(result, _) } + ObjectInternal getGeneric() { this = TSubscriptedType(result, _) } - ObjectInternal getSpecializer() { this = TSubscriptedType(_, result) } + ObjectInternal getSpecializer() { this = TSubscriptedType(_, result) } - override string getName() { result = this.getGeneric().getName() } + override string getName() { result = this.getGeneric().getName() } - override string toString() { - result = this.getGeneric().toString() + "[" + this.getSpecializer().toString() + "]" - } + override string toString() { + result = this.getGeneric().toString() + "[" + this.getSpecializer().toString() + "]" + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(ObjectInternal generic, ObjectInternal index | - this = TSubscriptedType(generic, index) and - Expressions::subscriptPartsPointsTo(node, context, generic, index) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(ObjectInternal generic, ObjectInternal index | + this = TSubscriptedType(generic, index) and + Expressions::subscriptPartsPointsTo(node, context, generic, index) + ) + } - /** Gets the class declaration for this object, if it is a class with a declaration. */ - override ClassDecl getClassDeclaration() { result = this.getGeneric().getClassDeclaration() } + /** Gets the class declaration for this object, if it is a class with a declaration. */ + override ClassDecl getClassDeclaration() { result = this.getGeneric().getClassDeclaration() } - /** True if this "object" is a class. That is, its class inherits from `type` */ - override boolean isClass() { result = true } + /** True if this "object" is a class. That is, its class inherits from `type` */ + override boolean isClass() { result = true } - override ObjectInternal getClass() { result = this.getGeneric().getClass() } + override ObjectInternal getClass() { result = this.getGeneric().getClass() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Classes aren't usually iterable, but can e.g. Enums */ - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + /* Classes aren't usually iterable, but can e.g. Enums */ + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { none() } + override predicate isNotSubscriptedType() { none() } } diff --git a/python/ql/src/semmle/python/objects/Constants.qll b/python/ql/src/semmle/python/objects/Constants.qll index 8cc0911af53..d2ba2f44655 100644 --- a/python/ql/src/semmle/python/objects/Constants.qll +++ b/python/ql/src/semmle/python/objects/Constants.qll @@ -12,290 +12,290 @@ private import semmle.python.types.Builtins * well as strings and integers. */ abstract class ConstantObjectInternal extends ObjectInternal { - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - // Constants aren't callable - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + // Constants aren't callable + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Constants aren't callable - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Constants aren't callable + none() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - PointsToInternal::attributeRequired(this, name) and - exists(ObjectInternal cls_attr, CfgOrigin attr_orig | - this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) and - cls_attr.isDescriptor() = true and - cls_attr.descriptorGetInstance(this, value, origin) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + PointsToInternal::attributeRequired(this, name) and + exists(ObjectInternal cls_attr, CfgOrigin attr_orig | + this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) and + cls_attr.isDescriptor() = true and + cls_attr.descriptorGetInstance(this, value, origin) + ) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(ClassObjectInternal cls | - receiver_type(_, name, this, cls) and - cls.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true - ) and - this = instance - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(ClassObjectInternal cls | + receiver_type(_, name, this, cls) and + cls.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true + ) and + this = instance + } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /** Gets an AST literal with the same value as this object */ - abstract ImmutableLiteral getLiteral(); + /** Gets an AST literal with the same value as this object */ + abstract ImmutableLiteral getLiteral(); - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } pragma[nomagic] private boolean callToBool(CallNode call, PointsToContext context) { - PointsToInternal::pointsTo(call.getFunction(), context, ClassValue::bool(), _) and - exists(ObjectInternal arg | - PointsToInternal::pointsTo(call.getArg(0), context, arg, _) and - arg.booleanValue() = result - ) + PointsToInternal::pointsTo(call.getFunction(), context, ClassValue::bool(), _) and + exists(ObjectInternal arg | + PointsToInternal::pointsTo(call.getArg(0), context, arg, _) and + arg.booleanValue() = result + ) } abstract private class BooleanObjectInternal extends ConstantObjectInternal { - override ObjectInternal getClass() { result = ClassValue::bool() } + override ObjectInternal getClass() { result = ClassValue::bool() } - override int length() { none() } + override int length() { none() } - override string strValue() { none() } + override string strValue() { none() } - /* Booleans aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Booleans aren't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { - result.(BooleanLiteral).booleanValue() = this.booleanValue() - } + override ImmutableLiteral getLiteral() { + result.(BooleanLiteral).booleanValue() = this.booleanValue() + } } private class TrueObjectInternal extends BooleanObjectInternal, TTrue { - override string toString() { result = "bool True" } + override string toString() { result = "bool True" } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - node.(NameNode).getId() = "True" and context.appliesTo(node) - or - callToBool(node, context) = true - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + node.(NameNode).getId() = "True" and context.appliesTo(node) + or + callToBool(node, context) = true + } - override int intValue() { result = 1 } + override int intValue() { result = 1 } - override Builtin getBuiltin() { result = Builtin::special("True") } + override Builtin getBuiltin() { result = Builtin::special("True") } } private class FalseObjectInternal extends BooleanObjectInternal, TFalse { - override string toString() { result = "bool False" } + override string toString() { result = "bool False" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - node.(NameNode).getId() = "False" and context.appliesTo(node) - or - callToBool(node, context) = false - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + node.(NameNode).getId() = "False" and context.appliesTo(node) + or + callToBool(node, context) = false + } - override int intValue() { result = 0 } + override int intValue() { result = 0 } - override Builtin getBuiltin() { result = Builtin::special("False") } + override Builtin getBuiltin() { result = Builtin::special("False") } } private class NoneObjectInternal extends ConstantObjectInternal, TNone { - override string toString() { result = "None" } + override string toString() { result = "None" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("NoneType")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("NoneType")) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - node.(NameNode).getId() = "None" and context.appliesTo(node) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + node.(NameNode).getId() = "None" and context.appliesTo(node) + } - override Builtin getBuiltin() { result = Builtin::special("None") } + override Builtin getBuiltin() { result = Builtin::special("None") } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override int length() { none() } + override int length() { none() } - /* None isn't iterable */ - override ObjectInternal getIterNext() { none() } + /* None isn't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { result instanceof None } + override ImmutableLiteral getLiteral() { result instanceof None } } class IntObjectInternal extends ConstantObjectInternal, TInt { - override string toString() { result = "int " + this.intValue().toString() } + override string toString() { result = "int " + this.intValue().toString() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(IntegerLiteral).getValue() = this.intValue() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(IntegerLiteral).getValue() = this.intValue() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("int")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("int")) } - override Builtin getBuiltin() { result.intValue() = this.intValue() } + override Builtin getBuiltin() { result.intValue() = this.intValue() } - override int intValue() { this = TInt(result) } + override int intValue() { this = TInt(result) } - override string strValue() { none() } + override string strValue() { none() } - override boolean booleanValue() { - this.intValue() = 0 and result = false - or - this.intValue() != 0 and result = true - } + override boolean booleanValue() { + this.intValue() = 0 and result = false + or + this.intValue() != 0 and result = true + } - override int length() { none() } + override int length() { none() } - /* ints aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* ints aren't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { - result.(IntegerLiteral).getValue() = this.intValue() - or - result.(NegativeIntegerLiteral).getOperand().(IntegerLiteral).getValue() = -this.intValue() - } + override ImmutableLiteral getLiteral() { + result.(IntegerLiteral).getValue() = this.intValue() + or + result.(NegativeIntegerLiteral).getOperand().(IntegerLiteral).getValue() = -this.intValue() + } } class FloatObjectInternal extends ConstantObjectInternal, TFloat { - override string toString() { - if this.floatValue() = this.floatValue().floor() - then result = "float " + this.floatValue().floor().toString() + ".0" - else result = "float " + this.floatValue().toString() - } + override string toString() { + if this.floatValue() = this.floatValue().floor() + then result = "float " + this.floatValue().floor().toString() + ".0" + else result = "float " + this.floatValue().toString() + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(FloatLiteral).getValue() = this.floatValue() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(FloatLiteral).getValue() = this.floatValue() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("float")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("float")) } - override Builtin getBuiltin() { result.floatValue() = this.floatValue() } + override Builtin getBuiltin() { result.floatValue() = this.floatValue() } - float floatValue() { this = TFloat(result) } + float floatValue() { this = TFloat(result) } - override int intValue() { this = TFloat(result) } + override int intValue() { this = TFloat(result) } - override string strValue() { none() } + override string strValue() { none() } - override boolean booleanValue() { - this.floatValue() = 0.0 and result = false - or - this.floatValue() != 0.0 and result = true - } + override boolean booleanValue() { + this.floatValue() = 0.0 and result = false + or + this.floatValue() != 0.0 and result = true + } - override int length() { none() } + override int length() { none() } - /* floats aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* floats aren't iterable */ + override ObjectInternal getIterNext() { none() } - override ImmutableLiteral getLiteral() { result.(FloatLiteral).getValue() = this.floatValue() } + override ImmutableLiteral getLiteral() { result.(FloatLiteral).getValue() = this.floatValue() } } class UnicodeObjectInternal extends ConstantObjectInternal, TUnicode { - override string toString() { result = "'" + this.strValue() + "'" } + override string toString() { result = "'" + this.strValue() + "'" } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(StrConst).getText() = this.strValue() and - node.getNode().(StrConst).isUnicode() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(StrConst).getText() = this.strValue() and + node.getNode().(StrConst).isUnicode() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("unicode")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("unicode")) } - override Builtin getBuiltin() { - result.(Builtin).strValue() = this.strValue() and - result.getClass() = Builtin::special("unicode") - } + override Builtin getBuiltin() { + result.(Builtin).strValue() = this.strValue() and + result.getClass() = Builtin::special("unicode") + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { this = TUnicode(result) } + override string strValue() { this = TUnicode(result) } - override boolean booleanValue() { - this.strValue() = "" and result = false - or - this.strValue() != "" and result = true - } + override boolean booleanValue() { + this.strValue() = "" and result = false + or + this.strValue() != "" and result = true + } - override int length() { result = this.strValue().length() } + override int length() { result = this.strValue().length() } - override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } + override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } - override ImmutableLiteral getLiteral() { result.(Unicode).getText() = this.strValue() } + override ImmutableLiteral getLiteral() { result.(Unicode).getText() = this.strValue() } } class BytesObjectInternal extends ConstantObjectInternal, TBytes { - override string toString() { result = "'" + this.strValue() + "'" } + override string toString() { result = "'" + this.strValue() + "'" } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - context.appliesTo(node) and - node.getNode().(StrConst).getText() = this.strValue() and - not node.getNode().(StrConst).isUnicode() - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + context.appliesTo(node) and + node.getNode().(StrConst).getText() = this.strValue() and + not node.getNode().(StrConst).isUnicode() + } - override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("bytes")) } + override ObjectInternal getClass() { result = TBuiltinClassObject(Builtin::special("bytes")) } - override Builtin getBuiltin() { - result.(Builtin).strValue() = this.strValue() and - result.getClass() = Builtin::special("bytes") - } + override Builtin getBuiltin() { + result.(Builtin).strValue() = this.strValue() and + result.getClass() = Builtin::special("bytes") + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { this = TBytes(result) } + override string strValue() { this = TBytes(result) } - override boolean booleanValue() { - this.strValue() = "" and result = false - or - this.strValue() != "" and result = true - } + override boolean booleanValue() { + this.strValue() = "" and result = false + or + this.strValue() != "" and result = true + } - override int length() { result = this.strValue().length() } + override int length() { result = this.strValue().length() } - override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } + override ObjectInternal getIterNext() { result = TUnknownInstance(this.getClass()) } - override ImmutableLiteral getLiteral() { result.(Bytes).getText() = this.strValue() } + override ImmutableLiteral getLiteral() { result.(Bytes).getText() = this.strValue() } } diff --git a/python/ql/src/semmle/python/objects/Descriptors.qll b/python/ql/src/semmle/python/objects/Descriptors.qll index ece746d8625..9a11a9fcae4 100644 --- a/python/ql/src/semmle/python/objects/Descriptors.qll +++ b/python/ql/src/semmle/python/objects/Descriptors.qll @@ -8,376 +8,376 @@ private import semmle.python.types.Builtins /** Class representing property objects in Python */ class PropertyInternal extends ObjectInternal, TProperty { - /** Gets the name of this property */ - override string getName() { result = this.getGetter().getName() } + /** Gets the name of this property */ + override string getName() { result = this.getGetter().getName() } - /** Gets the getter function of this property */ - CallableObjectInternal getGetter() { this = TProperty(_, _, result) } + /** Gets the getter function of this property */ + CallableObjectInternal getGetter() { this = TProperty(_, _, result) } - private CallNode getCallNode() { this = TProperty(result, _, _) } + private CallNode getCallNode() { this = TProperty(result, _, _) } - /** Gets the setter function of this property */ - CallableObjectInternal getSetter() { - // @x.setter - exists(CallNode call, AttrNode setter | - call.getFunction() = setter and - PointsToInternal::pointsTo(setter.getObject("setter"), this.getContext(), this, _) and - PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) - ) - or - // x = property(getter, setter, deleter) - exists(ControlFlowNode setter_arg | - setter_arg = getCallNode().getArg(1) or setter_arg = getCallNode().getArgByName("fset") - | - PointsToInternal::pointsTo(setter_arg, this.getContext(), result, _) - ) - } + /** Gets the setter function of this property */ + CallableObjectInternal getSetter() { + // @x.setter + exists(CallNode call, AttrNode setter | + call.getFunction() = setter and + PointsToInternal::pointsTo(setter.getObject("setter"), this.getContext(), this, _) and + PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) + ) + or + // x = property(getter, setter, deleter) + exists(ControlFlowNode setter_arg | + setter_arg = getCallNode().getArg(1) or setter_arg = getCallNode().getArgByName("fset") + | + PointsToInternal::pointsTo(setter_arg, this.getContext(), result, _) + ) + } - /** Gets the setter function of this property */ - CallableObjectInternal getDeleter() { - exists(CallNode call, AttrNode setter | - call.getFunction() = setter and - PointsToInternal::pointsTo(setter.getObject("deleter"), this.getContext(), this, _) and - PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) - ) - or - // x = property(getter, setter, deleter) - exists(ControlFlowNode deleter_arg | - deleter_arg = getCallNode().getArg(2) or deleter_arg = getCallNode().getArgByName("fdel") - | - PointsToInternal::pointsTo(deleter_arg, this.getContext(), result, _) - ) - } + /** Gets the setter function of this property */ + CallableObjectInternal getDeleter() { + exists(CallNode call, AttrNode setter | + call.getFunction() = setter and + PointsToInternal::pointsTo(setter.getObject("deleter"), this.getContext(), this, _) and + PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _) + ) + or + // x = property(getter, setter, deleter) + exists(ControlFlowNode deleter_arg | + deleter_arg = getCallNode().getArg(2) or deleter_arg = getCallNode().getArgByName("fdel") + | + PointsToInternal::pointsTo(deleter_arg, this.getContext(), result, _) + ) + } - private Context getContext() { this = TProperty(_, result, _) } + private Context getContext() { this = TProperty(_, result, _) } - override string toString() { result = "property " + this.getName() } + override string toString() { result = "property " + this.getName() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TProperty(node, context, _) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TProperty(node, context, _) + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::property() } + override ObjectInternal getClass() { result = ObjectInternal::property() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TProperty(result, _, _) } + override ControlFlowNode getOrigin() { this = TProperty(result, _, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = TPropertySetterOrDeleter(this, name) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = TPropertySetterOrDeleter(this, name) and origin = CfgOrigin::unknown() + } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - override int length() { none() } + override int length() { none() } - override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } + override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this and - origin = CfgOrigin::fromCfgNode(this.getOrigin()) - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this and + origin = CfgOrigin::fromCfgNode(this.getOrigin()) + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - /* - * Just give an unknown value for now. We could improve this, but it would mean - * changing Contexts to account for property accesses. - */ + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + /* + * Just give an unknown value for now. We could improve this, but it would mean + * changing Contexts to account for property accesses. + */ - exists(ClassObjectInternal cls, string name | - name = this.getName() and - receiver_type(_, name, instance, cls) and - cls.lookup(name, this, _) and - origin = CfgOrigin::unknown() and - value = ObjectInternal::unknown() - ) - } + exists(ClassObjectInternal cls, string name | + name = this.getName() and + receiver_type(_, name, instance, cls) and + cls.lookup(name, this, _) and + origin = CfgOrigin::unknown() and + value = ObjectInternal::unknown() + ) + } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Properties aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Properties aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private class PropertySetterOrDeleter extends ObjectInternal, TPropertySetterOrDeleter { - override string toString() { result = this.getProperty().toString() + "." + this.getName() } + override string toString() { result = this.getProperty().toString() + "." + this.getName() } - override string getName() { this = TPropertySetterOrDeleter(_, result) } + override string getName() { this = TPropertySetterOrDeleter(_, result) } - PropertyInternal getProperty() { this = TPropertySetterOrDeleter(result, _) } + PropertyInternal getProperty() { this = TPropertySetterOrDeleter(result, _) } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - exists(ControlFlowNode call | - obj = this.getProperty() and - obj = TProperty(call, _, _) and - origin = CfgOrigin::fromCfgNode(call) - ) - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + exists(ControlFlowNode call | + obj = this.getProperty() and + obj = TProperty(call, _, _) and + origin = CfgOrigin::fromCfgNode(call) + ) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { - result = TBuiltinClassObject(Builtin::special("MethodType")) - } + override ObjectInternal getClass() { + result = TBuiltinClassObject(Builtin::special("MethodType")) + } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - override int length() { none() } + override int length() { none() } - override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } + override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { none() } + override ObjectInternal getIterNext() { none() } - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A class representing classmethods in Python */ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod { - override string toString() { result = "classmethod(" + this.getFunction() + ")" } + override string toString() { result = "classmethod(" + this.getFunction() + ")" } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(CallableObjectInternal function | - this = TClassMethod(node, function) and - class_method(node, function, context) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(CallableObjectInternal function | + this = TClassMethod(node, function) and + class_method(node, function, context) + ) + } - /** Gets the function wrapped by this classmethod object */ - CallableObjectInternal getFunction() { this = TClassMethod(_, result) } + /** Gets the function wrapped by this classmethod object */ + CallableObjectInternal getFunction() { this = TClassMethod(_, result) } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::classMethod() } + override ObjectInternal getClass() { result = ObjectInternal::classMethod() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TClassMethod(result, _) } + override ControlFlowNode getOrigin() { this = TClassMethod(result, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - value = TBoundMethod(cls, this.getFunction()) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + value = TBoundMethod(cls, this.getFunction()) and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - any(ObjectInternal obj).binds(instance, _, this) and - value = TBoundMethod(instance.getClass(), this.getFunction()) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + any(ObjectInternal obj).binds(instance, _, this) and + value = TBoundMethod(instance.getClass(), this.getFunction()) and + origin = CfgOrigin::unknown() + } - /** - * Holds if attribute lookup on this object may "bind" `cls` to `descriptor`. - * `cls` will always be a class as this is a classmethod. - * Here "bind" means that `instance` is passed to the `classmethod.__get__()` method - * at runtime. The term "bind" is used as this most likely results in a bound-method. - */ - pragma[noinline] - override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { - descriptor = this.getFunction() and - exists(ObjectInternal instance | any(ObjectInternal obj).binds(instance, name, this) | - instance.isClass() = false and cls = instance.getClass() - or - instance.isClass() = true and cls = instance - ) - } + /** + * Holds if attribute lookup on this object may "bind" `cls` to `descriptor`. + * `cls` will always be a class as this is a classmethod. + * Here "bind" means that `instance` is passed to the `classmethod.__get__()` method + * at runtime. The term "bind" is used as this most likely results in a bound-method. + */ + pragma[noinline] + override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { + descriptor = this.getFunction() and + exists(ObjectInternal instance | any(ObjectInternal obj).binds(instance, name, this) | + instance.isClass() = false and cls = instance.getClass() + or + instance.isClass() = true and cls = instance + ) + } - override int length() { none() } + override int length() { none() } - override string getName() { result = this.getFunction().getName() } + override string getName() { result = this.getFunction().getName() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Classmethods aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Classmethods aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } class StaticMethodObjectInternal extends ObjectInternal, TStaticMethod { - override string toString() { result = "staticmethod()" } + override string toString() { result = "staticmethod()" } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(CallableObjectInternal function | - this = TStaticMethod(node, function) and - static_method(node, function, context) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(CallableObjectInternal function | + this = TStaticMethod(node, function) and + static_method(node, function, context) + ) + } - CallableObjectInternal getFunction() { this = TStaticMethod(_, result) } + CallableObjectInternal getFunction() { this = TStaticMethod(_, result) } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::builtin("staticmethod") } + override ObjectInternal getClass() { result = ObjectInternal::builtin("staticmethod") } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TStaticMethod(result, _) } + override ControlFlowNode getOrigin() { this = TStaticMethod(result, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { - this.getFunction().calleeAndOffset(scope, paramOffset) - } + override predicate calleeAndOffset(Function scope, int paramOffset) { + this.getFunction().calleeAndOffset(scope, paramOffset) + } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = true } + override boolean isDescriptor() { result = true } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - any(ObjectInternal obj).binds(cls, _, this) and - value = this.getFunction() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + any(ObjectInternal obj).binds(cls, _, this) and + value = this.getFunction() and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - any(ObjectInternal obj).binds(instance, _, this) and - value = this.getFunction() and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + any(ObjectInternal obj).binds(instance, _, this) and + value = this.getFunction() and + origin = CfgOrigin::unknown() + } - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override string getName() { result = this.getFunction().getName() } + override string getName() { result = this.getFunction().getName() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Staticmethods aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Staticmethods aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/Instances.qll b/python/ql/src/semmle/python/objects/Instances.qll index 396e26d8aad..0f29d16b447 100644 --- a/python/ql/src/semmle/python/objects/Instances.qll +++ b/python/ql/src/semmle/python/objects/Instances.qll @@ -8,59 +8,59 @@ private import semmle.python.types.Builtins /** A class representing instances */ abstract class InstanceObject extends ObjectInternal { - pragma[nomagic] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - exists(ObjectInternal cls_attr | this.classAttribute(name, cls_attr) | - /* - * If class attribute is not a descriptor, that usually means it is some sort of - * default value and likely overridden by an instance attribute. In that case - * use `unknown` to signal that an attribute exists but to avoid false positives - * f using the default value. - */ + pragma[nomagic] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + exists(ObjectInternal cls_attr | this.classAttribute(name, cls_attr) | + /* + * If class attribute is not a descriptor, that usually means it is some sort of + * default value and likely overridden by an instance attribute. In that case + * use `unknown` to signal that an attribute exists but to avoid false positives + * f using the default value. + */ - cls_attr.isDescriptor() = false and - value = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - or - cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) - ) - or - this.selfAttribute(name, value, origin) - } + cls_attr.isDescriptor() = false and + value = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + or + cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) + ) + or + this.selfAttribute(name, value, origin) + } - pragma[noinline] - private predicate classAttribute(string name, ObjectInternal cls_attr) { - PointsToInternal::attributeRequired(this, name) and - this.getClass().(ClassObjectInternal).lookup(name, cls_attr, _) - } + pragma[noinline] + private predicate classAttribute(string name, ObjectInternal cls_attr) { + PointsToInternal::attributeRequired(this, name) and + this.getClass().(ClassObjectInternal).lookup(name, cls_attr, _) + } - pragma[noinline] - private predicate selfAttribute(string name, ObjectInternal value, CfgOrigin origin) { - PointsToInternal::attributeRequired(this, name) and - exists(EssaVariable self, PythonFunctionObjectInternal init, Context callee | - this.initializer(init, callee) and - self_variable_reaching_init_exit(self) and - self.getScope() = init.getScope() and - AttributePointsTo::variableAttributePointsTo(self, callee, name, value, origin) - ) - } + pragma[noinline] + private predicate selfAttribute(string name, ObjectInternal value, CfgOrigin origin) { + PointsToInternal::attributeRequired(this, name) and + exists(EssaVariable self, PythonFunctionObjectInternal init, Context callee | + this.initializer(init, callee) and + self_variable_reaching_init_exit(self) and + self.getScope() = init.getScope() and + AttributePointsTo::variableAttributePointsTo(self, callee, name, value, origin) + ) + } - /** Holds if `init` in the context `callee` is the initializer of this instance */ - abstract predicate initializer(PythonFunctionObjectInternal init, Context callee); + /** Holds if `init` in the context `callee` is the initializer of this instance */ + abstract predicate initializer(PythonFunctionObjectInternal init, Context callee); - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private predicate self_variable_reaching_init_exit(EssaVariable self) { - BaseFlow::reaches_exit(self) and - self.getSourceVariable().(Variable).isSelf() and - self.getScope().getName() = "__init__" + BaseFlow::reaches_exit(self) and + self.getSourceVariable().(Variable).isSelf() and + self.getScope().getName() = "__init__" } /** @@ -68,421 +68,421 @@ private predicate self_variable_reaching_init_exit(EssaVariable self) { * For example the code `C()` would be a specific instance of `C`. */ class SpecificInstanceInternal extends TSpecificInstance, InstanceObject { - override string toString() { result = this.getOrigin().getNode().toString() } + override string toString() { result = this.getOrigin().getNode().toString() } - override boolean booleanValue() { - //result = this.getClass().instancesBooleanValue() - result = maybe() - } + override boolean booleanValue() { + //result = this.getClass().instancesBooleanValue() + result = maybe() + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TSpecificInstance(node, _, context) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TSpecificInstance(node, _, context) + } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override ObjectInternal getClass() { - exists(ClassObjectInternal cls, ClassDecl decl | - this = TSpecificInstance(_, cls, _) and - decl = cls.getClassDeclaration() - | - if decl.callReturnsInstance() then result = cls else result = TUnknownClass() - ) - } + override ObjectInternal getClass() { + exists(ClassObjectInternal cls, ClassDecl decl | + this = TSpecificInstance(_, cls, _) and + decl = cls.getClassDeclaration() + | + if decl.callReturnsInstance() then result = cls else result = TUnknownClass() + ) + } - /** - * Gets the `Builtin` for this object, if any. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getOrigin()`. - */ - override Builtin getBuiltin() { none() } + /** + * Gets the `Builtin` for this object, if any. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getOrigin()`. + */ + override Builtin getBuiltin() { none() } - /** - * Gets a control flow node that represents the source origin of this - * objects. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getBuiltin()`. - */ - override ControlFlowNode getOrigin() { this = TSpecificInstance(result, _, _) } + /** + * Gets a control flow node that represents the source origin of this + * objects. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getBuiltin()`. + */ + override ControlFlowNode getOrigin() { this = TSpecificInstance(result, _, _) } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // TO DO -- Handle cases where class overrides __call__ in more detail, like normal calls. - this.getClass().(ClassObjectInternal).lookup("__call__", _, _) and - obj = ObjectInternal::unknown() and - origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // TO DO -- Handle cases where class overrides __call__ in more detail, like normal calls. + this.getClass().(ClassObjectInternal).lookup("__call__", _, _) and + obj = ObjectInternal::unknown() and + origin = CfgOrigin::unknown() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(ClassObjectInternal cls | - receiver_type(_, name, this, cls) and - cls.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true - ) and - this = instance - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(ClassObjectInternal cls | + receiver_type(_, name, this, cls) and + cls.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true + ) and + this = instance + } - override int length() { result = lengthFromClass(this.getClass()) } + override int length() { result = lengthFromClass(this.getClass()) } - override predicate initializer(PythonFunctionObjectInternal init, Context callee) { - exists(CallNode call, Context caller, ClassObjectInternal cls | - this = TSpecificInstance(call, cls, caller) and - callee.fromCall(this.getOrigin(), caller) and - cls.lookup("__init__", init, _) - ) - } + override predicate initializer(PythonFunctionObjectInternal init, Context callee) { + exists(CallNode call, Context caller, ClassObjectInternal cls | + this = TSpecificInstance(call, cls, caller) and + callee.fromCall(this.getOrigin(), caller) and + cls.lookup("__init__", init, _) + ) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } } /** * A class representing context-free instances represented by `self` in the source code */ class SelfInstanceInternal extends TSelfInstance, InstanceObject { - override string toString() { - result = "self instance of " + this.getClass().(ClassObjectInternal).getName() - } + override string toString() { + result = "self instance of " + this.getClass().(ClassObjectInternal).getName() + } - /** The boolean value of this object, if it has one */ - override boolean booleanValue() { - //result = this.getClass().instancesBooleanValue() - result = maybe() - } + /** The boolean value of this object, if it has one */ + override boolean booleanValue() { + //result = this.getClass().instancesBooleanValue() + result = maybe() + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - predicate parameterAndContext(ParameterDefinition def, PointsToContext context) { - this = TSelfInstance(def, context, _) - } + predicate parameterAndContext(ParameterDefinition def, PointsToContext context) { + this = TSelfInstance(def, context, _) + } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override ObjectInternal getClass() { this = TSelfInstance(_, _, result) } + override ObjectInternal getClass() { this = TSelfInstance(_, _, result) } - ParameterDefinition getParameter() { this = TSelfInstance(result, _, _) } + ParameterDefinition getParameter() { this = TSelfInstance(result, _, _) } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { - exists(ParameterDefinition def | - this = TSelfInstance(def, _, _) and - result = def.getDefiningNode() - ) - } + override ControlFlowNode getOrigin() { + exists(ParameterDefinition def | + this = TSelfInstance(def, _, _) and + result = def.getDefiningNode() + ) + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // In general instances aren't callable, but some are... - // TO DO -- Handle cases where class overrides __call__ - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // In general instances aren't callable, but some are... + // TO DO -- Handle cases where class overrides __call__ + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(AttrNode attr, ClassObjectInternal cls | - receiver_type(attr, name, this, cls) and - cls_descriptor(cls, name, descriptor) - ) and - instance = this - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(AttrNode attr, ClassObjectInternal cls | + receiver_type(attr, name, this, cls) and + cls_descriptor(cls, name, descriptor) + ) and + instance = this + } - override int length() { result = lengthFromClass(this.getClass()) } + override int length() { result = lengthFromClass(this.getClass()) } - override predicate initializer(PythonFunctionObjectInternal init, Context callee) { - callee.isRuntime() and - init.getScope() != this.getParameter().getScope() and - this.getClass().attribute("__init__", init, _) - } + override predicate initializer(PythonFunctionObjectInternal init, Context callee) { + callee.isRuntime() and + init.getScope() != this.getParameter().getScope() and + this.getClass().attribute("__init__", init, _) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } } /** A class representing a value that has a known class, but no other information */ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal { - override string toString() { - result = "instance of " + this.getClass().(ClassObjectInternal).getName() - } + override string toString() { + result = "instance of " + this.getClass().(ClassObjectInternal).getName() + } - override boolean booleanValue() { result = maybe() } + override boolean booleanValue() { result = maybe() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override ObjectInternal getClass() { this = TUnknownInstance(result) } + override ObjectInternal getClass() { this = TUnknownInstance(result) } - /** - * Gets the `Builtin` for this object, if any. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getOrigin()`. - */ - override Builtin getBuiltin() { none() } + /** + * Gets the `Builtin` for this object, if any. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getOrigin()`. + */ + override Builtin getBuiltin() { none() } - /** - * Gets a control flow node that represents the source origin of this - * objects. - * All objects (except unknown and undefined values) should return - * exactly one result for either this method or `getBuiltin()`. - */ - override ControlFlowNode getOrigin() { none() } + /** + * Gets a control flow node that represents the source origin of this + * objects. + * All objects (except unknown and undefined values) should return + * exactly one result for either this method or `getBuiltin()`. + */ + override ControlFlowNode getOrigin() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // In general instances aren't callable, but some are... - // TO DO -- Handle cases where class overrides __call__ - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // In general instances aren't callable, but some are... + // TO DO -- Handle cases where class overrides __call__ + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - PointsToInternal::attributeRequired(this, name) and - exists(ObjectInternal cls_attr, CfgOrigin attr_orig | - this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) - | - cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig - or - cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + PointsToInternal::attributeRequired(this, name) and + exists(ObjectInternal cls_attr, CfgOrigin attr_orig | + this.getClass().(ClassObjectInternal).lookup(name, cls_attr, attr_orig) + | + cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig + or + cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin) + ) + } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - exists(AttrNode attr, ClassObjectInternal cls | - receiver_type(attr, name, this, cls) and - cls_descriptor(cls, name, descriptor) - ) and - instance = this - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + exists(AttrNode attr, ClassObjectInternal cls | + receiver_type(attr, name, this, cls) and + cls_descriptor(cls, name, descriptor) + ) and + instance = this + } - override int length() { result = lengthFromClass(this.getClass()) } + override int length() { result = lengthFromClass(this.getClass()) } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } private int lengthFromClass(ClassObjectInternal cls) { - Types::getMro(cls).declares("__len__") and result = -1 + Types::getMro(cls).declares("__len__") and result = -1 } private predicate cls_descriptor(ClassObjectInternal cls, string name, ObjectInternal descriptor) { - cls.lookup(name, descriptor, _) and - descriptor.isDescriptor() = true + cls.lookup(name, descriptor, _) and + descriptor.isDescriptor() = true } /** A class representing an instance of the `super` class */ class SuperInstance extends TSuperInstance, ObjectInternal { - override string toString() { - result = "super(" + this.getStartClass().toString() + ", " + this.getSelf().toString() + ")" - } + override string toString() { + result = "super(" + this.getStartClass().toString() + ", " + this.getSelf().toString() + ")" + } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(ObjectInternal self, ClassObjectInternal startclass | - super_instantiation(node, self, startclass, context) and - this = TSuperInstance(self, startclass) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(ObjectInternal self, ClassObjectInternal startclass | + super_instantiation(node, self, startclass, context) and + this = TSuperInstance(self, startclass) + ) + } - /** Gets the class declared as the starting point for MRO lookup. */ - ClassObjectInternal getStartClass() { this = TSuperInstance(_, result) } + /** Gets the class declared as the starting point for MRO lookup. */ + ClassObjectInternal getStartClass() { this = TSuperInstance(_, result) } - /** Gets 'self' object */ - ObjectInternal getSelf() { this = TSuperInstance(result, _) } + /** Gets 'self' object */ + ObjectInternal getSelf() { this = TSuperInstance(result, _) } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::superType() } + override ObjectInternal getClass() { result = ObjectInternal::superType() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - exists(ObjectInternal cls_attr, CfgOrigin attr_orig | - this.attribute_descriptor(name, cls_attr, attr_orig) - | - cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig - or - cls_attr.isDescriptor() = true and - cls_attr.descriptorGetInstance(this.getSelf(), value, origin) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + exists(ObjectInternal cls_attr, CfgOrigin attr_orig | + this.attribute_descriptor(name, cls_attr, attr_orig) + | + cls_attr.isDescriptor() = false and value = cls_attr and origin = attr_orig + or + cls_attr.isDescriptor() = true and + cls_attr.descriptorGetInstance(this.getSelf(), value, origin) + ) + } - /* Helper for `attribute` */ - pragma[noinline] - private predicate attribute_descriptor(string name, ObjectInternal cls_attr, CfgOrigin attr_orig) { - PointsToInternal::attributeRequired(this, name) and - this.lookup(name, cls_attr, attr_orig) - } + /* Helper for `attribute` */ + pragma[noinline] + private predicate attribute_descriptor(string name, ObjectInternal cls_attr, CfgOrigin attr_orig) { + PointsToInternal::attributeRequired(this, name) and + this.lookup(name, cls_attr, attr_orig) + } - private predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - Types::getMro(this.getSelf().getClass()) - .startingAt(this.getStartClass()) - .getTail() - .lookup(name, value, origin) - } + private predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + Types::getMro(this.getSelf().getClass()) + .startingAt(this.getStartClass()) + .getTail() + .lookup(name, value, origin) + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - descriptor.isDescriptor() = true and - this.lookup(name, descriptor, _) and - instance = this.getSelf() and - receiver_type(_, name, this, _) - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + descriptor.isDescriptor() = true and + this.lookup(name, descriptor, _) and + instance = this.getSelf() and + receiver_type(_, name, this, _) + } - override int length() { none() } + override int length() { none() } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/Modules.qll b/python/ql/src/semmle/python/objects/Modules.qll index 83b8fb5769c..ce40f28d74e 100644 --- a/python/ql/src/semmle/python/objects/Modules.qll +++ b/python/ql/src/semmle/python/objects/Modules.qll @@ -8,388 +8,388 @@ private import semmle.python.types.Builtins /** A class representing modules */ abstract class ModuleObjectInternal extends ObjectInternal { - /** Gets the source scope of this module, if it has one. */ - abstract Module getSourceModule(); + /** Gets the source scope of this module, if it has one. */ + abstract Module getSourceModule(); - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Modules aren't callable - none() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Modules aren't callable + none() + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - // Modules aren't callable - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + // Modules aren't callable + none() + } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override ObjectInternal getClass() { result = ObjectInternal::moduleType() } + override ObjectInternal getClass() { result = ObjectInternal::moduleType() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - /** Holds if this module is a `__init__.py` module. */ - predicate isInitModule() { any(PackageObjectInternal package).getInitModule() = this } + /** Holds if this module is a `__init__.py` module. */ + predicate isInitModule() { any(PackageObjectInternal package).getInitModule() = this } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Modules aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Modules aren't iterable */ + override ObjectInternal getIterNext() { none() } - /** - * Holds if this module "exports" name. - * That is, does it define `name` in `__all__` or is - * `__all__` not defined and `name` a global variable that does not start with "_" - * This is the set of names imported by `from ... import *`. - */ - predicate exports(string name) { - not this.(ModuleObjectInternal).attribute("__all__", _, _) and - this.hasAttribute(name) and - not name.charAt(0) = "_" - or - py_exports(this.getSourceModule(), name) - } + /** + * Holds if this module "exports" name. + * That is, does it define `name` in `__all__` or is + * `__all__` not defined and `name` a global variable that does not start with "_" + * This is the set of names imported by `from ... import *`. + */ + predicate exports(string name) { + not this.(ModuleObjectInternal).attribute("__all__", _, _) and + this.hasAttribute(name) and + not name.charAt(0) = "_" + or + py_exports(this.getSourceModule(), name) + } - /** Whether the complete set of names "exported" by this module can be accurately determined */ - abstract predicate hasCompleteExportInfo(); + /** Whether the complete set of names "exported" by this module can be accurately determined */ + abstract predicate hasCompleteExportInfo(); - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A class representing built-in modules */ class BuiltinModuleObjectInternal extends ModuleObjectInternal, TBuiltinModuleObject { - override Builtin getBuiltin() { this = TBuiltinModuleObject(result) } + override Builtin getBuiltin() { this = TBuiltinModuleObject(result) } - override string toString() { result = "Module " + this.getBuiltin().getName() } + override string toString() { result = "Module " + this.getBuiltin().getName() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { none() } + override Module getSourceModule() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate hasCompleteExportInfo() { any() } + override predicate hasCompleteExportInfo() { any() } } /** A class representing packages */ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { result = "Package " + this.getName() } + override string toString() { result = "Package " + this.getName() } - /** Gets the folder for this package */ - Folder getFolder() { this = TPackageObject(result) } + /** Gets the folder for this package */ + Folder getFolder() { this = TPackageObject(result) } - override string getName() { result = moduleNameFromFile(this.getFolder()) } + override string getName() { result = moduleNameFromFile(this.getFolder()) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { result.getFile() = this.getFolder().getFile("__init__.py") } + override Module getSourceModule() { result.getFile() = this.getFolder().getFile("__init__.py") } - /** Gets the init module of this package */ - PythonModuleObjectInternal getInitModule() { result = TPythonModule(this.getSourceModule()) } + /** Gets the init module of this package */ + PythonModuleObjectInternal getInitModule() { result = TPythonModule(this.getSourceModule()) } - predicate hasNoInitModule() { - exists(Folder f | - f = this.getFolder() and - not exists(f.getFile("__init__.py")) - ) - } + predicate hasNoInitModule() { + exists(Folder f | + f = this.getFolder() and + not exists(f.getFile("__init__.py")) + ) + } - /** Gets the submodule `name` of this package */ - ModuleObjectInternal submodule(string name) { - exists(string fullName, int lastDotIndex | - fullName = result.getName() and - lastDotIndex = max(fullName.indexOf(".")) and - name = fullName.substring(lastDotIndex + 1, fullName.length()) and - this.getName() = fullName.substring(0, lastDotIndex) - ) - } + /** Gets the submodule `name` of this package */ + ModuleObjectInternal submodule(string name) { + exists(string fullName, int lastDotIndex | + fullName = result.getName() and + lastDotIndex = max(fullName.indexOf(".")) and + name = fullName.substring(lastDotIndex + 1, fullName.length()) and + this.getName() = fullName.substring(0, lastDotIndex) + ) + } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - this.getInitModule().attribute(name, value, origin) - or - exists(Module init | - init = this.getSourceModule() and - /* The variable shadowing the name of the child module is undefined at exit */ - ModuleAttributes::pointsToAtExit(init, name, ObjectInternal::undefined(), _) and - not name = "__init__" and - value = this.submodule(name) and - origin = CfgOrigin::fromObject(value) - ) - or - this.hasNoInitModule() and - exists(ModuleObjectInternal mod | - mod = this.submodule(name) and - value = mod - | - origin = CfgOrigin::fromObject(mod) - ) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + this.getInitModule().attribute(name, value, origin) + or + exists(Module init | + init = this.getSourceModule() and + /* The variable shadowing the name of the child module is undefined at exit */ + ModuleAttributes::pointsToAtExit(init, name, ObjectInternal::undefined(), _) and + not name = "__init__" and + value = this.submodule(name) and + origin = CfgOrigin::fromObject(value) + ) + or + this.hasNoInitModule() and + exists(ModuleObjectInternal mod | + mod = this.submodule(name) and + value = mod + | + origin = CfgOrigin::fromObject(mod) + ) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { - exists(Module package | - package.isPackage() and - package.getPath() = this.getFolder() and - result = package.getEntryNode() - ) - } + override ControlFlowNode getOrigin() { + exists(Module package | + package.isPackage() and + package.getPath() = this.getFolder() and + result = package.getEntryNode() + ) + } - /** Holds if this value has the attribute `name` */ - override predicate hasAttribute(string name) { - this.getInitModule().hasAttribute(name) - or - exists(this.submodule(name)) - } + /** Holds if this value has the attribute `name` */ + override predicate hasAttribute(string name) { + this.getInitModule().hasAttribute(name) + or + exists(this.submodule(name)) + } - override predicate hasCompleteExportInfo() { - not exists(this.getInitModule()) - or - this.getInitModule().hasCompleteExportInfo() - } + override predicate hasCompleteExportInfo() { + not exists(this.getInitModule()) + or + this.getInitModule().hasCompleteExportInfo() + } } /** A class representing Python modules */ class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { result = this.getSourceModule().toString() } + override string toString() { result = this.getSourceModule().toString() } - override string getName() { result = this.getSourceModule().getName() } + override string getName() { result = this.getSourceModule().getName() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { this = TPythonModule(result) } + override Module getSourceModule() { this = TPythonModule(result) } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value != ObjectInternal::undefined() and - ModuleAttributes::pointsToAtExit(this.getSourceModule(), name, value, origin) - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value != ObjectInternal::undefined() and + ModuleAttributes::pointsToAtExit(this.getSourceModule(), name, value, origin) + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { result = this.getSourceModule().getEntryNode() } + override ControlFlowNode getOrigin() { result = this.getSourceModule().getEntryNode() } - /** Holds if this value has the attribute `name` */ - override predicate hasAttribute(string name) { - name = "__name__" - or - this.getSourceModule().(ImportTimeScope).definesName(name) - or - exists(ModuleObjectInternal mod, ImportStarNode imp | - PointsToInternal::pointsTo(imp, _, mod, _) and - imp.getScope() = this.getSourceModule() and - mod.exports(name) - ) - or - exists(ObjectInternal defined | - this.attribute(name, defined, _) and - not defined instanceof UndefinedInternal - ) - } + /** Holds if this value has the attribute `name` */ + override predicate hasAttribute(string name) { + name = "__name__" + or + this.getSourceModule().(ImportTimeScope).definesName(name) + or + exists(ModuleObjectInternal mod, ImportStarNode imp | + PointsToInternal::pointsTo(imp, _, mod, _) and + imp.getScope() = this.getSourceModule() and + mod.exports(name) + ) + or + exists(ObjectInternal defined | + this.attribute(name, defined, _) and + not defined instanceof UndefinedInternal + ) + } - override predicate hasCompleteExportInfo() { - not exists(Call modify, Attribute attr, GlobalVariable all | - modify.getScope() = this.getSourceModule() and - modify.getFunc() = attr and - all.getId() = "__all__" - | - attr.getObject().(Name).uses(all) - ) - } + override predicate hasCompleteExportInfo() { + not exists(Call modify, Attribute attr, GlobalVariable all | + modify.getScope() = this.getSourceModule() and + modify.getFunc() = attr and + all.getId() = "__all__" + | + attr.getObject().(Name).uses(all) + ) + } } /** A class representing a module that is missing from the DB, but inferred to exists from imports. */ class AbsentModuleObjectInternal extends ModuleObjectInternal, TAbsentModule { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { - if - exists(Module m, SyntaxError se | se.getFile() = m.getFile() and m.getName() = this.getName()) - then result = "Unparsable module " + this.getName() - else result = "Missing module " + this.getName() - } + override string toString() { + if + exists(Module m, SyntaxError se | se.getFile() = m.getFile() and m.getName() = this.getName()) + then result = "Unparsable module " + this.getName() + else result = "Missing module " + this.getName() + } - override string getName() { this = TAbsentModule(result) } + override string getName() { this = TAbsentModule(result) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - missing_imported_module(node, context, this.getName()) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + missing_imported_module(node, context, this.getName()) + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override Module getSourceModule() { none() } + override Module getSourceModule() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = TAbsentModuleAttribute(this, name) and origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = TAbsentModuleAttribute(this, name) and origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate hasCompleteExportInfo() { none() } + override predicate hasCompleteExportInfo() { none() } } /** A class representing an attribute of a missing module. */ class AbsentModuleAttributeObjectInternal extends ObjectInternal, TAbsentModuleAttribute { - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override string toString() { - exists(ModuleObjectInternal mod, string name | - this = TAbsentModuleAttribute(mod, name) and - result = "Missing module attribute " + mod.getName() + "." + name - ) - } + override string toString() { + exists(ModuleObjectInternal mod, string name | + this = TAbsentModuleAttribute(mod, name) and + result = "Missing module attribute " + mod.getName() + "." + name + ) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - exists(ModuleObjectInternal mod, string name | this = TAbsentModuleAttribute(mod, name) | - PointsToInternal::pointsTo(node.(AttrNode).getObject(name), context, mod, _) - or - PointsToInternal::pointsTo(node.(ImportMemberNode).getModule(name), context, mod, _) - ) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + exists(ModuleObjectInternal mod, string name | this = TAbsentModuleAttribute(mod, name) | + PointsToInternal::pointsTo(node.(AttrNode).getObject(name), context, mod, _) + or + PointsToInternal::pointsTo(node.(ImportMemberNode).getModule(name), context, mod, _) + ) + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override boolean isClass() { result = maybe() } + override boolean isClass() { result = maybe() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override boolean booleanValue() { result = maybe() } + override boolean booleanValue() { result = maybe() } - override ObjectInternal getClass() { result = ObjectInternal::unknownClass() } + override ObjectInternal getClass() { result = ObjectInternal::unknownClass() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - /* - * We know what this is called, but not its innate name. - * However, if we are looking for things by name, this is a reasonable approximation - */ + /* + * We know what this is called, but not its innate name. + * However, if we are looking for things by name, this is a reasonable approximation + */ - override string getName() { this = TAbsentModuleAttribute(_, result) } + override string getName() { this = TAbsentModuleAttribute(_, result) } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /* Modules aren't iterable */ - override ObjectInternal getIterNext() { none() } + /* Modules aren't iterable */ + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index 833a755c059..9af9074cefc 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -30,118 +30,118 @@ class ModuleScope = Module; * Each `Value` is a static approximation to a set of one or more real objects. */ class Value extends TObject { - Value() { - this != ObjectInternal::unknown() and - this != ObjectInternal::unknownClass() and - this != ObjectInternal::undefined() - } + Value() { + this != ObjectInternal::unknown() and + this != ObjectInternal::unknownClass() and + this != ObjectInternal::undefined() + } - /** Gets a textual representation of this element. */ - string toString() { result = this.(ObjectInternal).toString() } + /** Gets a textual representation of this element. */ + string toString() { result = this.(ObjectInternal).toString() } - /** Gets a `ControlFlowNode` that refers to this object. */ - ControlFlowNode getAReference() { PointsToInternal::pointsTo(result, _, this, _) } + /** Gets a `ControlFlowNode` that refers to this object. */ + ControlFlowNode getAReference() { PointsToInternal::pointsTo(result, _, this, _) } - /** Gets the origin CFG node for this value. */ - ControlFlowNode getOrigin() { result = this.(ObjectInternal).getOrigin() } + /** Gets the origin CFG node for this value. */ + ControlFlowNode getOrigin() { result = this.(ObjectInternal).getOrigin() } - /** - * Gets the class of this object. - * Strictly, the `Value` representing the class of the objects - * represented by this Value. - */ - ClassValue getClass() { result = this.(ObjectInternal).getClass() } + /** + * Gets the class of this object. + * Strictly, the `Value` representing the class of the objects + * represented by this Value. + */ + ClassValue getClass() { result = this.(ObjectInternal).getClass() } - /** Gets a call to this object */ - CallNode getACall() { result = this.getACall(_) } + /** Gets a call to this object */ + CallNode getACall() { result = this.getACall(_) } - /** Gets a call to this object with the given `caller` context. */ - CallNode getACall(PointsToContext caller) { - PointsToInternal::pointsTo(result.getFunction(), caller, this, _) - or - exists(BoundMethodObjectInternal bm | - PointsToInternal::pointsTo(result.getFunction(), caller, bm, _) and - bm.getFunction() = this - ) - } + /** Gets a call to this object with the given `caller` context. */ + CallNode getACall(PointsToContext caller) { + PointsToInternal::pointsTo(result.getFunction(), caller, this, _) + or + exists(BoundMethodObjectInternal bm | + PointsToInternal::pointsTo(result.getFunction(), caller, bm, _) and + bm.getFunction() = this + ) + } - /** Gets a `Value` that represents the attribute `name` of this object. */ - Value attr(string name) { this.(ObjectInternal).attribute(name, result, _) } + /** Gets a `Value` that represents the attribute `name` of this object. */ + Value attr(string name) { this.(ObjectInternal).attribute(name, result, _) } - /** - * Holds if this value is builtin. Applies to built-in functions and methods, - * but also integers and strings. - */ - predicate isBuiltin() { this.(ObjectInternal).isBuiltin() } + /** + * Holds if this value is builtin. Applies to built-in functions and methods, + * but also integers and strings. + */ + predicate isBuiltin() { this.(ObjectInternal).isBuiltin() } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this - .(ObjectInternal) - .getOrigin() - .getLocation() - .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - not exists(this.(ObjectInternal).getOrigin()) and - filepath = "" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this + .(ObjectInternal) + .getOrigin() + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + or + not exists(this.(ObjectInternal).getOrigin()) and + filepath = "" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - /** - * Gets the name of this value, if it has one. - * Note this is the innate name of the - * object, not necessarily all the names by which it can be called. - */ - final string getName() { result = this.(ObjectInternal).getName() } + /** + * Gets the name of this value, if it has one. + * Note this is the innate name of the + * object, not necessarily all the names by which it can be called. + */ + final string getName() { result = this.(ObjectInternal).getName() } - /** Holds if this value has the attribute `name` */ - predicate hasAttribute(string name) { this.(ObjectInternal).hasAttribute(name) } + /** Holds if this value has the attribute `name` */ + predicate hasAttribute(string name) { this.(ObjectInternal).hasAttribute(name) } - /** Whether this value is absent from the database, but has been inferred to likely exist */ - predicate isAbsent() { - this instanceof AbsentModuleObjectInternal - or - this instanceof AbsentModuleAttributeObjectInternal - } + /** Whether this value is absent from the database, but has been inferred to likely exist */ + predicate isAbsent() { + this instanceof AbsentModuleObjectInternal + or + this instanceof AbsentModuleAttributeObjectInternal + } - /** - * Whether this overrides v. In this context, "overrides" means that this object - * is a named attribute of a some class C and `v` is a named attribute of another - * class S, both attributes having the same name, and S is a super class of C. - */ - predicate overrides(Value v) { - exists(ClassValue my_class, ClassValue other_class, string name | - my_class.declaredAttribute(name) = this and - other_class.declaredAttribute(name) = v and - my_class.getABaseType+() = other_class - ) - } + /** + * Whether this overrides v. In this context, "overrides" means that this object + * is a named attribute of a some class C and `v` is a named attribute of another + * class S, both attributes having the same name, and S is a super class of C. + */ + predicate overrides(Value v) { + exists(ClassValue my_class, ClassValue other_class, string name | + my_class.declaredAttribute(name) = this and + other_class.declaredAttribute(name) = v and + my_class.getABaseType+() = other_class + ) + } - /** - * Gets the boolean interpretation of this value. - * Could be both `true` and `false`, if we can't determine the result more precisely. - */ - boolean getABooleanValue() { result = this.(ObjectInternal).booleanValue() } + /** + * Gets the boolean interpretation of this value. + * Could be both `true` and `false`, if we can't determine the result more precisely. + */ + boolean getABooleanValue() { result = this.(ObjectInternal).booleanValue() } - /** - * Gets the boolean interpretation of this value, only if we can determine the result precisely. - * The result can be `none()`, but never both `true` and `false`. - */ - boolean getDefiniteBooleanValue() { - result = getABooleanValue() and - not (getABooleanValue() = true and getABooleanValue() = false) - } + /** + * Gets the boolean interpretation of this value, only if we can determine the result precisely. + * The result can be `none()`, but never both `true` and `false`. + */ + boolean getDefiniteBooleanValue() { + result = getABooleanValue() and + not (getABooleanValue() = true and getABooleanValue() = false) + } } /** @@ -149,196 +149,196 @@ class Value extends TObject { * Each `ModuleValue` represents a module object in the Python program. */ class ModuleValue extends Value { - ModuleValue() { this instanceof ModuleObjectInternal } + ModuleValue() { this instanceof ModuleObjectInternal } - /** - * Holds if this module "exports" name. - * That is, does it define `name` in `__all__` or is - * `__all__` not defined and `name` a global variable that does not start with "_" - * This is the set of names imported by `from ... import *`. - */ - predicate exports(string name) { PointsTo::moduleExports(this, name) } + /** + * Holds if this module "exports" name. + * That is, does it define `name` in `__all__` or is + * `__all__` not defined and `name` a global variable that does not start with "_" + * This is the set of names imported by `from ... import *`. + */ + predicate exports(string name) { PointsTo::moduleExports(this, name) } - /** Gets the scope for this module, provided that it is a Python module. */ - ModuleScope getScope() { result = this.(ModuleObjectInternal).getSourceModule() } + /** Gets the scope for this module, provided that it is a Python module. */ + ModuleScope getScope() { result = this.(ModuleObjectInternal).getSourceModule() } - /** - * Gets the container path for this module. Will be the file for a Python module, - * the folder for a package and no result for a builtin module. - */ - Container getPath() { - result = this.(PackageObjectInternal).getFolder() - or - result = this.(PythonModuleObjectInternal).getSourceModule().getFile() - } + /** + * Gets the container path for this module. Will be the file for a Python module, + * the folder for a package and no result for a builtin module. + */ + Container getPath() { + result = this.(PackageObjectInternal).getFolder() + or + result = this.(PythonModuleObjectInternal).getSourceModule().getFile() + } - /** - * Whether this module is imported by 'import name'. For example on a linux system, - * the module 'posixpath' is imported as 'os.path' or as 'posixpath' - */ - predicate importedAs(string name) { PointsToInternal::module_imported_as(this, name) } + /** + * Whether this module is imported by 'import name'. For example on a linux system, + * the module 'posixpath' is imported as 'os.path' or as 'posixpath' + */ + predicate importedAs(string name) { PointsToInternal::module_imported_as(this, name) } - /** Whether this module is a package. */ - predicate isPackage() { this instanceof PackageObjectInternal } + /** Whether this module is a package. */ + predicate isPackage() { this instanceof PackageObjectInternal } - /** Whether the complete set of names "exported" by this module can be accurately determined */ - predicate hasCompleteExportInfo() { this.(ModuleObjectInternal).hasCompleteExportInfo() } + /** Whether the complete set of names "exported" by this module can be accurately determined */ + predicate hasCompleteExportInfo() { this.(ModuleObjectInternal).hasCompleteExportInfo() } - /** Get a module that this module imports */ - ModuleValue getAnImportedModule() { result.importedAs(this.getScope().getAnImportedModuleName()) } + /** Get a module that this module imports */ + ModuleValue getAnImportedModule() { result.importedAs(this.getScope().getAnImportedModuleName()) } - /** When used as a normal module (for example, imported and used by other modules) */ - predicate isUsedAsModule() { - this.isBuiltin() - or - this.isPackage() - or - exists(ImportingStmt i | this.importedAs(i.getAnImportedModuleName())) - or - this.getPath().getBaseName() = "__init__.py" - } + /** When used as a normal module (for example, imported and used by other modules) */ + predicate isUsedAsModule() { + this.isBuiltin() + or + this.isPackage() + or + exists(ImportingStmt i | this.importedAs(i.getAnImportedModuleName())) + or + this.getPath().getBaseName() = "__init__.py" + } - /** When used (exclusively) as a script (will not include normal modules that can also be run as a script) */ - predicate isUsedAsScript() { - not isUsedAsModule() and - ( - not this.getPath().getExtension() = "py" - or - exists(If i, Name name, StrConst main, Cmpop op | - i.getScope() = this.getScope() and - op instanceof Eq and - i.getTest().(Compare).compares(name, op, main) and - name.getId() = "__name__" and - main.getText() = "__main__" - ) - or - exists(Comment c | - c.getLocation().getFile() = this.getPath() and - c.getLocation().getStartLine() = 1 and - c.getText().regexpMatch("^#!/.*python(2|3)?[ \\\\t]*$") - ) - ) - } + /** When used (exclusively) as a script (will not include normal modules that can also be run as a script) */ + predicate isUsedAsScript() { + not isUsedAsModule() and + ( + not this.getPath().getExtension() = "py" + or + exists(If i, Name name, StrConst main, Cmpop op | + i.getScope() = this.getScope() and + op instanceof Eq and + i.getTest().(Compare).compares(name, op, main) and + name.getId() = "__name__" and + main.getText() = "__main__" + ) + or + exists(Comment c | + c.getLocation().getFile() = this.getPath() and + c.getLocation().getStartLine() = 1 and + c.getText().regexpMatch("^#!/.*python(2|3)?[ \\\\t]*$") + ) + ) + } } module Module { - /** - * Gets the `ModuleValue` named `name`. - * - * Note that the name used to refer to a module is not - * necessarily its name. For example, - * there are modules referred to by the name `os.path`, - * but that are not named `os.path`, for example the module `posixpath`. - * Such that the following is true: - * `Module::named("os.path").getName() = "posixpath" - */ - ModuleValue named(string name) { - result.getName() = name - or - result = named(name, _) - } + /** + * Gets the `ModuleValue` named `name`. + * + * Note that the name used to refer to a module is not + * necessarily its name. For example, + * there are modules referred to by the name `os.path`, + * but that are not named `os.path`, for example the module `posixpath`. + * Such that the following is true: + * `Module::named("os.path").getName() = "posixpath" + */ + ModuleValue named(string name) { + result.getName() = name + or + result = named(name, _) + } - /* Prevent runaway recursion when a module has itself as an attribute. */ - private ModuleValue named(string name, int dots) { - dots = 0 and - not name.charAt(_) = "." and - result.getName() = name - or - dots <= 3 and - exists(string modname, string attrname | name = modname + "." + attrname | - result = named(modname, dots - 1).attr(attrname) - ) - } + /* Prevent runaway recursion when a module has itself as an attribute. */ + private ModuleValue named(string name, int dots) { + dots = 0 and + not name.charAt(_) = "." and + result.getName() = name + or + dots <= 3 and + exists(string modname, string attrname | name = modname + "." + attrname | + result = named(modname, dots - 1).attr(attrname) + ) + } - /** Get the `ModuleValue` for the `builtin` module. */ - ModuleValue builtinModule() { result = TBuiltinModuleObject(Builtin::builtinModule()) } + /** Get the `ModuleValue` for the `builtin` module. */ + ModuleValue builtinModule() { result = TBuiltinModuleObject(Builtin::builtinModule()) } } module Value { - /** - * Gets the `Value` named `name`. - * If there is at least one '.' in `name`, then the part of - * the name to the left of the rightmost '.' is interpreted as a module name - * and the part after the rightmost '.' as an attribute of that module. - * For example, `Value::named("os.path.join")` is the `Value` representing the - * `join` function of the `os.path` module. - * If there is no '.' in `name`, then the `Value` returned is the builtin - * object of that name. - * For example `Value::named("len")` is the `Value` representing the `len` built-in function. - */ - Value named(string name) { - exists(string modname, string attrname | name = modname + "." + attrname | - result = Module::named(modname).attr(attrname) - ) - or - result = ObjectInternal::builtin(name) - or - name = "None" and result = ObjectInternal::none_() - or - name = "True" and result = TTrue() - or - name = "False" and result = TFalse() - } + /** + * Gets the `Value` named `name`. + * If there is at least one '.' in `name`, then the part of + * the name to the left of the rightmost '.' is interpreted as a module name + * and the part after the rightmost '.' as an attribute of that module. + * For example, `Value::named("os.path.join")` is the `Value` representing the + * `join` function of the `os.path` module. + * If there is no '.' in `name`, then the `Value` returned is the builtin + * object of that name. + * For example `Value::named("len")` is the `Value` representing the `len` built-in function. + */ + Value named(string name) { + exists(string modname, string attrname | name = modname + "." + attrname | + result = Module::named(modname).attr(attrname) + ) + or + result = ObjectInternal::builtin(name) + or + name = "None" and result = ObjectInternal::none_() + or + name = "True" and result = TTrue() + or + name = "False" and result = TFalse() + } - /** - * Gets the `NumericValue` for the integer constant `i`, if it exists. - * There will be no `NumericValue` for most integers, but the following are - * guaranteed to exist: - * * From zero to 511 inclusive. - * * All powers of 2 (up to 2**30) - * * Any integer explicitly mentioned in the source program. - */ - NumericValue forInt(int i) { result.(IntObjectInternal).intValue() = i } + /** + * Gets the `NumericValue` for the integer constant `i`, if it exists. + * There will be no `NumericValue` for most integers, but the following are + * guaranteed to exist: + * * From zero to 511 inclusive. + * * All powers of 2 (up to 2**30) + * * Any integer explicitly mentioned in the source program. + */ + NumericValue forInt(int i) { result.(IntObjectInternal).intValue() = i } - /** - * Gets the `Value` for the bytes constant `bytes`, if it exists. - * There will be no `Value` for most byte strings, unless it is explicitly - * declared in the source program. - */ - StringValue forBytes(string bytes) { result.(BytesObjectInternal).strValue() = bytes } + /** + * Gets the `Value` for the bytes constant `bytes`, if it exists. + * There will be no `Value` for most byte strings, unless it is explicitly + * declared in the source program. + */ + StringValue forBytes(string bytes) { result.(BytesObjectInternal).strValue() = bytes } - /** - * Gets the `Value` for the unicode constant `text`, if it exists. - * There will be no `Value` for most text strings, unless it is explicitly - * declared in the source program. - */ - StringValue forUnicode(string text) { result.(UnicodeObjectInternal).strValue() = text } + /** + * Gets the `Value` for the unicode constant `text`, if it exists. + * There will be no `Value` for most text strings, unless it is explicitly + * declared in the source program. + */ + StringValue forUnicode(string text) { result.(UnicodeObjectInternal).strValue() = text } - /** - * Gets a `Value` for the string `text`. May be a bytes or unicode string for Python 2. - * There will be no `Value` for most strings, unless it is explicitly - * declared in the source program. - */ - StringValue forString(string text) { - result.(UnicodeObjectInternal).strValue() = text - or - major_version() = 2 and - result.(BytesObjectInternal).strValue() = text - } + /** + * Gets a `Value` for the string `text`. May be a bytes or unicode string for Python 2. + * There will be no `Value` for most strings, unless it is explicitly + * declared in the source program. + */ + StringValue forString(string text) { + result.(UnicodeObjectInternal).strValue() = text + or + major_version() = 2 and + result.(BytesObjectInternal).strValue() = text + } - /** Gets the `Value` for the bool constant `b`. */ - Value forBool(boolean b) { - b = true and result = TTrue() - or - b = false and result = TFalse() - } + /** Gets the `Value` for the bool constant `b`. */ + Value forBool(boolean b) { + b = true and result = TTrue() + or + b = false and result = TFalse() + } - /** Gets the `Value` for `None`. */ - Value none_() { result = ObjectInternal::none_() } + /** Gets the `Value` for `None`. */ + Value none_() { result = ObjectInternal::none_() } - /** - * Shorcuts added by the `site` module to exit your interactive session. - * - * see https://docs.python.org/3/library/constants.html#constants-added-by-the-site-module - */ - Value siteQuitter(string name) { - ( - name = "exit" - or - name = "quit" - ) and - result = Value::named(name) - } + /** + * Shorcuts added by the `site` module to exit your interactive session. + * + * see https://docs.python.org/3/library/constants.html#constants-added-by-the-site-module + */ + Value siteQuitter(string name) { + ( + name = "exit" + or + name = "quit" + ) and + result = Value::named(name) + } } /** @@ -347,106 +347,106 @@ module Value { * but not classes. */ class CallableValue extends Value { - CallableValue() { this instanceof CallableObjectInternal } + CallableValue() { this instanceof CallableObjectInternal } - /** - * Holds if this callable never returns once called. - * For example, `sys.exit` - */ - predicate neverReturns() { this.(CallableObjectInternal).neverReturns() } + /** + * Holds if this callable never returns once called. + * For example, `sys.exit` + */ + predicate neverReturns() { this.(CallableObjectInternal).neverReturns() } - /** Gets the scope for this function, provided that it is a Python function. */ - FunctionScope getScope() { result = this.(PythonFunctionObjectInternal).getScope() } + /** Gets the scope for this function, provided that it is a Python function. */ + FunctionScope getScope() { result = this.(PythonFunctionObjectInternal).getScope() } - /** Gets the `n`th parameter node of this callable. */ - NameNode getParameter(int n) { result = this.(CallableObjectInternal).getParameter(n) } + /** Gets the `n`th parameter node of this callable. */ + NameNode getParameter(int n) { result = this.(CallableObjectInternal).getParameter(n) } - /** Gets the `name`d parameter node of this callable. */ - NameNode getParameterByName(string name) { - result = this.(CallableObjectInternal).getParameterByName(name) - } + /** Gets the `name`d parameter node of this callable. */ + NameNode getParameterByName(string name) { + result = this.(CallableObjectInternal).getParameterByName(name) + } - /** - * Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable. - * - * Use this method instead of `call.getArg(n)` to handle the fact that this function might be used as - * a bound-method, such that argument `n` of the call corresponds to the `n+1` parameter of the callable. - * - * This method also gives results when the argument is passed as a keyword argument in `call`, as long - * as `this` is not a builtin function or a builtin method. - * - * Examples: - * - * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents - * `func(10, 20)`, then `getArgumentForCall(call, 0)` will give the `ControlFlowNode` for `10`. - * - * - with `call` representing `func(b=20, a=10)`, `getArgumentForCall(call, 0)` will give - * the `ControlFlowNode` for `10`. - * - * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` - * represents `foo.func(10, 20)`, then `getArgumentForCall(call, 1)` will give the - * `ControlFlowNode` for `10`. - * Note: There will also exist a `BoundMethodValue bm` where `bm.getArgumentForCall(call, 0)` - * will give the `ControlFlowNode` for `10` (notice the shift in index used). - */ - cached - ControlFlowNode getArgumentForCall(CallNode call, int n) { - exists(ObjectInternal called, int offset | - PointsToInternal::pointsTo(call.getFunction(), _, called, _) and - called.functionAndOffset(this, offset) - | - call.getArg(n - offset) = result - or - exists(string name | - call.getArgByName(name) = result and - this.getParameter(n).getId() = name - ) - or - called instanceof BoundMethodObjectInternal and - offset = 1 and - n = 0 and - result = call.getFunction().(AttrNode).getObject() - ) - } + /** + * Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable. + * + * Use this method instead of `call.getArg(n)` to handle the fact that this function might be used as + * a bound-method, such that argument `n` of the call corresponds to the `n+1` parameter of the callable. + * + * This method also gives results when the argument is passed as a keyword argument in `call`, as long + * as `this` is not a builtin function or a builtin method. + * + * Examples: + * + * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents + * `func(10, 20)`, then `getArgumentForCall(call, 0)` will give the `ControlFlowNode` for `10`. + * + * - with `call` representing `func(b=20, a=10)`, `getArgumentForCall(call, 0)` will give + * the `ControlFlowNode` for `10`. + * + * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` + * represents `foo.func(10, 20)`, then `getArgumentForCall(call, 1)` will give the + * `ControlFlowNode` for `10`. + * Note: There will also exist a `BoundMethodValue bm` where `bm.getArgumentForCall(call, 0)` + * will give the `ControlFlowNode` for `10` (notice the shift in index used). + */ + cached + ControlFlowNode getArgumentForCall(CallNode call, int n) { + exists(ObjectInternal called, int offset | + PointsToInternal::pointsTo(call.getFunction(), _, called, _) and + called.functionAndOffset(this, offset) + | + call.getArg(n - offset) = result + or + exists(string name | + call.getArgByName(name) = result and + this.getParameter(n).getId() = name + ) + or + called instanceof BoundMethodObjectInternal and + offset = 1 and + n = 0 and + result = call.getFunction().(AttrNode).getObject() + ) + } - /** - * Gets the argument in `call` corresponding to the `name`d keyword parameter of this callable. - * - * This method also gives results when the argument is passed as a positional argument in `call`, as long - * as `this` is not a builtin function or a builtin method. - * - * Examples: - * - * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents - * `func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the `ControlFlowNode` for `10`. - * - * - with `call` representing `func(b=20, a=10)`, `getNamedArgumentForCall(call, "a")` will give - * the `ControlFlowNode` for `10`. - * - * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` - * represents `foo.func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the - * `ControlFlowNode` for `10`. - */ - cached - ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { - exists(CallableObjectInternal called, int offset | - PointsToInternal::pointsTo(call.getFunction(), _, called, _) and - called.functionAndOffset(this, offset) - | - call.getArgByName(name) = result - or - exists(int n | - call.getArg(n) = result and - this.getParameter(n + offset).getId() = name - // TODO: and not positional only argument (Python 3.8+) - ) - or - called instanceof BoundMethodObjectInternal and - offset = 1 and - name = "self" and - result = call.getFunction().(AttrNode).getObject() - ) - } + /** + * Gets the argument in `call` corresponding to the `name`d keyword parameter of this callable. + * + * This method also gives results when the argument is passed as a positional argument in `call`, as long + * as `this` is not a builtin function or a builtin method. + * + * Examples: + * + * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents + * `func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the `ControlFlowNode` for `10`. + * + * - with `call` representing `func(b=20, a=10)`, `getNamedArgumentForCall(call, "a")` will give + * the `ControlFlowNode` for `10`. + * + * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` + * represents `foo.func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the + * `ControlFlowNode` for `10`. + */ + cached + ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { + exists(CallableObjectInternal called, int offset | + PointsToInternal::pointsTo(call.getFunction(), _, called, _) and + called.functionAndOffset(this, offset) + | + call.getArgByName(name) = result + or + exists(int n | + call.getArg(n) = result and + this.getParameter(n + offset).getId() = name + // TODO: and not positional only argument (Python 3.8+) + ) + or + called instanceof BoundMethodObjectInternal and + offset = 1 and + name = "self" and + result = call.getFunction().(AttrNode).getObject() + ) + } } /** @@ -454,209 +454,209 @@ class CallableValue extends Value { * of a class that has a callable attribute `func`. */ class BoundMethodValue extends CallableValue { - BoundMethodValue() { this instanceof BoundMethodObjectInternal } + BoundMethodValue() { this instanceof BoundMethodObjectInternal } - /** - * Gets the callable that will be used when `this` is called. - * The actual callable for `func` in `o.func`. - */ - CallableValue getFunction() { result = this.(BoundMethodObjectInternal).getFunction() } + /** + * Gets the callable that will be used when `this` is called. + * The actual callable for `func` in `o.func`. + */ + CallableValue getFunction() { result = this.(BoundMethodObjectInternal).getFunction() } - /** - * Gets the value that will be used for the `self` parameter when `this` is called. - * The value for `o` in `o.func`. - */ - Value getSelf() { result = this.(BoundMethodObjectInternal).getSelf() } + /** + * Gets the value that will be used for the `self` parameter when `this` is called. + * The value for `o` in `o.func`. + */ + Value getSelf() { result = this.(BoundMethodObjectInternal).getSelf() } - /** Gets the parameter node that will be used for `self`. */ - NameNode getSelfParameter() { result = this.(BoundMethodObjectInternal).getSelfParameter() } + /** Gets the parameter node that will be used for `self`. */ + NameNode getSelfParameter() { result = this.(BoundMethodObjectInternal).getSelfParameter() } } /** * Class representing classes in the Python program, both Python and built-in. */ class ClassValue extends Value { - ClassValue() { this.(ObjectInternal).isClass() = true } + ClassValue() { this.(ObjectInternal).isClass() = true } - /** Gets an improper super type of this class. */ - ClassValue getASuperType() { result = this.getABaseType*() } + /** Gets an improper super type of this class. */ + ClassValue getASuperType() { result = this.getABaseType*() } - /** - * Looks up the attribute `name` on this class. - * Note that this may be different from `this.attr(name)`. - * For example given the class: - * ```class C: - * @classmethod - * def f(cls): pass - * ``` - * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method - * whereas - * `this.attr("f")` is equivalent to `C.f`, which is a bound-method. + /** + * Looks up the attribute `name` on this class. + * Note that this may be different from `this.attr(name)`. + * For example given the class: + * ```class C: + * @classmethod + * def f(cls): pass + * ``` + * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method + * whereas + * `this.attr("f")` is equivalent to `C.f`, which is a bound-method. + */ + Value lookup(string name) { this.(ClassObjectInternal).lookup(name, result, _) } + + predicate isCallable() { this.(ClassObjectInternal).lookup("__call__", _, _) } + + /** Holds if this class is an iterable. */ + predicate isIterable() { + this.hasAttribute("__iter__") + or + this.hasAttribute("__aiter__") + or + this.hasAttribute("__getitem__") + } + + /** Holds if this class is an iterator. */ + predicate isIterator() { + this.hasAttribute("__iter__") and + ( + major_version() = 3 and this.hasAttribute("__next__") + or + /* + * Because 'next' is a common method name we need to check that an __iter__ + * method actually returns this class. This is not needed for Py3 as the + * '__next__' method exists to define a class as an iterator. + */ + + major_version() = 2 and + this.hasAttribute("next") and + exists(ClassValue other, FunctionValue iter | other.declaredAttribute("__iter__") = iter | + iter.getAnInferredReturnType() = this + ) + ) + or + /* This will be redundant when we have C class information */ + this = ClassValue::generator() + } + + /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ + predicate isContainer() { exists(this.lookup("__getitem__")) } + + /** + * Holds if this class is a sequence. Mutually exclusive with `isMapping()`. + * + * Following the definition from + * https://docs.python.org/3/glossary.html#term-sequence. + * We don't look at the keys accepted by `__getitem__, but default to treating a class + * as a sequence (so might treat some mappings as sequences). + */ + predicate isSequence() { + /* + * To determine whether something is a sequence or a mapping is not entirely clear, + * so we need to guess a bit. */ - Value lookup(string name) { this.(ClassObjectInternal).lookup(name, result, _) } - predicate isCallable() { this.(ClassObjectInternal).lookup("__call__", _, _) } + this.getASuperType() = ClassValue::tuple() + or + this.getASuperType() = ClassValue::list() + or + this.getASuperType() = ClassValue::range() + or + this.getASuperType() = ClassValue::bytes() + or + this.getASuperType() = ClassValue::unicode() + or + major_version() = 2 and this.getASuperType() = Value::named("collections.Sequence") + or + major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Sequence") + or + this.hasAttribute("__getitem__") and + this.hasAttribute("__len__") and + not this.getASuperType() = ClassValue::dict() and + not this.getASuperType() = Value::named("collections.Mapping") and + not this.getASuperType() = Value::named("collections.abc.Mapping") + } - /** Holds if this class is an iterable. */ - predicate isIterable() { - this.hasAttribute("__iter__") - or - this.hasAttribute("__aiter__") - or - this.hasAttribute("__getitem__") - } + /** + * Holds if this class is a mapping. Mutually exclusive with `isSequence()`. + * + * Although a class will satisfy the requirement by the definition in + * https://docs.python.org/3.8/glossary.html#term-mapping, we don't look at the keys + * accepted by `__getitem__, but default to treating a class as a sequence (so might + * treat some mappings as sequences). + */ + predicate isMapping() { + major_version() = 2 and this.getASuperType() = Value::named("collections.Mapping") + or + major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Mapping") + or + this.hasAttribute("__getitem__") and + not this.isSequence() + } - /** Holds if this class is an iterator. */ - predicate isIterator() { - this.hasAttribute("__iter__") and - ( - major_version() = 3 and this.hasAttribute("__next__") - or - /* - * Because 'next' is a common method name we need to check that an __iter__ - * method actually returns this class. This is not needed for Py3 as the - * '__next__' method exists to define a class as an iterator. - */ + /** Holds if this class is a descriptor. */ + predicate isDescriptorType() { this.hasAttribute("__get__") } - major_version() = 2 and - this.hasAttribute("next") and - exists(ClassValue other, FunctionValue iter | other.declaredAttribute("__iter__") = iter | - iter.getAnInferredReturnType() = this - ) - ) - or - /* This will be redundant when we have C class information */ - this = ClassValue::generator() - } + /** Holds if this class is a context manager. */ + predicate isContextManager() { + this.hasAttribute("__enter__") and + this.hasAttribute("__exit__") + } - /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ - predicate isContainer() { exists(this.lookup("__getitem__")) } + /** + * Gets the qualified name for this class. + * Should return the same name as the `__qualname__` attribute on classes in Python 3. + */ + string getQualifiedName() { + result = this.(ClassObjectInternal).getBuiltin().getName() + or + result = this.(PythonClassObjectInternal).getScope().getQualifiedName() + } - /** - * Holds if this class is a sequence. Mutually exclusive with `isMapping()`. - * - * Following the definition from - * https://docs.python.org/3/glossary.html#term-sequence. - * We don't look at the keys accepted by `__getitem__, but default to treating a class - * as a sequence (so might treat some mappings as sequences). - */ - predicate isSequence() { - /* - * To determine whether something is a sequence or a mapping is not entirely clear, - * so we need to guess a bit. - */ + /** Gets the MRO for this class */ + MRO getMro() { result = Types::getMro(this) } - this.getASuperType() = ClassValue::tuple() - or - this.getASuperType() = ClassValue::list() - or - this.getASuperType() = ClassValue::range() - or - this.getASuperType() = ClassValue::bytes() - or - this.getASuperType() = ClassValue::unicode() - or - major_version() = 2 and this.getASuperType() = Value::named("collections.Sequence") - or - major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Sequence") - or - this.hasAttribute("__getitem__") and - this.hasAttribute("__len__") and - not this.getASuperType() = ClassValue::dict() and - not this.getASuperType() = Value::named("collections.Mapping") and - not this.getASuperType() = Value::named("collections.abc.Mapping") - } + predicate failedInference(string reason) { Types::failedInference(this, reason) } - /** - * Holds if this class is a mapping. Mutually exclusive with `isSequence()`. - * - * Although a class will satisfy the requirement by the definition in - * https://docs.python.org/3.8/glossary.html#term-mapping, we don't look at the keys - * accepted by `__getitem__, but default to treating a class as a sequence (so might - * treat some mappings as sequences). - */ - predicate isMapping() { - major_version() = 2 and this.getASuperType() = Value::named("collections.Mapping") - or - major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Mapping") - or - this.hasAttribute("__getitem__") and - not this.isSequence() - } + /** Gets the nth immediate base type of this class. */ + ClassValue getBaseType(int n) { result = Types::getBase(this, n) } - /** Holds if this class is a descriptor. */ - predicate isDescriptorType() { this.hasAttribute("__get__") } + /** Gets an immediate base type of this class. */ + ClassValue getABaseType() { result = Types::getBase(this, _) } - /** Holds if this class is a context manager. */ - predicate isContextManager() { - this.hasAttribute("__enter__") and - this.hasAttribute("__exit__") - } + /** + * Holds if this class is a new style class. + * A new style class is one that implicitly or explicitly inherits from `object`. + */ + predicate isNewStyle() { Types::isNewStyle(this) } - /** - * Gets the qualified name for this class. - * Should return the same name as the `__qualname__` attribute on classes in Python 3. - */ - string getQualifiedName() { - result = this.(ClassObjectInternal).getBuiltin().getName() - or - result = this.(PythonClassObjectInternal).getScope().getQualifiedName() - } + /** + * Holds if this class is an old style class. + * An old style class is one that does not inherit from `object`. + */ + predicate isOldStyle() { Types::isOldStyle(this) } - /** Gets the MRO for this class */ - MRO getMro() { result = Types::getMro(this) } + /** Gets the scope associated with this class, if it is not a builtin class */ + ClassScope getScope() { result = this.(PythonClassObjectInternal).getScope() } - predicate failedInference(string reason) { Types::failedInference(this, reason) } + /** Gets the attribute declared in this class */ + Value declaredAttribute(string name) { Types::declaredAttribute(this, name, result, _) } - /** Gets the nth immediate base type of this class. */ - ClassValue getBaseType(int n) { result = Types::getBase(this, n) } + /** + * Holds if this class has the attribute `name`, including attributes + * declared by super classes. + */ + override predicate hasAttribute(string name) { this.getMro().declares(name) } - /** Gets an immediate base type of this class. */ - ClassValue getABaseType() { result = Types::getBase(this, _) } + /** + * Holds if this class declares the attribute `name`, + * *not* including attributes declared by super classes. + */ + predicate declaresAttribute(string name) { + this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name) + } - /** - * Holds if this class is a new style class. - * A new style class is one that implicitly or explicitly inherits from `object`. - */ - predicate isNewStyle() { Types::isNewStyle(this) } - - /** - * Holds if this class is an old style class. - * An old style class is one that does not inherit from `object`. - */ - predicate isOldStyle() { Types::isOldStyle(this) } - - /** Gets the scope associated with this class, if it is not a builtin class */ - ClassScope getScope() { result = this.(PythonClassObjectInternal).getScope() } - - /** Gets the attribute declared in this class */ - Value declaredAttribute(string name) { Types::declaredAttribute(this, name, result, _) } - - /** - * Holds if this class has the attribute `name`, including attributes - * declared by super classes. - */ - override predicate hasAttribute(string name) { this.getMro().declares(name) } - - /** - * Holds if this class declares the attribute `name`, - * *not* including attributes declared by super classes. - */ - predicate declaresAttribute(string name) { - this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name) - } - - /** - * Whether this class is a legal exception class. - * What constitutes a legal exception class differs between major versions - */ - predicate isLegalExceptionType() { - not this.isNewStyle() - or - this.getASuperType() = ClassValue::baseException() - or - major_version() = 2 and this = ClassValue::tuple() - } + /** + * Whether this class is a legal exception class. + * What constitutes a legal exception class differs between major versions + */ + predicate isLegalExceptionType() { + not this.isNewStyle() + or + this.getASuperType() = ClassValue::baseException() + or + major_version() = 2 and this = ClassValue::tuple() + } } /** @@ -664,182 +664,182 @@ class ClassValue extends Value { * Note that this does not include other callables such as bound-methods. */ abstract class FunctionValue extends CallableValue { - /** - * Gets the qualified name for this function. - * Should return the same name as the `__qualname__` attribute on functions in Python 3. - */ - abstract string getQualifiedName(); + /** + * Gets the qualified name for this function. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ + abstract string getQualifiedName(); - /** Gets a longer, more descriptive version of toString() */ - abstract string descriptiveString(); + /** Gets a longer, more descriptive version of toString() */ + abstract string descriptiveString(); - /** Gets the minimum number of parameters that can be correctly passed to this function */ - abstract int minParameters(); + /** Gets the minimum number of parameters that can be correctly passed to this function */ + abstract int minParameters(); - /** Gets the maximum number of parameters that can be correctly passed to this function */ - abstract int maxParameters(); + /** Gets the maximum number of parameters that can be correctly passed to this function */ + abstract int maxParameters(); - predicate isOverridingMethod() { exists(Value f | this.overrides(f)) } + predicate isOverridingMethod() { exists(Value f | this.overrides(f)) } - predicate isOverriddenMethod() { exists(Value f | f.overrides(this)) } + predicate isOverriddenMethod() { exists(Value f | f.overrides(this)) } - /** Whether `name` is a legal argument name for this function */ - bindingset[name] - predicate isLegalArgumentName(string name) { - this.getScope().getAnArg().asName().getId() = name - or - this.getScope().getAKeywordOnlyArg().getId() = name - or - this.getScope().hasKwArg() - } + /** Whether `name` is a legal argument name for this function */ + bindingset[name] + predicate isLegalArgumentName(string name) { + this.getScope().getAnArg().asName().getId() = name + or + this.getScope().getAKeywordOnlyArg().getId() = name + or + this.getScope().hasKwArg() + } - /** - * Whether this is a "normal" method, that is, it is exists as a class attribute - * which is not a lambda and not the __new__ method. - */ - predicate isNormalMethod() { - exists(ClassValue cls, string name | - cls.declaredAttribute(name) = this and - name != "__new__" and - exists(Expr expr, AstNode origin | expr.pointsTo(this, origin) | not origin instanceof Lambda) - ) - } + /** + * Whether this is a "normal" method, that is, it is exists as a class attribute + * which is not a lambda and not the __new__ method. + */ + predicate isNormalMethod() { + exists(ClassValue cls, string name | + cls.declaredAttribute(name) = this and + name != "__new__" and + exists(Expr expr, AstNode origin | expr.pointsTo(this, origin) | not origin instanceof Lambda) + ) + } - /** Gets a class that may be raised by this function */ - abstract ClassValue getARaisedType(); + /** Gets a class that may be raised by this function */ + abstract ClassValue getARaisedType(); - /** Gets a call-site from where this function is called as a function */ - CallNode getAFunctionCall() { result.getFunction().pointsTo() = this } + /** Gets a call-site from where this function is called as a function */ + CallNode getAFunctionCall() { result.getFunction().pointsTo() = this } - /** Gets a call-site from where this function is called as a method */ - CallNode getAMethodCall() { - exists(BoundMethodObjectInternal bm | - result.getFunction().pointsTo() = bm and - bm.getFunction() = this - ) - } + /** Gets a call-site from where this function is called as a method */ + CallNode getAMethodCall() { + exists(BoundMethodObjectInternal bm | + result.getFunction().pointsTo() = bm and + bm.getFunction() = this + ) + } - /** Gets a class that this function may return */ - abstract ClassValue getAnInferredReturnType(); + /** Gets a class that this function may return */ + abstract ClassValue getAnInferredReturnType(); } /** Class representing Python functions */ class PythonFunctionValue extends FunctionValue { - PythonFunctionValue() { this instanceof PythonFunctionObjectInternal } + PythonFunctionValue() { this instanceof PythonFunctionObjectInternal } - override string getQualifiedName() { - result = this.(PythonFunctionObjectInternal).getScope().getQualifiedName() - } + override string getQualifiedName() { + result = this.(PythonFunctionObjectInternal).getScope().getQualifiedName() + } - override string descriptiveString() { - if this.getScope().isMethod() - then - exists(Class cls | this.getScope().getScope() = cls | - result = "method " + this.getQualifiedName() - ) - else result = "function " + this.getQualifiedName() - } + override string descriptiveString() { + if this.getScope().isMethod() + then + exists(Class cls | this.getScope().getScope() = cls | + result = "method " + this.getQualifiedName() + ) + else result = "function " + this.getQualifiedName() + } - override int minParameters() { - exists(Function f | - f = this.getScope() and - result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) - ) - } + override int minParameters() { + exists(Function f | + f = this.getScope() and + result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) + ) + } - override int maxParameters() { - exists(Function f | - f = this.getScope() and - if exists(f.getVararg()) - then result = 2147483647 // INT_MAX - else result = count(f.getAnArg()) - ) - } + override int maxParameters() { + exists(Function f | + f = this.getScope() and + if exists(f.getVararg()) + then result = 2147483647 // INT_MAX + else result = count(f.getAnArg()) + ) + } - /** Gets a control flow node corresponding to a return statement in this function */ - ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() } + /** Gets a control flow node corresponding to a return statement in this function */ + ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() } - override ClassValue getARaisedType() { scope_raises(result, this.getScope()) } + override ClassValue getARaisedType() { scope_raises(result, this.getScope()) } - override ClassValue getAnInferredReturnType() { - /* - * We have to do a special version of this because builtin functions have no - * explicit return nodes that we can query and get the class of. - */ + override ClassValue getAnInferredReturnType() { + /* + * We have to do a special version of this because builtin functions have no + * explicit return nodes that we can query and get the class of. + */ - result = this.getAReturnedNode().pointsTo().getClass() - } + result = this.getAReturnedNode().pointsTo().getClass() + } } /** Class representing builtin functions, such as `len` or `print` */ class BuiltinFunctionValue extends FunctionValue { - BuiltinFunctionValue() { this instanceof BuiltinFunctionObjectInternal } + BuiltinFunctionValue() { this instanceof BuiltinFunctionObjectInternal } - override string getQualifiedName() { result = this.(BuiltinFunctionObjectInternal).getName() } + override string getQualifiedName() { result = this.(BuiltinFunctionObjectInternal).getName() } - override string descriptiveString() { result = "builtin-function " + this.getName() } + override string descriptiveString() { result = "builtin-function " + this.getName() } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } - override ClassValue getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassValue getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override ClassValue getAnInferredReturnType() { - /* - * We have to do a special version of this because builtin functions have no - * explicit return nodes that we can query and get the class of. - */ + override ClassValue getAnInferredReturnType() { + /* + * We have to do a special version of this because builtin functions have no + * explicit return nodes that we can query and get the class of. + */ - result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType()) - } + result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType()) + } } /** Class representing builtin methods, such as `list.append` or `set.add` */ class BuiltinMethodValue extends FunctionValue { - BuiltinMethodValue() { this instanceof BuiltinMethodObjectInternal } + BuiltinMethodValue() { this instanceof BuiltinMethodObjectInternal } - override string getQualifiedName() { - exists(Builtin cls | - cls.isClass() and - cls.getMember(_) = this.(BuiltinMethodObjectInternal).getBuiltin() and - result = cls.getName() + "." + this.getName() - ) - } + override string getQualifiedName() { + exists(Builtin cls | + cls.isClass() and + cls.getMember(_) = this.(BuiltinMethodObjectInternal).getBuiltin() and + result = cls.getName() + "." + this.getName() + ) + } - override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } + override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } - override ClassValue getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassValue getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override ClassValue getAnInferredReturnType() { - result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType()) - } + override ClassValue getAnInferredReturnType() { + result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType()) + } } /** * A class representing sequence objects with a length and tracked items. */ class SequenceValue extends Value { - SequenceValue() { this instanceof SequenceObjectInternal } + SequenceValue() { this instanceof SequenceObjectInternal } - Value getItem(int n) { result = this.(SequenceObjectInternal).getItem(n) } + Value getItem(int n) { result = this.(SequenceObjectInternal).getItem(n) } - int length() { result = this.(SequenceObjectInternal).length() } + int length() { result = this.(SequenceObjectInternal).length() } } /** A class representing tuple objects */ class TupleValue extends SequenceValue { - TupleValue() { this instanceof TupleObjectInternal } + TupleValue() { this instanceof TupleObjectInternal } } /** @@ -847,16 +847,16 @@ class TupleValue extends SequenceValue { * in a builtin as a value. */ class StringValue extends Value { - StringValue() { - this instanceof BytesObjectInternal or - this instanceof UnicodeObjectInternal - } + StringValue() { + this instanceof BytesObjectInternal or + this instanceof UnicodeObjectInternal + } - string getText() { - result = this.(BytesObjectInternal).strValue() - or - result = this.(UnicodeObjectInternal).strValue() - } + string getText() { + result = this.(BytesObjectInternal).strValue() + or + result = this.(UnicodeObjectInternal).strValue() + } } /** @@ -864,16 +864,16 @@ class StringValue extends Value { * or in a builtin as a value. */ class NumericValue extends Value { - NumericValue() { - this instanceof IntObjectInternal or - this instanceof FloatObjectInternal - } + NumericValue() { + this instanceof IntObjectInternal or + this instanceof FloatObjectInternal + } - /** Gets the integer-value if it is a constant integer, and it fits in a QL int */ - int getIntValue() { result = this.(IntObjectInternal).intValue() } + /** Gets the integer-value if it is a constant integer, and it fits in a QL int */ + int getIntValue() { result = this.(IntObjectInternal).intValue() } - /** Gets the float-value if it is a constant float */ - int getFloatValue() { result = this.(FloatObjectInternal).floatValue() } + /** Gets the float-value if it is a constant float */ + int getFloatValue() { result = this.(FloatObjectInternal).floatValue() } } /** @@ -886,193 +886,193 @@ class NumericValue extends Value { * https://docs.python.org/3/library/functions.html#property */ class PropertyValue extends Value { - PropertyValue() { this instanceof PropertyInternal } + PropertyValue() { this instanceof PropertyInternal } - CallableValue getGetter() { result = this.(PropertyInternal).getGetter() } + CallableValue getGetter() { result = this.(PropertyInternal).getGetter() } - CallableValue getSetter() { result = this.(PropertyInternal).getSetter() } + CallableValue getSetter() { result = this.(PropertyInternal).getSetter() } - CallableValue getDeleter() { result = this.(PropertyInternal).getDeleter() } + CallableValue getDeleter() { result = this.(PropertyInternal).getDeleter() } } /** A method-resolution-order sequence of classes */ class MRO extends TClassList { - /** Gets a textual representation of this element. */ - string toString() { result = this.(ClassList).toString() } + /** Gets a textual representation of this element. */ + string toString() { result = this.(ClassList).toString() } - /** Gets the `n`th class in this MRO */ - ClassValue getItem(int n) { result = this.(ClassList).getItem(n) } + /** Gets the `n`th class in this MRO */ + ClassValue getItem(int n) { result = this.(ClassList).getItem(n) } - /** Holds if any class in this MRO declares the attribute `name` */ - predicate declares(string name) { this.(ClassList).declares(name) } + /** Holds if any class in this MRO declares the attribute `name` */ + predicate declares(string name) { this.(ClassList).declares(name) } - /** Gets the length of this MRO */ - int length() { result = this.(ClassList).length() } + /** Gets the length of this MRO */ + int length() { result = this.(ClassList).length() } - /** Holds if this MRO contains `cls` */ - predicate contains(ClassValue cls) { this.(ClassList).contains(cls) } + /** Holds if this MRO contains `cls` */ + predicate contains(ClassValue cls) { this.(ClassList).contains(cls) } - /** Gets the value from scanning for the attribute `name` in this MRO. */ - Value lookup(string name) { this.(ClassList).lookup(name, result, _) } + /** Gets the value from scanning for the attribute `name` in this MRO. */ + Value lookup(string name) { this.(ClassList).lookup(name, result, _) } - /** - * Gets the MRO formed by removing all classes before `cls` - * from this MRO. - */ - MRO startingAt(ClassValue cls) { result = this.(ClassList).startingAt(cls) } + /** + * Gets the MRO formed by removing all classes before `cls` + * from this MRO. + */ + MRO startingAt(ClassValue cls) { result = this.(ClassList).startingAt(cls) } } module ClassValue { - /** Get the `ClassValue` for the `bool` class. */ - ClassValue bool() { result = TBuiltinClassObject(Builtin::special("bool")) } + /** Get the `ClassValue` for the `bool` class. */ + ClassValue bool() { result = TBuiltinClassObject(Builtin::special("bool")) } - /** Get the `ClassValue` for the `tuple` class. */ - ClassValue tuple() { result = TBuiltinClassObject(Builtin::special("tuple")) } + /** Get the `ClassValue` for the `tuple` class. */ + ClassValue tuple() { result = TBuiltinClassObject(Builtin::special("tuple")) } - /** Get the `ClassValue` for the `list` class. */ - ClassValue list() { result = TBuiltinClassObject(Builtin::special("list")) } + /** Get the `ClassValue` for the `list` class. */ + ClassValue list() { result = TBuiltinClassObject(Builtin::special("list")) } - /** Get the `ClassValue` for `xrange` (Python 2), or `range` (only Python 3) */ - ClassValue range() { - major_version() = 2 and result = TBuiltinClassObject(Builtin::special("xrange")) - or - major_version() = 3 and result = TBuiltinClassObject(Builtin::special("range")) - } + /** Get the `ClassValue` for `xrange` (Python 2), or `range` (only Python 3) */ + ClassValue range() { + major_version() = 2 and result = TBuiltinClassObject(Builtin::special("xrange")) + or + major_version() = 3 and result = TBuiltinClassObject(Builtin::special("range")) + } - /** Get the `ClassValue` for the `dict` class. */ - ClassValue dict() { result = TBuiltinClassObject(Builtin::special("dict")) } + /** Get the `ClassValue` for the `dict` class. */ + ClassValue dict() { result = TBuiltinClassObject(Builtin::special("dict")) } - /** Get the `ClassValue` for the `set` class. */ - ClassValue set() { result = TBuiltinClassObject(Builtin::special("set")) } + /** Get the `ClassValue` for the `set` class. */ + ClassValue set() { result = TBuiltinClassObject(Builtin::special("set")) } - /** Get the `ClassValue` for the `object` class. */ - ClassValue object() { result = TBuiltinClassObject(Builtin::special("object")) } + /** Get the `ClassValue` for the `object` class. */ + ClassValue object() { result = TBuiltinClassObject(Builtin::special("object")) } - /** Get the `ClassValue` for the `int` class. */ - ClassValue int_() { result = TBuiltinClassObject(Builtin::special("int")) } + /** Get the `ClassValue` for the `int` class. */ + ClassValue int_() { result = TBuiltinClassObject(Builtin::special("int")) } - /** Get the `ClassValue` for the `long` class. */ - ClassValue long() { result = TBuiltinClassObject(Builtin::special("long")) } + /** Get the `ClassValue` for the `long` class. */ + ClassValue long() { result = TBuiltinClassObject(Builtin::special("long")) } - /** Get the `ClassValue` for the `float` class. */ - ClassValue float_() { result = TBuiltinClassObject(Builtin::special("float")) } + /** Get the `ClassValue` for the `float` class. */ + ClassValue float_() { result = TBuiltinClassObject(Builtin::special("float")) } - /** Get the `ClassValue` for the `complex` class. */ - ClassValue complex() { result = TBuiltinClassObject(Builtin::special("complex")) } + /** Get the `ClassValue` for the `complex` class. */ + ClassValue complex() { result = TBuiltinClassObject(Builtin::special("complex")) } - /** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */ - ClassValue bytes() { result = TBuiltinClassObject(Builtin::special("bytes")) } + /** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */ + ClassValue bytes() { result = TBuiltinClassObject(Builtin::special("bytes")) } - /** - * Get the `ClassValue` for the class of unicode strings. - * `str` in Python 3 and `unicode` in Python 2. - */ - ClassValue unicode() { result = TBuiltinClassObject(Builtin::special("unicode")) } + /** + * Get the `ClassValue` for the class of unicode strings. + * `str` in Python 3 and `unicode` in Python 2. + */ + ClassValue unicode() { result = TBuiltinClassObject(Builtin::special("unicode")) } - /** - * Get the `ClassValue` for the `str` class. This is `bytes` in Python 2, - * and `str` in Python 3. - */ - ClassValue str() { if major_version() = 2 then result = bytes() else result = unicode() } + /** + * Get the `ClassValue` for the `str` class. This is `bytes` in Python 2, + * and `str` in Python 3. + */ + ClassValue str() { if major_version() = 2 then result = bytes() else result = unicode() } - /** Get the `ClassValue` for the `property` class. */ - ClassValue property() { result = TBuiltinClassObject(Builtin::special("property")) } + /** Get the `ClassValue` for the `property` class. */ + ClassValue property() { result = TBuiltinClassObject(Builtin::special("property")) } - /** Get the `ClassValue` for the class of Python functions. */ - ClassValue functionType() { result = TBuiltinClassObject(Builtin::special("FunctionType")) } + /** Get the `ClassValue` for the class of Python functions. */ + ClassValue functionType() { result = TBuiltinClassObject(Builtin::special("FunctionType")) } - /** Get the `ClassValue` for the class of builtin functions. */ - ClassValue builtinFunction() { result = Value::named("len").getClass() } + /** Get the `ClassValue` for the class of builtin functions. */ + ClassValue builtinFunction() { result = Value::named("len").getClass() } - /** Get the `ClassValue` for the `generatorType` class. */ - ClassValue generator() { result = TBuiltinClassObject(Builtin::special("generator")) } + /** Get the `ClassValue` for the `generatorType` class. */ + ClassValue generator() { result = TBuiltinClassObject(Builtin::special("generator")) } - /** Get the `ClassValue` for the `type` class. */ - ClassValue type() { result = TType() } + /** Get the `ClassValue` for the `type` class. */ + ClassValue type() { result = TType() } - /** Get the `ClassValue` for `ClassType`. */ - ClassValue classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } + /** Get the `ClassValue` for `ClassType`. */ + ClassValue classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } - /** Get the `ClassValue` for `InstanceType`. */ - ClassValue instanceType() { result = TBuiltinClassObject(Builtin::special("InstanceType")) } + /** Get the `ClassValue` for `InstanceType`. */ + ClassValue instanceType() { result = TBuiltinClassObject(Builtin::special("InstanceType")) } - /** Get the `ClassValue` for `super`. */ - ClassValue super_() { result = TBuiltinClassObject(Builtin::special("super")) } + /** Get the `ClassValue` for `super`. */ + ClassValue super_() { result = TBuiltinClassObject(Builtin::special("super")) } - /** Get the `ClassValue` for the `classmethod` class. */ - ClassValue classmethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } + /** Get the `ClassValue` for the `classmethod` class. */ + ClassValue classmethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } - /** Get the `ClassValue` for the `staticmethod` class. */ - ClassValue staticmethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } + /** Get the `ClassValue` for the `staticmethod` class. */ + ClassValue staticmethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } - /** Get the `ClassValue` for the `MethodType` class. */ - pragma[noinline] - ClassValue methodType() { result = TBuiltinClassObject(Builtin::special("MethodType")) } + /** Get the `ClassValue` for the `MethodType` class. */ + pragma[noinline] + ClassValue methodType() { result = TBuiltinClassObject(Builtin::special("MethodType")) } - /** Get the `ClassValue` for the `MethodDescriptorType` class. */ - ClassValue methodDescriptorType() { - result = TBuiltinClassObject(Builtin::special("MethodDescriptorType")) - } + /** Get the `ClassValue` for the `MethodDescriptorType` class. */ + ClassValue methodDescriptorType() { + result = TBuiltinClassObject(Builtin::special("MethodDescriptorType")) + } - /** Get the `ClassValue` for the `GetSetDescriptorType` class. */ - ClassValue getSetDescriptorType() { - result = TBuiltinClassObject(Builtin::special("GetSetDescriptorType")) - } + /** Get the `ClassValue` for the `GetSetDescriptorType` class. */ + ClassValue getSetDescriptorType() { + result = TBuiltinClassObject(Builtin::special("GetSetDescriptorType")) + } - /** Get the `ClassValue` for the `StopIteration` class. */ - ClassValue stopIteration() { result = TBuiltinClassObject(Builtin::builtin("StopIteration")) } + /** Get the `ClassValue` for the `StopIteration` class. */ + ClassValue stopIteration() { result = TBuiltinClassObject(Builtin::builtin("StopIteration")) } - /** Get the `ClassValue` for the class of modules. */ - ClassValue module_() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } + /** Get the `ClassValue` for the class of modules. */ + ClassValue module_() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } - /** Get the `ClassValue` for the `Exception` class. */ - ClassValue exception() { result = TBuiltinClassObject(Builtin::special("Exception")) } + /** Get the `ClassValue` for the `Exception` class. */ + ClassValue exception() { result = TBuiltinClassObject(Builtin::special("Exception")) } - /** Get the `ClassValue` for the `BaseException` class. */ - ClassValue baseException() { result = TBuiltinClassObject(Builtin::special("BaseException")) } + /** Get the `ClassValue` for the `BaseException` class. */ + ClassValue baseException() { result = TBuiltinClassObject(Builtin::special("BaseException")) } - /** Get the `ClassValue` for the `NoneType` class. */ - ClassValue nonetype() { result = TBuiltinClassObject(Builtin::special("NoneType")) } + /** Get the `ClassValue` for the `NoneType` class. */ + ClassValue nonetype() { result = TBuiltinClassObject(Builtin::special("NoneType")) } - /** Get the `ClassValue` for the `TypeError` class */ - ClassValue typeError() { result = TBuiltinClassObject(Builtin::special("TypeError")) } + /** Get the `ClassValue` for the `TypeError` class */ + ClassValue typeError() { result = TBuiltinClassObject(Builtin::special("TypeError")) } - /** Get the `ClassValue` for the `NameError` class. */ - ClassValue nameError() { result = TBuiltinClassObject(Builtin::builtin("NameError")) } + /** Get the `ClassValue` for the `NameError` class. */ + ClassValue nameError() { result = TBuiltinClassObject(Builtin::builtin("NameError")) } - /** Get the `ClassValue` for the `AttributeError` class. */ - ClassValue attributeError() { result = TBuiltinClassObject(Builtin::builtin("AttributeError")) } + /** Get the `ClassValue` for the `AttributeError` class. */ + ClassValue attributeError() { result = TBuiltinClassObject(Builtin::builtin("AttributeError")) } - /** Get the `ClassValue` for the `KeyError` class. */ - ClassValue keyError() { result = TBuiltinClassObject(Builtin::builtin("KeyError")) } + /** Get the `ClassValue` for the `KeyError` class. */ + ClassValue keyError() { result = TBuiltinClassObject(Builtin::builtin("KeyError")) } - /** Get the `ClassValue` for the `LookupError` class. */ - ClassValue lookupError() { result = TBuiltinClassObject(Builtin::builtin("LookupError")) } + /** Get the `ClassValue` for the `LookupError` class. */ + ClassValue lookupError() { result = TBuiltinClassObject(Builtin::builtin("LookupError")) } - /** Get the `ClassValue` for the `IndexError` class. */ - ClassValue indexError() { result = TBuiltinClassObject(Builtin::builtin("IndexError")) } + /** Get the `ClassValue` for the `IndexError` class. */ + ClassValue indexError() { result = TBuiltinClassObject(Builtin::builtin("IndexError")) } - /** Get the `ClassValue` for the `IOError` class. */ - ClassValue ioError() { result = TBuiltinClassObject(Builtin::builtin("IOError")) } + /** Get the `ClassValue` for the `IOError` class. */ + ClassValue ioError() { result = TBuiltinClassObject(Builtin::builtin("IOError")) } - /** Get the `ClassValue` for the `NotImplementedError` class. */ - ClassValue notImplementedError() { - result = TBuiltinClassObject(Builtin::builtin("NotImplementedError")) - } + /** Get the `ClassValue` for the `NotImplementedError` class. */ + ClassValue notImplementedError() { + result = TBuiltinClassObject(Builtin::builtin("NotImplementedError")) + } - /** Get the `ClassValue` for the `ImportError` class. */ - ClassValue importError() { result = TBuiltinClassObject(Builtin::builtin("ImportError")) } + /** Get the `ClassValue` for the `ImportError` class. */ + ClassValue importError() { result = TBuiltinClassObject(Builtin::builtin("ImportError")) } - /** Get the `ClassValue` for the `UnicodeEncodeError` class. */ - ClassValue unicodeEncodeError() { - result = TBuiltinClassObject(Builtin::builtin("UnicodeEncodeError")) - } + /** Get the `ClassValue` for the `UnicodeEncodeError` class. */ + ClassValue unicodeEncodeError() { + result = TBuiltinClassObject(Builtin::builtin("UnicodeEncodeError")) + } - /** Get the `ClassValue` for the `UnicodeDecodeError` class. */ - ClassValue unicodeDecodeError() { - result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError")) - } + /** Get the `ClassValue` for the `UnicodeDecodeError` class. */ + ClassValue unicodeDecodeError() { + result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError")) + } - /** Get the `ClassValue` for the `SystemExit` class. */ - ClassValue systemExit() { result = TBuiltinClassObject(Builtin::builtin("SystemExit")) } + /** Get the `ClassValue` for the `SystemExit` class. */ + ClassValue systemExit() { result = TBuiltinClassObject(Builtin::builtin("SystemExit")) } } diff --git a/python/ql/src/semmle/python/objects/ObjectInternal.qll b/python/ql/src/semmle/python/objects/ObjectInternal.qll index 21ba4b24211..f70df7f7c57 100644 --- a/python/ql/src/semmle/python/objects/ObjectInternal.qll +++ b/python/ql/src/semmle/python/objects/ObjectInternal.qll @@ -17,571 +17,571 @@ import semmle.python.objects.Sequences import semmle.python.objects.Descriptors class ObjectInternal extends TObject { - /** Gets a textual representation of this element. */ - abstract string toString(); + /** Gets a textual representation of this element. */ + abstract string toString(); - /** - * The boolean value of this object, this may be both - * true and false if the "object" represents a set of possible objects. - */ - abstract boolean booleanValue(); + /** + * The boolean value of this object, this may be both + * true and false if the "object" represents a set of possible objects. + */ + abstract boolean booleanValue(); - /** - * Holds if this object is introduced into the code base at `node` given the `context` - * This means that `node`, in `context`, points-to this object, but the object has not flowed - * there from anywhere else. - * Examples: - * * The object `None` is "introduced" by the keyword "None". - * * A bound method would be "introduced" when relevant attribute on an instance - * is accessed. In `x = X(); x.m` `x.m` introduces the bound method. - */ - abstract predicate introducedAt(ControlFlowNode node, PointsToContext context); + /** + * Holds if this object is introduced into the code base at `node` given the `context` + * This means that `node`, in `context`, points-to this object, but the object has not flowed + * there from anywhere else. + * Examples: + * * The object `None` is "introduced" by the keyword "None". + * * A bound method would be "introduced" when relevant attribute on an instance + * is accessed. In `x = X(); x.m` `x.m` introduces the bound method. + */ + abstract predicate introducedAt(ControlFlowNode node, PointsToContext context); - /** Gets the class declaration for this object, if it is a class with a declaration. */ - abstract ClassDecl getClassDeclaration(); + /** Gets the class declaration for this object, if it is a class with a declaration. */ + abstract ClassDecl getClassDeclaration(); - /** True if this "object" is a class. That is, its class inherits from `type` */ - abstract boolean isClass(); + /** True if this "object" is a class. That is, its class inherits from `type` */ + abstract boolean isClass(); - /** Gets the class of this object. */ - abstract ObjectInternal getClass(); + /** Gets the class of this object. */ + abstract ObjectInternal getClass(); - /** - * True if this "object" can be meaningfully analysed to determine the boolean value of - * equality tests on it. - * For example, `None` or `int` can be, but `int()` or an unknown string cannot. - */ - abstract predicate notTestableForEquality(); + /** + * True if this "object" can be meaningfully analysed to determine the boolean value of + * equality tests on it. + * For example, `None` or `int` can be, but `int()` or an unknown string cannot. + */ + abstract predicate notTestableForEquality(); - /** - * Gets the `Builtin` for this object, if any. - * Objects (except unknown and undefined values) should attempt to return - * exactly one result for either this method or `getOrigin()`. - */ - abstract Builtin getBuiltin(); + /** + * Gets the `Builtin` for this object, if any. + * Objects (except unknown and undefined values) should attempt to return + * exactly one result for either this method or `getOrigin()`. + */ + abstract Builtin getBuiltin(); - /** - * Gets a control flow node that represents the source origin of this - * object, if it has a meaningful location in the source code. - * This method exists primarily for providing backwards compatibility and - * locations for source objects. - * Source code objects should attempt to return exactly one result for this method. - */ - abstract ControlFlowNode getOrigin(); + /** + * Gets a control flow node that represents the source origin of this + * object, if it has a meaningful location in the source code. + * This method exists primarily for providing backwards compatibility and + * locations for source objects. + * Source code objects should attempt to return exactly one result for this method. + */ + abstract ControlFlowNode getOrigin(); - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj`. - * - * This is the context-insensitive version. - * Generally, if this holds for any object `obj` then `callResult/3` should never hold for that object. - */ - abstract predicate callResult(ObjectInternal obj, CfgOrigin origin); + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj`. + * + * This is the context-insensitive version. + * Generally, if this holds for any object `obj` then `callResult/3` should never hold for that object. + */ + abstract predicate callResult(ObjectInternal obj, CfgOrigin origin); - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj` with callee context `callee`. - * - * This is the context-sensitive version. - * Generally, if this holds for any object `obj` then `callResult/2` should never hold for that object. - */ - abstract predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin); + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj` with callee context `callee`. + * + * This is the context-sensitive version. + * Generally, if this holds for any object `obj` then `callResult/2` should never hold for that object. + */ + abstract predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin); - /** - * The integer value of things that have integer values and whose integer value is - * tracked. - * That is, some ints, mainly small numbers, and bools. - */ - abstract int intValue(); + /** + * The integer value of things that have integer values and whose integer value is + * tracked. + * That is, some ints, mainly small numbers, and bools. + */ + abstract int intValue(); - /** - * The string value of things that have string values. - * That is, strings. - */ - abstract string strValue(); + /** + * The string value of things that have string values. + * That is, strings. + */ + abstract string strValue(); - /** - * Holds if the function `scope` is called when this object is called and `paramOffset` - * is the difference from the parameter position and the argument position. - * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. - * Used by points-to to help determine flow from arguments to parameters. - */ - abstract predicate calleeAndOffset(Function scope, int paramOffset); + /** + * Holds if the function `scope` is called when this object is called and `paramOffset` + * is the difference from the parameter position and the argument position. + * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. + * Used by points-to to help determine flow from arguments to parameters. + */ + abstract predicate calleeAndOffset(Function scope, int paramOffset); - final predicate isBuiltin() { exists(this.getBuiltin()) } + final predicate isBuiltin() { exists(this.getBuiltin()) } - /** - * Holds if the result of getting the attribute `name` is `value` and that `value` comes - * from `origin`. Note this is *not* the same as class lookup. For example - * for an object `x` the attribute `name` (`x.name`) may refer to a bound-method, an attribute of the - * instance, or an attribute of the class. - */ - pragma[nomagic] - abstract predicate attribute(string name, ObjectInternal value, CfgOrigin origin); + /** + * Holds if the result of getting the attribute `name` is `value` and that `value` comes + * from `origin`. Note this is *not* the same as class lookup. For example + * for an object `x` the attribute `name` (`x.name`) may refer to a bound-method, an attribute of the + * instance, or an attribute of the class. + */ + pragma[nomagic] + abstract predicate attribute(string name, ObjectInternal value, CfgOrigin origin); - /** Holds if the attributes of this object are wholly or partly unknowable */ - abstract predicate attributesUnknown(); + /** Holds if the attributes of this object are wholly or partly unknowable */ + abstract predicate attributesUnknown(); - /** Holds if the result of subscripting this object are wholly or partly unknowable */ - abstract predicate subscriptUnknown(); + /** Holds if the result of subscripting this object are wholly or partly unknowable */ + abstract predicate subscriptUnknown(); - /** - * For backwards compatibility shim -- Not all objects have a "source". - * Objects (except unknown and undefined values) should attempt to return - * exactly one result for this method. - */ - @py_object getSource() { - result = this.getOrigin() - or - result = this.getBuiltin() - } + /** + * For backwards compatibility shim -- Not all objects have a "source". + * Objects (except unknown and undefined values) should attempt to return + * exactly one result for this method. + */ + @py_object getSource() { + result = this.getOrigin() + or + result = this.getBuiltin() + } - /** - * Holds if this object is a descriptor. - * Holds, for example, for functions and properties and not for integers. - */ - abstract boolean isDescriptor(); + /** + * Holds if this object is a descriptor. + * Holds, for example, for functions and properties and not for integers. + */ + abstract boolean isDescriptor(); - /** - * Holds if the result of attribute access on the class holding this descriptor is `value`, originating at `origin` - * For example, although `T.__dict__['name'] = classmethod(f)`, `T.name` is a bound-method, binding `f` and `T` - */ - pragma[nomagic] - abstract predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin); + /** + * Holds if the result of attribute access on the class holding this descriptor is `value`, originating at `origin` + * For example, although `T.__dict__['name'] = classmethod(f)`, `T.name` is a bound-method, binding `f` and `T` + */ + pragma[nomagic] + abstract predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin); - /** - * Holds if the result of attribute access on an instance of a class holding this descriptor is `value`, originating at `origin` - * For example, with `T.__dict__['name'] = classmethod(f)`, `T().name` is a bound-method, binding `f` and `T` - */ - pragma[nomagic] - abstract predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ); + /** + * Holds if the result of attribute access on an instance of a class holding this descriptor is `value`, originating at `origin` + * For example, with `T.__dict__['name'] = classmethod(f)`, `T().name` is a bound-method, binding `f` and `T` + */ + pragma[nomagic] + abstract predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ); - /** - * Holds if attribute lookup on this object may "bind" `instance` to `descriptor`. - * Here "bind" means that `instance` is passed to the `descriptor.__get__()` method - * at runtime. The term "bind" is used as this most likely results in a bound-method. - */ - abstract predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor); + /** + * Holds if attribute lookup on this object may "bind" `instance` to `descriptor`. + * Here "bind" means that `instance` is passed to the `descriptor.__get__()` method + * at runtime. The term "bind" is used as this most likely results in a bound-method. + */ + abstract predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor); - /** - * Gets the length of the sequence that this "object" represents. - * Always returns a value for a sequence, will be -1 if the object has no fixed length. - */ - abstract int length(); + /** + * Gets the length of the sequence that this "object" represents. + * Always returns a value for a sequence, will be -1 if the object has no fixed length. + */ + abstract int length(); - /** - * Holds if the object `function` is called when this object is called and `paramOffset` - * is the difference from the parameter position and the argument position. - * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. - * This is used to implement the `CallableValue` public API. - */ - predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } + /** + * Holds if the object `function` is called when this object is called and `paramOffset` + * is the difference from the parameter position and the argument position. + * For a normal function `paramOffset` is 0. For classes and bound-methods it is 1. + * This is used to implement the `CallableValue` public API. + */ + predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } - /** - * Holds if this 'object' represents an entity that should be exposed to the legacy points_to API - * This should hold for almost all objects that do not have an underlying DB object representing their source, - * for example `super` objects and bound-method. This should not hold for objects that are inferred to exists by - * an import statements or the like, but which aren't in the database. - */ - /* This predicate can be removed when the legacy points_to API is removed. */ - abstract predicate useOriginAsLegacyObject(); + /** + * Holds if this 'object' represents an entity that should be exposed to the legacy points_to API + * This should hold for almost all objects that do not have an underlying DB object representing their source, + * for example `super` objects and bound-method. This should not hold for objects that are inferred to exists by + * an import statements or the like, but which aren't in the database. + */ + /* This predicate can be removed when the legacy points_to API is removed. */ + abstract predicate useOriginAsLegacyObject(); - /** - * Gets the name of this of this object if it has a meaningful name. - * Note that the name of an object is not necessarily the name by which it is called - * For example the function named `posixpath.join` will be called `os.path.join`. - */ - abstract string getName(); + /** + * Gets the name of this of this object if it has a meaningful name. + * Note that the name of an object is not necessarily the name by which it is called + * For example the function named `posixpath.join` will be called `os.path.join`. + */ + abstract string getName(); - abstract predicate contextSensitiveCallee(); + abstract predicate contextSensitiveCallee(); - /** - * Gets the 'object' resulting from iterating over this object. - * Used in the context `for i in this:`. The result is the 'object' - * assigned to `i`. - */ - abstract ObjectInternal getIterNext(); + /** + * Gets the 'object' resulting from iterating over this object. + * Used in the context `for i in this:`. The result is the 'object' + * assigned to `i`. + */ + abstract ObjectInternal getIterNext(); - /** Holds if this value has the attribute `name` */ - predicate hasAttribute(string name) { this.(ObjectInternal).attribute(name, _, _) } + /** Holds if this value has the attribute `name` */ + predicate hasAttribute(string name) { this.(ObjectInternal).attribute(name, _, _) } - abstract predicate isNotSubscriptedType(); + abstract predicate isNotSubscriptedType(); } class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject { - override Builtin getBuiltin() { this = TBuiltinOpaqueObject(result) } + override Builtin getBuiltin() { this = TBuiltinOpaqueObject(result) } - override string toString() { result = this.getBuiltin().getClass().getName() + " object" } + override string toString() { result = this.getBuiltin().getClass().getName() + " object" } - override boolean booleanValue() { - // TO DO ... Depends on class. `result = this.getClass().instancesBooleanValue()` - result = maybe() - } + override boolean booleanValue() { + // TO DO ... Depends on class. `result = this.getClass().instancesBooleanValue()` + result = maybe() + } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } + override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { - value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and - origin = CfgOrigin::unknown() - } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { + value = ObjectInternal::fromBuiltin(this.getBuiltin().getMember(name)) and + origin = CfgOrigin::unknown() + } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { exists(this.getBuiltin().getItem(_)) } + override predicate subscriptUnknown() { exists(this.getBuiltin().getItem(_)) } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override string getName() { result = this.getBuiltin().getName() } + override string getName() { result = this.getBuiltin().getName() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } class UnknownInternal extends ObjectInternal, TUnknown { - override string toString() { result = "Unknown value" } + override string toString() { result = "Unknown value" } - override boolean booleanValue() { result = maybe() } + override boolean booleanValue() { result = maybe() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = TUnknownClass() } + override ObjectInternal getClass() { result = TUnknownClass() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override Builtin getBuiltin() { result = Builtin::unknown() } + override Builtin getBuiltin() { result = Builtin::unknown() } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { any() } + pragma[noinline] + override predicate attributesUnknown() { any() } - override predicate subscriptUnknown() { any() } + override predicate subscriptUnknown() { any() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { result = -1 } + override int length() { result = -1 } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } + override ObjectInternal getIterNext() { result = ObjectInternal::unknown() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } class UndefinedInternal extends ObjectInternal, TUndefined { - override string toString() { result = "Undefined variable" } + override string toString() { result = "Undefined variable" } - override boolean booleanValue() { none() } + override boolean booleanValue() { none() } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override predicate notTestableForEquality() { any() } + override predicate notTestableForEquality() { any() } - override ObjectInternal getClass() { none() } + override ObjectInternal getClass() { none() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - // Accessing an undefined value raises a NameError, but if during import it probably - // means that we missed an import. - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + // Accessing an undefined value raises a NameError, but if during import it probably + // means that we missed an import. + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { none() } + override boolean isDescriptor() { none() } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override string getName() { none() } + override string getName() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - /** - * Holds if this object requires context to determine the object resulting from a call to it. - * True for most callables. - */ - override predicate contextSensitiveCallee() { none() } + /** + * Holds if this object requires context to determine the object resulting from a call to it. + * True for most callables. + */ + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { none() } + override ObjectInternal getIterNext() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } module ObjectInternal { - ObjectInternal bool(boolean b) { - b = true and result = TTrue() - or - b = false and result = TFalse() - } + ObjectInternal bool(boolean b) { + b = true and result = TTrue() + or + b = false and result = TFalse() + } - ObjectInternal none_() { result = TNone() } + ObjectInternal none_() { result = TNone() } - ObjectInternal unknown() { result = TUnknown() } + ObjectInternal unknown() { result = TUnknown() } - ClassObjectInternal unknownClass() { result = TUnknownClass() } + ClassObjectInternal unknownClass() { result = TUnknownClass() } - ObjectInternal undefined() { result = TUndefined() } + ObjectInternal undefined() { result = TUndefined() } - ObjectInternal builtin(string name) { - result = TBuiltinClassObject(Builtin::builtin(name)) - or - result = TBuiltinFunctionObject(Builtin::builtin(name)) - or - result = TBuiltinOpaqueObject(Builtin::builtin(name)) - or - name = "type" and result = TType() - } + ObjectInternal builtin(string name) { + result = TBuiltinClassObject(Builtin::builtin(name)) + or + result = TBuiltinFunctionObject(Builtin::builtin(name)) + or + result = TBuiltinOpaqueObject(Builtin::builtin(name)) + or + name = "type" and result = TType() + } - ObjectInternal sysModules() { - result = TBuiltinOpaqueObject(Builtin::special("sys").getMember("modules")) - } + ObjectInternal sysModules() { + result = TBuiltinOpaqueObject(Builtin::special("sys").getMember("modules")) + } - ObjectInternal fromInt(int n) { result = TInt(n) } + ObjectInternal fromInt(int n) { result = TInt(n) } - ObjectInternal fromBuiltin(Builtin b) { - b = result.getBuiltin() and - not b = Builtin::unknown() and - not b = Builtin::unknownType() and - not b = Builtin::special("sys").getMember("version_info") - or - b = Builtin::special("sys").getMember("version_info") and result = TSysVersionInfo() - } + ObjectInternal fromBuiltin(Builtin b) { + b = result.getBuiltin() and + not b = Builtin::unknown() and + not b = Builtin::unknownType() and + not b = Builtin::special("sys").getMember("version_info") + or + b = Builtin::special("sys").getMember("version_info") and result = TSysVersionInfo() + } - ObjectInternal classMethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } + ObjectInternal classMethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } - ObjectInternal staticMethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } + ObjectInternal staticMethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } - ObjectInternal boundMethod() { result = TBuiltinClassObject(Builtin::special("MethodType")) } + ObjectInternal boundMethod() { result = TBuiltinClassObject(Builtin::special("MethodType")) } - ObjectInternal moduleType() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } + ObjectInternal moduleType() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } - ObjectInternal noneType() { result = TBuiltinClassObject(Builtin::special("NoneType")) } + ObjectInternal noneType() { result = TBuiltinClassObject(Builtin::special("NoneType")) } - ObjectInternal type() { result = TType() } + ObjectInternal type() { result = TType() } - ObjectInternal property() { result = TBuiltinClassObject(Builtin::special("property")) } + ObjectInternal property() { result = TBuiltinClassObject(Builtin::special("property")) } - ObjectInternal superType() { result = TBuiltinClassObject(Builtin::special("super")) } + ObjectInternal superType() { result = TBuiltinClassObject(Builtin::special("super")) } - /** The old-style class type (Python 2 only) */ - ObjectInternal classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } + /** The old-style class type (Python 2 only) */ + ObjectInternal classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } - ObjectInternal emptyTuple() { result.(BuiltinTupleObjectInternal).length() = 0 } + ObjectInternal emptyTuple() { result.(BuiltinTupleObjectInternal).length() = 0 } } class DecoratedFunction extends ObjectInternal, TDecoratedFunction { - CallNode getDecoratorCall() { this = TDecoratedFunction(result) } + CallNode getDecoratorCall() { this = TDecoratedFunction(result) } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - private ObjectInternal decoratedObject() { - PointsTo::pointsTo(this.getDecoratorCall().getArg(0), _, result, _) - } + private ObjectInternal decoratedObject() { + PointsTo::pointsTo(this.getDecoratorCall().getArg(0), _, result, _) + } - override string getName() { result = this.decoratedObject().getName() } + override string getName() { result = this.decoratedObject().getName() } - override string toString() { - result = "Decorated " + this.decoratedObject().toString() - or - not exists(this.decoratedObject()) and result = "Decorated function" - } + override string toString() { + result = "Decorated " + this.decoratedObject().toString() + or + not exists(this.decoratedObject()) and result = "Decorated function" + } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } - override ClassDecl getClassDeclaration() { none() } + override ClassDecl getClassDeclaration() { none() } - override boolean isClass() { result = false } + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = TUnknownClass() } + override ObjectInternal getClass() { result = TUnknownClass() } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { - obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() - } + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { + obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() + } - override ControlFlowNode getOrigin() { result = this.getDecoratorCall() } + override ControlFlowNode getOrigin() { result = this.getDecoratorCall() } - override int intValue() { none() } + override int intValue() { none() } - override string strValue() { none() } + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override int length() { none() } + override int length() { none() } - override ObjectInternal getIterNext() { none() } + override ObjectInternal getIterNext() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** Helper for boolean predicates returning both `true` and `false` */ @@ -590,5 +590,5 @@ boolean maybe() { result = true or result = false } /** Helper for attributes */ pragma[nomagic] predicate receiver_type(AttrNode attr, string name, ObjectInternal value, ClassObjectInternal cls) { - PointsToInternal::pointsTo(attr.getObject(name), _, value, _) and value.getClass() = cls + PointsToInternal::pointsTo(attr.getObject(name), _, value, _) and value.getClass() = cls } diff --git a/python/ql/src/semmle/python/objects/Sequences.qll b/python/ql/src/semmle/python/objects/Sequences.qll index 1259aadc07b..c1aba84b137 100644 --- a/python/ql/src/semmle/python/objects/Sequences.qll +++ b/python/ql/src/semmle/python/objects/Sequences.qll @@ -7,187 +7,187 @@ private import semmle.python.pointsto.MRO private import semmle.python.types.Builtins abstract class SequenceObjectInternal extends ObjectInternal { - /** Gets the `n`th item of this sequence, if one exists. */ - abstract ObjectInternal getItem(int n); + /** Gets the `n`th item of this sequence, if one exists. */ + abstract ObjectInternal getItem(int n); - override boolean booleanValue() { - this.length() = 0 and result = false - or - this.length() != 0 and result = true - } + override boolean booleanValue() { + this.length() = 0 and result = false + or + this.length() != 0 and result = true + } - override boolean isDescriptor() { result = false } + override boolean isDescriptor() { result = false } - pragma[noinline] - override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { - none() - } + pragma[noinline] + override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { + none() + } - pragma[noinline] - override predicate descriptorGetInstance( - ObjectInternal instance, ObjectInternal value, CfgOrigin origin - ) { - none() - } + pragma[noinline] + override predicate descriptorGetInstance( + ObjectInternal instance, ObjectInternal value, CfgOrigin origin + ) { + none() + } - pragma[noinline] - override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { - none() - } + pragma[noinline] + override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { + none() + } - override string getName() { none() } + override string getName() { none() } - override predicate contextSensitiveCallee() { none() } + override predicate contextSensitiveCallee() { none() } - override ObjectInternal getIterNext() { result = this.getItem(_) } + override ObjectInternal getIterNext() { result = this.getItem(_) } } abstract class TupleObjectInternal extends SequenceObjectInternal { - override string toString() { result = "(" + this.contents(0) + ")" } + override string toString() { result = "(" + this.contents(0) + ")" } - private string contents(int n) { - n < 4 and n = this.length() and result = "" - or - n = 3 and this.length() > 3 and result = (this.length() - 3).toString() + " more..." - or - result = this.item(n) + ", " + this.contents(n + 1) - } + private string contents(int n) { + n < 4 and n = this.length() and result = "" + or + n = 3 and this.length() > 3 and result = (this.length() - 3).toString() + " more..." + or + result = this.item(n) + ", " + this.contents(n + 1) + } - private string item(int n) { - exists(ObjectInternal item | item = this.getItem(n) | - // To avoid infinite recursion, nested tuples are replaced with the string "...". - if item instanceof TupleObjectInternal then result = "(...)" else result = item.toString() - ) - or - n in [0 .. this.length() - 1] and - not exists(this.getItem(n)) and - result = "?" - } + private string item(int n) { + exists(ObjectInternal item | item = this.getItem(n) | + // To avoid infinite recursion, nested tuples are replaced with the string "...". + if item instanceof TupleObjectInternal then result = "(...)" else result = item.toString() + ) + or + n in [0 .. this.length() - 1] and + not exists(this.getItem(n)) and + result = "?" + } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { none() } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { none() } - /** True if this "object" is a class. */ - override boolean isClass() { result = false } + /** True if this "object" is a class. */ + override boolean isClass() { result = false } - override ObjectInternal getClass() { result = ObjectInternal::builtin("tuple") } + override ObjectInternal getClass() { result = ObjectInternal::builtin("tuple") } - /** - * True if this "object" can be meaningfully analysed for - * truth or false in comparisons. For example, `None` or `int` can be, but `int()` - * or an unknown string cannot. - */ - override predicate notTestableForEquality() { none() } + /** + * True if this "object" can be meaningfully analysed for + * truth or false in comparisons. For example, `None` or `int` can be, but `int()` + * or an unknown string cannot. + */ + override predicate notTestableForEquality() { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj`. - */ - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj`. + */ + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj` with callee context `callee`. - */ - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj` with callee context `callee`. + */ + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - /** - * The integer value of things that have integer values. - * That is, ints and bools. - */ - override int intValue() { none() } + /** + * The integer value of things that have integer values. + * That is, ints and bools. + */ + override int intValue() { none() } - /** - * The integer value of things that have integer values. - * That is, strings. - */ - override string strValue() { none() } + /** + * The integer value of things that have integer values. + * That is, strings. + */ + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - pragma[noinline] - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + pragma[noinline] + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - pragma[noinline] - override predicate attributesUnknown() { none() } + pragma[noinline] + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } } /** A tuple built-in to the interpreter, including the empty tuple. */ class BuiltinTupleObjectInternal extends TBuiltinTuple, TupleObjectInternal { - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override Builtin getBuiltin() { this = TBuiltinTuple(result) } + override Builtin getBuiltin() { this = TBuiltinTuple(result) } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override ObjectInternal getItem(int n) { result.getBuiltin() = this.getBuiltin().getItem(n) } + override ObjectInternal getItem(int n) { result.getBuiltin() = this.getBuiltin().getItem(n) } - override int length() { - exists(Builtin b | - b = this.getBuiltin() and - result = count(int n | exists(b.getItem(n))) - ) - } + override int length() { + exists(Builtin b | + b = this.getBuiltin() and + result = count(int n | exists(b.getItem(n))) + ) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A tuple declared by a tuple expression in the Python source code */ class PythonTupleObjectInternal extends TPythonTuple, TupleObjectInternal { - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { - this = TPythonTuple(node, context) - } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { + this = TPythonTuple(node, context) + } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { this = TPythonTuple(result, _) } + override ControlFlowNode getOrigin() { this = TPythonTuple(result, _) } - override ObjectInternal getItem(int n) { - exists(TupleNode t, PointsToContext context | - this = TPythonTuple(t, context) and - PointsToInternal::pointsTo(t.getElement(n), context, result, _) - ) - } + override ObjectInternal getItem(int n) { + exists(TupleNode t, PointsToContext context | + this = TPythonTuple(t, context) and + PointsToInternal::pointsTo(t.getElement(n), context, result, _) + ) + } - override int length() { - exists(TupleNode t | - this = TPythonTuple(t, _) and - result = count(int n | exists(t.getElement(n))) - ) - } + override int length() { + exists(TupleNode t | + this = TPythonTuple(t, _) and + result = count(int n | exists(t.getElement(n))) + ) + } - override predicate useOriginAsLegacyObject() { none() } + override predicate useOriginAsLegacyObject() { none() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** A tuple created by a `*` parameter */ class VarargsTupleObjectInternal extends TVarargsTuple, TupleObjectInternal { - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - override Builtin getBuiltin() { none() } + override Builtin getBuiltin() { none() } - override ControlFlowNode getOrigin() { none() } + override ControlFlowNode getOrigin() { none() } - override ObjectInternal getItem(int n) { - exists(CallNode call, PointsToContext context, int offset, int length | - this = TVarargsTuple(call, context, offset, length) and - n < length and - InterProceduralPointsTo::positional_argument_points_to(call, offset + n, context, result, _) - ) - } + override ObjectInternal getItem(int n) { + exists(CallNode call, PointsToContext context, int offset, int length | + this = TVarargsTuple(call, context, offset, length) and + n < length and + InterProceduralPointsTo::positional_argument_points_to(call, offset + n, context, result, _) + ) + } - override int length() { this = TVarargsTuple(_, _, _, result) } + override int length() { this = TVarargsTuple(_, _, _, result) } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } /** @@ -195,84 +195,84 @@ class VarargsTupleObjectInternal extends TVarargsTuple, TupleObjectInternal { * false positives when we are unsure of the actual version of Python that the code is expecting. */ class SysVersionInfoObjectInternal extends TSysVersionInfo, SequenceObjectInternal { - override string toString() { result = "sys.version_info" } + override string toString() { result = "sys.version_info" } - override ObjectInternal getItem(int n) { - n = 0 and result = TInt(major_version()) - or - n = 1 and result = TInt(minor_version()) - } + override ObjectInternal getItem(int n) { + n = 0 and result = TInt(major_version()) + or + n = 1 and result = TInt(minor_version()) + } - override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } + override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() } - /** Gets the class declaration for this object, if it is a declared class. */ - override ClassDecl getClassDeclaration() { - result = Builtin::special("sys").getMember("version_info").getClass() - } + /** Gets the class declaration for this object, if it is a declared class. */ + override ClassDecl getClassDeclaration() { + result = Builtin::special("sys").getMember("version_info").getClass() + } - /** True if this "object" is a class. */ - override boolean isClass() { result = false } + /** True if this "object" is a class. */ + override boolean isClass() { result = false } - override ObjectInternal getClass() { result.getBuiltin() = this.getClassDeclaration() } + override ObjectInternal getClass() { result.getBuiltin() = this.getClassDeclaration() } - override predicate notTestableForEquality() { none() } + override predicate notTestableForEquality() { none() } - /** - * Gets the `Builtin` for this object, if any. - * Objects (except unknown and undefined values) should attempt to return - * exactly one result for either this method or `getOrigin()`. - */ - override Builtin getBuiltin() { none() } + /** + * Gets the `Builtin` for this object, if any. + * Objects (except unknown and undefined values) should attempt to return + * exactly one result for either this method or `getOrigin()`. + */ + override Builtin getBuiltin() { none() } - /** - * Gets a control flow node that represents the source origin of this - * objects. - */ - override ControlFlowNode getOrigin() { none() } + /** + * Gets a control flow node that represents the source origin of this + * objects. + */ + override ControlFlowNode getOrigin() { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj`. - */ - override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj`. + */ + override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() } - /** - * Holds if `obj` is the result of calling `this` and `origin` is - * the origin of `obj` with callee context `callee`. - */ - override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { - none() - } + /** + * Holds if `obj` is the result of calling `this` and `origin` is + * the origin of `obj` with callee context `callee`. + */ + override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { + none() + } - /** - * The integer value of things that have integer values. - * That is, ints and bools. - */ - override int intValue() { none() } + /** + * The integer value of things that have integer values. + * That is, ints and bools. + */ + override int intValue() { none() } - /** - * The integer value of things that have integer values. - * That is, strings. - */ - override string strValue() { none() } + /** + * The integer value of things that have integer values. + * That is, strings. + */ + override string strValue() { none() } - override predicate calleeAndOffset(Function scope, int paramOffset) { none() } + override predicate calleeAndOffset(Function scope, int paramOffset) { none() } - override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } + override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() } - override predicate attributesUnknown() { none() } + override predicate attributesUnknown() { none() } - override predicate subscriptUnknown() { none() } + override predicate subscriptUnknown() { none() } - /** - * Gets the length of the sequence that this "object" represents. - * Always returns a value for a sequence, will be -1 if object has no fixed length. - */ - override int length() { result = 5 } + /** + * Gets the length of the sequence that this "object" represents. + * Always returns a value for a sequence, will be -1 if object has no fixed length. + */ + override int length() { result = 5 } - override predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } + override predicate functionAndOffset(CallableObjectInternal function, int offset) { none() } - override predicate useOriginAsLegacyObject() { any() } + override predicate useOriginAsLegacyObject() { any() } - override predicate isNotSubscriptedType() { any() } + override predicate isNotSubscriptedType() { any() } } diff --git a/python/ql/src/semmle/python/objects/TObject.qll b/python/ql/src/semmle/python/objects/TObject.qll index 8d24b9d85d2..511bde44995 100644 --- a/python/ql/src/semmle/python/objects/TObject.qll +++ b/python/ql/src/semmle/python/objects/TObject.qll @@ -12,427 +12,426 @@ private import semmle.python.pointsto.PointsToContext */ cached newtype TObject = - /** Builtin class objects */ - TBuiltinClassObject(Builtin bltn) { - bltn.isClass() and - not bltn = Builtin::unknownType() and - not bltn = Builtin::special("type") - } or - /** Builtin function objects (module members) */ - TBuiltinFunctionObject(Builtin bltn) { bltn.isFunction() } or - /** Builtin method objects (class members) */ - TBuiltinMethodObject(Builtin bltn) { bltn.isMethod() } or - /** Builtin module objects */ - TBuiltinModuleObject(Builtin bltn) { bltn.isModule() } or - /** Other builtin objects from the interpreter */ - TBuiltinOpaqueObject(Builtin bltn) { - not bltn.isClass() and - not bltn.isFunction() and - not bltn.isMethod() and - not bltn.isModule() and - not bltn.getClass() = Builtin::special("tuple") and - not exists(bltn.intValue()) and - not exists(bltn.floatValue()) and - not exists(bltn.strValue()) and - not py_special_objects(bltn, _) - } or - /** Python function objects (including lambdas) */ - TPythonFunctionObject(ControlFlowNode callable) { callable.getNode() instanceof CallableExpr } or - /** Python class objects */ - TPythonClassObject(ControlFlowNode classexpr) { classexpr.getNode() instanceof ClassExpr } or - /** Package objects */ - TPackageObject(Folder f) { isPreferredModuleForName(f, _) } or - /** Python module objects */ - TPythonModule(Module m) { - not m.isPackage() and - isPreferredModuleForName(m.getFile(), _) and - not exists(SyntaxError se | se.getFile() = m.getFile()) - } or - /** `True` */ - TTrue() or - /** `False` */ - TFalse() or - /** `None` */ - TNone() or - /** Represents any value about which nothing useful is known */ - TUnknown() or - /** Represents any value known to be a class, but not known to be any specific class */ - TUnknownClass() or - /** Represents the absence of a value. Used by points-to for tracking undefined variables */ - TUndefined() or - /** The integer `n` */ - TInt(int n) { - // Powers of 2 are used for flags - is_power_2(n) - or - // And all combinations of flags up to 2^8 - n in [0 .. 511] - or - // Any number explicitly mentioned in the source code. - exists(IntegerLiteral num | - n = num.getValue() - or - exists(UnaryExpr neg | neg.getOp() instanceof USub and neg.getOperand() = num) and - n = -num.getN().toInt() - ) - or - n = any(Builtin b).intValue() - } or - /** The float `f` */ - TFloat(float f) { f = any(FloatLiteral num).getValue() } or - /** The unicode string `s` */ - TUnicode(string s) { - // Any string explicitly mentioned in the source code. - exists(StrConst str | - s = str.getText() and - str.isUnicode() - ) - or - // Any string from the library put in the DB by the extractor. - exists(Builtin b | - s = b.strValue() and - b.getClass() = Builtin::special("unicode") - ) - or - s = "__main__" - } or - /** The byte string `s` */ - TBytes(string s) { - // Any string explicitly mentioned in the source code. - exists(StrConst str | - s = str.getText() and - not str.isUnicode() - ) - or - // Any string from the library put in the DB by the extractor. - exists(Builtin b | - s = b.strValue() and - b.getClass() = Builtin::special("bytes") - ) - or - s = "__main__" - } or - /** An instance of `cls`, instantiated at `instantiation` given the `context`. */ - TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) { - PointsToInternal::pointsTo(instantiation.(CallNode).getFunction(), context, cls, _) and - cls.isSpecial() = false - or - literal_instantiation(instantiation, cls, context) - } or - /** A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */ - TSelfInstance(ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls) { - self_parameter(def, context, cls) - } or - /** A bound method */ - TBoundMethod(ObjectInternal self, CallableObjectInternal function) { - any(ObjectInternal obj).binds(self, _, function) and - function.isDescriptor() = true - } or - /** Represents any value whose class is known, but nothing else */ - TUnknownInstance(BuiltinClassObjectInternal cls) { - cls != ObjectInternal::superType() and - cls != ObjectInternal::builtin("bool") and - cls != ObjectInternal::noneType() - } or - /** Represents an instance of `super` */ - TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) { - super_instantiation(_, self, startclass, _) - } or - /** Represents an instance of `classmethod` */ - TClassMethod(CallNode instantiation, CallableObjectInternal function) { - class_method(instantiation, function, _) - } or - /** Represents an instance of `staticmethod` */ - TStaticMethod(CallNode instantiation, CallableObjectInternal function) { - static_method(instantiation, function, _) - } or - /** Represents a builtin tuple */ - TBuiltinTuple(Builtin bltn) { bltn.getClass() = Builtin::special("tuple") } or - /** Represents a tuple in the Python source */ - TPythonTuple(TupleNode origin, PointsToContext context) { - origin.isLoad() and - context.appliesTo(origin) - } or - /** Varargs tuple */ - TVarargsTuple(CallNode call, PointsToContext context, int offset, int length) { - InterProceduralPointsTo::varargs_tuple(call, context, _, _, offset, length) - } or - /** `type` */ - TType() or - /** Represents an instance of `property` */ - TProperty(CallNode call, Context ctx, CallableObjectInternal getter) { - PointsToInternal::pointsTo(call.getFunction(), ctx, ObjectInternal::property(), _) and - PointsToInternal::pointsTo(call.getArg(0), ctx, getter, _) - } or - /** Represents the `setter` or `deleter` method of a property object. */ - TPropertySetterOrDeleter(PropertyInternal property, string method) { - exists(AttrNode attr | PointsToInternal::pointsTo(attr.getObject(method), _, property, _)) and - (method = "setter" or method = "deleter") - } or - /** Represents a dynamically created class */ - TDynamicClass(CallNode instantiation, ClassObjectInternal metacls, PointsToContext context) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, metacls, _) and - not count(instantiation.getAnArg()) = 1 and - Types::getMro(metacls).contains(TType()) - } or - /** Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */ - TSysVersionInfo() or - /** Represents a module that is inferred to perhaps exist, but is not present in the database. */ - TAbsentModule(string name) { missing_imported_module(_, _, name) } or - /** Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */ - TAbsentModuleAttribute(AbsentModuleObjectInternal mod, string attrname) { - ( - PointsToInternal::pointsTo(any(AttrNode attr).getObject(attrname), _, mod, _) - or - PointsToInternal::pointsTo(any(ImportMemberNode imp).getModule(attrname), _, mod, _) - ) and - exists(string modname | - modname = mod.getName() and - not common_module_name(modname + "." + attrname) - ) - } or - /** Opaque object representing the result of calling a decorator on a function that we don't understand */ - TDecoratedFunction(CallNode call) { call.isFunctionDecoratorCall() } or - /** Represents a subscript operation applied to a type. For type-hint analysis */ - TSubscriptedType(ObjectInternal generic, ObjectInternal index) { - isType(generic) and - generic.isNotSubscriptedType() and - index.isNotSubscriptedType() and - Expressions::subscriptPartsPointsTo(_, _, generic, index) - } + /** Builtin class objects */ + TBuiltinClassObject(Builtin bltn) { + bltn.isClass() and + not bltn = Builtin::unknownType() and + not bltn = Builtin::special("type") + } or + /** Builtin function objects (module members) */ + TBuiltinFunctionObject(Builtin bltn) { bltn.isFunction() } or + /** Builtin method objects (class members) */ + TBuiltinMethodObject(Builtin bltn) { bltn.isMethod() } or + /** Builtin module objects */ + TBuiltinModuleObject(Builtin bltn) { bltn.isModule() } or + /** Other builtin objects from the interpreter */ + TBuiltinOpaqueObject(Builtin bltn) { + not bltn.isClass() and + not bltn.isFunction() and + not bltn.isMethod() and + not bltn.isModule() and + not bltn.getClass() = Builtin::special("tuple") and + not exists(bltn.intValue()) and + not exists(bltn.floatValue()) and + not exists(bltn.strValue()) and + not py_special_objects(bltn, _) + } or + /** Python function objects (including lambdas) */ + TPythonFunctionObject(ControlFlowNode callable) { callable.getNode() instanceof CallableExpr } or + /** Python class objects */ + TPythonClassObject(ControlFlowNode classexpr) { classexpr.getNode() instanceof ClassExpr } or + /** Package objects */ + TPackageObject(Folder f) { isPreferredModuleForName(f, _) } or + /** Python module objects */ + TPythonModule(Module m) { + not m.isPackage() and + isPreferredModuleForName(m.getFile(), _) and + not exists(SyntaxError se | se.getFile() = m.getFile()) + } or + /** `True` */ + TTrue() or + /** `False` */ + TFalse() or + /** `None` */ + TNone() or + /** Represents any value about which nothing useful is known */ + TUnknown() or + /** Represents any value known to be a class, but not known to be any specific class */ + TUnknownClass() or + /** Represents the absence of a value. Used by points-to for tracking undefined variables */ + TUndefined() or + /** The integer `n` */ + TInt(int n) { + // Powers of 2 are used for flags + is_power_2(n) + or + // And all combinations of flags up to 2^8 + n in [0 .. 511] + or + // Any number explicitly mentioned in the source code. + exists(IntegerLiteral num | + n = num.getValue() + or + exists(UnaryExpr neg | neg.getOp() instanceof USub and neg.getOperand() = num) and + n = -num.getN().toInt() + ) + or + n = any(Builtin b).intValue() + } or + /** The float `f` */ + TFloat(float f) { f = any(FloatLiteral num).getValue() } or + /** The unicode string `s` */ + TUnicode(string s) { + // Any string explicitly mentioned in the source code. + exists(StrConst str | + s = str.getText() and + str.isUnicode() + ) + or + // Any string from the library put in the DB by the extractor. + exists(Builtin b | + s = b.strValue() and + b.getClass() = Builtin::special("unicode") + ) + or + s = "__main__" + } or + /** The byte string `s` */ + TBytes(string s) { + // Any string explicitly mentioned in the source code. + exists(StrConst str | + s = str.getText() and + not str.isUnicode() + ) + or + // Any string from the library put in the DB by the extractor. + exists(Builtin b | + s = b.strValue() and + b.getClass() = Builtin::special("bytes") + ) + or + s = "__main__" + } or + /** An instance of `cls`, instantiated at `instantiation` given the `context`. */ + TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) { + PointsToInternal::pointsTo(instantiation.(CallNode).getFunction(), context, cls, _) and + cls.isSpecial() = false + or + literal_instantiation(instantiation, cls, context) + } or + /** A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */ + TSelfInstance(ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls) { + self_parameter(def, context, cls) + } or + /** A bound method */ + TBoundMethod(ObjectInternal self, CallableObjectInternal function) { + any(ObjectInternal obj).binds(self, _, function) and + function.isDescriptor() = true + } or + /** Represents any value whose class is known, but nothing else */ + TUnknownInstance(BuiltinClassObjectInternal cls) { + cls != ObjectInternal::superType() and + cls != ObjectInternal::builtin("bool") and + cls != ObjectInternal::noneType() + } or + /** Represents an instance of `super` */ + TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) { + super_instantiation(_, self, startclass, _) + } or + /** Represents an instance of `classmethod` */ + TClassMethod(CallNode instantiation, CallableObjectInternal function) { + class_method(instantiation, function, _) + } or + /** Represents an instance of `staticmethod` */ + TStaticMethod(CallNode instantiation, CallableObjectInternal function) { + static_method(instantiation, function, _) + } or + /** Represents a builtin tuple */ + TBuiltinTuple(Builtin bltn) { bltn.getClass() = Builtin::special("tuple") } or + /** Represents a tuple in the Python source */ + TPythonTuple(TupleNode origin, PointsToContext context) { + origin.isLoad() and + context.appliesTo(origin) + } or + /** Varargs tuple */ + TVarargsTuple(CallNode call, PointsToContext context, int offset, int length) { + InterProceduralPointsTo::varargs_tuple(call, context, _, _, offset, length) + } or + /** `type` */ + TType() or + /** Represents an instance of `property` */ + TProperty(CallNode call, Context ctx, CallableObjectInternal getter) { + PointsToInternal::pointsTo(call.getFunction(), ctx, ObjectInternal::property(), _) and + PointsToInternal::pointsTo(call.getArg(0), ctx, getter, _) + } or + /** Represents the `setter` or `deleter` method of a property object. */ + TPropertySetterOrDeleter(PropertyInternal property, string method) { + exists(AttrNode attr | PointsToInternal::pointsTo(attr.getObject(method), _, property, _)) and + (method = "setter" or method = "deleter") + } or + /** Represents a dynamically created class */ + TDynamicClass(CallNode instantiation, ClassObjectInternal metacls, PointsToContext context) { + PointsToInternal::pointsTo(instantiation.getFunction(), context, metacls, _) and + not count(instantiation.getAnArg()) = 1 and + Types::getMro(metacls).contains(TType()) + } or + /** Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */ + TSysVersionInfo() or + /** Represents a module that is inferred to perhaps exist, but is not present in the database. */ + TAbsentModule(string name) { missing_imported_module(_, _, name) } or + /** Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */ + TAbsentModuleAttribute(AbsentModuleObjectInternal mod, string attrname) { + ( + PointsToInternal::pointsTo(any(AttrNode attr).getObject(attrname), _, mod, _) + or + PointsToInternal::pointsTo(any(ImportMemberNode imp).getModule(attrname), _, mod, _) + ) and + exists(string modname | + modname = mod.getName() and + not common_module_name(modname + "." + attrname) + ) + } or + /** Opaque object representing the result of calling a decorator on a function that we don't understand */ + TDecoratedFunction(CallNode call) { call.isFunctionDecoratorCall() } or + /** Represents a subscript operation applied to a type. For type-hint analysis */ + TSubscriptedType(ObjectInternal generic, ObjectInternal index) { + isType(generic) and + generic.isNotSubscriptedType() and + index.isNotSubscriptedType() and + Expressions::subscriptPartsPointsTo(_, _, generic, index) + } /** Holds if the object `t` is a type. */ predicate isType(ObjectInternal t) { - t.isClass() = true - or - t.getOrigin().getEnclosingModule().getName().matches("%typing") + t.isClass() = true + or + t.getOrigin().getEnclosingModule().getName().matches("%typing") } private predicate is_power_2(int n) { - n = 1 - or - exists(int half | is_power_2(half) and n = half * 2) + n = 1 + or + exists(int half | is_power_2(half) and n = half * 2) } predicate static_method( - CallNode instantiation, CallableObjectInternal function, PointsToContext context + CallNode instantiation, CallableObjectInternal function, PointsToContext context ) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, - ObjectInternal::builtin("staticmethod"), _) and - PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) + PointsToInternal::pointsTo(instantiation.getFunction(), context, + ObjectInternal::builtin("staticmethod"), _) and + PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) } predicate class_method( - CallNode instantiation, CallableObjectInternal function, PointsToContext context + CallNode instantiation, CallableObjectInternal function, PointsToContext context ) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::classMethod(), _) and - PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) + PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::classMethod(), _) and + PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _) } predicate literal_instantiation(ControlFlowNode n, ClassObjectInternal cls, PointsToContext context) { - context.appliesTo(n) and - ( - n instanceof ListNode and cls = ObjectInternal::builtin("list") - or - n instanceof DictNode and cls = ObjectInternal::builtin("dict") - or - n instanceof SetNode and cls = ObjectInternal::builtin("set") - or - n.getNode() instanceof ImaginaryLiteral and cls = ObjectInternal::builtin("complex") - or - n.getNode() instanceof ListComp and cls = ObjectInternal::builtin("list") - or - n.getNode() instanceof SetComp and cls = ObjectInternal::builtin("set") - or - n.getNode() instanceof DictComp and cls = ObjectInternal::builtin("dict") - ) + context.appliesTo(n) and + ( + n instanceof ListNode and cls = ObjectInternal::builtin("list") + or + n instanceof DictNode and cls = ObjectInternal::builtin("dict") + or + n instanceof SetNode and cls = ObjectInternal::builtin("set") + or + n.getNode() instanceof ImaginaryLiteral and cls = ObjectInternal::builtin("complex") + or + n.getNode() instanceof ListComp and cls = ObjectInternal::builtin("list") + or + n.getNode() instanceof SetComp and cls = ObjectInternal::builtin("set") + or + n.getNode() instanceof DictComp and cls = ObjectInternal::builtin("dict") + ) } predicate super_instantiation( - CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, - PointsToContext context + CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, + PointsToContext context ) { - super_2args(instantiation, self, startclass, context) - or - super_noargs(instantiation, self, startclass, context) + super_2args(instantiation, self, startclass, context) + or + super_noargs(instantiation, self, startclass, context) } pragma[noinline] private predicate super_2args( - CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, - PointsToContext context + CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, + PointsToContext context ) { - exists(ControlFlowNode arg0, ControlFlowNode arg1 | - super_call2(instantiation, arg0, arg1, context) and - PointsToInternal::pointsTo(arg0, context, startclass, _) and - PointsToInternal::pointsTo(arg1, context, self, _) - ) + exists(ControlFlowNode arg0, ControlFlowNode arg1 | + super_call2(instantiation, arg0, arg1, context) and + PointsToInternal::pointsTo(arg0, context, startclass, _) and + PointsToInternal::pointsTo(arg1, context, self, _) + ) } pragma[noinline] private predicate super_call2( - CallNode call, ControlFlowNode arg0, ControlFlowNode arg1, PointsToContext context + CallNode call, ControlFlowNode arg0, ControlFlowNode arg1, PointsToContext context ) { - exists(ControlFlowNode func | - call2(call, func, arg0, arg1) and - PointsToInternal::pointsTo(func, context, ObjectInternal::superType(), _) - ) + exists(ControlFlowNode func | + call2(call, func, arg0, arg1) and + PointsToInternal::pointsTo(func, context, ObjectInternal::superType(), _) + ) } pragma[noinline] private predicate super_noargs( - CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, - PointsToContext context + CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, + PointsToContext context ) { - PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::builtin("super"), - _) and - not exists(instantiation.getArg(0)) and - exists(Function func | - instantiation.getScope() = func and - /* Implicit class argument is lexically enclosing scope */ - func.getScope() = startclass.(PythonClassObjectInternal).getScope() and - /* Implicit 'self' is the `self` parameter of the enclosing function */ - self.(SelfInstanceInternal).getParameter().getParameter() = func.getArg(0) - ) + PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::builtin("super"), + _) and + not exists(instantiation.getArg(0)) and + exists(Function func | + instantiation.getScope() = func and + /* Implicit class argument is lexically enclosing scope */ + func.getScope() = startclass.(PythonClassObjectInternal).getScope() and + /* Implicit 'self' is the `self` parameter of the enclosing function */ + self.(SelfInstanceInternal).getParameter().getParameter() = func.getArg(0) + ) } predicate call2(CallNode call, ControlFlowNode func, ControlFlowNode arg0, ControlFlowNode arg1) { - not exists(call.getArg(2)) and - func = call.getFunction() and - arg0 = call.getArg(0) and - arg1 = call.getArg(1) + not exists(call.getArg(2)) and + func = call.getFunction() and + arg0 = call.getArg(0) and + arg1 = call.getArg(1) } predicate call3( - CallNode call, ControlFlowNode func, ControlFlowNode arg0, ControlFlowNode arg1, - ControlFlowNode arg2 + CallNode call, ControlFlowNode func, ControlFlowNode arg0, ControlFlowNode arg1, + ControlFlowNode arg2 ) { - not exists(call.getArg(3)) and - func = call.getFunction() and - arg0 = call.getArg(0) and - arg1 = call.getArg(1) and - arg2 = call.getArg(2) + not exists(call.getArg(3)) and + func = call.getFunction() and + arg0 = call.getArg(0) and + arg1 = call.getArg(1) and + arg2 = call.getArg(2) } bindingset[self, function] predicate method_binding( - AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, - PointsToContext context + AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, + PointsToContext context ) { - exists(ObjectInternal obj, string name | receiver(instantiation, context, obj, name) | - exists(ObjectInternal cls | - cls = obj.getClass() and - cls != ObjectInternal::superType() and - cls.attribute(name, function, _) and - self = obj - ) - or - exists(SuperInstance sup, ClassObjectInternal decl | - sup = obj and - decl = Types::getMro(self.getClass()).startingAt(sup.getStartClass()).findDeclaringClass(name) and - Types::declaredAttribute(decl, name, function, _) and - self = sup.getSelf() - ) + exists(ObjectInternal obj, string name | receiver(instantiation, context, obj, name) | + exists(ObjectInternal cls | + cls = obj.getClass() and + cls != ObjectInternal::superType() and + cls.attribute(name, function, _) and + self = obj ) + or + exists(SuperInstance sup, ClassObjectInternal decl | + sup = obj and + decl = Types::getMro(self.getClass()).startingAt(sup.getStartClass()).findDeclaringClass(name) and + Types::declaredAttribute(decl, name, function, _) and + self = sup.getSelf() + ) + ) } /** Helper for method_binding */ pragma[noinline] predicate receiver(AttrNode instantiation, PointsToContext context, ObjectInternal obj, string name) { - PointsToInternal::pointsTo(instantiation.getObject(name), context, obj, _) + PointsToInternal::pointsTo(instantiation.getObject(name), context, obj, _) } /** Helper self parameters: `def meth(self, ...): ...`. */ pragma[noinline] private predicate self_parameter( - ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls + ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls ) { - def.isSelf() and - /* Exclude the special parameter name `.0` which is used for unfolded comprehensions. */ - def.getName() != ".0" and - exists(Function scope | - def.getScope() = scope and - context.isRuntime() and - context.appliesToScope(scope) and - scope.getScope() = cls.getScope() and - concrete_class(cls) and - /* - * We want to allow decorated functions, otherwise we lose a lot of useful information. - * However, we want to exclude any function whose arguments are permuted by the decorator. - * In general we can't do that, but we can special case the most common ones. - */ + def.isSelf() and + /* Exclude the special parameter name `.0` which is used for unfolded comprehensions. */ + def.getName() != ".0" and + exists(Function scope | + def.getScope() = scope and + context.isRuntime() and + context.appliesToScope(scope) and + scope.getScope() = cls.getScope() and + concrete_class(cls) and + /* + * We want to allow decorated functions, otherwise we lose a lot of useful information. + * However, we want to exclude any function whose arguments are permuted by the decorator. + * In general we can't do that, but we can special case the most common ones. + */ - neither_class_nor_static_method(scope) - ) + neither_class_nor_static_method(scope) + ) } cached private predicate concrete_class(PythonClassObjectInternal cls) { - cls.getClass() != abcMetaClassObject() - or - exists(Class c | - c = cls.getScope() and - not exists(c.getMetaClass()) - | - forall(Function f | f.getScope() = c | - not exists(Raise r, Name ex | - r.getScope() = f and - (r.getException() = ex or r.getException().(Call).getFunc() = ex) and - (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") - ) - ) + cls.getClass() != abcMetaClassObject() + or + exists(Class c | + c = cls.getScope() and + not exists(c.getMetaClass()) + | + forall(Function f | f.getScope() = c | + not exists(Raise r, Name ex | + r.getScope() = f and + (r.getException() = ex or r.getException().(Call).getFunc() = ex) and + (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") + ) ) + ) } private PythonClassObjectInternal abcMetaClassObject() { - /* Avoid using points-to and thus negative recursion */ - exists(Class abcmeta | result.getScope() = abcmeta | - abcmeta.getName() = "ABCMeta" and - abcmeta.getScope().getName() = "abc" - ) + /* Avoid using points-to and thus negative recursion */ + exists(Class abcmeta | result.getScope() = abcmeta | + abcmeta.getName() = "ABCMeta" and + abcmeta.getScope().getName() = "abc" + ) } private predicate neither_class_nor_static_method(Function f) { - not exists(f.getADecorator()) - or - exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() | - exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) | - o != ObjectInternal::staticMethod() and - o != ObjectInternal::classMethod() - ) - or - not deco instanceof NameNode + not exists(f.getADecorator()) + or + exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() | + exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) | + o != ObjectInternal::staticMethod() and + o != ObjectInternal::classMethod() ) + or + not deco instanceof NameNode + ) } predicate missing_imported_module(ControlFlowNode imp, Context ctx, string name) { - ctx.isImport() and - imp.(ImportExprNode).getNode().getAnImportedModuleName() = name and - ( - not exists(Module m | m.getName() = name) and - not exists(Builtin b | b.isModule() and b.getName() = name) - or - exists(Module m, SyntaxError se | - m.getName() = name and - se.getFile() = m.getFile() - ) - ) + ctx.isImport() and + imp.(ImportExprNode).getNode().getAnImportedModuleName() = name and + ( + not exists(Module m | m.getName() = name) and + not exists(Builtin b | b.isModule() and b.getName() = name) or - exists(AbsentModuleObjectInternal mod | - PointsToInternal::pointsTo(imp.(ImportMemberNode).getModule(name), ctx, mod, _) and - common_module_name(mod.getName() + "." + name) + exists(Module m, SyntaxError se | + m.getName() = name and + se.getFile() = m.getFile() ) + ) + or + exists(AbsentModuleObjectInternal mod | + PointsToInternal::pointsTo(imp.(ImportMemberNode).getModule(name), ctx, mod, _) and + common_module_name(mod.getName() + "." + name) + ) } /** * Helper for missing modules to determine if name `x.y` is a module `x.y` or * an attribute `y` of module `x`. This list should be added to as required. */ - predicate common_module_name(string name) { - name = "zope.interface" - or - name = "six.moves" + name = "zope.interface" + or + name = "six.moves" } /** @@ -441,78 +440,78 @@ predicate common_module_name(string name) { * recursion. */ library class ClassDecl extends @py_object { - ClassDecl() { - this.(Builtin).isClass() and not this = Builtin::unknownType() - or - this.(ControlFlowNode).getNode() instanceof ClassExpr - } + ClassDecl() { + this.(Builtin).isClass() and not this = Builtin::unknownType() + or + this.(ControlFlowNode).getNode() instanceof ClassExpr + } - /** Gets a textual representation of this element. */ - string toString() { result = "ClassDecl" } + /** Gets a textual representation of this element. */ + string toString() { result = "ClassDecl" } - /** Gets the class scope for Python class declarations */ - Class getClass() { result = this.(ControlFlowNode).getNode().(ClassExpr).getInnerScope() } + /** Gets the class scope for Python class declarations */ + Class getClass() { result = this.(ControlFlowNode).getNode().(ClassExpr).getInnerScope() } - /** Holds if this class declares the attribute `name` */ - predicate declaresAttribute(string name) { - exists(this.(Builtin).getMember(name)) - or - exists(SsaVariable var | - name = var.getId() and var.getAUse() = this.getClass().getANormalExit() - ) - } + /** Holds if this class declares the attribute `name` */ + predicate declaresAttribute(string name) { + exists(this.(Builtin).getMember(name)) + or + exists(SsaVariable var | + name = var.getId() and var.getAUse() = this.getClass().getANormalExit() + ) + } - /** Gets the name of this class */ - string getName() { - result = this.(Builtin).getName() - or - result = this.getClass().getName() - } + /** Gets the name of this class */ + string getName() { + result = this.(Builtin).getName() + or + result = this.getClass().getName() + } - /** - * Whether this is a class whose instances must be treated specially, rather than as generic instances. - */ - predicate isSpecial() { - exists(string name | this = Builtin::special(name) | - name = "type" or - name = "super" or - name = "bool" or - name = "NoneType" or - name = "tuple" or - name = "property" or - name = "ClassMethod" or - name = "StaticMethod" or - name = "MethodType" or - name = "ModuleType" - ) - } + /** + * Whether this is a class whose instances must be treated specially, rather than as generic instances. + */ + predicate isSpecial() { + exists(string name | this = Builtin::special(name) | + name = "type" or + name = "super" or + name = "bool" or + name = "NoneType" or + name = "tuple" or + name = "property" or + name = "ClassMethod" or + name = "StaticMethod" or + name = "MethodType" or + name = "ModuleType" + ) + } - /** Holds if for class `C`, `C()` returns an instance of `C` */ - predicate callReturnsInstance() { - exists(Class pycls | pycls = this.getClass() | - /* Django does this, so we need to account for it */ - not exists(Function init, LocalVariable self | - /* `self.__class__ = ...` in the `__init__` method */ - pycls.getInitMethod() = init and - self.isSelf() and - self.getScope() = init and - exists(AttrNode a | a.isStore() and a.getObject("__class__") = self.getAUse()) - ) and - not exists(Function new | new.getName() = "__new__" and new.getScope() = pycls) - ) - or - this instanceof Builtin - } + /** Holds if for class `C`, `C()` returns an instance of `C` */ + predicate callReturnsInstance() { + exists(Class pycls | pycls = this.getClass() | + /* Django does this, so we need to account for it */ + not exists(Function init, LocalVariable self | + /* `self.__class__ = ...` in the `__init__` method */ + pycls.getInitMethod() = init and + self.isSelf() and + self.getScope() = init and + exists(AttrNode a | a.isStore() and a.getObject("__class__") = self.getAUse()) + ) and + not exists(Function new | new.getName() = "__new__" and new.getScope() = pycls) + ) + or + this instanceof Builtin + } - /** Holds if this class is the abstract base class */ - predicate isAbstractBaseClass(string name) { - exists(Module m | - m.getName() = "_abcoll" - or - m.getName() = "_collections_abc" - | - this.getClass().getScope() = m and - this.getName() = name - ) - } + /** Holds if this class is the abstract base class */ + predicate isAbstractBaseClass(string name) { + exists(Module m | + m.getName() = "_abcoll" + or + m.getName() = "_collections_abc" + | + this.getClass().getScope() = m and + this.getName() = name + ) + } } diff --git a/python/ql/src/semmle/python/pointsto/Base.qll b/python/ql/src/semmle/python/pointsto/Base.qll index f18db539cc4..bff183d0efe 100644 --- a/python/ql/src/semmle/python/pointsto/Base.qll +++ b/python/ql/src/semmle/python/pointsto/Base.qll @@ -13,28 +13,28 @@ import semmle.python.essa.SsaDefinitions private import semmle.python.types.Builtins module BasePointsTo { - /** INTERNAL -- Use n.refersTo(value, _, origin) instead */ - pragma[noinline] - predicate points_to(ControlFlowNode f, Object value, ControlFlowNode origin) { - ( - f.isLiteral() and value = f and not f.getNode() instanceof ImmutableLiteral - or - f.isFunction() and value = f - ) and - origin = f - } + /** INTERNAL -- Use n.refersTo(value, _, origin) instead */ + pragma[noinline] + predicate points_to(ControlFlowNode f, Object value, ControlFlowNode origin) { + ( + f.isLiteral() and value = f and not f.getNode() instanceof ImmutableLiteral + or + f.isFunction() and value = f + ) and + origin = f + } } /** The kwargs parameter (**kwargs) in a function definition is always a dict */ predicate kwargs_points_to(ControlFlowNode f, ClassObject cls) { - exists(Function func | func.getKwarg() = f.getNode()) and - cls = theDictType() + exists(Function func | func.getKwarg() = f.getNode()) and + cls = theDictType() } /** The varargs (*varargs) in a function definition is always a tuple */ predicate varargs_points_to(ControlFlowNode f, ClassObject cls) { - exists(Function func | func.getVararg() = f.getNode()) and - cls = theTupleType() + exists(Function func | func.getVararg() = f.getNode()) and + cls = theTupleType() } /** @@ -45,88 +45,88 @@ predicate varargs_points_to(ControlFlowNode f, ClassObject cls) { */ pragma[noinline] ClassObject simple_types(Object obj) { - result = comprehension(obj.getOrigin()) - or - result = collection_literal(obj.getOrigin()) - or - obj.getOrigin() instanceof CallableExpr and result = thePyFunctionType() - or - obj.getOrigin() instanceof Module and result = theModuleType() - or - result.asBuiltin() = obj.asBuiltin().getClass() - or - obj = unknownValue() and result = theUnknownType() + result = comprehension(obj.getOrigin()) + or + result = collection_literal(obj.getOrigin()) + or + obj.getOrigin() instanceof CallableExpr and result = thePyFunctionType() + or + obj.getOrigin() instanceof Module and result = theModuleType() + or + result.asBuiltin() = obj.asBuiltin().getClass() + or + obj = unknownValue() and result = theUnknownType() } private ClassObject comprehension(Expr e) { - e instanceof ListComp and result = theListType() - or - e instanceof SetComp and result = theSetType() - or - e instanceof DictComp and result = theDictType() - or - e instanceof GeneratorExp and result = theGeneratorType() + e instanceof ListComp and result = theListType() + or + e instanceof SetComp and result = theSetType() + or + e instanceof DictComp and result = theDictType() + or + e instanceof GeneratorExp and result = theGeneratorType() } private ClassObject collection_literal(Expr e) { - e instanceof List and result = theListType() - or - e instanceof Set and result = theSetType() - or - e instanceof Dict and result = theDictType() - or - e instanceof Tuple and result = theTupleType() + e instanceof List and result = theListType() + or + e instanceof Set and result = theSetType() + or + e instanceof Dict and result = theDictType() + or + e instanceof Tuple and result = theTupleType() } private int tuple_index_value(Object t, int i) { - result = t.(TupleNode).getElement(i).getNode().(Num).getN().toInt() - or - exists(Object item | - py_citems(t, i, item) and - result = item.(NumericObject).intValue() - ) + result = t.(TupleNode).getElement(i).getNode().(Num).getN().toInt() + or + exists(Object item | + py_citems(t, i, item) and + result = item.(NumericObject).intValue() + ) } pragma[noinline] int version_tuple_value(Object t) { - not exists(tuple_index_value(t, 1)) and result = tuple_index_value(t, 0) * 10 - or - not exists(tuple_index_value(t, 2)) and - result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) - or - tuple_index_value(t, 2) = 0 and result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) - or - tuple_index_value(t, 2) > 0 and - result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + 1 + not exists(tuple_index_value(t, 1)) and result = tuple_index_value(t, 0) * 10 + or + not exists(tuple_index_value(t, 2)) and + result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + or + tuple_index_value(t, 2) = 0 and result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + or + tuple_index_value(t, 2) > 0 and + result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + 1 } /** Choose a version numbers that represent the extreme of supported versions. */ private int major_minor() { - if major_version() = 3 - then ( - result = 33 or result = 37 - ) else ( - // 3.3 to 3.7 - result = 25 or result = 27 - ) // 2.5 to 2.7 + if major_version() = 3 + then ( + result = 33 or result = 37 + ) else ( + // 3.3 to 3.7 + result = 25 or result = 27 + ) // 2.5 to 2.7 } /** Compares the given tuple object to both the maximum and minimum possible sys.version_info values */ int version_tuple_compare(Object t) { - version_tuple_value(t) < major_minor() and result = -1 - or - version_tuple_value(t) = major_minor() and result = 0 - or - version_tuple_value(t) > major_minor() and result = 1 + version_tuple_value(t) < major_minor() and result = -1 + or + version_tuple_value(t) = major_minor() and result = 0 + or + version_tuple_value(t) > major_minor() and result = 1 } /* Holds if `cls` is a new-style class if it were to have no explicit base classes */ predicate baseless_is_new_style(ClassObject cls) { - cls.isBuiltin() - or - major_version() = 3 - or - exists(cls.declaredMetaClass()) + cls.isBuiltin() + or + major_version() = 3 + or + exists(cls.declaredMetaClass()) } /* @@ -139,123 +139,123 @@ predicate baseless_is_new_style(ClassObject cls) { /** Holds if this class (not on a super-class) declares name */ pragma[noinline] predicate class_declares_attribute(ClassObject cls, string name) { - exists(Class defn | - defn = cls.getPyClass() and - class_defines_name(defn, name) - ) - or - exists(Builtin o | - o = cls.asBuiltin().getMember(name) and - not exists(Builtin sup | - sup = cls.asBuiltin().getBaseClass() and - o = sup.getMember(name) - ) + exists(Class defn | + defn = cls.getPyClass() and + class_defines_name(defn, name) + ) + or + exists(Builtin o | + o = cls.asBuiltin().getMember(name) and + not exists(Builtin sup | + sup = cls.asBuiltin().getBaseClass() and + o = sup.getMember(name) ) + ) } /** Holds if the class defines name */ private predicate class_defines_name(Class cls, string name) { - exists(SsaVariable var | name = var.getId() and var.getAUse() = cls.getANormalExit()) + exists(SsaVariable var | name = var.getId() and var.getAUse() = cls.getANormalExit()) } /** Gets a return value CFG node, provided that is safe to track across returns */ ControlFlowNode safe_return_node(PyFunctionObject func) { - result = func.getAReturnedNode() and - // Not a parameter - not exists(Parameter p, SsaVariable pvar | - p.asName().getAFlowNode() = pvar.getDefinition() and - result = pvar.getAUse() - ) and - // No alternatives - not exists(ControlFlowNode branch | branch.isBranch() and branch.getScope() = func.getFunction()) + result = func.getAReturnedNode() and + // Not a parameter + not exists(Parameter p, SsaVariable pvar | + p.asName().getAFlowNode() = pvar.getDefinition() and + result = pvar.getAUse() + ) and + // No alternatives + not exists(ControlFlowNode branch | branch.isBranch() and branch.getScope() = func.getFunction()) } /** Holds if it can be determined from the control flow graph alone that this function can never return */ predicate function_can_never_return(FunctionObject func) { - /* - * A Python function never returns if it has no normal exits that are not dominated by a - * call to a function which itself never returns. - */ + /* + * A Python function never returns if it has no normal exits that are not dominated by a + * call to a function which itself never returns. + */ - exists(Function f | - f = func.getFunction() and - not exists(f.getAnExitNode()) - ) - or - func = ModuleObject::named("sys").attr("exit") + exists(Function f | + f = func.getFunction() and + not exists(f.getAnExitNode()) + ) + or + func = ModuleObject::named("sys").attr("exit") } private newtype TIterationDefinition = - TIterationDefinition_(SsaSourceVariable var, ControlFlowNode def, ControlFlowNode sequence) { - SsaSource::iteration_defined_variable(var, def, sequence) - } + TIterationDefinition_(SsaSourceVariable var, ControlFlowNode def, ControlFlowNode sequence) { + SsaSource::iteration_defined_variable(var, def, sequence) + } /** * DEPRECATED. For backwards compatibility only. * A definition of a variable in a for loop `for v in ...:` */ deprecated class IterationDefinition extends TIterationDefinition { - /** Gets a textual representation of this element. */ - string toString() { result = "IterationDefinition" } + /** Gets a textual representation of this element. */ + string toString() { result = "IterationDefinition" } - ControlFlowNode getSequence() { this = TIterationDefinition_(_, _, result) } + ControlFlowNode getSequence() { this = TIterationDefinition_(_, _, result) } } /** Hold if outer contains inner, both are contained within a test and inner is a use is a plain use or an attribute lookup */ pragma[noinline] predicate contains_interesting_expression_within_test(ControlFlowNode outer, ControlFlowNode inner) { - inner.isLoad() and - exists(ControlFlowNode test | - outer.getAChild*() = inner and - test_contains(test, outer) and - test_contains(test, inner) - | - inner instanceof NameNode or - inner instanceof AttrNode - ) + inner.isLoad() and + exists(ControlFlowNode test | + outer.getAChild*() = inner and + test_contains(test, outer) and + test_contains(test, inner) + | + inner instanceof NameNode or + inner instanceof AttrNode + ) } /** Hold if `expr` is a test (a branch) and `use` is within that test */ predicate test_contains(ControlFlowNode expr, ControlFlowNode use) { - expr.getNode() instanceof Expr and - expr.isBranch() and - expr.getAChild*() = use + expr.getNode() instanceof Expr and + expr.isBranch() and + expr.getAChild*() = use } /** Holds if `test` is a test (a branch), `use` is within that test and `def` is an edge from that test with `sense` */ predicate refinement_test( - ControlFlowNode test, ControlFlowNode use, boolean sense, PyEdgeRefinement def + ControlFlowNode test, ControlFlowNode use, boolean sense, PyEdgeRefinement def ) { - /* - * Because calls such as `len` may create a new variable, we need to go via the source variable - * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. - */ + /* + * Because calls such as `len` may create a new variable, we need to go via the source variable + * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. + */ - use = def.getInput().getSourceVariable().(Variable).getAUse() and - test = def.getPredecessor().getLastNode() and - test_contains(test, use) and - sense = def.getSense() + use = def.getInput().getSourceVariable().(Variable).getAUse() and + test = def.getPredecessor().getLastNode() and + test_contains(test, use) and + sense = def.getSense() } /** Holds if `f` is an import of the form `from .[...] import name` and the enclosing scope is an __init__ module */ pragma[noinline] predicate live_import_from_dot_in_init(ImportMemberNode f, EssaVariable var) { - exists(string name | - import_from_dot_in_init(f.getModule(name)) and - var.getSourceVariable().getName() = name and - var.getAUse() = f - ) + exists(string name | + import_from_dot_in_init(f.getModule(name)) and + var.getSourceVariable().getName() = name and + var.getAUse() = f + ) } /** Holds if `f` is an import of the form `from .[...] import ...` and the enclosing scope is an __init__ module */ predicate import_from_dot_in_init(ImportExprNode f) { - f.getScope() = any(Module m).getInitModule() and - ( - f.getNode().getLevel() = 1 and - not exists(f.getNode().getName()) - or - f.getNode().getImportedModuleName() = f.getEnclosingModule().getPackage().getName() - ) + f.getScope() = any(Module m).getInitModule() and + ( + f.getNode().getLevel() = 1 and + not exists(f.getNode().getName()) + or + f.getNode().getImportedModuleName() = f.getEnclosingModule().getPackage().getName() + ) } /** Gets the pseudo-object representing the value referred to by an undefined variable */ @@ -265,73 +265,73 @@ Object undefinedVariable() { py_special_objects(result, "_semmle_undefined_value Object unknownValue() { result.asBuiltin() = Builtin::unknown() } BuiltinCallable theTypeNewMethod() { - result.asBuiltin() = theTypeType().asBuiltin().getMember("__new__") + result.asBuiltin() = theTypeType().asBuiltin().getMember("__new__") } /** Gets the `value, cls, origin` that `f` would refer to if it has not been assigned some other value */ pragma[noinline] predicate potential_builtin_points_to( - NameNode f, Object value, ClassObject cls, ControlFlowNode origin + NameNode f, Object value, ClassObject cls, ControlFlowNode origin ) { - f.isGlobal() and - f.isLoad() and - origin = f and - ( - builtin_name_points_to(f.getId(), value, cls) - or - not exists(Object::builtin(f.getId())) and value = unknownValue() and cls = theUnknownType() - ) + f.isGlobal() and + f.isLoad() and + origin = f and + ( + builtin_name_points_to(f.getId(), value, cls) + or + not exists(Object::builtin(f.getId())) and value = unknownValue() and cls = theUnknownType() + ) } pragma[noinline] predicate builtin_name_points_to(string name, Object value, ClassObject cls) { - value = Object::builtin(name) and cls.asBuiltin() = value.asBuiltin().getClass() + value = Object::builtin(name) and cls.asBuiltin() = value.asBuiltin().getClass() } module BaseFlow { - predicate reaches_exit(EssaVariable var) { var.getAUse() = var.getScope().getANormalExit() } + predicate reaches_exit(EssaVariable var) { var.getAUse() = var.getScope().getANormalExit() } - /* Helper for this_scope_entry_value_transfer(...). Transfer of values from earlier scope to later on */ - cached - predicate scope_entry_value_transfer_from_earlier( - EssaVariable pred_var, Scope pred_scope, ScopeEntryDefinition succ_def, Scope succ_scope - ) { - exists(SsaSourceVariable var | - reaches_exit(pred_var) and - pred_var.getScope() = pred_scope and - var = pred_var.getSourceVariable() and - var = succ_def.getSourceVariable() and - succ_def.getScope() = succ_scope - | - pred_scope.precedes(succ_scope) - or - /* - * If an `__init__` method does not modify the global variable, then - * we can skip it and take the value directly from the module. - */ + /* Helper for this_scope_entry_value_transfer(...). Transfer of values from earlier scope to later on */ + cached + predicate scope_entry_value_transfer_from_earlier( + EssaVariable pred_var, Scope pred_scope, ScopeEntryDefinition succ_def, Scope succ_scope + ) { + exists(SsaSourceVariable var | + reaches_exit(pred_var) and + pred_var.getScope() = pred_scope and + var = pred_var.getSourceVariable() and + var = succ_def.getSourceVariable() and + succ_def.getScope() = succ_scope + | + pred_scope.precedes(succ_scope) + or + /* + * If an `__init__` method does not modify the global variable, then + * we can skip it and take the value directly from the module. + */ - exists(Scope init | - init.getName() = "__init__" and - init.precedes(succ_scope) and - pred_scope.precedes(init) and - not var.(Variable).getAStore().getScope() = init and - var instanceof GlobalVariable - ) - ) - } + exists(Scope init | + init.getName() = "__init__" and + init.precedes(succ_scope) and + pred_scope.precedes(init) and + not var.(Variable).getAStore().getScope() = init and + var instanceof GlobalVariable + ) + ) + } } /** Points-to for syntactic elements where context is not relevant */ predicate simple_points_to(ControlFlowNode f, Object value, ClassObject cls, ControlFlowNode origin) { - kwargs_points_to(f, cls) and value = f and origin = f - or - varargs_points_to(f, cls) and value = f and origin = f - or - BasePointsTo::points_to(f, value, origin) and cls = simple_types(value) - or - value = f.getNode().(ImmutableLiteral).getLiteralObject() and - cls = simple_types(value) and - origin = f + kwargs_points_to(f, cls) and value = f and origin = f + or + varargs_points_to(f, cls) and value = f and origin = f + or + BasePointsTo::points_to(f, value, origin) and cls = simple_types(value) + or + value = f.getNode().(ImmutableLiteral).getLiteralObject() and + cls = simple_types(value) and + origin = f } /** @@ -339,25 +339,25 @@ predicate simple_points_to(ControlFlowNode f, Object value, ClassObject cls, Con * Helper for `this_binary_expr_points_to`. */ predicate bitwise_expression_node(BinaryExprNode bit, ControlFlowNode left, ControlFlowNode right) { - exists(Operator op | op = bit.getNode().getOp() | - op instanceof BitAnd or - op instanceof BitOr or - op instanceof BitXor - ) and - left = bit.getLeft() and - right = bit.getRight() + exists(Operator op | op = bit.getNode().getOp() | + op instanceof BitAnd or + op instanceof BitOr or + op instanceof BitXor + ) and + left = bit.getLeft() and + right = bit.getRight() } private Module theCollectionsAbcModule() { - result.getName() = "_abcoll" - or - result.getName() = "_collections_abc" + result.getName() = "_abcoll" + or + result.getName() = "_collections_abc" } ClassObject collectionsAbcClass(string name) { - exists(Class cls | - result.getPyClass() = cls and - cls.getName() = name and - cls.getScope() = theCollectionsAbcModule() - ) + exists(Class cls | + result.getPyClass() = cls and + cls.getName() = name and + cls.getScope() = theCollectionsAbcModule() + ) } diff --git a/python/ql/src/semmle/python/pointsto/CallGraph.qll b/python/ql/src/semmle/python/pointsto/CallGraph.qll index 0c10e67a867..545dd945cf1 100644 --- a/python/ql/src/semmle/python/pointsto/CallGraph.qll +++ b/python/ql/src/semmle/python/pointsto/CallGraph.qll @@ -13,14 +13,14 @@ import python private import semmle.python.pointsto.PointsToContext private newtype TTInvocation = - TInvocation(FunctionObject f, Context c) { - exists(Context outer, CallNode call | - call = f.getACall(outer) and - c.fromCall(call, outer) - ) - or - c.appliesToScope(f.getFunction()) - } + TInvocation(FunctionObject f, Context c) { + exists(Context outer, CallNode call | + call = f.getACall(outer) and + c.fromCall(call, outer) + ) + or + c.appliesToScope(f.getFunction()) + } /** * This class represents a static approximation to the @@ -28,46 +28,46 @@ private newtype TTInvocation = * all calls made to a function for a given context. */ class FunctionInvocation extends TTInvocation { - /** Gets a textual representation of this element. */ - string toString() { result = "Invocation" } + /** Gets a textual representation of this element. */ + string toString() { result = "Invocation" } - FunctionObject getFunction() { this = TInvocation(result, _) } + FunctionObject getFunction() { this = TInvocation(result, _) } - Context getContext() { this = TInvocation(_, result) } + Context getContext() { this = TInvocation(_, result) } - /** - * Gets the callee invocation for the given callsite. - * The callsite must be within the function of this invocation. - */ - FunctionInvocation getCallee(CallNode call) { - exists( - FunctionObject callee, Context callee_context, FunctionObject caller, Context caller_context - | - this = TInvocation(caller, caller_context) and - result = TInvocation(callee, callee_context) and - call = callee.getACall(caller_context) and - callee_context.fromCall(call, caller_context) and - call.getScope() = caller.getFunction() - ) - } + /** + * Gets the callee invocation for the given callsite. + * The callsite must be within the function of this invocation. + */ + FunctionInvocation getCallee(CallNode call) { + exists( + FunctionObject callee, Context callee_context, FunctionObject caller, Context caller_context + | + this = TInvocation(caller, caller_context) and + result = TInvocation(callee, callee_context) and + call = callee.getACall(caller_context) and + callee_context.fromCall(call, caller_context) and + call.getScope() = caller.getFunction() + ) + } - /** - * Gets a callee invocation. - * That is any invocation made from within this invocation. - */ - FunctionInvocation getACallee() { result = this.getCallee(_) } + /** + * Gets a callee invocation. + * That is any invocation made from within this invocation. + */ + FunctionInvocation getACallee() { result = this.getCallee(_) } - /** Holds if this is an invocation `f` in the "runtime" context. */ - predicate runtime(FunctionObject f) { - exists(Context c | - c.isRuntime() and - this = TInvocation(f, c) - ) - } + /** Holds if this is an invocation `f` in the "runtime" context. */ + predicate runtime(FunctionObject f) { + exists(Context c | + c.isRuntime() and + this = TInvocation(f, c) + ) + } - /** Gets the call from which this invocation was made. */ - CallNode getCall() { this.getContext().fromCall(result, _) } + /** Gets the call from which this invocation was made. */ + CallNode getCall() { this.getContext().fromCall(result, _) } - /** Gets the caller invocation of this invocation, if any. */ - FunctionInvocation getCaller() { this = result.getCallee(_) } + /** Gets the caller invocation of this invocation, if any. */ + FunctionInvocation getCaller() { this = result.getCallee(_) } } diff --git a/python/ql/src/semmle/python/pointsto/Filters.qll b/python/ql/src/semmle/python/pointsto/Filters.qll index 114711b217c..117845d3c7f 100644 --- a/python/ql/src/semmle/python/pointsto/Filters.qll +++ b/python/ql/src/semmle/python/pointsto/Filters.qll @@ -7,45 +7,45 @@ import python /** Holds if `c` is a call to `hasattr(obj, attr)`. */ predicate hasattr(CallNode c, ControlFlowNode obj, string attr) { - c.getFunction().getNode().(Name).getId() = "hasattr" and - c.getArg(0) = obj and - c.getArg(1).getNode().(StrConst).getText() = attr + c.getFunction().getNode().(Name).getId() = "hasattr" and + c.getArg(0) = obj and + c.getArg(1).getNode().(StrConst).getText() = attr } /** Holds if `c` is a call to `callable(obj)`. */ predicate is_callable(CallNode c, ControlFlowNode obj) { - c.getFunction().(NameNode).getId() = "callable" and - obj = c.getArg(0) + c.getFunction().(NameNode).getId() = "callable" and + obj = c.getArg(0) } /** Holds if `c` is a call to `isinstance(use, cls)`. */ predicate isinstance(CallNode fc, ControlFlowNode cls, ControlFlowNode use) { - fc.getFunction().(NameNode).getId() = "isinstance" and - cls = fc.getArg(1) and - fc.getArg(0) = use + fc.getFunction().(NameNode).getId() = "isinstance" and + cls = fc.getArg(1) and + fc.getArg(0) = use } /** Holds if `c` is a call to `issubclass(use, cls)`. */ predicate issubclass(CallNode fc, ControlFlowNode cls, ControlFlowNode use) { - fc.getFunction().(NameNode).getId() = "issubclass" and - fc.getArg(0) = use and - cls = fc.getArg(1) + fc.getFunction().(NameNode).getId() = "issubclass" and + fc.getArg(0) = use and + cls = fc.getArg(1) } /** Holds if `c` is a test comparing `x` and `y`. `is` is true if the operator is `is` or `==`, it is false if the operator is `is not` or `!=`. */ predicate equality_test(CompareNode c, ControlFlowNode x, boolean is, ControlFlowNode y) { - exists(Cmpop op | - c.operands(x, op, y) or - c.operands(y, op, x) - | - ( - is = true and op instanceof Is - or - is = false and op instanceof IsNot - or - is = true and op instanceof Eq - or - is = false and op instanceof NotEq - ) + exists(Cmpop op | + c.operands(x, op, y) or + c.operands(y, op, x) + | + ( + is = true and op instanceof Is + or + is = false and op instanceof IsNot + or + is = true and op instanceof Eq + or + is = false and op instanceof NotEq ) + ) } diff --git a/python/ql/src/semmle/python/pointsto/MRO.qll b/python/ql/src/semmle/python/pointsto/MRO.qll index 1e8cd5a1908..34e6325b714 100644 --- a/python/ql/src/semmle/python/pointsto/MRO.qll +++ b/python/ql/src/semmle/python/pointsto/MRO.qll @@ -26,383 +26,383 @@ private import semmle.python.types.Builtins cached newtype TClassList = - Empty() or - Cons(ClassObjectInternal head, TClassList tail) { required_cons(head, tail) } + Empty() or + Cons(ClassObjectInternal head, TClassList tail) { required_cons(head, tail) } /* Keep ClassList finite and as small as possible */ private predicate required_cons(ClassObjectInternal head, ClassList tail) { - tail = Mro::newStyleMro(sole_base(head)) - or - tail = merge_of_linearization_of_bases(head) - or - exists(ClassObjectInternal cls, int n | - head = Types::getBase(cls, n) and tail = bases(cls, n + 1) - ) - or - head = ObjectInternal::builtin("object") and tail = Empty() - or - reverse_step(_, Cons(head, _), tail) - or - exists(ClassListList list | - merge_step(tail, list, _) and - head = list.bestMergeCandidate() - ) - or - exists(ClassList list, int n | - n = list.firstIndex(head) and - tail = list.deduplicate(n + 1) - ) - or - exists(ClassListList list, int n | - head = list.getHead().getItem(n) and - tail = flatten_list(list, n + 1) - ) - or - tail = list_old_style_base_mros(head).flatten() + tail = Mro::newStyleMro(sole_base(head)) + or + tail = merge_of_linearization_of_bases(head) + or + exists(ClassObjectInternal cls, int n | + head = Types::getBase(cls, n) and tail = bases(cls, n + 1) + ) + or + head = ObjectInternal::builtin("object") and tail = Empty() + or + reverse_step(_, Cons(head, _), tail) + or + exists(ClassListList list | + merge_step(tail, list, _) and + head = list.bestMergeCandidate() + ) + or + exists(ClassList list, int n | + n = list.firstIndex(head) and + tail = list.deduplicate(n + 1) + ) + or + exists(ClassListList list, int n | + head = list.getHead().getItem(n) and + tail = flatten_list(list, n + 1) + ) + or + tail = list_old_style_base_mros(head).flatten() } private ClassObjectInternal sole_base(ClassObjectInternal cls) { - Types::base_count(cls) = 1 and - result = Types::getBase(cls, 0) + Types::base_count(cls) = 1 and + result = Types::getBase(cls, 0) } /** A list of classes, used to represent the MRO of a class */ class ClassList extends TClassList { - /** Gets a textual representation of this element. */ - string toString() { result = "[" + this.contents() + "]" } + /** Gets a textual representation of this element. */ + string toString() { result = "[" + this.contents() + "]" } - string contents() { - this = Empty() and result = "" - or - exists(ClassObjectInternal head | head = this.getHead() | - this.getTail() = Empty() and result = className(head) - or - this.getTail() != Empty() and result = className(head) + ", " + this.getTail().contents() - ) - } + string contents() { + this = Empty() and result = "" + or + exists(ClassObjectInternal head | head = this.getHead() | + this.getTail() = Empty() and result = className(head) + or + this.getTail() != Empty() and result = className(head) + ", " + this.getTail().contents() + ) + } - private string className(ClassObjectInternal cls) { - result = cls.getName() - or - cls = ObjectInternal::unknownClass() and result = "??" - } + private string className(ClassObjectInternal cls) { + result = cls.getName() + or + cls = ObjectInternal::unknownClass() and result = "??" + } - int length() { - this = Empty() and result = 0 - or - result = this.getTail().length() + 1 - } + int length() { + this = Empty() and result = 0 + or + result = this.getTail().length() + 1 + } - ClassObjectInternal getHead() { this = Cons(result, _) } + ClassObjectInternal getHead() { this = Cons(result, _) } - ClassList getTail() { this = Cons(_, result) } + ClassList getTail() { this = Cons(_, result) } - ClassObjectInternal getItem(int n) { - n = 0 and result = this.getHead() - or - result = this.getTail().getItem(n - 1) - } + ClassObjectInternal getItem(int n) { + n = 0 and result = this.getHead() + or + result = this.getTail().getItem(n - 1) + } - ClassObjectInternal getAnItem() { result = this.getItem(_) } + ClassObjectInternal getAnItem() { result = this.getItem(_) } - pragma[inline] - ClassList removeHead(ClassObjectInternal cls) { - this.getHead() = cls and result = this.getTail() - or - this.getHead() != cls and result = this - or - this = Empty() and result = Empty() - } + pragma[inline] + ClassList removeHead(ClassObjectInternal cls) { + this.getHead() = cls and result = this.getTail() + or + this.getHead() != cls and result = this + or + this = Empty() and result = Empty() + } - predicate legalMergeHead(ClassObjectInternal cls) { - this.getTail().doesNotContain(cls) - or - this = Empty() - } + predicate legalMergeHead(ClassObjectInternal cls) { + this.getTail().doesNotContain(cls) + or + this = Empty() + } - predicate contains(ClassObjectInternal cls) { - cls = this.getHead() - or - this.getTail().contains(cls) - } + predicate contains(ClassObjectInternal cls) { + cls = this.getHead() + or + this.getTail().contains(cls) + } - /** Use negative formulation to avoid negative recursion */ - predicate doesNotContain(ClassObjectInternal cls) { - this.relevantForContains(cls) and - cls != this.getHead() and - this.getTail().doesNotContain(cls) - or - this = Empty() - } + /** Use negative formulation to avoid negative recursion */ + predicate doesNotContain(ClassObjectInternal cls) { + this.relevantForContains(cls) and + cls != this.getHead() and + this.getTail().doesNotContain(cls) + or + this = Empty() + } - private predicate relevantForContains(ClassObjectInternal cls) { - exists(ClassListList list | - list.getItem(_).getHead() = cls and - list.getItem(_) = this - ) - or - exists(ClassList l | - l.relevantForContains(cls) and - this = l.getTail() - ) - } + private predicate relevantForContains(ClassObjectInternal cls) { + exists(ClassListList list | + list.getItem(_).getHead() = cls and + list.getItem(_) = this + ) + or + exists(ClassList l | + l.relevantForContains(cls) and + this = l.getTail() + ) + } - ClassObjectInternal findDeclaringClass(string name) { - exists(ClassDecl head | head = this.getHead().getClassDeclaration() | - if head.declaresAttribute(name) - then result = this.getHead() - else result = this.getTail().findDeclaringClass(name) - ) - } + ClassObjectInternal findDeclaringClass(string name) { + exists(ClassDecl head | head = this.getHead().getClassDeclaration() | + if head.declaresAttribute(name) + then result = this.getHead() + else result = this.getTail().findDeclaringClass(name) + ) + } - predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { - exists(ClassObjectInternal decl | decl = this.findDeclaringClass(name) | - Types::declaredAttribute(decl, name, value, origin) - ) - } + predicate lookup(string name, ObjectInternal value, CfgOrigin origin) { + exists(ClassObjectInternal decl | decl = this.findDeclaringClass(name) | + Types::declaredAttribute(decl, name, value, origin) + ) + } - predicate declares(string name) { - this.getHead().getClassDeclaration().declaresAttribute(name) - or - this.getTail().declares(name) - } + predicate declares(string name) { + this.getHead().getClassDeclaration().declaresAttribute(name) + or + this.getTail().declares(name) + } - ClassList startingAt(ClassObjectInternal cls) { - exists(ClassObjectInternal head | head = this.getHead() | - if head = cls then result = this else result = this.getTail().startingAt(cls) - ) - } + ClassList startingAt(ClassObjectInternal cls) { + exists(ClassObjectInternal head | head = this.getHead() | + if head = cls then result = this else result = this.getTail().startingAt(cls) + ) + } - ClassList deduplicate() { result = this.deduplicate(0) } + ClassList deduplicate() { result = this.deduplicate(0) } - /* Helpers for `deduplicate()` */ - int firstIndex(ClassObjectInternal cls) { result = this.firstIndex(cls, 0) } + /* Helpers for `deduplicate()` */ + int firstIndex(ClassObjectInternal cls) { result = this.firstIndex(cls, 0) } - /* Helper for firstIndex(cls), getting the first index of `cls` where result >= n */ - private int firstIndex(ClassObjectInternal cls, int n) { - this.getItem(n) = cls and result = n - or - this.getItem(n) != cls and result = this.firstIndex(cls, n + 1) - } + /* Helper for firstIndex(cls), getting the first index of `cls` where result >= n */ + private int firstIndex(ClassObjectInternal cls, int n) { + this.getItem(n) = cls and result = n + or + this.getItem(n) != cls and result = this.firstIndex(cls, n + 1) + } - /** Holds if the class at `n` is a duplicate of an earlier position. */ - private predicate duplicate(int n) { - exists(ClassObjectInternal cls | cls = this.getItem(n) and this.firstIndex(cls) < n) - } + /** Holds if the class at `n` is a duplicate of an earlier position. */ + private predicate duplicate(int n) { + exists(ClassObjectInternal cls | cls = this.getItem(n) and this.firstIndex(cls) < n) + } - /** - * Gets a class list which is the de-duplicated form of the list containing elements of - * this list from `n` onwards. - */ - ClassList deduplicate(int n) { - n = this.length() and result = Empty() - or - this.duplicate(n) and result = this.deduplicate(n + 1) - or - exists(ClassObjectInternal cls | - n = this.firstIndex(cls) and - result = Cons(cls, this.deduplicate(n + 1)) - ) - } + /** + * Gets a class list which is the de-duplicated form of the list containing elements of + * this list from `n` onwards. + */ + ClassList deduplicate(int n) { + n = this.length() and result = Empty() + or + this.duplicate(n) and result = this.deduplicate(n + 1) + or + exists(ClassObjectInternal cls | + n = this.firstIndex(cls) and + result = Cons(cls, this.deduplicate(n + 1)) + ) + } - predicate isEmpty() { this = Empty() } + predicate isEmpty() { this = Empty() } - ClassList reverse() { reverse_step(this, Empty(), result) } + ClassList reverse() { reverse_step(this, Empty(), result) } - /** - * Holds if this MRO contains a class whose instances we treat specially, rather than as a generic instance. - * For example, `type` or `int`. - */ - boolean containsSpecial() { - this = Empty() and result = false - or - exists(ClassDecl decl | decl = this.getHead().getClassDeclaration() | - if decl.isSpecial() then result = true else result = this.getTail().containsSpecial() - ) - } + /** + * Holds if this MRO contains a class whose instances we treat specially, rather than as a generic instance. + * For example, `type` or `int`. + */ + boolean containsSpecial() { + this = Empty() and result = false + or + exists(ClassDecl decl | decl = this.getHead().getClassDeclaration() | + if decl.isSpecial() then result = true else result = this.getTail().containsSpecial() + ) + } } private newtype TClassListList = - EmptyList() or - ConsList(TClassList head, TClassListList tail) { required_list(head, tail) } + EmptyList() or + ConsList(TClassList head, TClassListList tail) { required_list(head, tail) } /* Keep ClassListList finite and as small as possible */ private predicate required_list(ClassList head, ClassListList tail) { - any(ClassListList x).removedClassParts(_, head, tail, _) - or - head = bases(_) and tail = EmptyList() - or - exists(ClassObjectInternal cls, int n | - head = Mro::newStyleMro(Types::getBase(cls, n)) and - tail = list_of_linearization_of_bases_plus_bases(cls, n + 1) - ) - or - exists(ClassObjectInternal cls, int n | - head = Mro::oldStyleMro(Types::getBase(cls, n)) and - tail = list_old_style_base_mros(cls, n + 1) - ) + any(ClassListList x).removedClassParts(_, head, tail, _) + or + head = bases(_) and tail = EmptyList() + or + exists(ClassObjectInternal cls, int n | + head = Mro::newStyleMro(Types::getBase(cls, n)) and + tail = list_of_linearization_of_bases_plus_bases(cls, n + 1) + ) + or + exists(ClassObjectInternal cls, int n | + head = Mro::oldStyleMro(Types::getBase(cls, n)) and + tail = list_old_style_base_mros(cls, n + 1) + ) } private class ClassListList extends TClassListList { - /** Gets a textual representation of this element. */ - string toString() { result = "[" + this.contents() + "]" } + /** Gets a textual representation of this element. */ + string toString() { result = "[" + this.contents() + "]" } - string contents() { - this = EmptyList() and result = "" - or - exists(ClassList head | head = this.getHead() | - this.getTail() = EmptyList() and result = head.toString() - or - this.getTail() != EmptyList() and result = head.toString() + ", " + this.getTail().contents() - ) - } + string contents() { + this = EmptyList() and result = "" + or + exists(ClassList head | head = this.getHead() | + this.getTail() = EmptyList() and result = head.toString() + or + this.getTail() != EmptyList() and result = head.toString() + ", " + this.getTail().contents() + ) + } - int length() { - this = EmptyList() and result = 0 - or - result = this.getTail().length() + 1 - } + int length() { + this = EmptyList() and result = 0 + or + result = this.getTail().length() + 1 + } - ClassList getHead() { this = ConsList(result, _) } + ClassList getHead() { this = ConsList(result, _) } - ClassListList getTail() { this = ConsList(_, result) } + ClassListList getTail() { this = ConsList(_, result) } - ClassList getItem(int n) { - n = 0 and result = this.getHead() - or - result = this.getTail().getItem(n - 1) - } + ClassList getItem(int n) { + n = 0 and result = this.getHead() + or + result = this.getTail().getItem(n - 1) + } - private ClassObjectInternal getAHead() { - result = this.getHead().getHead() - or - result = this.getTail().getAHead() - } + private ClassObjectInternal getAHead() { + result = this.getHead().getHead() + or + result = this.getTail().getAHead() + } - pragma[nomagic] - ClassList merge() { - exists(ClassList reversed | - merge_step(reversed, EmptyList(), this) and - result = reversed.reverse() - ) - or - this = EmptyList() and result = Empty() - } + pragma[nomagic] + ClassList merge() { + exists(ClassList reversed | + merge_step(reversed, EmptyList(), this) and + result = reversed.reverse() + ) + or + this = EmptyList() and result = Empty() + } - /* Join ordering helper */ - pragma[noinline] - predicate removedClassParts( - ClassObjectInternal cls, ClassList removed_head, ClassListList removed_tail, int n - ) { - cls = this.bestMergeCandidate() and - n = this.length() - 1 and - removed_head = this.getItem(n).removeHead(cls) and - removed_tail = EmptyList() - or - exists(ClassList prev_head, ClassListList prev_tail | - this.removedClassParts(cls, prev_head, prev_tail, n + 1) and - removed_head = this.getItem(n).removeHead(cls) and - removed_tail = ConsList(prev_head, prev_tail) - ) - } + /* Join ordering helper */ + pragma[noinline] + predicate removedClassParts( + ClassObjectInternal cls, ClassList removed_head, ClassListList removed_tail, int n + ) { + cls = this.bestMergeCandidate() and + n = this.length() - 1 and + removed_head = this.getItem(n).removeHead(cls) and + removed_tail = EmptyList() + or + exists(ClassList prev_head, ClassListList prev_tail | + this.removedClassParts(cls, prev_head, prev_tail, n + 1) and + removed_head = this.getItem(n).removeHead(cls) and + removed_tail = ConsList(prev_head, prev_tail) + ) + } - ClassListList remove(ClassObjectInternal cls) { - exists(ClassList removed_head, ClassListList removed_tail | - this.removedClassParts(cls, removed_head, removed_tail, 0) and - result = ConsList(removed_head, removed_tail) - ) - or - this = EmptyList() and result = EmptyList() - } + ClassListList remove(ClassObjectInternal cls) { + exists(ClassList removed_head, ClassListList removed_tail | + this.removedClassParts(cls, removed_head, removed_tail, 0) and + result = ConsList(removed_head, removed_tail) + ) + or + this = EmptyList() and result = EmptyList() + } - predicate legalMergeCandidate(ClassObjectInternal cls, int n) { - cls = this.getAHead() and n = this.length() - or - this.getItem(n).legalMergeHead(cls) and - this.legalMergeCandidate(cls, n + 1) - } + predicate legalMergeCandidate(ClassObjectInternal cls, int n) { + cls = this.getAHead() and n = this.length() + or + this.getItem(n).legalMergeHead(cls) and + this.legalMergeCandidate(cls, n + 1) + } - predicate legalMergeCandidate(ClassObjectInternal cls) { this.legalMergeCandidate(cls, 0) } + predicate legalMergeCandidate(ClassObjectInternal cls) { this.legalMergeCandidate(cls, 0) } - predicate illegalMergeCandidate(ClassObjectInternal cls) { - cls = this.getAHead() and - this.getItem(_).getTail().contains(cls) - } + predicate illegalMergeCandidate(ClassObjectInternal cls) { + cls = this.getAHead() and + this.getItem(_).getTail().contains(cls) + } - ClassObjectInternal bestMergeCandidate(int n) { - exists(ClassObjectInternal head | head = this.getItem(n).getHead() | - legalMergeCandidate(head) and result = head - or - illegalMergeCandidate(head) and result = this.bestMergeCandidate(n + 1) - ) - } + ClassObjectInternal bestMergeCandidate(int n) { + exists(ClassObjectInternal head | head = this.getItem(n).getHead() | + legalMergeCandidate(head) and result = head + or + illegalMergeCandidate(head) and result = this.bestMergeCandidate(n + 1) + ) + } - ClassObjectInternal bestMergeCandidate() { result = this.bestMergeCandidate(0) } + ClassObjectInternal bestMergeCandidate() { result = this.bestMergeCandidate(0) } - /** - * Gets a ClassList representing the this list of list flattened into a single list. - * Used for old-style MRO computation. - */ - ClassList flatten() { - this = EmptyList() and result = Empty() - or - result = flatten_list(this, 0) - } + /** + * Gets a ClassList representing the this list of list flattened into a single list. + * Used for old-style MRO computation. + */ + ClassList flatten() { + this = EmptyList() and result = Empty() + or + result = flatten_list(this, 0) + } } private ClassList flatten_list(ClassListList list, int n) { - need_flattening(list) and - exists(ClassList head, ClassListList tail | list = ConsList(head, tail) | - n = head.length() and result = tail.flatten() - or - result = Cons(head.getItem(n), flatten_list(list, n + 1)) - ) + need_flattening(list) and + exists(ClassList head, ClassListList tail | list = ConsList(head, tail) | + n = head.length() and result = tail.flatten() + or + result = Cons(head.getItem(n), flatten_list(list, n + 1)) + ) } /* Restrict flattening to those lists that need to be flattened */ private predicate need_flattening(ClassListList list) { - list = list_old_style_base_mros(_) - or - exists(ClassListList toflatten | - need_flattening(toflatten) and - list = toflatten.getTail() - ) + list = list_old_style_base_mros(_) + or + exists(ClassListList toflatten | + need_flattening(toflatten) and + list = toflatten.getTail() + ) } private ClassList bases(ClassObjectInternal cls) { result = bases(cls, 0) } private ClassList bases(ClassObjectInternal cls, int n) { - result = Cons(Types::getBase(cls, n), bases(cls, n + 1)) - or - result = Empty() and n = Types::base_count(cls) + result = Cons(Types::getBase(cls, n), bases(cls, n + 1)) + or + result = Empty() and n = Types::base_count(cls) } private ClassListList list_of_linearization_of_bases_plus_bases(ClassObjectInternal cls) { - result = list_of_linearization_of_bases_plus_bases(cls, 0) + result = list_of_linearization_of_bases_plus_bases(cls, 0) } private ClassListList list_of_linearization_of_bases_plus_bases(ClassObjectInternal cls, int n) { - result = ConsList(bases(cls), EmptyList()) and n = Types::base_count(cls) and n > 1 - or - exists(ClassListList partial | - partial = list_of_linearization_of_bases_plus_bases(cls, n + 1) and - result = ConsList(Mro::newStyleMro(Types::getBase(cls, n)), partial) - ) + result = ConsList(bases(cls), EmptyList()) and n = Types::base_count(cls) and n > 1 + or + exists(ClassListList partial | + partial = list_of_linearization_of_bases_plus_bases(cls, n + 1) and + result = ConsList(Mro::newStyleMro(Types::getBase(cls, n)), partial) + ) } private ClassList merge_of_linearization_of_bases(ClassObjectInternal cls) { - result = list_of_linearization_of_bases_plus_bases(cls).merge() + result = list_of_linearization_of_bases_plus_bases(cls).merge() } private ClassListList list_old_style_base_mros(ClassObjectInternal cls) { - result = list_old_style_base_mros(cls, 0) + result = list_old_style_base_mros(cls, 0) } pragma[nomagic] private ClassListList list_old_style_base_mros(ClassObjectInternal cls, int n) { - n = Types::base_count(cls) and result = EmptyList() - or - result = ConsList(Mro::oldStyleMro(Types::getBase(cls, n)), list_old_style_base_mros(cls, n + 1)) + n = Types::base_count(cls) and result = EmptyList() + or + result = ConsList(Mro::oldStyleMro(Types::getBase(cls, n)), list_old_style_base_mros(cls, n + 1)) } /** @@ -410,52 +410,52 @@ private ClassListList list_old_style_base_mros(ClassObjectInternal cls, int n) { * of computing the C3 linearization of `original`. */ private predicate merge_step( - ClassList reversed_mro, ClassListList remaining_list, ClassListList original + ClassList reversed_mro, ClassListList remaining_list, ClassListList original ) { - remaining_list = list_of_linearization_of_bases_plus_bases(_) and - reversed_mro = Empty() and - remaining_list = original - or - /* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */ - exists(ClassObjectInternal head, ClassList prev_reverse_mro, ClassListList prev_list | - merge_step(prev_reverse_mro, prev_list, original) and - head = prev_list.bestMergeCandidate() and - reversed_mro = Cons(head, prev_reverse_mro) and - remaining_list = prev_list.remove(head) - ) - or - merge_step(reversed_mro, ConsList(Empty(), remaining_list), original) + remaining_list = list_of_linearization_of_bases_plus_bases(_) and + reversed_mro = Empty() and + remaining_list = original + or + /* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */ + exists(ClassObjectInternal head, ClassList prev_reverse_mro, ClassListList prev_list | + merge_step(prev_reverse_mro, prev_list, original) and + head = prev_list.bestMergeCandidate() and + reversed_mro = Cons(head, prev_reverse_mro) and + remaining_list = prev_list.remove(head) + ) + or + merge_step(reversed_mro, ConsList(Empty(), remaining_list), original) } /* Helpers for `ClassList.reverse()` */ private predicate needs_reversing(ClassList lst) { - merge_step(lst, EmptyList(), _) - or - lst = Empty() + merge_step(lst, EmptyList(), _) + or + lst = Empty() } private predicate reverse_step(ClassList lst, ClassList remainder, ClassList reversed) { - needs_reversing(lst) and remainder = lst and reversed = Empty() - or - exists(ClassObjectInternal head, ClassList tail | - reversed = Cons(head, tail) and - reverse_step(lst, Cons(head, remainder), tail) - ) + needs_reversing(lst) and remainder = lst and reversed = Empty() + or + exists(ClassObjectInternal head, ClassList tail | + reversed = Cons(head, tail) and + reverse_step(lst, Cons(head, remainder), tail) + ) } module Mro { - cached - ClassList newStyleMro(ClassObjectInternal cls) { - cls = ObjectInternal::builtin("object") and result = Cons(cls, Empty()) - or - result = Cons(cls, merge_of_linearization_of_bases(cls)) - or - result = Cons(cls, newStyleMro(sole_base(cls))) - } + cached + ClassList newStyleMro(ClassObjectInternal cls) { + cls = ObjectInternal::builtin("object") and result = Cons(cls, Empty()) + or + result = Cons(cls, merge_of_linearization_of_bases(cls)) + or + result = Cons(cls, newStyleMro(sole_base(cls))) + } - cached - ClassList oldStyleMro(ClassObjectInternal cls) { - Types::isOldStyle(cls) and - result = Cons(cls, list_old_style_base_mros(cls).flatten()).(ClassList).deduplicate() - } + cached + ClassList oldStyleMro(ClassObjectInternal cls) { + Types::isOldStyle(cls) and + result = Cons(cls, list_old_style_base_mros(cls).flatten()).(ClassList).deduplicate() + } } diff --git a/python/ql/src/semmle/python/pointsto/PointsTo.qll b/python/ql/src/semmle/python/pointsto/PointsTo.qll index b4ed40ab5d6..9614b02d2f6 100644 --- a/python/ql/src/semmle/python/pointsto/PointsTo.qll +++ b/python/ql/src/semmle/python/pointsto/PointsTo.qll @@ -9,41 +9,41 @@ private import semmle.python.types.Extensions /* Use this version for speed */ library class CfgOrigin extends @py_object { - /** Gets a textual representation of this element. */ - string toString() { - /* Not to be displayed */ - result = "CfgOrigin" - } + /** Gets a textual representation of this element. */ + string toString() { + /* Not to be displayed */ + result = "CfgOrigin" + } - /** - * Get a `ControlFlowNode` from `this` or `here`. - * If `this` is a ControlFlowNode then use that, otherwise fall back on `here` - */ - pragma[inline] - ControlFlowNode asCfgNodeOrHere(ControlFlowNode here) { - result = this - or - not this instanceof ControlFlowNode and result = here - } + /** + * Get a `ControlFlowNode` from `this` or `here`. + * If `this` is a ControlFlowNode then use that, otherwise fall back on `here` + */ + pragma[inline] + ControlFlowNode asCfgNodeOrHere(ControlFlowNode here) { + result = this + or + not this instanceof ControlFlowNode and result = here + } - ControlFlowNode toCfgNode() { result = this } + ControlFlowNode toCfgNode() { result = this } - pragma[inline] - CfgOrigin fix(ControlFlowNode here) { - if this = Builtin::unknown() then result = here else result = this - } + pragma[inline] + CfgOrigin fix(ControlFlowNode here) { + if this = Builtin::unknown() then result = here else result = this + } } module CfgOrigin { - CfgOrigin fromCfgNode(ControlFlowNode f) { result = f } + CfgOrigin fromCfgNode(ControlFlowNode f) { result = f } - CfgOrigin unknown() { result = Builtin::unknown() } + CfgOrigin unknown() { result = Builtin::unknown() } - CfgOrigin fromObject(ObjectInternal obj) { - obj.isBuiltin() and result = unknown() - or - result = obj.getOrigin() - } + CfgOrigin fromObject(ObjectInternal obj) { + obj.isBuiltin() and result = unknown() + or + result = obj.getOrigin() + } } /* Use this version for stronger type-checking */ @@ -101,2639 +101,2639 @@ module CfgOrigin { //} /* The API */ module PointsTo { - predicate pointsTo( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - PointsToInternal::pointsTo(f, context, value, origin) - } + predicate pointsTo( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + PointsToInternal::pointsTo(f, context, value, origin) + } - predicate variablePointsTo( - EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - PointsToInternal::variablePointsTo(var, context, value, origin) - } + predicate variablePointsTo( + EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + PointsToInternal::variablePointsTo(var, context, value, origin) + } - /* Backwards compatibility */ - cached - predicate points_to( - ControlFlowNode f, PointsToContext context, Object obj, ClassObject cls, ControlFlowNode origin - ) { - exists(ObjectInternal value | - PointsToInternal::pointsTo(f, context, value, origin) and - cls = value.getClass().getSource() - | - obj = value.getSource() - or - value.useOriginAsLegacyObject() and obj = origin - ) - or - /* Backwards compatibility for *args and **kwargs */ - exists(Function func | obj = f and origin = f and context.isRuntime() | - func.getVararg() = f.getNode() and cls = theTupleType() - or - func.getKwarg() = f.getNode() and cls = theDictType() - ) - or - not f.isParameter() and - exists(ObjectInternal value | - PointsToInternal::pointsTo(f.(DefinitionNode).getValue(), context, value, origin) and - cls = value.getClass().getSource() - | - obj = value.getSource() - or - value.useOriginAsLegacyObject() and obj = origin - ) - } + /* Backwards compatibility */ + cached + predicate points_to( + ControlFlowNode f, PointsToContext context, Object obj, ClassObject cls, ControlFlowNode origin + ) { + exists(ObjectInternal value | + PointsToInternal::pointsTo(f, context, value, origin) and + cls = value.getClass().getSource() + | + obj = value.getSource() + or + value.useOriginAsLegacyObject() and obj = origin + ) + or + /* Backwards compatibility for *args and **kwargs */ + exists(Function func | obj = f and origin = f and context.isRuntime() | + func.getVararg() = f.getNode() and cls = theTupleType() + or + func.getKwarg() = f.getNode() and cls = theDictType() + ) + or + not f.isParameter() and + exists(ObjectInternal value | + PointsToInternal::pointsTo(f.(DefinitionNode).getValue(), context, value, origin) and + cls = value.getClass().getSource() + | + obj = value.getSource() + or + value.useOriginAsLegacyObject() and obj = origin + ) + } - deprecated predicate ssa_variable_points_to( - EssaVariable var, PointsToContext context, Object obj, ClassObject cls, CfgOrigin origin - ) { - exists(ObjectInternal value | - PointsToInternal::variablePointsTo(var, context, value, origin) and - cls = value.getClass().getSource() - | - obj = value.getSource() - ) - } + deprecated predicate ssa_variable_points_to( + EssaVariable var, PointsToContext context, Object obj, ClassObject cls, CfgOrigin origin + ) { + exists(ObjectInternal value | + PointsToInternal::variablePointsTo(var, context, value, origin) and + cls = value.getClass().getSource() + | + obj = value.getSource() + ) + } - deprecated CallNode get_a_call(Object func, PointsToContext context) { - exists(ObjectInternal value | - result = value.(Value).getACall(context) and - func = value.getSource() - ) - } + deprecated CallNode get_a_call(Object func, PointsToContext context) { + exists(ObjectInternal value | + result = value.(Value).getACall(context) and + func = value.getSource() + ) + } - cached - predicate moduleExports(ModuleObjectInternal mod, string name) { - InterModulePointsTo::moduleExportsBoolean(mod, name) = true - } + cached + predicate moduleExports(ModuleObjectInternal mod, string name) { + InterModulePointsTo::moduleExportsBoolean(mod, name) = true + } } cached module PointsToInternal { - pragma[noinline] - cached - predicate importCtxPointsTo(ControlFlowNode f, ObjectInternal value, ControlFlowNode origin) { - PointsToInternal::pointsTo(f, any(Context ctx | ctx.isImport()), value, origin) - } + pragma[noinline] + cached + predicate importCtxPointsTo(ControlFlowNode f, ObjectInternal value, ControlFlowNode origin) { + PointsToInternal::pointsTo(f, any(Context ctx | ctx.isImport()), value, origin) + } - /** INTERNAL -- Use `f.refersTo(value, origin)` instead. */ - cached - predicate pointsTo( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - points_to_candidate(f, context, value, origin) and - reachableBlock(f.getBasicBlock(), context) - } + /** INTERNAL -- Use `f.refersTo(value, origin)` instead. */ + cached + predicate pointsTo( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + points_to_candidate(f, context, value, origin) and + reachableBlock(f.getBasicBlock(), context) + } - cached - predicate pointsToString(ControlFlowNode f, PointsToContext context, string value) { - exists(ObjectInternal str | - PointsToInternal::pointsTo(f, context, str, _) and - str.strValue() = value - ) - } + cached + predicate pointsToString(ControlFlowNode f, PointsToContext context, string value) { + exists(ObjectInternal str | + PointsToInternal::pointsTo(f, context, str, _) and + str.strValue() = value + ) + } - private predicate points_to_candidate( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - use_points_to(f, context, value, origin) - or - attribute_load_points_to(f, context, value, origin) - or - Expressions::pointsTo(f, context, value, origin, _, _) - or - if_exp_points_to(f, context, value, origin) - or - origin = f and value.introducedAt(f, context) - or - InterModulePointsTo::import_points_to(f, context, value, origin) - or - InterModulePointsTo::from_import_points_to(f, context, value, origin) - or - InterProceduralPointsTo::call_points_to(f, context, value, origin) - or - AttributePointsTo::pointsTo(f, context, value, origin) - or - f.(PointsToExtension).pointsTo(context, value, origin) - or - iteration_points_to(f, context, value, origin) - } + private predicate points_to_candidate( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + use_points_to(f, context, value, origin) + or + attribute_load_points_to(f, context, value, origin) + or + Expressions::pointsTo(f, context, value, origin, _, _) + or + if_exp_points_to(f, context, value, origin) + or + origin = f and value.introducedAt(f, context) + or + InterModulePointsTo::import_points_to(f, context, value, origin) + or + InterModulePointsTo::from_import_points_to(f, context, value, origin) + or + InterProceduralPointsTo::call_points_to(f, context, value, origin) + or + AttributePointsTo::pointsTo(f, context, value, origin) + or + f.(PointsToExtension).pointsTo(context, value, origin) + or + iteration_points_to(f, context, value, origin) + } - /** - * Holds if the attribute `name` is required for `obj` - * For object `x` and attribute `name` it means that there exists somewhere in the code - * `x.name` or `getattr(x, "name")`. - */ - cached - predicate attributeRequired(ObjectInternal obj, string name) { - pointsTo(any(AttrNode a).getObject(name), _, obj, _) + /** + * Holds if the attribute `name` is required for `obj` + * For object `x` and attribute `name` it means that there exists somewhere in the code + * `x.name` or `getattr(x, "name")`. + */ + cached + predicate attributeRequired(ObjectInternal obj, string name) { + pointsTo(any(AttrNode a).getObject(name), _, obj, _) + or + Expressions::getattr_call(_, _, _, obj, name) + } + + /* Holds if BasicBlock `b` is reachable, given the context `context`. */ + cached + predicate reachableBlock(BasicBlock b, PointsToContext context) { + exists(Scope scope | + context.appliesToScope(scope) and + scope.getEntryNode().getBasicBlock() = b + ) + or + reachableEdge(_, b, context) + or + exists(BasicBlock pred | + reachableBlock(pred, context) and + pred.alwaysReaches(b) + ) + } + + private predicate reachableEdge(BasicBlock pred, BasicBlock succ, PointsToContext context) { + reachableBlock(pred, context) and + ( + pred.getAnUnconditionalSuccessor() = succ + or + exists(ObjectInternal value, boolean sense, ControlFlowNode test | + test = pred.getLastNode() and + pointsTo(test, context, value, _) and + sense = value.booleanValue() + | + sense = true and succ = pred.getATrueSuccessor() or - Expressions::getattr_call(_, _, _, obj, name) - } + sense = false and succ = pred.getAFalseSuccessor() + ) + ) + } - /* Holds if BasicBlock `b` is reachable, given the context `context`. */ - cached - predicate reachableBlock(BasicBlock b, PointsToContext context) { - exists(Scope scope | - context.appliesToScope(scope) and - scope.getEntryNode().getBasicBlock() = b - ) - or - reachableEdge(_, b, context) - or - exists(BasicBlock pred | - reachableBlock(pred, context) and - pred.alwaysReaches(b) - ) - } + /** Gets an object pointed to by a use (of a variable). */ + pragma[noinline] + private predicate use_points_to( + NameNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(CfgOrigin origin_or_obj | + value != ObjectInternal::undefined() and + use_points_to_maybe_origin(f, context, value, origin_or_obj) + | + origin = origin_or_obj.asCfgNodeOrHere(f) + ) + } - private predicate reachableEdge(BasicBlock pred, BasicBlock succ, PointsToContext context) { - reachableBlock(pred, context) and - ( - pred.getAnUnconditionalSuccessor() = succ - or - exists(ObjectInternal value, boolean sense, ControlFlowNode test | - test = pred.getLastNode() and - pointsTo(test, context, value, _) and - sense = value.booleanValue() - | - sense = true and succ = pred.getATrueSuccessor() - or - sense = false and succ = pred.getAFalseSuccessor() - ) - ) - } + pragma[noinline] + private predicate use_points_to_maybe_origin( + NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj + ) { + variablePointsTo(fast_local_variable(f), context, value, origin_or_obj) + or + name_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) + or + not exists(fast_local_variable(f)) and + not exists(name_local_variable(f)) and + global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) + } - /** Gets an object pointed to by a use (of a variable). */ - pragma[noinline] - private predicate use_points_to( - NameNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(CfgOrigin origin_or_obj | - value != ObjectInternal::undefined() and - use_points_to_maybe_origin(f, context, value, origin_or_obj) - | - origin = origin_or_obj.asCfgNodeOrHere(f) - ) - } - - pragma[noinline] - private predicate use_points_to_maybe_origin( - NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj - ) { - variablePointsTo(fast_local_variable(f), context, value, origin_or_obj) - or - name_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) - or - not exists(fast_local_variable(f)) and - not exists(name_local_variable(f)) and - global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) - } - - /** Holds if `var` refers to `(value, origin)` given the context `context`. */ - pragma[noinline] - cached - predicate variablePointsTo( - EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - ssa_definition_points_to(var.getDefinition(), context, value, origin) - or - exists(EssaVariable prev | - ssaShortCut+(prev, var) and - variablePointsTo(prev, context, value, origin) - ) - } - - private predicate ssaShortCut(EssaVariable start, EssaVariable end) { - end.getDefinition().(PhiFunction).getShortCircuitInput() = start - or - /* Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ - exists(AttributeAssignment def | - not def.getName() = "__class__" and - start = def.getInput() and - end.getDefinition() = def - ) - or - /* - * Ignore the effects of calls on their arguments. PointsTo is an approximation, - * but attempting to improve accuracy would be very expensive for very little gain. - */ - - exists(ArgumentRefinement def | - start = def.getInput() and - end.getDefinition() = def - ) - } - - pragma[noinline] - private predicate name_lookup_points_to_maybe_origin( - NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj - ) { - exists(EssaVariable var | var = name_local_variable(f) | - variablePointsTo(var, context, value, origin_or_obj) - ) - or - local_variable_undefined(f, context) and - global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) - } - - pragma[noinline] - private predicate local_variable_undefined(NameNode f, PointsToContext context) { - variablePointsTo(name_local_variable(f), context, ObjectInternal::undefined(), _) - } - - pragma[noinline] - private predicate global_lookup_points_to_maybe_origin( - NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj - ) { - variablePointsTo(global_variable(f), context, value, origin_or_obj) - or - exists(ControlFlowNode origin | origin_or_obj = CfgOrigin::fromCfgNode(origin) | - variablePointsTo(global_variable(f), context, ObjectInternal::undefined(), _) and - potential_builtin_points_to(f, value, origin) - or - not exists(global_variable(f)) and - context.appliesToScope(f.getScope()) and - potential_builtin_points_to(f, value, origin) - ) - } - - /** The ESSA variable with fast-local lookup (LOAD_FAST bytecode). */ - private EssaVariable fast_local_variable(NameNode n) { - n.isLoad() and - result.getASourceUse() = n and - result.getSourceVariable() instanceof FastLocalVariable - } - - /** The ESSA variable with name-local lookup (LOAD_NAME bytecode). */ - private EssaVariable name_local_variable(NameNode n) { - n.isLoad() and - result.getASourceUse() = n and - result.getSourceVariable() instanceof NameLocalVariable - } - - /** The ESSA variable for the global variable lookup. */ - private EssaVariable global_variable(NameNode n) { - n.isLoad() and - result.getASourceUse() = n and - result.getSourceVariable() instanceof GlobalVariable - } - - /** Holds if `f` is an attribute `x.attr` and points to `(value, cls, origin)`. */ - pragma[noinline] - private predicate attribute_load_points_to( - AttrNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - none() - // TO DO -- Support CustomPointsToAttribute - //or - //exists(CustomPointsToAttribute object, string name | - // pointsTo(f.getObject(name), context, object, _, _) and - // object.attributePointsTo(name, value, cls, origin) - //) - } + /** Holds if `var` refers to `(value, origin)` given the context `context`. */ + pragma[noinline] + cached + predicate variablePointsTo( + EssaVariable var, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + ssa_definition_points_to(var.getDefinition(), context, value, origin) + or + exists(EssaVariable prev | + ssaShortCut+(prev, var) and + variablePointsTo(prev, context, value, origin) + ) + } + private predicate ssaShortCut(EssaVariable start, EssaVariable end) { + end.getDefinition().(PhiFunction).getShortCircuitInput() = start + or + /* Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ + exists(AttributeAssignment def | + not def.getName() = "__class__" and + start = def.getInput() and + end.getDefinition() = def + ) + or /* - * Treat `ForNode` as intermediate step between sequence and iteration variable. - * In otherwords treat `for i in x:` as being equivalent to `i = next(iter(x))` - * attaching the value of `next(iter(x))` to the `ForNode`. + * Ignore the effects of calls on their arguments. PointsTo is an approximation, + * but attempting to improve accuracy would be very expensive for very little gain. + */ + + exists(ArgumentRefinement def | + start = def.getInput() and + end.getDefinition() = def + ) + } + + pragma[noinline] + private predicate name_lookup_points_to_maybe_origin( + NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj + ) { + exists(EssaVariable var | var = name_local_variable(f) | + variablePointsTo(var, context, value, origin_or_obj) + ) + or + local_variable_undefined(f, context) and + global_lookup_points_to_maybe_origin(f, context, value, origin_or_obj) + } + + pragma[noinline] + private predicate local_variable_undefined(NameNode f, PointsToContext context) { + variablePointsTo(name_local_variable(f), context, ObjectInternal::undefined(), _) + } + + pragma[noinline] + private predicate global_lookup_points_to_maybe_origin( + NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj + ) { + variablePointsTo(global_variable(f), context, value, origin_or_obj) + or + exists(ControlFlowNode origin | origin_or_obj = CfgOrigin::fromCfgNode(origin) | + variablePointsTo(global_variable(f), context, ObjectInternal::undefined(), _) and + potential_builtin_points_to(f, value, origin) + or + not exists(global_variable(f)) and + context.appliesToScope(f.getScope()) and + potential_builtin_points_to(f, value, origin) + ) + } + + /** The ESSA variable with fast-local lookup (LOAD_FAST bytecode). */ + private EssaVariable fast_local_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof FastLocalVariable + } + + /** The ESSA variable with name-local lookup (LOAD_NAME bytecode). */ + private EssaVariable name_local_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof NameLocalVariable + } + + /** The ESSA variable for the global variable lookup. */ + private EssaVariable global_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof GlobalVariable + } + + /** Holds if `f` is an attribute `x.attr` and points to `(value, cls, origin)`. */ + pragma[noinline] + private predicate attribute_load_points_to( + AttrNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + none() + // TO DO -- Support CustomPointsToAttribute + //or + //exists(CustomPointsToAttribute object, string name | + // pointsTo(f.getObject(name), context, object, _, _) and + // object.attributePointsTo(name, value, cls, origin) + //) + } + + /* + * Treat `ForNode` as intermediate step between sequence and iteration variable. + * In otherwords treat `for i in x:` as being equivalent to `i = next(iter(x))` + * attaching the value of `next(iter(x))` to the `ForNode`. + */ + + pragma[noinline] + private predicate iteration_points_to( + ForNode for, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(ControlFlowNode seqNode, ObjectInternal seq | + for.iterates(_, seqNode) and + pointsTo(seqNode, context, seq, _) and + value = seq.getIterNext() and + origin = for + ) + } + + /** Holds if the ESSA definition `def` refers to `(value, origin)` given the context `context`. */ + private predicate ssa_definition_points_to( + EssaDefinition def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + ssa_phi_points_to(def, context, value, origin) + or + exists(ControlFlowNode orig | + ssa_node_definition_points_to(def, context, value, orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + or + ssa_filter_definition_points_to(def, context, value, origin) + or + ssa_node_refinement_points_to(def, context, value, origin) + } + + pragma[noinline] + private predicate ssa_node_definition_points_to( + EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + reachableBlock(def.getDefiningNode().getBasicBlock(), context) and + ssa_node_definition_points_to_unpruned(def, context, value, origin) + } + + pragma[nomagic] + private predicate ssa_node_definition_points_to_unpruned( + EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + InterProceduralPointsTo::parameter_points_to(def, context, value, origin) + or + assignment_points_to(def, context, value, origin) + or + multi_assignment_points_to(def, context, value, origin) + or + self_parameter_points_to(def, context, value, origin) + or + delete_points_to(def, context, value, origin) + or + module_name_points_to(def, context, value, origin) + or + scope_entry_points_to(def, context, value, origin) + or + InterModulePointsTo::implicit_submodule_points_to(def, value, origin) and context.isImport() + /* + * No points-to for non-local function entry definitions yet. */ - pragma[noinline] - private predicate iteration_points_to( - ForNode for, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(ControlFlowNode seqNode, ObjectInternal seq | - for.iterates(_, seqNode) and - pointsTo(seqNode, context, seq, _) and - value = seq.getIterNext() and - origin = for - ) } - /** Holds if the ESSA definition `def` refers to `(value, origin)` given the context `context`. */ - private predicate ssa_definition_points_to( - EssaDefinition def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - ssa_phi_points_to(def, context, value, origin) - or - exists(ControlFlowNode orig | - ssa_node_definition_points_to(def, context, value, orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - or - ssa_filter_definition_points_to(def, context, value, origin) - or - ssa_node_refinement_points_to(def, context, value, origin) - } + pragma[noinline] + private predicate ssa_node_refinement_points_to( + EssaNodeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + method_callsite_points_to(def, context, value, origin) + or + InterModulePointsTo::import_star_points_to(def, context, value, origin) + or + attribute_assignment_points_to(def, context, value, origin) + or + InterProceduralPointsTo::callsite_points_to(def, context, value, origin) + or + attribute_delete_points_to(def, context, value, origin) + or + uni_edged_pi_points_to(def, context, value, origin) + } - pragma[noinline] - private predicate ssa_node_definition_points_to( - EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - reachableBlock(def.getDefiningNode().getBasicBlock(), context) and - ssa_node_definition_points_to_unpruned(def, context, value, origin) - } + /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ + private predicate method_callsite_points_to( + MethodCallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + /* The value of self remains the same, only the attributes may change */ + variablePointsTo(def.getInput(), context, value, origin) + } - pragma[nomagic] - private predicate ssa_node_definition_points_to_unpruned( - EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - InterProceduralPointsTo::parameter_points_to(def, context, value, origin) - or - assignment_points_to(def, context, value, origin) - or - multi_assignment_points_to(def, context, value, origin) - or - self_parameter_points_to(def, context, value, origin) - or - delete_points_to(def, context, value, origin) - or - module_name_points_to(def, context, value, origin) - or - scope_entry_points_to(def, context, value, origin) - or - InterModulePointsTo::implicit_submodule_points_to(def, value, origin) and context.isImport() - /* - * No points-to for non-local function entry definitions yet. - */ + /** Attribute deletions have no effect as far as value tracking is concerned. */ + pragma[noinline] + private predicate attribute_delete_points_to( + EssaAttributeDeletion def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + variablePointsTo(def.getInput(), context, value, origin) + } - } + /** Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ + pragma[noinline] + private predicate attribute_assignment_points_to( + AttributeAssignment def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + def.getName() = "__class__" and + exists(ObjectInternal cls | + pointsTo(def.getValue(), context, cls, _) and + value = TUnknownInstance(cls) and + origin = CfgOrigin::fromCfgNode(def.getDefiningNode()) + ) + } - pragma[noinline] - private predicate ssa_node_refinement_points_to( - EssaNodeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - method_callsite_points_to(def, context, value, origin) - or - InterModulePointsTo::import_star_points_to(def, context, value, origin) - or - attribute_assignment_points_to(def, context, value, origin) - or - InterProceduralPointsTo::callsite_points_to(def, context, value, origin) - or - attribute_delete_points_to(def, context, value, origin) - or - uni_edged_pi_points_to(def, context, value, origin) - } + private predicate self_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + origin = def.getDefiningNode() and + value.(SelfInstanceInternal).parameterAndContext(def, context) + } - /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ - private predicate method_callsite_points_to( - MethodCallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - /* The value of self remains the same, only the attributes may change */ - variablePointsTo(def.getInput(), context, value, origin) - } + /** Holds if ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */ + private predicate ssa_filter_definition_points_to( + PyEdgeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(ControlFlowNode orig | + def.getSense() = ssa_filter_definition_bool(def, context, value, orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + } - /** Attribute deletions have no effect as far as value tracking is concerned. */ - pragma[noinline] - private predicate attribute_delete_points_to( - EssaAttributeDeletion def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - variablePointsTo(def.getInput(), context, value, origin) - } + private boolean ssa_filter_definition_bool( + PyEdgeRefinement def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + result = + Conditionals::testEvaluates(def.getTest(), def.getInput().getASourceUse(), context, value, + origin) + } - /** Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ - pragma[noinline] - private predicate attribute_assignment_points_to( - AttributeAssignment def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - def.getName() = "__class__" and - exists(ObjectInternal cls | - pointsTo(def.getValue(), context, cls, _) and - value = TUnknownInstance(cls) and - origin = CfgOrigin::fromCfgNode(def.getDefiningNode()) - ) - } + /** Holds if ESSA definition, `unipi`, refers to `(value, origin)`. */ + pragma[noinline] + private predicate uni_edged_pi_points_to( + SingleSuccessorGuard unipi, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(ControlFlowNode test, ControlFlowNode use, ControlFlowNode orig | + /* + * Because calls such as `len` may create a new variable, we need to go via the source variable + * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. + */ - private predicate self_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - origin = def.getDefiningNode() and - value.(SelfInstanceInternal).parameterAndContext(def, context) - } + unipi.useAndTest(use, test) and + unipi.getSense() = Conditionals::testEvaluates(test, use, context, value, orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + } - /** Holds if ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */ - private predicate ssa_filter_definition_points_to( - PyEdgeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(ControlFlowNode orig | - def.getSense() = ssa_filter_definition_bool(def, context, value, orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - } + /** Points-to for normal assignments `def = ...`. */ + pragma[noinline] + private predicate assignment_points_to( + AssignmentDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + pointsTo(def.getValue(), context, value, origin) + } - private boolean ssa_filter_definition_bool( - PyEdgeRefinement def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - result = - Conditionals::testEvaluates(def.getTest(), def.getInput().getASourceUse(), context, value, - origin) - } + pragma[nomagic] + private predicate sequence_index_points_to( + ControlFlowNode f, PointsToContext context, SequenceObjectInternal sequence, + ObjectInternal value, int index + ) { + pointsTo(f, context, sequence, _) and + value = sequence.getItem(index) + } - /** Holds if ESSA definition, `unipi`, refers to `(value, origin)`. */ - pragma[noinline] - private predicate uni_edged_pi_points_to( - SingleSuccessorGuard unipi, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(ControlFlowNode test, ControlFlowNode use, ControlFlowNode orig | - /* - * Because calls such as `len` may create a new variable, we need to go via the source variable - * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. - */ + pragma[noinline] + private predicate multi_assignment_points_to( + MultiAssignmentDefinition def, PointsToContext context, ObjectInternal value, + ControlFlowNode origin + ) { + exists(int index, ControlFlowNode lhs, ControlFlowNode rhs, ObjectInternal sequence | + def.indexOf(index, lhs) and + lhs.(DefinitionNode).getValue() = rhs and + origin = def.getDefiningNode() + | + sequence_index_points_to(rhs, context, sequence, value, index) + or + pointsTo(rhs, context, sequence, _) and + sequence.subscriptUnknown() and + value = TUnknown() + ) + } - unipi.useAndTest(use, test) and - unipi.getSense() = Conditionals::testEvaluates(test, use, context, value, orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - } + /** Points-to for deletion: `del name`. */ + pragma[noinline] + private predicate delete_points_to( + DeletionDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + value = ObjectInternal::undefined() and + origin = def.getDefiningNode() and + context.appliesToScope(def.getScope()) + } - /** Points-to for normal assignments `def = ...`. */ - pragma[noinline] - private predicate assignment_points_to( - AssignmentDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - pointsTo(def.getValue(), context, value, origin) - } + /** Implicit "definition" of `__name__` at the start of a module. */ + pragma[noinline] + private predicate module_name_points_to( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + def.getVariable().getName() = "__name__" and + exists(Module m | m = def.getScope() | + value = module_dunder_name(m) and context.isImport() + or + value.strValue() = "__main__" and context.isMain() and context.appliesToScope(m) + ) and + origin = def.getDefiningNode() + } - pragma[nomagic] - private predicate sequence_index_points_to( - ControlFlowNode f, PointsToContext context, SequenceObjectInternal sequence, - ObjectInternal value, int index - ) { - pointsTo(f, context, sequence, _) and - value = sequence.getItem(index) - } + private ObjectInternal module_dunder_name(Module m) { + exists(string name | result.strValue() = name | + if m.isPackageInit() then name = m.getPackage().getName() else name = m.getName() + ) + } - pragma[noinline] - private predicate multi_assignment_points_to( - MultiAssignmentDefinition def, PointsToContext context, ObjectInternal value, - ControlFlowNode origin - ) { - exists(int index, ControlFlowNode lhs, ControlFlowNode rhs, ObjectInternal sequence | - def.indexOf(index, lhs) and - lhs.(DefinitionNode).getValue() = rhs and - origin = def.getDefiningNode() - | - sequence_index_points_to(rhs, context, sequence, value, index) - or - pointsTo(rhs, context, sequence, _) and - sequence.subscriptUnknown() and - value = TUnknown() - ) - } + /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ + pragma[nomagic] + private predicate ssa_phi_points_to( + PhiFunction phi, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(EssaVariable input | + ssa_phi_reachable_from_input(phi, context, input) and + variablePointsTo(input, context, value, origin) + ) + } - /** Points-to for deletion: `del name`. */ - pragma[noinline] - private predicate delete_points_to( - DeletionDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - value = ObjectInternal::undefined() and - origin = def.getDefiningNode() and - context.appliesToScope(def.getScope()) - } + /* Helper for ssa_phi_points_to */ + cached + predicate ssa_phi_reachable_from_input( + PhiFunction phi, PointsToContext context, EssaVariable input + ) { + exists(BasicBlock pred | + input = phi.getInput(pred) and + reachableEdge(pred, phi.getBasicBlock(), context) + ) + } - /** Implicit "definition" of `__name__` at the start of a module. */ - pragma[noinline] - private predicate module_name_points_to( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - def.getVariable().getName() = "__name__" and - exists(Module m | m = def.getScope() | - value = module_dunder_name(m) and context.isImport() - or - value.strValue() = "__main__" and context.isMain() and context.appliesToScope(m) - ) and - origin = def.getDefiningNode() - } + /** Points-to for implicit variable declarations at scope-entry. */ + pragma[noinline] + private predicate scope_entry_points_to( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + /* Transfer from another scope */ + exists(EssaVariable var, PointsToContext outer, CfgOrigin orig | + InterProceduralPointsTo::scope_entry_value_transfer(var, outer, def, context) and + variablePointsTo(var, outer, value, orig) and + origin = orig.asCfgNodeOrHere(def.getDefiningNode()) + ) + or + /* Undefined variable */ + undefined_variable(def, context, value, origin) + or + /* Builtin not defined in outer scope */ + builtin_not_in_outer_scope(def, context, value, origin) + } - private ObjectInternal module_dunder_name(Module m) { - exists(string name | result.strValue() = name | - if m.isPackageInit() then name = m.getPackage().getName() else name = m.getName() - ) - } + private predicate undefined_variable( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(Scope scope | + not def.getVariable().getName() = "__name__" and + not def.getVariable().isMetaVariable() and + def.getScope() = scope and + context.appliesToScope(scope) + | + def.getSourceVariable() instanceof GlobalVariable and scope instanceof Module + or + def.getSourceVariable() instanceof LocalVariable and + (context.isImport() or context.isRuntime() or context.isMain()) + ) and + value = ObjectInternal::undefined() and + origin = def.getDefiningNode() + } - /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ - pragma[nomagic] - private predicate ssa_phi_points_to( - PhiFunction phi, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(EssaVariable input | - ssa_phi_reachable_from_input(phi, context, input) and - variablePointsTo(input, context, value, origin) - ) - } + private predicate builtin_not_in_outer_scope( + ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(Module mod, GlobalVariable var | + var = def.getSourceVariable() and + mod = def.getScope().getEnclosingModule() and + context.appliesToScope(def.getScope()) and + not exists(EssaVariable v | v.getSourceVariable() = var and v.getScope() = mod) and + value = ObjectInternal::builtin(var.getId()) and + origin = def.getDefiningNode() + ) + } - /* Helper for ssa_phi_points_to */ - cached - predicate ssa_phi_reachable_from_input( - PhiFunction phi, PointsToContext context, EssaVariable input - ) { - exists(BasicBlock pred | - input = phi.getInput(pred) and - reachableEdge(pred, phi.getBasicBlock(), context) - ) - } + /** Holds if `f` is an expression node `tval if cond else fval` and points to `(value, origin)`. */ + private predicate if_exp_points_to( + IfExprNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + pointsTo(f.getAnOperand(), context, value, origin) + } - /** Points-to for implicit variable declarations at scope-entry. */ - pragma[noinline] - private predicate scope_entry_points_to( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - /* Transfer from another scope */ - exists(EssaVariable var, PointsToContext outer, CfgOrigin orig | - InterProceduralPointsTo::scope_entry_value_transfer(var, outer, def, context) and - variablePointsTo(var, outer, value, orig) and - origin = orig.asCfgNodeOrHere(def.getDefiningNode()) - ) - or - /* Undefined variable */ - undefined_variable(def, context, value, origin) - or - /* Builtin not defined in outer scope */ - builtin_not_in_outer_scope(def, context, value, origin) - } - - private predicate undefined_variable( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(Scope scope | - not def.getVariable().getName() = "__name__" and - not def.getVariable().isMetaVariable() and - def.getScope() = scope and - context.appliesToScope(scope) - | - def.getSourceVariable() instanceof GlobalVariable and scope instanceof Module - or - def.getSourceVariable() instanceof LocalVariable and - (context.isImport() or context.isRuntime() or context.isMain()) - ) and - value = ObjectInternal::undefined() and - origin = def.getDefiningNode() - } - - private predicate builtin_not_in_outer_scope( - ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(Module mod, GlobalVariable var | - var = def.getSourceVariable() and - mod = def.getScope().getEnclosingModule() and - context.appliesToScope(def.getScope()) and - not exists(EssaVariable v | v.getSourceVariable() = var and v.getScope() = mod) and - value = ObjectInternal::builtin(var.getId()) and - origin = def.getDefiningNode() - ) - } - - /** Holds if `f` is an expression node `tval if cond else fval` and points to `(value, origin)`. */ - private predicate if_exp_points_to( - IfExprNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - pointsTo(f.getAnOperand(), context, value, origin) - } - - /* Holds if `import name` will import the module `m`. */ - cached - predicate module_imported_as(ModuleObjectInternal m, string name) { - /* Normal imports */ - m.getName() = name - or - /* sys.modules['name'] = m */ - exists(ControlFlowNode sys_modules_flow, ControlFlowNode n, ControlFlowNode mod | - /* Use previous points-to here to avoid slowing down the recursion too much */ - exists(SubscriptNode sub | - sub.getObject() = sys_modules_flow and - pointsTo(sys_modules_flow, _, ObjectInternal::sysModules(), _) and - sub.getIndex() = n and - n.getNode().(StrConst).getText() = name and - sub.(DefinitionNode).getValue() = mod and - pointsTo(mod, _, m, _) - ) - ) - } + /* Holds if `import name` will import the module `m`. */ + cached + predicate module_imported_as(ModuleObjectInternal m, string name) { + /* Normal imports */ + m.getName() = name + or + /* sys.modules['name'] = m */ + exists(ControlFlowNode sys_modules_flow, ControlFlowNode n, ControlFlowNode mod | + /* Use previous points-to here to avoid slowing down the recursion too much */ + exists(SubscriptNode sub | + sub.getObject() = sys_modules_flow and + pointsTo(sys_modules_flow, _, ObjectInternal::sysModules(), _) and + sub.getIndex() = n and + n.getNode().(StrConst).getText() = name and + sub.(DefinitionNode).getValue() = mod and + pointsTo(mod, _, m, _) + ) + ) + } } private module InterModulePointsTo { - pragma[noinline] - predicate import_points_to( - ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(string name, ImportExpr i | - i.getAFlowNode() = f and - i.getImportedModuleName() = name and - PointsToInternal::module_imported_as(value, name) and - origin = f and - context.appliesTo(f) - ) - } + pragma[noinline] + predicate import_points_to( + ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(string name, ImportExpr i | + i.getAFlowNode() = f and + i.getImportedModuleName() = name and + PointsToInternal::module_imported_as(value, name) and + origin = f and + context.appliesTo(f) + ) + } - predicate from_import_points_to( - ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - from_self_import_points_to(f, context, value, origin) + predicate from_import_points_to( + ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + from_self_import_points_to(f, context, value, origin) + or + from_other_import_points_to(f, context, value, origin) + } + + pragma[noinline] + predicate from_self_import_points_to( + ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(EssaVariable var, CfgOrigin orig | + var = ssa_variable_for_module_attribute(f, context) and + PointsToInternal::variablePointsTo(var, context, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + } + + pragma[noinline] + predicate from_other_import_points_to( + ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(string name, ModuleObjectInternal mod, CfgOrigin orig | + from_import_imports(f, context, mod, name) and + (mod.getSourceModule() != f.getEnclosingModule() or mod.isBuiltin()) and + mod.attribute(name, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + or + PointsToInternal::pointsTo(f.getModule(_), context, ObjectInternal::unknown(), _) and + value = ObjectInternal::unknown() and + origin = f + } + + private predicate from_import_imports( + ImportMemberNode f, PointsToContext context, ModuleObjectInternal mod, string name + ) { + PointsToInternal::pointsTo(f.getModule(name), context, mod, _) + } + + pragma[noinline] + private EssaVariable ssa_variable_for_module_attribute(ImportMemberNode f, PointsToContext context) { + exists(string name, ModuleObjectInternal mod, Module m | + mod.getSourceModule() = m and + m = result.getScope() and + PointsToInternal::pointsTo(f.getModule(name), context, mod, _) and + result = ssa_variable_for_module_attribute_helper(f, name, m) + ) + } + + pragma[noinline] + private EssaVariable ssa_variable_for_module_attribute_helper( + ImportMemberNode f, string name, Module m + ) { + result.getSourceVariable().getName() = name and + result.getAUse() = f and + m = f.getEnclosingModule() + } + + /* Helper for implicit_submodule_points_to */ + private ModuleObjectInternal getModule(ImplicitSubModuleDefinition def) { + exists(PackageObjectInternal package | + package.getSourceModule() = def.getDefiningNode().getScope() and + result = package.submodule(def.getSourceVariable().getName()) + ) + } + + /** + * Implicit "definition" of the names of submodules at the start of an `__init__.py` file. + * + * PointsTo isn't exactly how the interpreter works, but is the best approximation we can manage statically. + */ + pragma[noinline] + predicate implicit_submodule_points_to( + ImplicitSubModuleDefinition def, ModuleObjectInternal value, ControlFlowNode origin + ) { + value = getModule(def) and + origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(def.getDefiningNode()) + } + + /** Points-to for `from ... import *`. */ + predicate import_star_points_to( + ImportStarRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + /* Attribute from imported module */ + exists(CfgOrigin orig, ImportStarNode imp, ModuleObjectInternal mod, string name | + imp = def.getDefiningNode() and + PointsToInternal::pointsTo(imp.getModule(), context, mod, _) and + name = def.getSourceVariable().getName() and + moduleExportsBoolean(mod, name) = true and + mod.attribute(name, value, orig) and + origin = orig.fix(imp) + ) + or + /* Retain value held before import */ + exists(EssaVariable var | + variable_not_redefined_by_import_star(var, context, def) and + PointsToInternal::variablePointsTo(var, context, value, origin) + ) + } + + /** Holds if `def` is technically a definition of `var`, but the `from ... import *` does not in fact define `var`. */ + cached + predicate variable_not_redefined_by_import_star( + EssaVariable var, PointsToContext context, ImportStarRefinement def + ) { + var = def.getInput() and + exists(ModuleObjectInternal mod | + PointsToInternal::pointsTo(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _) + | + moduleExportsBoolean(mod, var.getSourceVariable().getName()) = false + or + var.getSourceVariable().getName().charAt(0) = "_" + or + exists(Module m, string name | + m = mod.getSourceModule() and name = var.getSourceVariable().getName() + | + not m.declaredInAll(_) and name.charAt(0) = "_" + ) + ) + } + + predicate ofInterestInExports(ModuleObjectInternal mod, string name) { + exists(ImportStarNode imp, ImportStarRefinement def, EssaVariable var | + imp = def.getDefiningNode() and + PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) and + var = def.getVariable() + | + if var.isMetaVariable() + then ModuleAttributes::attributePointsTo(def.getInput().getDefinition(), name, _, _) + else def.getVariable().getName() = name + ) + or + exists(PackageObjectInternal package | + ofInterestInExports(package, name) and + package.getInitModule() = mod + ) + } + + private boolean pythonModuleExportsBoolean(PythonModuleObjectInternal mod, string name) { + exists(Module src | src = mod.getSourceModule() | + src.declaredInAll(name) and result = true + or + declared_all_is_simple(src) and + not src.declaredInAll(name) and + ofInterestInExports(mod, name) and + result = false + or + (not src.declaredInAll(name) and not declared_all_is_simple(src)) and + exists(ObjectInternal val | ModuleAttributes::pointsToAtExit(src, name, val, _) | + val = ObjectInternal::undefined() and result = false or - from_other_import_points_to(f, context, value, origin) - } + val != ObjectInternal::undefined() and result = true + ) + ) + } - pragma[noinline] - predicate from_self_import_points_to( - ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(EssaVariable var, CfgOrigin orig | - var = ssa_variable_for_module_attribute(f, context) and - PointsToInternal::variablePointsTo(var, context, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - } + /** Holds if __all__ is declared and not mutated */ + private predicate declared_all_is_simple(Module m) { + exists(AssignStmt a, GlobalVariable all | + a.defines(all) and + a.getScope() = m and + all.getId() = "__all__" and + not exists(Attribute attr | all.getALoad() = attr.getObject()) + ) + } - pragma[noinline] - predicate from_other_import_points_to( - ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(string name, ModuleObjectInternal mod, CfgOrigin orig | - from_import_imports(f, context, mod, name) and - (mod.getSourceModule() != f.getEnclosingModule() or mod.isBuiltin()) and - mod.attribute(name, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - or - PointsToInternal::pointsTo(f.getModule(_), context, ObjectInternal::unknown(), _) and - value = ObjectInternal::unknown() and - origin = f - } + private boolean packageExportsBoolean(PackageObjectInternal mod, string name) { + exists(Folder folder | folder = mod.getFolder() | + exportsSubmodule(folder, name) and result = true + or + not exportsSubmodule(folder, name) and + result = moduleExportsBoolean(mod.getInitModule(), name) + or + mod.hasNoInitModule() and + not exportsSubmodule(folder, name) and + ofInterestInExports(mod, name) and + result = false + ) + } - private predicate from_import_imports( - ImportMemberNode f, PointsToContext context, ModuleObjectInternal mod, string name - ) { - PointsToInternal::pointsTo(f.getModule(name), context, mod, _) - } + private predicate exportsSubmodule(Folder folder, string name) { + name.regexpMatch("\\p{L}(\\p{L}|\\d|_)*") and + ( + exists(Folder child | child = folder.getChildContainer(name)) + or + exists(folder.getFile(name + ".py")) + ) + } - pragma[noinline] - private EssaVariable ssa_variable_for_module_attribute(ImportMemberNode f, PointsToContext context) { - exists(string name, ModuleObjectInternal mod, Module m | - mod.getSourceModule() = m and - m = result.getScope() and - PointsToInternal::pointsTo(f.getModule(name), context, mod, _) and - result = ssa_variable_for_module_attribute_helper(f, name, m) - ) - } + boolean builtinModuleExportsBoolean(BuiltinModuleObjectInternal mod, string name) { + exists(Builtin bltn | bltn = mod.getBuiltin() | + exists(bltn.getMember(name)) and result = true + or + ofInterestInExports(mod, name) and not exists(bltn.getMember(name)) and result = false + ) + } - pragma[noinline] - private EssaVariable ssa_variable_for_module_attribute_helper( - ImportMemberNode f, string name, Module m - ) { - result.getSourceVariable().getName() = name and - result.getAUse() = f and - m = f.getEnclosingModule() - } - - /* Helper for implicit_submodule_points_to */ - private ModuleObjectInternal getModule(ImplicitSubModuleDefinition def) { - exists(PackageObjectInternal package | - package.getSourceModule() = def.getDefiningNode().getScope() and - result = package.submodule(def.getSourceVariable().getName()) - ) - } - - /** - * Implicit "definition" of the names of submodules at the start of an `__init__.py` file. - * - * PointsTo isn't exactly how the interpreter works, but is the best approximation we can manage statically. - */ - pragma[noinline] - predicate implicit_submodule_points_to( - ImplicitSubModuleDefinition def, ModuleObjectInternal value, ControlFlowNode origin - ) { - value = getModule(def) and - origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(def.getDefiningNode()) - } - - /** Points-to for `from ... import *`. */ - predicate import_star_points_to( - ImportStarRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - /* Attribute from imported module */ - exists(CfgOrigin orig, ImportStarNode imp, ModuleObjectInternal mod, string name | - imp = def.getDefiningNode() and - PointsToInternal::pointsTo(imp.getModule(), context, mod, _) and - name = def.getSourceVariable().getName() and - moduleExportsBoolean(mod, name) = true and - mod.attribute(name, value, orig) and - origin = orig.fix(imp) - ) - or - /* Retain value held before import */ - exists(EssaVariable var | - variable_not_redefined_by_import_star(var, context, def) and - PointsToInternal::variablePointsTo(var, context, value, origin) - ) - } - - /** Holds if `def` is technically a definition of `var`, but the `from ... import *` does not in fact define `var`. */ - cached - predicate variable_not_redefined_by_import_star( - EssaVariable var, PointsToContext context, ImportStarRefinement def - ) { - var = def.getInput() and - exists(ModuleObjectInternal mod | - PointsToInternal::pointsTo(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _) - | - moduleExportsBoolean(mod, var.getSourceVariable().getName()) = false - or - var.getSourceVariable().getName().charAt(0) = "_" - or - exists(Module m, string name | - m = mod.getSourceModule() and name = var.getSourceVariable().getName() - | - not m.declaredInAll(_) and name.charAt(0) = "_" - ) - ) - } - - predicate ofInterestInExports(ModuleObjectInternal mod, string name) { - exists(ImportStarNode imp, ImportStarRefinement def, EssaVariable var | - imp = def.getDefiningNode() and - PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) and - var = def.getVariable() - | - if var.isMetaVariable() - then ModuleAttributes::attributePointsTo(def.getInput().getDefinition(), name, _, _) - else def.getVariable().getName() = name - ) - or - exists(PackageObjectInternal package | - ofInterestInExports(package, name) and - package.getInitModule() = mod - ) - } - - private boolean pythonModuleExportsBoolean(PythonModuleObjectInternal mod, string name) { - exists(Module src | src = mod.getSourceModule() | - src.declaredInAll(name) and result = true - or - declared_all_is_simple(src) and - not src.declaredInAll(name) and - ofInterestInExports(mod, name) and - result = false - or - (not src.declaredInAll(name) and not declared_all_is_simple(src)) and - exists(ObjectInternal val | ModuleAttributes::pointsToAtExit(src, name, val, _) | - val = ObjectInternal::undefined() and result = false - or - val != ObjectInternal::undefined() and result = true - ) - ) - } - - /** Holds if __all__ is declared and not mutated */ - private predicate declared_all_is_simple(Module m) { - exists(AssignStmt a, GlobalVariable all | - a.defines(all) and - a.getScope() = m and - all.getId() = "__all__" and - not exists(Attribute attr | all.getALoad() = attr.getObject()) - ) - } - - private boolean packageExportsBoolean(PackageObjectInternal mod, string name) { - exists(Folder folder | folder = mod.getFolder() | - exportsSubmodule(folder, name) and result = true - or - not exportsSubmodule(folder, name) and - result = moduleExportsBoolean(mod.getInitModule(), name) - or - mod.hasNoInitModule() and - not exportsSubmodule(folder, name) and - ofInterestInExports(mod, name) and - result = false - ) - } - - private predicate exportsSubmodule(Folder folder, string name) { - name.regexpMatch("\\p{L}(\\p{L}|\\d|_)*") and - ( - exists(Folder child | child = folder.getChildContainer(name)) - or - exists(folder.getFile(name + ".py")) - ) - } - - boolean builtinModuleExportsBoolean(BuiltinModuleObjectInternal mod, string name) { - exists(Builtin bltn | bltn = mod.getBuiltin() | - exists(bltn.getMember(name)) and result = true - or - ofInterestInExports(mod, name) and not exists(bltn.getMember(name)) and result = false - ) - } - - boolean moduleExportsBoolean(ModuleObjectInternal mod, string name) { - not name.charAt(0) = "_" and - ( - result = pythonModuleExportsBoolean(mod, name) - or - result = packageExportsBoolean(mod, name) - or - result = builtinModuleExportsBoolean(mod, name) - ) - } + boolean moduleExportsBoolean(ModuleObjectInternal mod, string name) { + not name.charAt(0) = "_" and + ( + result = pythonModuleExportsBoolean(mod, name) + or + result = packageExportsBoolean(mod, name) + or + result = builtinModuleExportsBoolean(mod, name) + ) + } } module InterProceduralPointsTo { - cached - predicate call(CallNode call, PointsToContext caller, ObjectInternal value) { - PointsToInternal::pointsTo(call.getFunction(), caller, value, _) - } + cached + predicate call(CallNode call, PointsToContext caller, ObjectInternal value) { + PointsToInternal::pointsTo(call.getFunction(), caller, value, _) + } - cached - predicate callWithContext( - CallNode call, PointsToContext caller, ObjectInternal value, PointsToContext callee - ) { - callee.fromCall(call, caller) and - PointsToInternal::pointsTo(call.getFunction(), caller, value, _) - } + cached + predicate callWithContext( + CallNode call, PointsToContext caller, ObjectInternal value, PointsToContext callee + ) { + callee.fromCall(call, caller) and + PointsToInternal::pointsTo(call.getFunction(), caller, value, _) + } - pragma[noinline] - predicate call_points_to( - CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - /* Either not a decorator, or we understand the return value */ - (value != ObjectInternal::unknown() or not f.isDecoratorCall()) and - call_points_to_from_callee(f, context, value, origin) - or - f.isFunctionDecoratorCall() and - call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and - value = TDecoratedFunction(f) and - origin = f - or - f.isClassDecoratorCall() and - call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and - PointsToInternal::pointsTo(f.getArg(0), context, value, origin) - or - Types::six_add_metaclass(f, context, _, _) and - PointsToInternal::pointsTo(f.getArg(0), context, value, origin) - or - Expressions::typeCallPointsTo(f, context, value, origin, _, _) - } + pragma[noinline] + predicate call_points_to( + CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + /* Either not a decorator, or we understand the return value */ + (value != ObjectInternal::unknown() or not f.isDecoratorCall()) and + call_points_to_from_callee(f, context, value, origin) + or + f.isFunctionDecoratorCall() and + call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and + value = TDecoratedFunction(f) and + origin = f + or + f.isClassDecoratorCall() and + call_points_to_from_callee(f, context, ObjectInternal::unknown(), _) and + PointsToInternal::pointsTo(f.getArg(0), context, value, origin) + or + Types::six_add_metaclass(f, context, _, _) and + PointsToInternal::pointsTo(f.getArg(0), context, value, origin) + or + Expressions::typeCallPointsTo(f, context, value, origin, _, _) + } - /** Helper for call_points_to to improve join-order */ - pragma[noinline] - private predicate call_points_to_from_callee( - CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(ObjectInternal func | call(f, context, func) | - exists(CfgOrigin orig, PointsToContext callee | - callee.fromCall(f, context) and - func.callResult(callee, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - or - context.untrackableCall(f) and - func.contextSensitiveCallee() and - value = ObjectInternal::unknown() and - origin = f - or - exists(CfgOrigin orig | - func.callResult(value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) and - context.appliesTo(f) + /** Helper for call_points_to to improve join-order */ + pragma[noinline] + private predicate call_points_to_from_callee( + CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(ObjectInternal func | call(f, context, func) | + exists(CfgOrigin orig, PointsToContext callee | + callee.fromCall(f, context) and + func.callResult(callee, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + or + context.untrackableCall(f) and + func.contextSensitiveCallee() and + value = ObjectInternal::unknown() and + origin = f + or + exists(CfgOrigin orig | + func.callResult(value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) and + context.appliesTo(f) + ) + } + + /** Points-to for parameter. `def foo(param): ...`. */ + pragma[noinline] + predicate parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + self_parameter_points_to(def, context, value, origin) + or + normal_parameter_points_to(def, context, value, origin) + or + default_parameter_points_to(def, context, value, origin) + or + special_parameter_points_to(def, context, value, origin) + } + + /** Helper for `parameter_points_to` */ + pragma[noinline] + private predicate normal_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(PointsToContext caller, ControlFlowNode arg | + PointsToInternal::pointsTo(arg, caller, value, origin) and + named_argument_transfer(arg, caller, def, context) + ) + or + not def.isSelf() and + not def.isVarargs() and + not def.isKwargs() and + context.isRuntime() and + value = ObjectInternal::unknown() and + origin = def.getDefiningNode() + or + positional_parameter_points_to(def, context, value, origin) + } + + pragma[noinline] + private predicate self_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + def.isSelf() and + exists(CallNode call, BoundMethodObjectInternal method, Function func, PointsToContext caller | + callWithContext(call, caller, method, context) and + func = method.getScope() and + def.getScope() = func and + value = method.getSelf() and + origin = value.getOrigin() + ) + } + + predicate selfMethodCall( + SelfCallsiteRefinement def, PointsToContext caller, Function func, PointsToContext callee + ) { + def.getInput().getSourceVariable().(Variable).isSelf() and + exists(PythonFunctionObjectInternal method, CallNode call | + method.getScope() = func and + call = method.getACall() and + call = def.getDefiningNode() and + callee.fromCall(call, caller) + ) + } + + /** Helper for parameter_points_to */ + pragma[noinline] + private predicate default_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + PointsToInternal::importCtxPointsTo(def.getDefault(), value, origin) and + context_for_default_value(def, context) + } + + /** Helper for default_parameter_points_to */ + pragma[noinline] + private predicate context_for_default_value(ParameterDefinition def, PointsToContext context) { + context.isRuntime() + or + exists(PointsToContext caller, CallNode call, PythonFunctionObjectInternal func, int n | + context.fromCall(call, func, caller) and + func.getScope().getArg(n) = def.getParameter() and + not exists(call.getArg(n)) and + not exists(call.getArgByName(def.getVariable().getName())) and + not exists(call.getNode().getKwargs()) and + not exists(call.getNode().getStarargs()) + ) + } + + /** Helper for parameter_points_to */ + pragma[noinline] + private predicate special_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + /* Runtime: Just an unknown tuple (or dict for `**` args) */ + special_parameter_value(def, value) and + context.isRuntime() and + origin = def.getDefiningNode() + or + /* A tuple constructed from positional arguments for a `*` parameter. */ + def.isVarargs() and + exists(CallNode call, Function scope, PointsToContext caller, int offset, int length | + varargs_tuple(call, caller, scope, context, offset, length) and + value = TVarargsTuple(call, caller, offset, length) and + def.getScope() = scope + ) and + origin = def.getDefiningNode() + or + /* A `*` parameter with no surplus positional arguments; an empty tuple */ + def.isVarargs() and + exists(Function scope | + varargs_empty_tuple(scope, context) and + value = ObjectInternal::emptyTuple() and + def.getScope() = scope + ) and + origin = def.getDefiningNode() + } + + /** + * Holds if `call` in context `caller` calls into the function scope `scope` in context `callee` and + * that the number of position arguments (including expansion of `*` argument) exceeds the number of positional arguments by + * `length` and that the excess arguments start at `start`. + */ + predicate varargs_tuple( + CallNode call, PointsToContext caller, Function scope, PointsToContext callee, int start, + int length + ) { + exists(int parameter_offset | + callsite_calls_function(call, caller, scope, callee, parameter_offset) and + start = scope.getPositionalParameterCount() - parameter_offset and + length = positional_argument_count(call, caller) - start and + length > 0 + ) + } + + /** Holds if for function scope `func` in context `callee` the `*` parameter will hold the empty tuple. */ + predicate varargs_empty_tuple(Function func, PointsToContext callee) { + exists(CallNode call, PointsToContext caller, int parameter_offset | + callsite_calls_function(call, caller, func, callee, parameter_offset) and + func.getPositionalParameterCount() - parameter_offset >= + positional_argument_count(call, caller) + ) + } + + /** Helper predicate for special_parameter_points_to */ + private predicate special_parameter_value(ParameterDefinition p, ObjectInternal value) { + p.isVarargs() and value = TUnknownInstance(ObjectInternal::builtin("tuple")) + or + p.isKwargs() and value = TUnknownInstance(ObjectInternal::builtin("dict")) + } + + /** + * Holds if the `n`th argument in call `call` with context `caller` points-to `value` from `origin`, including values in tuples + * expanded by a `*` argument. For example, for the call `f('a', *(`x`,`y`))` the arguments are `('a', 'x', y')` + */ + predicate positional_argument_points_to( + CallNode call, int n, PointsToContext caller, ObjectInternal value, ControlFlowNode origin + ) { + PointsToInternal::pointsTo(call.getArg(n), caller, value, origin) + or + exists(SequenceObjectInternal arg, int pos | + pos = call.getNode().getPositionalArgumentCount() and + PointsToInternal::pointsTo(origin, caller, arg, _) and + value = arg.getItem(n - pos) and + origin = call.getStarArg() + ) + } + + /** Gets the number of positional arguments including values in tuples expanded by a `*` argument. */ + private int positional_argument_count(CallNode call, PointsToContext caller) { + result = call.getNode().getPositionalArgumentCount() and + not exists(call.getStarArg()) and + caller.appliesTo(call) + or + exists(SequenceObjectInternal arg, int pos | + pos = call.getNode().getPositionalArgumentCount() and + PointsToInternal::pointsTo(call.getStarArg(), caller, arg, _) and + result = pos + arg.length() + ) + } + + /** Holds if the parameter definition `def` points-to `value` from `origin` given the context `context` */ + predicate positional_parameter_points_to( + ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin + ) { + exists(CallNode call, int argument, PointsToContext caller, Function func, int offset | + positional_argument_points_to(call, argument, caller, value, origin) and + callsite_calls_function(call, caller, func, context, offset) and + def.getParameter() = func.getArg(argument + offset) + ) + } + + /** Holds if the named `argument` given the context `caller` is transferred to the parameter `param` with conntext `callee` by a call. */ + cached + predicate named_argument_transfer( + ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, + PointsToContext callee + ) { + exists(CallNode call, Function func, int offset | + callsite_calls_function(call, caller, func, callee, offset) + | + exists(string name | + argument = call.getArgByName(name) and + param.getParameter() = func.getArgByName(name) + ) + ) + } + + /** + * Holds if the `call` with context `caller` calls the function `scope` in context `callee` + * and the offset from argument to parameter is `parameter_offset` + */ + cached + predicate callsite_calls_function( + CallNode call, PointsToContext caller, Function scope, PointsToContext callee, + int parameter_offset + ) { + exists(ObjectInternal func | + callWithContext(call, caller, func, callee) and + func.calleeAndOffset(scope, parameter_offset) + ) + } + + /** Model the transfer of values at scope-entry points. Transfer from `(pred_var, pred_context)` to `(succ_def, succ_context)`. */ + cached + predicate scope_entry_value_transfer( + EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, + PointsToContext succ_context + ) { + scope_entry_value_transfer_from_earlier(pred_var, pred_context, succ_def, succ_context) + or + callsite_entry_value_transfer(pred_var, pred_context, succ_def, succ_context) + or + pred_context.isImport() and + pred_context = succ_context and + class_entry_value_transfer(pred_var, succ_def) + } + + /** + * Helper for `scope_entry_value_transfer`. Transfer of values from a temporally earlier scope to later scope. + * Earlier and later scopes are, for example, a module and functions in that module, or an __init__ method and another method. + */ + pragma[noinline] + private predicate scope_entry_value_transfer_from_earlier( + EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, + PointsToContext succ_context + ) { + exists(Scope pred_scope, Scope succ_scope | + BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, pred_scope, succ_def, succ_scope) and + succ_context.appliesToScope(succ_scope) + | + succ_context.isRuntime() and succ_context = pred_context + or + pred_context.isImport() and + pred_scope instanceof ImportTimeScope and + ( + succ_context.fromRuntime() + or + /* A call made at import time, but from another module. Assume this module has been fully imported. */ + succ_context.isCall() and + exists(CallNode call | + succ_context.fromCall(call, _) and call.getEnclosingModule() != pred_scope ) - } + ) + or + /* + * If predecessor scope is main, then we assume that any global defined exactly once + * is available to all functions. Although not strictly true, this gives less surprising + * results in practice. + */ - /** Points-to for parameter. `def foo(param): ...`. */ - pragma[noinline] - predicate parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - self_parameter_points_to(def, context, value, origin) - or - normal_parameter_points_to(def, context, value, origin) - or - default_parameter_points_to(def, context, value, origin) - or - special_parameter_points_to(def, context, value, origin) - } + pred_context.isMain() and + pred_scope instanceof Module and + succ_context.fromRuntime() and + exists(Variable v | + v = pred_var.getSourceVariable() and + not strictcount(v.getAStore()) > 1 + ) + ) + or + exists(NonEscapingGlobalVariable var | + var = pred_var.getSourceVariable() and + var = succ_def.getSourceVariable() and + pred_var.getAUse() = succ_context.getRootCall() and + pred_context.isImport() and + succ_context.appliesToScope(succ_def.getScope()) + ) + } - /** Helper for `parameter_points_to` */ - pragma[noinline] - private predicate normal_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(PointsToContext caller, ControlFlowNode arg | - PointsToInternal::pointsTo(arg, caller, value, origin) and - named_argument_transfer(arg, caller, def, context) + /** + * Helper for `scope_entry_value_transfer`. + * Transfer of values from the callsite to the callee, for enclosing variables, but not arguments/parameters. + */ + pragma[noinline] + private predicate callsite_entry_value_transfer( + EssaVariable caller_var, PointsToContext caller, ScopeEntryDefinition entry_def, + PointsToContext callee + ) { + exists(ControlFlowNode use, SsaSourceVariable var | + var_and_use(caller_var, use, var) and + entry_def.getSourceVariable() = var and + callsite_calls_function(use, caller, entry_def.getScope(), callee, _) + ) + } + + pragma[nomagic] + private predicate var_and_use(EssaVariable caller_var, ControlFlowNode use, SsaSourceVariable var) { + use = caller_var.getAUse() and + var = caller_var.getSourceVariable() + } + + /** Helper for `scope_entry_value_transfer`. */ + private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { + exists(ImportTimeScope scope, ControlFlowNode class_def | + class_def = pred_var.getAUse() and + scope.entryEdge(class_def, succ_def.getDefiningNode()) and + pred_var.getSourceVariable() = succ_def.getSourceVariable() + ) + } + + /** + * Points-to for a variable (possibly) redefined by a call: + * `var = ...; foo(); use(var)` + * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). + */ + pragma[noinline] + predicate callsite_points_to( + CallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin + ) { + exists(SsaSourceVariable srcvar | srcvar = def.getSourceVariable() | + if srcvar instanceof EscapingAssignmentGlobalVariable + then + /* If global variable can be reassigned, we need to track it through calls */ + exists(EssaVariable var, Function func, PointsToContext callee | + callsite_calls_function(def.getCall(), context, func, callee, _) and + var_at_exit(srcvar, func, var) and + PointsToInternal::variablePointsTo(var, callee, value, origin) ) or - not def.isSelf() and - not def.isVarargs() and - not def.isKwargs() and - context.isRuntime() and - value = ObjectInternal::unknown() and - origin = def.getDefiningNode() - or - positional_parameter_points_to(def, context, value, origin) - } - - pragma[noinline] - private predicate self_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - def.isSelf() and - exists(CallNode call, BoundMethodObjectInternal method, Function func, PointsToContext caller | - callWithContext(call, caller, method, context) and - func = method.getScope() and - def.getScope() = func and - value = method.getSelf() and - origin = value.getOrigin() + exists(ObjectInternal callable | + PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and + exists(callable.getBuiltin()) and + PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) ) - } + else + /* Otherwise we can assume its value (but not those of its attributes or members) has not changed. */ + PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) + ) + } - predicate selfMethodCall( - SelfCallsiteRefinement def, PointsToContext caller, Function func, PointsToContext callee - ) { - def.getInput().getSourceVariable().(Variable).isSelf() and - exists(PythonFunctionObjectInternal method, CallNode call | - method.getScope() = func and - call = method.getACall() and - call = def.getDefiningNode() and - callee.fromCall(call, caller) - ) - } + /* Helper for computing ESSA variables at scope exit. */ + private predicate var_at_exit(Variable var, Scope scope, EssaVariable evar) { + not var instanceof LocalVariable and + evar.getSourceVariable() = var and + evar.getScope() = scope and + BaseFlow::reaches_exit(evar) + } - /** Helper for parameter_points_to */ - pragma[noinline] - private predicate default_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - PointsToInternal::importCtxPointsTo(def.getDefault(), value, origin) and - context_for_default_value(def, context) - } - - /** Helper for default_parameter_points_to */ - pragma[noinline] - private predicate context_for_default_value(ParameterDefinition def, PointsToContext context) { - context.isRuntime() - or - exists(PointsToContext caller, CallNode call, PythonFunctionObjectInternal func, int n | - context.fromCall(call, func, caller) and - func.getScope().getArg(n) = def.getParameter() and - not exists(call.getArg(n)) and - not exists(call.getArgByName(def.getVariable().getName())) and - not exists(call.getNode().getKwargs()) and - not exists(call.getNode().getStarargs()) - ) - } - - /** Helper for parameter_points_to */ - pragma[noinline] - private predicate special_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - /* Runtime: Just an unknown tuple (or dict for `**` args) */ - special_parameter_value(def, value) and - context.isRuntime() and - origin = def.getDefiningNode() - or - /* A tuple constructed from positional arguments for a `*` parameter. */ - def.isVarargs() and - exists(CallNode call, Function scope, PointsToContext caller, int offset, int length | - varargs_tuple(call, caller, scope, context, offset, length) and - value = TVarargsTuple(call, caller, offset, length) and - def.getScope() = scope - ) and - origin = def.getDefiningNode() - or - /* A `*` parameter with no surplus positional arguments; an empty tuple */ - def.isVarargs() and - exists(Function scope | - varargs_empty_tuple(scope, context) and - value = ObjectInternal::emptyTuple() and - def.getScope() = scope - ) and - origin = def.getDefiningNode() - } - - /** - * Holds if `call` in context `caller` calls into the function scope `scope` in context `callee` and - * that the number of position arguments (including expansion of `*` argument) exceeds the number of positional arguments by - * `length` and that the excess arguments start at `start`. + /** + * INTERNAL -- Use `FunctionObject.neverReturns()` instead. + * Whether function `func` never returns. Slightly conservative approximation, this predicate may be false + * for a function that can never return. + */ + cached + predicate neverReturns(Function f) { + /* + * A Python function never returns if it has no normal exits that are not dominated by a + * call to a function which itself never returns. */ - predicate varargs_tuple( - CallNode call, PointsToContext caller, Function scope, PointsToContext callee, int start, - int length - ) { - exists(int parameter_offset | - callsite_calls_function(call, caller, scope, callee, parameter_offset) and - start = scope.getPositionalParameterCount() - parameter_offset and - length = positional_argument_count(call, caller) - start and - length > 0 - ) - } - /** Holds if for function scope `func` in context `callee` the `*` parameter will hold the empty tuple. */ - predicate varargs_empty_tuple(Function func, PointsToContext callee) { - exists(CallNode call, PointsToContext caller, int parameter_offset | - callsite_calls_function(call, caller, func, callee, parameter_offset) and - func.getPositionalParameterCount() - parameter_offset >= - positional_argument_count(call, caller) - ) - } - - /** Helper predicate for special_parameter_points_to */ - private predicate special_parameter_value(ParameterDefinition p, ObjectInternal value) { - p.isVarargs() and value = TUnknownInstance(ObjectInternal::builtin("tuple")) - or - p.isKwargs() and value = TUnknownInstance(ObjectInternal::builtin("dict")) - } - - /** - * Holds if the `n`th argument in call `call` with context `caller` points-to `value` from `origin`, including values in tuples - * expanded by a `*` argument. For example, for the call `f('a', *(`x`,`y`))` the arguments are `('a', 'x', y')` - */ - predicate positional_argument_points_to( - CallNode call, int n, PointsToContext caller, ObjectInternal value, ControlFlowNode origin - ) { - PointsToInternal::pointsTo(call.getArg(n), caller, value, origin) - or - exists(SequenceObjectInternal arg, int pos | - pos = call.getNode().getPositionalArgumentCount() and - PointsToInternal::pointsTo(origin, caller, arg, _) and - value = arg.getItem(n - pos) and - origin = call.getStarArg() - ) - } - - /** Gets the number of positional arguments including values in tuples expanded by a `*` argument. */ - private int positional_argument_count(CallNode call, PointsToContext caller) { - result = call.getNode().getPositionalArgumentCount() and - not exists(call.getStarArg()) and - caller.appliesTo(call) - or - exists(SequenceObjectInternal arg, int pos | - pos = call.getNode().getPositionalArgumentCount() and - PointsToInternal::pointsTo(call.getStarArg(), caller, arg, _) and - result = pos + arg.length() - ) - } - - /** Holds if the parameter definition `def` points-to `value` from `origin` given the context `context` */ - predicate positional_parameter_points_to( - ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin - ) { - exists(CallNode call, int argument, PointsToContext caller, Function func, int offset | - positional_argument_points_to(call, argument, caller, value, origin) and - callsite_calls_function(call, caller, func, context, offset) and - def.getParameter() = func.getArg(argument + offset) - ) - } - - /** Holds if the named `argument` given the context `caller` is transferred to the parameter `param` with conntext `callee` by a call. */ - cached - predicate named_argument_transfer( - ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, - PointsToContext callee - ) { - exists(CallNode call, Function func, int offset | - callsite_calls_function(call, caller, func, callee, offset) - | - exists(string name | - argument = call.getArgByName(name) and - param.getParameter() = func.getArgByName(name) - ) - ) - } - - /** - * Holds if the `call` with context `caller` calls the function `scope` in context `callee` - * and the offset from argument to parameter is `parameter_offset` - */ - cached - predicate callsite_calls_function( - CallNode call, PointsToContext caller, Function scope, PointsToContext callee, - int parameter_offset - ) { - exists(ObjectInternal func | - callWithContext(call, caller, func, callee) and - func.calleeAndOffset(scope, parameter_offset) - ) - } - - /** Model the transfer of values at scope-entry points. Transfer from `(pred_var, pred_context)` to `(succ_def, succ_context)`. */ - cached - predicate scope_entry_value_transfer( - EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, - PointsToContext succ_context - ) { - scope_entry_value_transfer_from_earlier(pred_var, pred_context, succ_def, succ_context) - or - callsite_entry_value_transfer(pred_var, pred_context, succ_def, succ_context) - or - pred_context.isImport() and - pred_context = succ_context and - class_entry_value_transfer(pred_var, succ_def) - } - - /** - * Helper for `scope_entry_value_transfer`. Transfer of values from a temporally earlier scope to later scope. - * Earlier and later scopes are, for example, a module and functions in that module, or an __init__ method and another method. - */ - pragma[noinline] - private predicate scope_entry_value_transfer_from_earlier( - EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, - PointsToContext succ_context - ) { - exists(Scope pred_scope, Scope succ_scope | - BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, pred_scope, succ_def, succ_scope) and - succ_context.appliesToScope(succ_scope) - | - succ_context.isRuntime() and succ_context = pred_context - or - pred_context.isImport() and - pred_scope instanceof ImportTimeScope and - ( - succ_context.fromRuntime() - or - /* A call made at import time, but from another module. Assume this module has been fully imported. */ - succ_context.isCall() and - exists(CallNode call | - succ_context.fromCall(call, _) and call.getEnclosingModule() != pred_scope - ) - ) - or - /* - * If predecessor scope is main, then we assume that any global defined exactly once - * is available to all functions. Although not strictly true, this gives less surprising - * results in practice. - */ - - pred_context.isMain() and - pred_scope instanceof Module and - succ_context.fromRuntime() and - exists(Variable v | - v = pred_var.getSourceVariable() and - not strictcount(v.getAStore()) > 1 - ) - ) - or - exists(NonEscapingGlobalVariable var | - var = pred_var.getSourceVariable() and - var = succ_def.getSourceVariable() and - pred_var.getAUse() = succ_context.getRootCall() and - pred_context.isImport() and - succ_context.appliesToScope(succ_def.getScope()) - ) - } - - /** - * Helper for `scope_entry_value_transfer`. - * Transfer of values from the callsite to the callee, for enclosing variables, but not arguments/parameters. - */ - pragma[noinline] - private predicate callsite_entry_value_transfer( - EssaVariable caller_var, PointsToContext caller, ScopeEntryDefinition entry_def, - PointsToContext callee - ) { - exists(ControlFlowNode use, SsaSourceVariable var | - var_and_use(caller_var, use, var) and - entry_def.getSourceVariable() = var and - callsite_calls_function(use, caller, entry_def.getScope(), callee, _) - ) - } - - pragma[nomagic] - private predicate var_and_use(EssaVariable caller_var, ControlFlowNode use, SsaSourceVariable var) { - use = caller_var.getAUse() and - var = caller_var.getSourceVariable() - } - - /** Helper for `scope_entry_value_transfer`. */ - private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { - exists(ImportTimeScope scope, ControlFlowNode class_def | - class_def = pred_var.getAUse() and - scope.entryEdge(class_def, succ_def.getDefiningNode()) and - pred_var.getSourceVariable() = succ_def.getSourceVariable() - ) - } - - /** - * Points-to for a variable (possibly) redefined by a call: - * `var = ...; foo(); use(var)` - * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). - */ - pragma[noinline] - predicate callsite_points_to( - CallsiteRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin - ) { - exists(SsaSourceVariable srcvar | srcvar = def.getSourceVariable() | - if srcvar instanceof EscapingAssignmentGlobalVariable - then - /* If global variable can be reassigned, we need to track it through calls */ - exists(EssaVariable var, Function func, PointsToContext callee | - callsite_calls_function(def.getCall(), context, func, callee, _) and - var_at_exit(srcvar, func, var) and - PointsToInternal::variablePointsTo(var, callee, value, origin) - ) - or - exists(ObjectInternal callable | - PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and - exists(callable.getBuiltin()) and - PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) - ) - else - /* Otherwise we can assume its value (but not those of its attributes or members) has not changed. */ - PointsToInternal::variablePointsTo(def.getInput(), context, value, origin) - ) - } - - /* Helper for computing ESSA variables at scope exit. */ - private predicate var_at_exit(Variable var, Scope scope, EssaVariable evar) { - not var instanceof LocalVariable and - evar.getSourceVariable() = var and - evar.getScope() = scope and - BaseFlow::reaches_exit(evar) - } - - /** - * INTERNAL -- Use `FunctionObject.neverReturns()` instead. - * Whether function `func` never returns. Slightly conservative approximation, this predicate may be false - * for a function that can never return. - */ - cached - predicate neverReturns(Function f) { - /* - * A Python function never returns if it has no normal exits that are not dominated by a - * call to a function which itself never returns. - */ - - forall(BasicBlock exit | exit = f.getANormalExit().getBasicBlock() | - exists(FunctionObject callee, BasicBlock call | - callee.getACall().getBasicBlock() = call and - callee.neverReturns() and - call.dominates(exit) - ) - ) - } + forall(BasicBlock exit | exit = f.getANormalExit().getBasicBlock() | + exists(FunctionObject callee, BasicBlock call | + callee.getACall().getBasicBlock() = call and + callee.neverReturns() and + call.dominates(exit) + ) + ) + } } /** Gets the `value, origin` that `f` would refer to if it has not been assigned some other value */ pragma[noinline] private predicate potential_builtin_points_to( - NameNode f, ObjectInternal value, ControlFlowNode origin + NameNode f, ObjectInternal value, ControlFlowNode origin ) { - f.isGlobal() and - f.isLoad() and - origin = f and - ( - value = ObjectInternal::builtin(f.getId()) - or - not exists(Builtin::builtin(f.getId())) and value = ObjectInternal::unknown() - ) + f.isGlobal() and + f.isLoad() and + origin = f and + ( + value = ObjectInternal::builtin(f.getId()) + or + not exists(Builtin::builtin(f.getId())) and value = ObjectInternal::unknown() + ) } module Expressions { - pragma[noinline] - private predicate attributeObjectPointsto( - AttrNode attr, PointsToContext context, string name, ControlFlowNode obj, - ObjectInternal objvalue - ) { - attr.isLoad() and - attr.getObject(name) = obj and - PointsToInternal::pointsTo(obj, context, objvalue, _) - } + pragma[noinline] + private predicate attributeObjectPointsto( + AttrNode attr, PointsToContext context, string name, ControlFlowNode obj, + ObjectInternal objvalue + ) { + attr.isLoad() and + attr.getObject(name) = obj and + PointsToInternal::pointsTo(obj, context, objvalue, _) + } - pragma[noinline] - predicate attributePointsTo( - AttrNode attr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode obj, ObjectInternal objvalue - ) { - exists(string name | attributeObjectPointsto(attr, context, name, obj, objvalue) | - exists(CfgOrigin orig | - objvalue.attribute(name, value, orig) and - origin = orig.asCfgNodeOrHere(attr) - ) - or - objvalue.attributesUnknown() and - origin = attr and - value = ObjectInternal::unknown() - ) - } + pragma[noinline] + predicate attributePointsTo( + AttrNode attr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode obj, ObjectInternal objvalue + ) { + exists(string name | attributeObjectPointsto(attr, context, name, obj, objvalue) | + exists(CfgOrigin orig | + objvalue.attribute(name, value, orig) and + origin = orig.asCfgNodeOrHere(attr) + ) + or + objvalue.attributesUnknown() and + origin = attr and + value = ObjectInternal::unknown() + ) + } - pragma[noinline] - predicate subscriptPointsTo( - SubscriptNode subscr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode obj, ObjectInternal objvalue - ) { - exists(ControlFlowNode index | subscriptObjectAndIndex(subscr, context, obj, objvalue, index) | - objvalue.subscriptUnknown() and - value = ObjectInternal::unknown() - or - exists(int n | - PointsToInternal::pointsTo(index, context, TInt(n), _) and - value = objvalue.(SequenceObjectInternal).getItem(n) - ) - ) and - origin = subscr - } + pragma[noinline] + predicate subscriptPointsTo( + SubscriptNode subscr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode obj, ObjectInternal objvalue + ) { + exists(ControlFlowNode index | subscriptObjectAndIndex(subscr, context, obj, objvalue, index) | + objvalue.subscriptUnknown() and + value = ObjectInternal::unknown() + or + exists(int n | + PointsToInternal::pointsTo(index, context, TInt(n), _) and + value = objvalue.(SequenceObjectInternal).getItem(n) + ) + ) and + origin = subscr + } - predicate subscriptPartsPointsTo( - SubscriptNode subscr, PointsToContext context, ObjectInternal objvalue, - ObjectInternal indexvalue - ) { - exists(ControlFlowNode index | - subscriptObjectAndIndex(subscr, context, _, objvalue, index) and - PointsToInternal::pointsTo(index, context, indexvalue, _) - ) - } + predicate subscriptPartsPointsTo( + SubscriptNode subscr, PointsToContext context, ObjectInternal objvalue, + ObjectInternal indexvalue + ) { + exists(ControlFlowNode index | + subscriptObjectAndIndex(subscr, context, _, objvalue, index) and + PointsToInternal::pointsTo(index, context, indexvalue, _) + ) + } - pragma[noinline] - private predicate subscriptObjectAndIndex( - SubscriptNode subscr, PointsToContext context, ControlFlowNode obj, ObjectInternal objvalue, - ControlFlowNode index - ) { - subscr.isLoad() and - obj = subscr.getObject() and - PointsToInternal::pointsTo(obj, context, objvalue, _) and - index = subscr.getIndex() - } + pragma[noinline] + private predicate subscriptObjectAndIndex( + SubscriptNode subscr, PointsToContext context, ControlFlowNode obj, ObjectInternal objvalue, + ControlFlowNode index + ) { + subscr.isLoad() and + obj = subscr.getObject() and + PointsToInternal::pointsTo(obj, context, objvalue, _) and + index = subscr.getIndex() + } - /** - * Tracking too many binary expressions is likely to kill performance, so just say anything other than addition or bitwise or is 'unknown'. - */ - pragma[noinline] - predicate binaryPointsTo( - BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - origin = b and - operand = genericBinaryOperand(b) and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - value = ObjectInternal::unknown() - } + /** + * Tracking too many binary expressions is likely to kill performance, so just say anything other than addition or bitwise or is 'unknown'. + */ + pragma[noinline] + predicate binaryPointsTo( + BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + origin = b and + operand = genericBinaryOperand(b) and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + value = ObjectInternal::unknown() + } - private ControlFlowNode genericBinaryOperand(BinaryExprNode b) { - exists(Operator op | - b.operands(result, op, _) - or - b.operands(_, op, result) - | - not op instanceof BitOr and - not op instanceof Add - ) - } + private ControlFlowNode genericBinaryOperand(BinaryExprNode b) { + exists(Operator op | + b.operands(result, op, _) + or + b.operands(_, op, result) + | + not op instanceof BitOr and + not op instanceof Add + ) + } - pragma[noinline] - predicate addPointsTo( - BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - origin = b and - exists(Operator op | - b.operands(operand, op, _) - or - b.operands(_, op, operand) - | - op instanceof Add and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - value = TUnknownInstance(opvalue.getClass()) - ) - } + pragma[noinline] + predicate addPointsTo( + BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + origin = b and + exists(Operator op | + b.operands(operand, op, _) + or + b.operands(_, op, operand) + | + op instanceof Add and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + value = TUnknownInstance(opvalue.getClass()) + ) + } - pragma[noinline] - predicate bitOrPointsTo( - BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - origin = b and - exists(Operator op, ControlFlowNode other | - b.operands(operand, op, other) - or - b.operands(other, op, operand) - | - op instanceof BitOr and - exists(ObjectInternal obj, int i1, int i2 | - pointsToInt(operand, context, opvalue, i1) and - pointsToInt(other, context, obj, i2) and - value = TInt(i1.bitOr(i2)) - ) - ) - } + pragma[noinline] + predicate bitOrPointsTo( + BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + origin = b and + exists(Operator op, ControlFlowNode other | + b.operands(operand, op, other) + or + b.operands(other, op, operand) + | + op instanceof BitOr and + exists(ObjectInternal obj, int i1, int i2 | + pointsToInt(operand, context, opvalue, i1) and + pointsToInt(other, context, obj, i2) and + value = TInt(i1.bitOr(i2)) + ) + ) + } - predicate pointsToInt(ControlFlowNode n, PointsToContext context, ObjectInternal obj, int value) { - PointsToInternal::pointsTo(n, context, obj, _) and - value = obj.intValue() - } + predicate pointsToInt(ControlFlowNode n, PointsToContext context, ObjectInternal obj, int value) { + PointsToInternal::pointsTo(n, context, obj, _) and + value = obj.intValue() + } - pragma[noinline] - predicate unaryPointsTo( - UnaryExprNode u, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode operand, ObjectInternal opvalue - ) { - exists(Unaryop op | - op = u.getNode().getOp() and - operand = u.getOperand() and - PointsToInternal::pointsTo(operand, context, opvalue, _) - | - op instanceof Not and value = ObjectInternal::bool(opvalue.booleanValue().booleanNot()) - or - op instanceof USub and value = ObjectInternal::fromInt(-opvalue.intValue()) - or - not op instanceof Not and opvalue = ObjectInternal::unknown() and value = opvalue - ) and - origin = u - } + pragma[noinline] + predicate unaryPointsTo( + UnaryExprNode u, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode operand, ObjectInternal opvalue + ) { + exists(Unaryop op | + op = u.getNode().getOp() and + operand = u.getOperand() and + PointsToInternal::pointsTo(operand, context, opvalue, _) + | + op instanceof Not and value = ObjectInternal::bool(opvalue.booleanValue().booleanNot()) + or + op instanceof USub and value = ObjectInternal::fromInt(-opvalue.intValue()) + or + not op instanceof Not and opvalue = ObjectInternal::unknown() and value = opvalue + ) and + origin = u + } - pragma[noinline] - predicate builtinCallPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - PointsToInternal::pointsTo(arg, context, argvalue, _) and - arg = call.getArg(0) and - exists(BuiltinFunctionObjectInternal callable | - PointsToInternal::pointsTo(call.getFunction(), context, callable, _) - | - callable != ObjectInternal::builtin("len") and - callable != ObjectInternal::builtin("callable") and - callable != ObjectInternal::builtin("isinstance") and - callable != ObjectInternal::builtin("issubclass") and - callable != ObjectInternal::builtin("hasattr") and - callable.isClass() = false and - value = ObjectInternal::unknown() - ) and - origin = call - } + pragma[noinline] + predicate builtinCallPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + PointsToInternal::pointsTo(arg, context, argvalue, _) and + arg = call.getArg(0) and + exists(BuiltinFunctionObjectInternal callable | + PointsToInternal::pointsTo(call.getFunction(), context, callable, _) + | + callable != ObjectInternal::builtin("len") and + callable != ObjectInternal::builtin("callable") and + callable != ObjectInternal::builtin("isinstance") and + callable != ObjectInternal::builtin("issubclass") and + callable != ObjectInternal::builtin("hasattr") and + callable.isClass() = false and + value = ObjectInternal::unknown() + ) and + origin = call + } - pragma[noinline] - predicate typeCallPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - type_call1(call, arg, context, argvalue) and - value = argvalue.getClass() and - origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(call) - } + pragma[noinline] + predicate typeCallPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + type_call1(call, arg, context, argvalue) and + value = argvalue.getClass() and + origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(call) + } - pragma[noinline] - private predicate lenCallPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - len_call(call, arg, context, argvalue) and - origin = call and - exists(int len | len = argvalue.length() | - value = TInt(len) and len >= 0 - or - len < 0 and value = TUnknownInstance(ObjectInternal::builtin("int")) - ) - } + pragma[noinline] + private predicate lenCallPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + len_call(call, arg, context, argvalue) and + origin = call and + exists(int len | len = argvalue.length() | + value = TInt(len) and len >= 0 + or + len < 0 and value = TUnknownInstance(ObjectInternal::builtin("int")) + ) + } - pragma[noinline] - private predicate getattrPointsTo( - CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode arg, ObjectInternal argvalue - ) { - exists(string name | getattr_call(call, arg, context, argvalue, name) | - argvalue.attributesUnknown() and value = ObjectInternal::unknown() and origin = call - or - exists(CfgOrigin valOrigin | - argvalue.attribute(name, value, valOrigin) and origin = valOrigin.asCfgNodeOrHere(call) - ) - ) - } + pragma[noinline] + private predicate getattrPointsTo( + CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode arg, ObjectInternal argvalue + ) { + exists(string name | getattr_call(call, arg, context, argvalue, name) | + argvalue.attributesUnknown() and value = ObjectInternal::unknown() and origin = call + or + exists(CfgOrigin valOrigin | + argvalue.attribute(name, value, valOrigin) and origin = valOrigin.asCfgNodeOrHere(call) + ) + ) + } - pragma[noinline] - predicate getattr_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name - ) { - exists(ControlFlowNode arg1 | - call_and_args_for_getattr(call, context, use, arg1) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsToString(arg1, context, name) - ) - } + pragma[noinline] + predicate getattr_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name + ) { + exists(ControlFlowNode arg1 | + call_and_args_for_getattr(call, context, use, arg1) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsToString(arg1, context, name) + ) + } - pragma[noinline] - private predicate call_and_args_for_getattr( - ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 - ) { - exists(ControlFlowNode func | - call2(call, func, arg0, arg1) and - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("getattr"), _) - ) - } + pragma[noinline] + private predicate call_and_args_for_getattr( + ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 + ) { + exists(ControlFlowNode func | + call2(call, func, arg0, arg1) and + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("getattr"), _) + ) + } - pragma[noinline] - predicate setattr_call( - CallNode call, PointsToContext context, ControlFlowNode obj, string name, ObjectInternal val, - ControlFlowNode origin - ) { - exists(ControlFlowNode arg1, ControlFlowNode arg2 | - call_and_args_for_setattr(call, context, obj, arg1, arg2) and - PointsToInternal::pointsTo(arg2, context, val, origin) and - PointsToInternal::pointsToString(arg1, context, name) - ) - } + pragma[noinline] + predicate setattr_call( + CallNode call, PointsToContext context, ControlFlowNode obj, string name, ObjectInternal val, + ControlFlowNode origin + ) { + exists(ControlFlowNode arg1, ControlFlowNode arg2 | + call_and_args_for_setattr(call, context, obj, arg1, arg2) and + PointsToInternal::pointsTo(arg2, context, val, origin) and + PointsToInternal::pointsToString(arg1, context, name) + ) + } - pragma[noinline] - private predicate call_and_args_for_setattr( - ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1, - ControlFlowNode arg2 - ) { - exists(ControlFlowNode func | - call3(call, func, arg0, arg1, arg2) and - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("setattr"), _) - ) - } + pragma[noinline] + private predicate call_and_args_for_setattr( + ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1, + ControlFlowNode arg2 + ) { + exists(ControlFlowNode func | + call3(call, func, arg0, arg1, arg2) and + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("setattr"), _) + ) + } - pragma[noinline] - private boolean containsComparisonEvaluatesTo( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue - ) { - exists(Cmpop op | - comp.operands(operand, op, _) or - comp.operands(_, op, operand) - | - (op instanceof In or op instanceof NotIn) and - PointsToInternal::pointsTo(operand, context, opvalue, _) - ) and - result = maybe() - } + pragma[noinline] + private boolean containsComparisonEvaluatesTo( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue + ) { + exists(Cmpop op | + comp.operands(operand, op, _) or + comp.operands(_, op, operand) + | + (op instanceof In or op instanceof NotIn) and + PointsToInternal::pointsTo(operand, context, opvalue, _) + ) and + result = maybe() + } - pragma[noinline] - private boolean equalityEvaluatesTo( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue - ) { - exists(ObjectInternal other, boolean sense | - equalityTest(comp, context, operand, opvalue, other, sense) - | - other = opvalue and result = sense - or - other != opvalue and result = sense.booleanNot() - or - opvalue.notTestableForEquality() and result = maybe() - or - other.notTestableForEquality() and result = maybe() - ) - } + pragma[noinline] + private boolean equalityEvaluatesTo( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue + ) { + exists(ObjectInternal other, boolean sense | + equalityTest(comp, context, operand, opvalue, other, sense) + | + other = opvalue and result = sense + or + other != opvalue and result = sense.booleanNot() + or + opvalue.notTestableForEquality() and result = maybe() + or + other.notTestableForEquality() and result = maybe() + ) + } - pragma[noinline] - private boolean comparesToUnknown( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue - ) { - (comp.operands(operand, _, _) or comp.operands(_, _, operand)) and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - opvalue = ObjectInternal::unknown() and - result = maybe() - } + pragma[noinline] + private boolean comparesToUnknown( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue + ) { + (comp.operands(operand, _, _) or comp.operands(_, _, operand)) and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + opvalue = ObjectInternal::unknown() and + result = maybe() + } - pragma[noinline] - private predicate equalityTest( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, - ObjectInternal other, boolean sense - ) { - exists(ControlFlowNode r | - equality_test(comp, operand, sense, r) and - PointsToInternal::pointsTo(operand, context, opvalue, _) and - PointsToInternal::pointsTo(r, context, other, _) - ) - } + pragma[noinline] + private predicate equalityTest( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, + ObjectInternal other, boolean sense + ) { + exists(ControlFlowNode r | + equality_test(comp, operand, sense, r) and + PointsToInternal::pointsTo(operand, context, opvalue, _) and + PointsToInternal::pointsTo(r, context, other, _) + ) + } - pragma[noinline] - private boolean inequalityEvaluatesTo( - CompareNode comp, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(boolean strict, boolean sense, ObjectInternal other | - inequalityTest(comp, context, use, val, other, strict, sense) - | - compare(val, other) = -1 and result = sense - or - compare(val, other) = 0 and result = strict.booleanNot() - or - compare(val, other) = 1 and result = sense.booleanNot() - or - val.notTestableForEquality() and result = maybe() - or - other.notTestableForEquality() and result = maybe() - ) - } + pragma[noinline] + private boolean inequalityEvaluatesTo( + CompareNode comp, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(boolean strict, boolean sense, ObjectInternal other | + inequalityTest(comp, context, use, val, other, strict, sense) + | + compare(val, other) = -1 and result = sense + or + compare(val, other) = 0 and result = strict.booleanNot() + or + compare(val, other) = 1 and result = sense.booleanNot() + or + val.notTestableForEquality() and result = maybe() + or + other.notTestableForEquality() and result = maybe() + ) + } - private int compare(ObjectInternal val, ObjectInternal other) { - inequalityTest(_, _, _, val, other, _, _) and - result = compare_unbound(val, other) - or - result = compare_sequence(val, other, 0) - } + private int compare(ObjectInternal val, ObjectInternal other) { + inequalityTest(_, _, _, val, other, _, _) and + result = compare_unbound(val, other) + or + result = compare_sequence(val, other, 0) + } - bindingset[val, other] - private int compare_unbound(ObjectInternal val, ObjectInternal other) { - val.intValue() < other.intValue() and result = -1 - or - val.intValue() > other.intValue() and result = 1 - or - val.intValue() = other.intValue() and result = 0 - or - val.strValue() < other.strValue() and result = -1 - or - val.strValue() > other.strValue() and result = 1 - or - val.strValue() = other.strValue() and result = 0 - } + bindingset[val, other] + private int compare_unbound(ObjectInternal val, ObjectInternal other) { + val.intValue() < other.intValue() and result = -1 + or + val.intValue() > other.intValue() and result = 1 + or + val.intValue() = other.intValue() and result = 0 + or + val.strValue() < other.strValue() and result = -1 + or + val.strValue() > other.strValue() and result = 1 + or + val.strValue() = other.strValue() and result = 0 + } - pragma[nomagic] - private int compare_sequence(SequenceObjectInternal val, SequenceObjectInternal other, int n) { - exists(int vlen, int olen | sequence_lengths_in_comparison(val, other, vlen, olen) | - n = vlen and olen > n and result = -1 - or - n = olen and vlen > n and result = 1 - or - n = olen and n = vlen and result = 0 - ) - or - result != 0 and result = compare_item(val, other, n) - or - compare_item(val, other, n) = 0 and result = compare_sequence(val, other, n + 1) - } + pragma[nomagic] + private int compare_sequence(SequenceObjectInternal val, SequenceObjectInternal other, int n) { + exists(int vlen, int olen | sequence_lengths_in_comparison(val, other, vlen, olen) | + n = vlen and olen > n and result = -1 + or + n = olen and vlen > n and result = 1 + or + n = olen and n = vlen and result = 0 + ) + or + result != 0 and result = compare_item(val, other, n) + or + compare_item(val, other, n) = 0 and result = compare_sequence(val, other, n + 1) + } - private predicate sequence_lengths_in_comparison( - SequenceObjectInternal val, SequenceObjectInternal other, int vlen, int olen - ) { - inequalityTest(_, _, _, val, other, _, _) and - vlen = val.length() and - olen = other.length() - } + private predicate sequence_lengths_in_comparison( + SequenceObjectInternal val, SequenceObjectInternal other, int vlen, int olen + ) { + inequalityTest(_, _, _, val, other, _, _) and + vlen = val.length() and + olen = other.length() + } - pragma[noinline] - private int compare_item(SequenceObjectInternal val, SequenceObjectInternal other, int n) { - inequalityTest(_, _, _, val, other, _, _) and - result = compare_unbound(val.getItem(n), other.getItem(n)) - } + pragma[noinline] + private int compare_item(SequenceObjectInternal val, SequenceObjectInternal other, int n) { + inequalityTest(_, _, _, val, other, _, _) and + result = compare_unbound(val.getItem(n), other.getItem(n)) + } - pragma[noinline] - private predicate inequalityTest( - CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, - ObjectInternal other, boolean strict, boolean sense - ) { - exists(ControlFlowNode r | - inequality(comp, operand, r, strict) and sense = true - or - inequality(comp, r, operand, strict) and sense = false - | - PointsToInternal::pointsTo(operand, context, opvalue, _) and - PointsToInternal::pointsTo(r, context, other, _) - ) - } + pragma[noinline] + private predicate inequalityTest( + CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, + ObjectInternal other, boolean strict, boolean sense + ) { + exists(ControlFlowNode r | + inequality(comp, operand, r, strict) and sense = true + or + inequality(comp, r, operand, strict) and sense = false + | + PointsToInternal::pointsTo(operand, context, opvalue, _) and + PointsToInternal::pointsTo(r, context, other, _) + ) + } - /** Helper for comparisons. */ - pragma[noinline] - private predicate inequality( - CompareNode cmp, ControlFlowNode lesser, ControlFlowNode greater, boolean strict - ) { - exists(Cmpop op | - cmp.operands(lesser, op, greater) and op.getSymbol() = "<" and strict = true - or - cmp.operands(lesser, op, greater) and op.getSymbol() = "<=" and strict = false - or - cmp.operands(greater, op, lesser) and op.getSymbol() = ">" and strict = true - or - cmp.operands(greater, op, lesser) and op.getSymbol() = ">=" and strict = false - ) - } + /** Helper for comparisons. */ + pragma[noinline] + private predicate inequality( + CompareNode cmp, ControlFlowNode lesser, ControlFlowNode greater, boolean strict + ) { + exists(Cmpop op | + cmp.operands(lesser, op, greater) and op.getSymbol() = "<" and strict = true + or + cmp.operands(lesser, op, greater) and op.getSymbol() = "<=" and strict = false + or + cmp.operands(greater, op, lesser) and op.getSymbol() = ">" and strict = true + or + cmp.operands(greater, op, lesser) and op.getSymbol() = ">=" and strict = false + ) + } - predicate pointsTo( - ControlFlowNode expr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, - ControlFlowNode subexpr, ObjectInternal subvalue - ) { - attributePointsTo(expr, context, value, origin, subexpr, subvalue) - or - subscriptPointsTo(expr, context, value, origin, subexpr, subvalue) - or - addPointsTo(expr, context, value, origin, subexpr, subvalue) - or - bitOrPointsTo(expr, context, value, origin, subexpr, subvalue) - or - binaryPointsTo(expr, context, value, origin, subexpr, subvalue) - or - unaryPointsTo(expr, context, value, origin, subexpr, subvalue) - or - builtinCallPointsTo(expr, context, value, origin, subexpr, subvalue) - or - lenCallPointsTo(expr, context, value, origin, subexpr, subvalue) - or - typeCallPointsTo(expr, context, value, origin, subexpr, subvalue) - or - getattrPointsTo(expr, context, value, origin, subexpr, subvalue) - or - value = ObjectInternal::bool(evaluatesTo(expr, context, subexpr, subvalue)) and origin = expr - } + predicate pointsTo( + ControlFlowNode expr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, + ControlFlowNode subexpr, ObjectInternal subvalue + ) { + attributePointsTo(expr, context, value, origin, subexpr, subvalue) + or + subscriptPointsTo(expr, context, value, origin, subexpr, subvalue) + or + addPointsTo(expr, context, value, origin, subexpr, subvalue) + or + bitOrPointsTo(expr, context, value, origin, subexpr, subvalue) + or + binaryPointsTo(expr, context, value, origin, subexpr, subvalue) + or + unaryPointsTo(expr, context, value, origin, subexpr, subvalue) + or + builtinCallPointsTo(expr, context, value, origin, subexpr, subvalue) + or + lenCallPointsTo(expr, context, value, origin, subexpr, subvalue) + or + typeCallPointsTo(expr, context, value, origin, subexpr, subvalue) + or + getattrPointsTo(expr, context, value, origin, subexpr, subvalue) + or + value = ObjectInternal::bool(evaluatesTo(expr, context, subexpr, subvalue)) and origin = expr + } - pragma[noinline] - boolean evaluatesTo( - ControlFlowNode expr, PointsToContext context, ControlFlowNode subexpr, ObjectInternal subvalue - ) { - result = equalityEvaluatesTo(expr, context, subexpr, subvalue) - or - result = inequalityEvaluatesTo(expr, context, subexpr, subvalue) - or - result = containsComparisonEvaluatesTo(expr, context, subexpr, subvalue) - or - result = comparesToUnknown(expr, context, subexpr, subvalue) - or - result = isinstanceEvaluatesTo(expr, context, subexpr, subvalue) - or - result = issubclassEvaluatesTo(expr, context, subexpr, subvalue) - or - result = callableEvaluatesTo(expr, context, subexpr, subvalue) - or - result = hasattrEvaluatesTo(expr, context, subexpr, subvalue) - } + pragma[noinline] + boolean evaluatesTo( + ControlFlowNode expr, PointsToContext context, ControlFlowNode subexpr, ObjectInternal subvalue + ) { + result = equalityEvaluatesTo(expr, context, subexpr, subvalue) + or + result = inequalityEvaluatesTo(expr, context, subexpr, subvalue) + or + result = containsComparisonEvaluatesTo(expr, context, subexpr, subvalue) + or + result = comparesToUnknown(expr, context, subexpr, subvalue) + or + result = isinstanceEvaluatesTo(expr, context, subexpr, subvalue) + or + result = issubclassEvaluatesTo(expr, context, subexpr, subvalue) + or + result = callableEvaluatesTo(expr, context, subexpr, subvalue) + or + result = hasattrEvaluatesTo(expr, context, subexpr, subvalue) + } - pragma[nomagic] - private boolean isinstanceEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(ObjectInternal cls | isinstance_call(call, use, context, val, cls) | - result = Types::improperSubclass(val.getClass(), cls) - or - val = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknownClass() and result = maybe() - ) - } + pragma[nomagic] + private boolean isinstanceEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(ObjectInternal cls | isinstance_call(call, use, context, val, cls) | + result = Types::improperSubclass(val.getClass(), cls) + or + val = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknownClass() and result = maybe() + ) + } - private predicate isinstance_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, - ObjectInternal cls - ) { - exists(ControlFlowNode func, ControlFlowNode arg1 | - call2(call, func, use, arg1) and - points_to_isinstance(func, context) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsTo(arg1, context, cls, _) - ) - } + private predicate isinstance_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, + ObjectInternal cls + ) { + exists(ControlFlowNode func, ControlFlowNode arg1 | + call2(call, func, use, arg1) and + points_to_isinstance(func, context) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsTo(arg1, context, cls, _) + ) + } - private predicate issubclass_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, - ObjectInternal cls - ) { - exists(ControlFlowNode func, ControlFlowNode arg1 | - call2(call, func, use, arg1) and - points_to_issubclass(func, context) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsTo(arg1, context, cls, _) - ) - } + private predicate issubclass_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, + ObjectInternal cls + ) { + exists(ControlFlowNode func, ControlFlowNode arg1 | + call2(call, func, use, arg1) and + points_to_issubclass(func, context) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsTo(arg1, context, cls, _) + ) + } - pragma[noinline] - private predicate points_to_isinstance(ControlFlowNode func, PointsToContext context) { - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("isinstance"), _) - } + pragma[noinline] + private predicate points_to_isinstance(ControlFlowNode func, PointsToContext context) { + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("isinstance"), _) + } - pragma[noinline] - private predicate points_to_issubclass(ControlFlowNode func, PointsToContext context) { - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("issubclass"), _) - } + pragma[noinline] + private predicate points_to_issubclass(ControlFlowNode func, PointsToContext context) { + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("issubclass"), _) + } - private predicate callable_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val - ) { - PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("callable"), _) and - use = call.getArg(0) and - PointsToInternal::pointsTo(use, context, val, _) - } + private predicate callable_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val + ) { + PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("callable"), _) and + use = call.getArg(0) and + PointsToInternal::pointsTo(use, context, val, _) + } - private predicate len_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val - ) { - PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("len"), _) and - use = call.getArg(0) and - PointsToInternal::pointsTo(use, context, val, _) - } + private predicate len_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val + ) { + PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("len"), _) and + use = call.getArg(0) and + PointsToInternal::pointsTo(use, context, val, _) + } - private predicate type_call1( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val - ) { - PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("type"), _) and - use = call.getArg(0) and - not exists(call.getArg(1)) and - PointsToInternal::pointsTo(use, context, val, _) - } + private predicate type_call1( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val + ) { + PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("type"), _) and + use = call.getArg(0) and + not exists(call.getArg(1)) and + PointsToInternal::pointsTo(use, context, val, _) + } - private predicate hasattr_call( - CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name - ) { - exists(ControlFlowNode arg1 | - call_to_hasattr(call, context, use, arg1) and - PointsToInternal::pointsTo(use, context, val, _) and - PointsToInternal::pointsToString(arg1, context, name) - ) - } + private predicate hasattr_call( + CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name + ) { + exists(ControlFlowNode arg1 | + call_to_hasattr(call, context, use, arg1) and + PointsToInternal::pointsTo(use, context, val, _) and + PointsToInternal::pointsToString(arg1, context, name) + ) + } - pragma[noinline] - private predicate call_to_hasattr( - ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 - ) { - exists(ControlFlowNode func | - call2(call, func, arg0, arg1) and - PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("hasattr"), _) - ) - } + pragma[noinline] + private predicate call_to_hasattr( + ControlFlowNode call, PointsToContext context, ControlFlowNode arg0, ControlFlowNode arg1 + ) { + exists(ControlFlowNode func | + call2(call, func, arg0, arg1) and + PointsToInternal::pointsTo(func, context, ObjectInternal::builtin("hasattr"), _) + ) + } - pragma[nomagic] - private boolean issubclassEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(ObjectInternal cls | issubclass_call(call, use, context, val, cls) | - result = Types::improperSubclass(val, cls) - or - val = ObjectInternal::unknownClass() and result = maybe() - or - val = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknown() and result = maybe() - or - cls = ObjectInternal::unknownClass() and result = maybe() - ) - } + pragma[nomagic] + private boolean issubclassEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(ObjectInternal cls | issubclass_call(call, use, context, val, cls) | + result = Types::improperSubclass(val, cls) + or + val = ObjectInternal::unknownClass() and result = maybe() + or + val = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknown() and result = maybe() + or + cls = ObjectInternal::unknownClass() and result = maybe() + ) + } - pragma[noinline] - private boolean callableEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - callable_call(call, use, context, val) and - ( - val = ObjectInternal::unknown() and result = maybe() - or - val = ObjectInternal::unknownClass() and result = maybe() - or - result = Types::hasAttr(val.getClass(), "__call__") - ) - } + pragma[noinline] + private boolean callableEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + callable_call(call, use, context, val) and + ( + val = ObjectInternal::unknown() and result = maybe() + or + val = ObjectInternal::unknownClass() and result = maybe() + or + result = Types::hasAttr(val.getClass(), "__call__") + ) + } - pragma[noinline] - private boolean hasattrEvaluatesTo( - CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val - ) { - exists(string name | hasattr_call(call, use, context, val, name) | - val = ObjectInternal::unknown() and result = maybe() - or - val = ObjectInternal::unknownClass() and result = maybe() - or - result = Types::hasAttr(val.getClass(), name) - ) - } + pragma[noinline] + private boolean hasattrEvaluatesTo( + CallNode call, PointsToContext context, ControlFlowNode use, ObjectInternal val + ) { + exists(string name | hasattr_call(call, use, context, val, name) | + val = ObjectInternal::unknown() and result = maybe() + or + val = ObjectInternal::unknownClass() and result = maybe() + or + result = Types::hasAttr(val.getClass(), name) + ) + } - predicate requireSubClass(ObjectInternal sub, ObjectInternal sup) { - sup != ObjectInternal::unknownClass() and - sub != ObjectInternal::unknownClass() and - exists(ObjectInternal sup_or_tuple | - issubclass_call(_, _, _, sub, sup_or_tuple) and sub.isClass() = true - or - exists(ObjectInternal val | - isinstance_call(_, _, _, val, sup_or_tuple) and - sub = val.getClass() - ) - | - sup = sup_or_tuple - or - sup = sup_or_tuple.(TupleObjectInternal).getItem(_) - ) - } + predicate requireSubClass(ObjectInternal sub, ObjectInternal sup) { + sup != ObjectInternal::unknownClass() and + sub != ObjectInternal::unknownClass() and + exists(ObjectInternal sup_or_tuple | + issubclass_call(_, _, _, sub, sup_or_tuple) and sub.isClass() = true + or + exists(ObjectInternal val | + isinstance_call(_, _, _, val, sup_or_tuple) and + sub = val.getClass() + ) + | + sup = sup_or_tuple + or + sup = sup_or_tuple.(TupleObjectInternal).getItem(_) + ) + } - predicate requireHasAttr(ClassObjectInternal cls, string name) { - cls != ObjectInternal::unknownClass() and - exists(ObjectInternal val | val.getClass() = cls | - name = "__call__" and callable_call(_, _, _, val) - or - hasattr_call(_, _, _, val, name) - ) - } + predicate requireHasAttr(ClassObjectInternal cls, string name) { + cls != ObjectInternal::unknownClass() and + exists(ObjectInternal val | val.getClass() = cls | + name = "__call__" and callable_call(_, _, _, val) + or + hasattr_call(_, _, _, val, name) + ) + } } module Conditionals { - boolean testEvaluates( - ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal value, - ControlFlowNode origin - ) { - pinode_test(expr, use) and - result = evaluates(expr, use, context, value, origin).booleanValue() - } + boolean testEvaluates( + ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal value, + ControlFlowNode origin + ) { + pinode_test(expr, use) and + result = evaluates(expr, use, context, value, origin).booleanValue() + } - pragma[noinline] - ObjectInternal evaluates( - ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal val, - ControlFlowNode origin - ) { - PointsToInternal::pointsTo(use, context, val, origin) and - pinode_test(_, use) and - expr = use and - result = val - or - exists(ControlFlowNode part, ObjectInternal partval | - pinode_test_part(expr, part) and - partval = evaluates(part, use, context, val, origin) and - Expressions::pointsTo(expr, context, result, _, part, partval) - ) - } + pragma[noinline] + ObjectInternal evaluates( + ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal val, + ControlFlowNode origin + ) { + PointsToInternal::pointsTo(use, context, val, origin) and + pinode_test(_, use) and + expr = use and + result = val + or + exists(ControlFlowNode part, ObjectInternal partval | + pinode_test_part(expr, part) and + partval = evaluates(part, use, context, val, origin) and + Expressions::pointsTo(expr, context, result, _, part, partval) + ) + } - private predicate pinode_test(ControlFlowNode test, NameNode use) { - exists(PyEdgeRefinement pi | - pi.getInput().getASourceUse() = use and - pi.getTest() = test and - test.getAChild*() = use - ) - or - any(SingleSuccessorGuard ssg).useAndTest(use, test) - } + private predicate pinode_test(ControlFlowNode test, NameNode use) { + exists(PyEdgeRefinement pi | + pi.getInput().getASourceUse() = use and + pi.getTest() = test and + test.getAChild*() = use + ) + or + any(SingleSuccessorGuard ssg).useAndTest(use, test) + } - private predicate pinode_test_part(ControlFlowNode outer, ControlFlowNode inner) { - exists(ControlFlowNode test, NameNode use | - pinode_test(test, use) and - test.getAChild*() = outer and - outer.getAChild+() = inner and - inner.getAChild*() = use - ) - } + private predicate pinode_test_part(ControlFlowNode outer, ControlFlowNode inner) { + exists(ControlFlowNode test, NameNode use | + pinode_test(test, use) and + test.getAChild*() = outer and + outer.getAChild+() = inner and + inner.getAChild*() = use + ) + } } cached module Types { - cached - int base_count(ClassObjectInternal cls) { - cls = ObjectInternal::builtin("object") and result = 0 - or - exists(cls.getBuiltin()) and cls != ObjectInternal::builtin("object") and result = 1 - or - exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | - result = strictcount(pycls.getABase()) - or - isNewStyle(cls) and not exists(pycls.getABase()) and result = 1 - or - isOldStyle(cls) and not exists(pycls.getABase()) and result = 0 - ) - } + cached + int base_count(ClassObjectInternal cls) { + cls = ObjectInternal::builtin("object") and result = 0 + or + exists(cls.getBuiltin()) and cls != ObjectInternal::builtin("object") and result = 1 + or + exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | + result = strictcount(pycls.getABase()) + or + isNewStyle(cls) and not exists(pycls.getABase()) and result = 1 + or + isOldStyle(cls) and not exists(pycls.getABase()) and result = 0 + ) + } - cached - ObjectInternal getBase(ClassObjectInternal cls, int n) { - result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0 + cached + ObjectInternal getBase(ClassObjectInternal cls, int n) { + result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0 + or + exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | + exists(ObjectInternal base | + PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _) + | + result = base and base != ObjectInternal::unknown() or - exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | - exists(ObjectInternal base | - PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _) - | - result = base and base != ObjectInternal::unknown() - or - base = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() - ) - or - not exists(pycls.getABase()) and - n = 0 and - isNewStyle(cls) and - result = ObjectInternal::builtin("object") - ) - or - cls = ObjectInternal::unknownClass() and - n = 0 and - result = ObjectInternal::builtin("object") - } + base = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() + ) + or + not exists(pycls.getABase()) and + n = 0 and + isNewStyle(cls) and + result = ObjectInternal::builtin("object") + ) + or + cls = ObjectInternal::unknownClass() and + n = 0 and + result = ObjectInternal::builtin("object") + } - cached - predicate isOldStyle(ClassObjectInternal cls) { newStylePython2(cls, 0) = false } + cached + predicate isOldStyle(ClassObjectInternal cls) { newStylePython2(cls, 0) = false } - cached - predicate isNewStyle(ClassObjectInternal cls) { - major_version() = 3 - or - cls.isBuiltin() - or - newStylePython2(cls, 0) = true - } + cached + predicate isNewStyle(ClassObjectInternal cls) { + major_version() = 3 + or + cls.isBuiltin() + or + newStylePython2(cls, 0) = true + } - private boolean newStylePython2(ClassObjectInternal cls, int n) { - major_version() = 2 and - ( - hasDeclaredMetaclass(cls) = false and - exists(Class pycls | - pycls = cls.getClassDeclaration().getClass() and - n = count(pycls.getABase()) and - result = false - ) - or - exists(ClassObjectInternal base | base = getBase(cls, n) | - hasDeclaredMetaclass(cls) = false and - isOldStyle(base) and - result = newStylePython2(cls, n + 1) - or - isNewStyle(base) and result = true - ) - or - getMro(declaredMetaClass(cls)).contains(ObjectInternal::type()) and - n = 0 and - result = true - ) - } + private boolean newStylePython2(ClassObjectInternal cls, int n) { + major_version() = 2 and + ( + hasDeclaredMetaclass(cls) = false and + exists(Class pycls | + pycls = cls.getClassDeclaration().getClass() and + n = count(pycls.getABase()) and + result = false + ) + or + exists(ClassObjectInternal base | base = getBase(cls, n) | + hasDeclaredMetaclass(cls) = false and + isOldStyle(base) and + result = newStylePython2(cls, n + 1) + or + isNewStyle(base) and result = true + ) + or + getMro(declaredMetaClass(cls)).contains(ObjectInternal::type()) and + n = 0 and + result = true + ) + } - cached - ClassList getMro(ClassObjectInternal cls) { - isNewStyle(cls) and - result = Mro::newStyleMro(cls) - or - isOldStyle(cls) and - result = Mro::oldStyleMro(cls) - } + cached + ClassList getMro(ClassObjectInternal cls) { + isNewStyle(cls) and + result = Mro::newStyleMro(cls) + or + isOldStyle(cls) and + result = Mro::oldStyleMro(cls) + } - cached - predicate declaredAttribute( - ClassObjectInternal cls, string name, ObjectInternal value, CfgOrigin origin - ) { - value = ObjectInternal::fromBuiltin(cls.getBuiltin().getMember(name)) and - origin = CfgOrigin::unknown() - or - value != ObjectInternal::undefined() and - exists(EssaVariable var | - name = var.getName() and - var.getAUse() = cls.(PythonClassObjectInternal).getScope().getANormalExit() and - PointsToInternal::variablePointsTo(var, _, value, origin) - ) - } + cached + predicate declaredAttribute( + ClassObjectInternal cls, string name, ObjectInternal value, CfgOrigin origin + ) { + value = ObjectInternal::fromBuiltin(cls.getBuiltin().getMember(name)) and + origin = CfgOrigin::unknown() + or + value != ObjectInternal::undefined() and + exists(EssaVariable var | + name = var.getName() and + var.getAUse() = cls.(PythonClassObjectInternal).getScope().getANormalExit() and + PointsToInternal::variablePointsTo(var, _, value, origin) + ) + } - cached - ClassObjectInternal getMetaClass(PythonClassObjectInternal cls) { - result = declaredMetaClass(cls) - or - hasDeclaredMetaclass(cls) = false and result = getInheritedMetaclass(cls) - } + cached + ClassObjectInternal getMetaClass(PythonClassObjectInternal cls) { + result = declaredMetaClass(cls) + or + hasDeclaredMetaclass(cls) = false and result = getInheritedMetaclass(cls) + } - private ClassObjectInternal declaredMetaClass(PythonClassObjectInternal cls) { - exists(ObjectInternal obj | - PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) - | - result = obj - or - obj = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() - ) - or - exists(ControlFlowNode meta | - six_add_metaclass(_, _, cls, meta) and - PointsToInternal::pointsTo(meta, _, result, _) - ) - } + private ClassObjectInternal declaredMetaClass(PythonClassObjectInternal cls) { + exists(ObjectInternal obj | + PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) + | + result = obj + or + obj = ObjectInternal::unknown() and result = ObjectInternal::unknownClass() + ) + or + exists(ControlFlowNode meta | + six_add_metaclass(_, _, cls, meta) and + PointsToInternal::pointsTo(meta, _, result, _) + ) + } - private boolean hasDeclaredMetaclass(PythonClassObjectInternal cls) { - result = has_six_add_metaclass(cls).booleanOr(has_metaclass_var_metaclass(cls)) - } + private boolean hasDeclaredMetaclass(PythonClassObjectInternal cls) { + result = has_six_add_metaclass(cls).booleanOr(has_metaclass_var_metaclass(cls)) + } - private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) { - result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction() - } + private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) { + result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction() + } - private boolean has_six_add_metaclass(PythonClassObjectInternal cls) { - exists(ControlFlowNode callee, ObjectInternal func | - callee = decorator_call_callee(cls) and - PointsToInternal::pointsTo(callee, _, func, _) - | - func = six_add_metaclass_function() and result = true - or - func != six_add_metaclass_function() and result = false - ) - or - not exists(Module m | m.getName() = "six") and result = false - or - exists(Class pycls | - pycls = cls.getScope() and - not exists(pycls.getADecorator()) and - result = false - ) - } + private boolean has_six_add_metaclass(PythonClassObjectInternal cls) { + exists(ControlFlowNode callee, ObjectInternal func | + callee = decorator_call_callee(cls) and + PointsToInternal::pointsTo(callee, _, func, _) + | + func = six_add_metaclass_function() and result = true + or + func != six_add_metaclass_function() and result = false + ) + or + not exists(Module m | m.getName() = "six") and result = false + or + exists(Class pycls | + pycls = cls.getScope() and + not exists(pycls.getADecorator()) and + result = false + ) + } - private boolean has_metaclass_var_metaclass(PythonClassObjectInternal cls) { - exists(ObjectInternal obj | - PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) - | - obj = ObjectInternal::undefined() and result = false - or - obj != ObjectInternal::undefined() and result = true - ) - or - exists(Class pycls | - pycls = cls.getScope() and - not exists(metaclass_var(pycls)) and - result = false - ) - } + private boolean has_metaclass_var_metaclass(PythonClassObjectInternal cls) { + exists(ObjectInternal obj | + PointsToInternal::variablePointsTo(metaclass_var(cls.getScope()), _, obj, _) + | + obj = ObjectInternal::undefined() and result = false + or + obj != ObjectInternal::undefined() and result = true + ) + or + exists(Class pycls | + pycls = cls.getScope() and + not exists(metaclass_var(pycls)) and + result = false + ) + } - private EssaVariable metaclass_var(Class cls) { - result.getASourceUse() = cls.getMetaClass().getAFlowNode() - or - major_version() = 2 and - not exists(cls.getMetaClass()) and - result.getName() = "__metaclass__" and - cls.(ImportTimeScope).entryEdge(result.getAUse(), _) - } + private EssaVariable metaclass_var(Class cls) { + result.getASourceUse() = cls.getMetaClass().getAFlowNode() + or + major_version() = 2 and + not exists(cls.getMetaClass()) and + result.getName() = "__metaclass__" and + cls.(ImportTimeScope).entryEdge(result.getAUse(), _) + } - cached - predicate six_add_metaclass( - CallNode decorator_call, PointsToContext context, ClassObjectInternal decorated, - ControlFlowNode metaclass - ) { - exists(CallNode decorator | - PointsToInternal::pointsTo(decorator_call.getArg(0), context, decorated, _) and - decorator = decorator_call.getFunction() and - decorator.getArg(0) = metaclass - | - PointsToInternal::pointsTo(decorator.getFunction(), context, six_add_metaclass_function(), _) - or - exists(ModuleObjectInternal six | - six.getName() = "six" and - PointsToInternal::pointsTo(decorator.getFunction().(AttrNode).getObject("add_metaclass"), - context, six, _) - ) - ) - } + cached + predicate six_add_metaclass( + CallNode decorator_call, PointsToContext context, ClassObjectInternal decorated, + ControlFlowNode metaclass + ) { + exists(CallNode decorator | + PointsToInternal::pointsTo(decorator_call.getArg(0), context, decorated, _) and + decorator = decorator_call.getFunction() and + decorator.getArg(0) = metaclass + | + PointsToInternal::pointsTo(decorator.getFunction(), context, six_add_metaclass_function(), _) + or + exists(ModuleObjectInternal six | + six.getName() = "six" and + PointsToInternal::pointsTo(decorator.getFunction().(AttrNode).getObject("add_metaclass"), + context, six, _) + ) + ) + } - private ObjectInternal six_add_metaclass_function() { - exists(ModuleObjectInternal six | - six.getName() = "six" and - six.attribute("add_metaclass", result, _) - ) - } + private ObjectInternal six_add_metaclass_function() { + exists(ModuleObjectInternal six | + six.getName() = "six" and + six.attribute("add_metaclass", result, _) + ) + } - pragma[nomagic] - private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls) { - result = getInheritedMetaclass(cls, 0) - or - // Best guess if base is not a known class - hasUnknownBase(cls) and result = ObjectInternal::unknownClass() - } + pragma[nomagic] + private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls) { + result = getInheritedMetaclass(cls, 0) + or + // Best guess if base is not a known class + hasUnknownBase(cls) and result = ObjectInternal::unknownClass() + } - /* Helper for getInheritedMetaclass */ - private predicate hasUnknownBase(ClassObjectInternal cls) { - exists(ObjectInternal base | base = getBase(cls, _) | - base.isClass() = false - or - base = ObjectInternal::unknownClass() - ) - } + /* Helper for getInheritedMetaclass */ + private predicate hasUnknownBase(ClassObjectInternal cls) { + exists(ObjectInternal base | base = getBase(cls, _) | + base.isClass() = false + or + base = ObjectInternal::unknownClass() + ) + } - private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls, int n) { - exists(Class c | - c = cls.(PythonClassObjectInternal).getScope() and - n = count(c.getABase()) and - n != 1 - | - result = ObjectInternal::type() and major_version() = 3 - or - result = ObjectInternal::classType() and major_version() = 2 - ) - or - base_count(cls) = 1 and - n = 0 and - result = getBase(cls, 0).getClass() - or - exists(ClassObjectInternal meta1, ClassObjectInternal meta2 | - base_count(cls) > 1 and - meta1 = getBase(cls, n).getClass() and - meta2 = getInheritedMetaclass(cls, n + 1) - | - /* Choose sub-class */ - improperSuperType(meta1) = meta2 and result = meta1 - or - improperSuperType(meta2) = meta1 and result = meta2 - or - meta2 = ObjectInternal::classType() and result = meta1 - or - /* Make sure we have a metaclass, even if base is unknown */ - meta1 = ObjectInternal::unknownClass() and result = ObjectInternal::builtin("type") - or - meta2 = ObjectInternal::unknownClass() and result = meta1 - ) - } + private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls, int n) { + exists(Class c | + c = cls.(PythonClassObjectInternal).getScope() and + n = count(c.getABase()) and + n != 1 + | + result = ObjectInternal::type() and major_version() = 3 + or + result = ObjectInternal::classType() and major_version() = 2 + ) + or + base_count(cls) = 1 and + n = 0 and + result = getBase(cls, 0).getClass() + or + exists(ClassObjectInternal meta1, ClassObjectInternal meta2 | + base_count(cls) > 1 and + meta1 = getBase(cls, n).getClass() and + meta2 = getInheritedMetaclass(cls, n + 1) + | + /* Choose sub-class */ + improperSuperType(meta1) = meta2 and result = meta1 + or + improperSuperType(meta2) = meta1 and result = meta2 + or + meta2 = ObjectInternal::classType() and result = meta1 + or + /* Make sure we have a metaclass, even if base is unknown */ + meta1 = ObjectInternal::unknownClass() and result = ObjectInternal::builtin("type") + or + meta2 = ObjectInternal::unknownClass() and result = meta1 + ) + } - private ClassObjectInternal improperSuperType(ClassObjectInternal cls) { - result = cls - or - result = improperSuperType(getBase(cls, _)) - } + private ClassObjectInternal improperSuperType(ClassObjectInternal cls) { + result = cls + or + result = improperSuperType(getBase(cls, _)) + } - /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ - cached - predicate failedInference(ClassObjectInternal cls, string reason) { - exists(int priority | - failedInference(cls, reason, priority) and - priority = max(int p | failedInference(cls, _, p)) - ) - } + /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ + cached + predicate failedInference(ClassObjectInternal cls, string reason) { + exists(int priority | + failedInference(cls, reason, priority) and + priority = max(int p | failedInference(cls, _, p)) + ) + } - /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ - private predicate failedInference(ClassObjectInternal cls, string reason, int priority) { - strictcount(cls.(PythonClassObjectInternal).getScope().getADecorator()) > 1 and - reason = "Multiple decorators" and - priority = 0 - or - exists(cls.(PythonClassObjectInternal).getScope().getADecorator()) and - not six_add_metaclass(_, _, cls, _) and - reason = "Decorator not understood" and - priority = 1 - or - reason = "Missing base " + missingBase(cls) and priority = 6 - or - not exists(ObjectInternal meta | - meta = cls.getClass() and not meta = ObjectInternal::unknownClass() - ) and - reason = "Failed to infer metaclass" and - priority = 4 - or - exists(int i, ObjectInternal base1, ObjectInternal base2 | - base1 = getBase(cls, i) and - base2 = getBase(cls, i) and - base1 != base2 and - reason = "Multiple bases at position " + i - ) and - priority = 6 - or - duplicateBase(cls) and reason = "Duplicate bases classes" and priority = 6 - or - not exists(getMro(cls)) and reason = "Failed to compute MRO" and priority = 3 - or - exists(int i | - failedInference(getBase(cls, i), _, _) and - reason = "Failed inference for base class at position " + i - ) and - priority = 5 - } + /* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ + private predicate failedInference(ClassObjectInternal cls, string reason, int priority) { + strictcount(cls.(PythonClassObjectInternal).getScope().getADecorator()) > 1 and + reason = "Multiple decorators" and + priority = 0 + or + exists(cls.(PythonClassObjectInternal).getScope().getADecorator()) and + not six_add_metaclass(_, _, cls, _) and + reason = "Decorator not understood" and + priority = 1 + or + reason = "Missing base " + missingBase(cls) and priority = 6 + or + not exists(ObjectInternal meta | + meta = cls.getClass() and not meta = ObjectInternal::unknownClass() + ) and + reason = "Failed to infer metaclass" and + priority = 4 + or + exists(int i, ObjectInternal base1, ObjectInternal base2 | + base1 = getBase(cls, i) and + base2 = getBase(cls, i) and + base1 != base2 and + reason = "Multiple bases at position " + i + ) and + priority = 6 + or + duplicateBase(cls) and reason = "Duplicate bases classes" and priority = 6 + or + not exists(getMro(cls)) and reason = "Failed to compute MRO" and priority = 3 + or + exists(int i | + failedInference(getBase(cls, i), _, _) and + reason = "Failed inference for base class at position " + i + ) and + priority = 5 + } - private int missingBase(ClassObjectInternal cls) { - exists(cls.(PythonClassObjectInternal).getScope().getBase(result)) and - not exists(ObjectInternal base | - base = getBase(cls, result) and not base = ObjectInternal::unknownClass() - ) - } + private int missingBase(ClassObjectInternal cls) { + exists(cls.(PythonClassObjectInternal).getScope().getBase(result)) and + not exists(ObjectInternal base | + base = getBase(cls, result) and not base = ObjectInternal::unknownClass() + ) + } - private predicate duplicateBase(ClassObjectInternal cls) { - exists(int i, int j, ClassObjectInternal dup | - dup = getBase(cls, i) and - dup != ObjectInternal::unknownClass() and - dup = getBase(cls, j) and - i != j - ) - } + private predicate duplicateBase(ClassObjectInternal cls) { + exists(int i, int j, ClassObjectInternal dup | + dup = getBase(cls, i) and + dup != ObjectInternal::unknownClass() and + dup = getBase(cls, j) and + i != j + ) + } - cached - boolean improperSubclass(ObjectInternal sub, ObjectInternal sup) { - sub = sup and result = true - or - result = true and mroContains(Types::getMro(sub), sup) - or - result = false and mroDoesnotContain(Types::getMro(sub), sup, 0) - or - result = tupleSubclass(sub, sup, 0) - } + cached + boolean improperSubclass(ObjectInternal sub, ObjectInternal sup) { + sub = sup and result = true + or + result = true and mroContains(Types::getMro(sub), sup) + or + result = false and mroDoesnotContain(Types::getMro(sub), sup, 0) + or + result = tupleSubclass(sub, sup, 0) + } - private boolean tupleSubclass(ObjectInternal cls, TupleObjectInternal tpl, int n) { - Expressions::requireSubClass(cls, tpl) and - ( - n = tpl.length() and result = false - or - result = improperSubclass(cls, tpl.getItem(n)).booleanOr(tupleSubclass(cls, tpl, n + 1)) - ) - } + private boolean tupleSubclass(ObjectInternal cls, TupleObjectInternal tpl, int n) { + Expressions::requireSubClass(cls, tpl) and + ( + n = tpl.length() and result = false + or + result = improperSubclass(cls, tpl.getItem(n)).booleanOr(tupleSubclass(cls, tpl, n + 1)) + ) + } - private predicate mroContains(ClassList mro, ClassObjectInternal sup) { - mro.contains(sup) - or - exists(ClassDecl item, ClassDecl sdecl | - item = mro.getAnItem().getClassDeclaration() and - sdecl = sup.getClassDeclaration() and - is_abstract_subclass(item, sdecl) - ) - } + private predicate mroContains(ClassList mro, ClassObjectInternal sup) { + mro.contains(sup) + or + exists(ClassDecl item, ClassDecl sdecl | + item = mro.getAnItem().getClassDeclaration() and + sdecl = sup.getClassDeclaration() and + is_abstract_subclass(item, sdecl) + ) + } - private predicate mroDoesnotContain(ClassList mro, ClassObjectInternal sup, int n) { - exists(ClassObjectInternal cls | - Expressions::requireSubClass(cls, sup) and - mro = getMro(cls) - ) and - ( - n = mro.length() - or - mroDoesnotContain(mro, sup, n + 1) and - mro.getItem(n) != sup and - exists(ClassDecl item, ClassDecl sdecl | - item = mro.getItem(n).getClassDeclaration() and - sdecl = sup.getClassDeclaration() and - not is_abstract_subclass(item, sdecl) - ) - ) - } + private predicate mroDoesnotContain(ClassList mro, ClassObjectInternal sup, int n) { + exists(ClassObjectInternal cls | + Expressions::requireSubClass(cls, sup) and + mro = getMro(cls) + ) and + ( + n = mro.length() + or + mroDoesnotContain(mro, sup, n + 1) and + mro.getItem(n) != sup and + exists(ClassDecl item, ClassDecl sdecl | + item = mro.getItem(n).getClassDeclaration() and + sdecl = sup.getClassDeclaration() and + not is_abstract_subclass(item, sdecl) + ) + ) + } - private predicate is_abstract_subclass(ClassDecl cls, ClassDecl sup) { - cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Sequence") - or - cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Set") - or - cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Mapping") - or - cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Iterable") - or - cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Iterable") - or - cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Iterable") - } + private predicate is_abstract_subclass(ClassDecl cls, ClassDecl sup) { + cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Sequence") + or + cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Set") + or + cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Mapping") + or + cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Iterable") + or + cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Iterable") + or + cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Iterable") + } - cached - boolean hasAttr(ObjectInternal cls, string name) { - result = mroHasAttr(Types::getMro(cls), name, 0) - } + cached + boolean hasAttr(ObjectInternal cls, string name) { + result = mroHasAttr(Types::getMro(cls), name, 0) + } - private boolean mroHasAttr(ClassList mro, string name, int n) { - exists(ClassObjectInternal cls | - Expressions::requireHasAttr(cls, name) and - mro = getMro(cls) - ) and - ( - n = mro.length() and result = false - or - exists(ClassDecl decl | decl = mro.getItem(n).getClassDeclaration() | - if decl.declaresAttribute(name) - then result = true - else result = mroHasAttr(mro, name, n + 1) - ) - ) - } + private boolean mroHasAttr(ClassList mro, string name, int n) { + exists(ClassObjectInternal cls | + Expressions::requireHasAttr(cls, name) and + mro = getMro(cls) + ) and + ( + n = mro.length() and result = false + or + exists(ClassDecl decl | decl = mro.getItem(n).getClassDeclaration() | + if decl.declaresAttribute(name) + then result = true + else result = mroHasAttr(mro, name, n + 1) + ) + ) + } } module AttributePointsTo { - pragma[noinline] - predicate pointsTo( - ControlFlowNode f, Context context, ObjectInternal value, ControlFlowNode origin - ) { - exists(EssaVariable var, string name, CfgOrigin orig | - getsVariableAttribute(f, var, name) and - variableAttributePointsTo(var, context, name, value, orig) and - origin = orig.asCfgNodeOrHere(f) - ) - } + pragma[noinline] + predicate pointsTo( + ControlFlowNode f, Context context, ObjectInternal value, ControlFlowNode origin + ) { + exists(EssaVariable var, string name, CfgOrigin orig | + getsVariableAttribute(f, var, name) and + variableAttributePointsTo(var, context, name, value, orig) and + origin = orig.asCfgNodeOrHere(f) + ) + } - pragma[noinline] - private predicate getsVariableAttribute(ControlFlowNode f, EssaVariable var, string name) { - Expressions::getattr_call(f, var.getASourceUse(), _, _, name) - or - f.isLoad() and var.getASourceUse() = f.(AttrNode).getObject(name) - } + pragma[noinline] + private predicate getsVariableAttribute(ControlFlowNode f, EssaVariable var, string name) { + Expressions::getattr_call(f, var.getASourceUse(), _, _, name) + or + f.isLoad() and var.getASourceUse() = f.(AttrNode).getObject(name) + } - pragma[nomagic] - predicate variableAttributePointsTo( - EssaVariable var, Context context, string name, ObjectInternal value, CfgOrigin origin - ) { - definitionAttributePointsTo(var.getDefinition(), context, name, value, origin) - or - exists(EssaVariable prev | - var.getDefinition().(PhiFunction).getShortCircuitInput() = prev and - variableAttributePointsTo(prev, context, name, value, origin) - ) - } + pragma[nomagic] + predicate variableAttributePointsTo( + EssaVariable var, Context context, string name, ObjectInternal value, CfgOrigin origin + ) { + definitionAttributePointsTo(var.getDefinition(), context, name, value, origin) + or + exists(EssaVariable prev | + var.getDefinition().(PhiFunction).getShortCircuitInput() = prev and + variableAttributePointsTo(prev, context, name, value, origin) + ) + } - predicate definitionAttributePointsTo( - EssaDefinition def, Context context, string name, ObjectInternal value, CfgOrigin origin - ) { - variableAttributePointsTo(def.(PhiFunction).getAnInput(), context, name, value, origin) - or - piNodeAttributePointsTo(def, context, name, value, origin) - or - refinementAttributePointsTo(def, context, name, value, origin) - or - selfParameterAttributePointsTo(def, context, name, value, origin) - or - selfMethodCallsitePointsTo(def, context, name, value, origin) - or - argumentRefinementPointsTo(def, context, name, value, origin) - } + predicate definitionAttributePointsTo( + EssaDefinition def, Context context, string name, ObjectInternal value, CfgOrigin origin + ) { + variableAttributePointsTo(def.(PhiFunction).getAnInput(), context, name, value, origin) + or + piNodeAttributePointsTo(def, context, name, value, origin) + or + refinementAttributePointsTo(def, context, name, value, origin) + or + selfParameterAttributePointsTo(def, context, name, value, origin) + or + selfMethodCallsitePointsTo(def, context, name, value, origin) + or + argumentRefinementPointsTo(def, context, name, value, origin) + } - pragma[noinline] - private predicate refinementAttributePointsTo( - EssaNodeRefinement def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - attributeAssignmentAttributePointsTo(def, context, name, value, origin) - or - attributeDeleteAttributePointsTo(def, context, name, value, origin) - or - uniEdgedPhiAttributePointsTo(def, context, name, value, origin) - } + pragma[noinline] + private predicate refinementAttributePointsTo( + EssaNodeRefinement def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + attributeAssignmentAttributePointsTo(def, context, name, value, origin) + or + attributeDeleteAttributePointsTo(def, context, name, value, origin) + or + uniEdgedPhiAttributePointsTo(def, context, name, value, origin) + } - /** Attribute deletions have no effect as far as value tracking is concerned. */ - pragma[noinline] - private predicate attributeAssignmentAttributePointsTo( - AttributeAssignment def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - def.getName() != name and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - or - def.getName() = name and - exists(ControlFlowNode cfgnode | - PointsToInternal::pointsTo(def.getValue(), context, value, cfgnode) and - origin = CfgOrigin::fromCfgNode(cfgnode) - ) - } + /** Attribute deletions have no effect as far as value tracking is concerned. */ + pragma[noinline] + private predicate attributeAssignmentAttributePointsTo( + AttributeAssignment def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + def.getName() != name and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + or + def.getName() = name and + exists(ControlFlowNode cfgnode | + PointsToInternal::pointsTo(def.getValue(), context, value, cfgnode) and + origin = CfgOrigin::fromCfgNode(cfgnode) + ) + } - /** Attribute deletions have no effect as far as value tracking is concerned. */ - pragma[noinline] - private predicate attributeDeleteAttributePointsTo( - EssaAttributeDeletion def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - def.getName() != name and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - } + /** Attribute deletions have no effect as far as value tracking is concerned. */ + pragma[noinline] + private predicate attributeDeleteAttributePointsTo( + EssaAttributeDeletion def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + def.getName() != name and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + } - private predicate uniEdgedPhiAttributePointsTo( - SingleSuccessorGuard unipi, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - variableAttributePointsTo(unipi.getInput(), context, name, value, origin) - } + private predicate uniEdgedPhiAttributePointsTo( + SingleSuccessorGuard unipi, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + variableAttributePointsTo(unipi.getInput(), context, name, value, origin) + } - private predicate piNodeAttributePointsTo( - PyEdgeRefinement pi, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - variableAttributePointsTo(pi.getInput(), context, name, value, origin) - } + private predicate piNodeAttributePointsTo( + PyEdgeRefinement pi, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + variableAttributePointsTo(pi.getInput(), context, name, value, origin) + } - private predicate selfParameterAttributePointsTo( - ParameterDefinition def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - exists(SelfCallsiteRefinement call, Function func, PointsToContext caller | - InterProceduralPointsTo::selfMethodCall(call, caller, func, context) and - def.isSelf() and - def.getScope() = func and - variableAttributePointsTo(call.getInput(), caller, name, value, origin) - ) - } + private predicate selfParameterAttributePointsTo( + ParameterDefinition def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + exists(SelfCallsiteRefinement call, Function func, PointsToContext caller | + InterProceduralPointsTo::selfMethodCall(call, caller, func, context) and + def.isSelf() and + def.getScope() = func and + variableAttributePointsTo(call.getInput(), caller, name, value, origin) + ) + } - /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ - private predicate selfMethodCallsitePointsTo( - SelfCallsiteRefinement def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - /* The value of self remains the same, only the attributes may change */ - exists(Function func, PointsToContext callee, EssaVariable exit_self | - InterProceduralPointsTo::selfMethodCall(def, context, func, callee) and - exit_self.getSourceVariable().(Variable).isSelf() and - exit_self.getScope() = func and - BaseFlow::reaches_exit(exit_self) and - variableAttributePointsTo(exit_self, callee, name, value, origin) - ) - } + /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ + private predicate selfMethodCallsitePointsTo( + SelfCallsiteRefinement def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + /* The value of self remains the same, only the attributes may change */ + exists(Function func, PointsToContext callee, EssaVariable exit_self | + InterProceduralPointsTo::selfMethodCall(def, context, func, callee) and + exit_self.getSourceVariable().(Variable).isSelf() and + exit_self.getScope() = func and + BaseFlow::reaches_exit(exit_self) and + variableAttributePointsTo(exit_self, callee, name, value, origin) + ) + } - private predicate argumentRefinementPointsTo( - ArgumentRefinement def, PointsToContext context, string name, ObjectInternal value, - CfgOrigin origin - ) { - exists(ObjectInternal callable | - PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and - callable != ObjectInternal::builtin("setattr") - ) and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - or - exists(string othername | - Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), othername, - _, _) and - not othername = name - ) and - variableAttributePointsTo(def.getInput(), context, name, value, origin) - or - exists(ControlFlowNode orig | - Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), name, value, - orig) and - origin = CfgOrigin::fromCfgNode(orig) - ) - } + private predicate argumentRefinementPointsTo( + ArgumentRefinement def, PointsToContext context, string name, ObjectInternal value, + CfgOrigin origin + ) { + exists(ObjectInternal callable | + PointsToInternal::pointsTo(def.getCall().getFunction(), context, callable, _) and + callable != ObjectInternal::builtin("setattr") + ) and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + or + exists(string othername | + Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), othername, + _, _) and + not othername = name + ) and + variableAttributePointsTo(def.getInput(), context, name, value, origin) + or + exists(ControlFlowNode orig | + Expressions::setattr_call(def.getCall(), context, def.getInput().getASourceUse(), name, value, + orig) and + origin = CfgOrigin::fromCfgNode(orig) + ) + } } cached module ModuleAttributes { - private EssaVariable varAtExit(Module mod, string name) { - result.getName() = name and result.getAUse() = mod.getANormalExit() - } + private EssaVariable varAtExit(Module mod, string name) { + result.getName() = name and result.getAUse() = mod.getANormalExit() + } - private EssaVariable moduleStateVariable(ControlFlowNode use) { - result.isMetaVariable() and result.getAUse() = use - } + private EssaVariable moduleStateVariable(ControlFlowNode use) { + result.isMetaVariable() and result.getAUse() = use + } - private EssaVariable moduleStateVarAtExit(Module mod) { - result = moduleStateVariable(mod.getANormalExit()) - } + private EssaVariable moduleStateVarAtExit(Module mod) { + result = moduleStateVariable(mod.getANormalExit()) + } - cached - predicate pointsToAtExit(Module mod, string name, ObjectInternal value, CfgOrigin origin) { - if exists(varAtExit(mod, name)) - then - PointsToInternal::variablePointsTo(varAtExit(mod, name), any(Context c | c.isImport()), value, - origin) - else attributePointsTo(moduleStateVarAtExit(mod), name, value, origin) - } + cached + predicate pointsToAtExit(Module mod, string name, ObjectInternal value, CfgOrigin origin) { + if exists(varAtExit(mod, name)) + then + PointsToInternal::variablePointsTo(varAtExit(mod, name), any(Context c | c.isImport()), value, + origin) + else attributePointsTo(moduleStateVarAtExit(mod), name, value, origin) + } - cached - predicate attributePointsTo(EssaVariable var, string name, ObjectInternal value, CfgOrigin origin) { - importStarPointsTo(var.getDefinition(), name, value, origin) - or - callsitePointsTo(var.getDefinition(), name, value, origin) - or - scopeEntryPointsTo(var.getDefinition(), name, value, origin) - or - phiPointsTo(var.getDefinition(), name, value, origin) - } + cached + predicate attributePointsTo(EssaVariable var, string name, ObjectInternal value, CfgOrigin origin) { + importStarPointsTo(var.getDefinition(), name, value, origin) + or + callsitePointsTo(var.getDefinition(), name, value, origin) + or + scopeEntryPointsTo(var.getDefinition(), name, value, origin) + or + phiPointsTo(var.getDefinition(), name, value, origin) + } - /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ - pragma[nomagic] - private predicate phiPointsTo(PhiFunction phi, string name, ObjectInternal value, CfgOrigin origin) { - exists(EssaVariable input | - PointsToInternal::ssa_phi_reachable_from_input(phi, any(Context c | c.isImport()), input) and - attributePointsTo(input, name, value, origin) - ) - } + /** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */ + pragma[nomagic] + private predicate phiPointsTo(PhiFunction phi, string name, ObjectInternal value, CfgOrigin origin) { + exists(EssaVariable input | + PointsToInternal::ssa_phi_reachable_from_input(phi, any(Context c | c.isImport()), input) and + attributePointsTo(input, name, value, origin) + ) + } - pragma[nomagic] - private predicate importStarPointsTo( - ImportStarRefinement def, string name, ObjectInternal value, CfgOrigin origin - ) { - def.getVariable().isMetaVariable() and - /* Attribute from imported module */ - exists(ModuleObjectInternal mod | - importStarDef(def, _, mod) and - /* Attribute from imported module */ - exists(CfgOrigin orig | - InterModulePointsTo::moduleExportsBoolean(mod, name) = true and - mod.attribute(name, value, orig) and - origin = orig.fix(def.getDefiningNode()) and - not exists(Variable v | v.getId() = name and v.getScope() = def.getScope()) - ) - ) - or - /* Retain value held before import */ - exists(ModuleObjectInternal mod, EssaVariable input | - importStarDef(def, input, mod) and - (InterModulePointsTo::moduleExportsBoolean(mod, name) = false or name.charAt(0) = "_") and - attributePointsTo(def.getInput(), name, value, origin) - ) - } + pragma[nomagic] + private predicate importStarPointsTo( + ImportStarRefinement def, string name, ObjectInternal value, CfgOrigin origin + ) { + def.getVariable().isMetaVariable() and + /* Attribute from imported module */ + exists(ModuleObjectInternal mod | + importStarDef(def, _, mod) and + /* Attribute from imported module */ + exists(CfgOrigin orig | + InterModulePointsTo::moduleExportsBoolean(mod, name) = true and + mod.attribute(name, value, orig) and + origin = orig.fix(def.getDefiningNode()) and + not exists(Variable v | v.getId() = name and v.getScope() = def.getScope()) + ) + ) + or + /* Retain value held before import */ + exists(ModuleObjectInternal mod, EssaVariable input | + importStarDef(def, input, mod) and + (InterModulePointsTo::moduleExportsBoolean(mod, name) = false or name.charAt(0) = "_") and + attributePointsTo(def.getInput(), name, value, origin) + ) + } - private predicate importStarDef( - ImportStarRefinement def, EssaVariable input, ModuleObjectInternal mod - ) { - exists(ImportStarNode imp | - def.getVariable().getName() = "$" and - imp = def.getDefiningNode() and - input = def.getInput() and - PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) - ) - } + private predicate importStarDef( + ImportStarRefinement def, EssaVariable input, ModuleObjectInternal mod + ) { + exists(ImportStarNode imp | + def.getVariable().getName() = "$" and + imp = def.getDefiningNode() and + input = def.getInput() and + PointsToInternal::importCtxPointsTo(imp.getModule(), mod, _) + ) + } - /** - * Points-to for a variable (possibly) redefined by a call: - * `var = ...; foo(); use(var)` - * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). - */ - pragma[noinline] - private predicate callsitePointsTo( - CallsiteRefinement def, string name, ObjectInternal value, CfgOrigin origin - ) { - def.getVariable().isMetaVariable() and - exists(EssaVariable var, Function func, PointsToContext callee | - InterProceduralPointsTo::callsite_calls_function(def.getCall(), _, func, callee, _) and - var = moduleStateVariable(func.getANormalExit()) and - attributePointsTo(var, name, value, origin) - ) - } + /** + * Points-to for a variable (possibly) redefined by a call: + * `var = ...; foo(); use(var)` + * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). + */ + pragma[noinline] + private predicate callsitePointsTo( + CallsiteRefinement def, string name, ObjectInternal value, CfgOrigin origin + ) { + def.getVariable().isMetaVariable() and + exists(EssaVariable var, Function func, PointsToContext callee | + InterProceduralPointsTo::callsite_calls_function(def.getCall(), _, func, callee, _) and + var = moduleStateVariable(func.getANormalExit()) and + attributePointsTo(var, name, value, origin) + ) + } - /** - * Holds if the attribute name of the implicit '$' variable refers to `value` at the start of the scope. - * Since it cannot refer to any actual value, it is set to "undefined" for sub module names. - */ - pragma[noinline] - private predicate scopeEntryPointsTo( - ScopeEntryDefinition def, string name, ObjectInternal value, CfgOrigin origin - ) { - def.getVariable().isMetaVariable() and - exists(Module m | - def.getScope() = m and - not exists(EssaVariable named | named.getName() = name and named.getScope() = m) and - value = ObjectInternal::undefined() and - origin = CfgOrigin::unknown() - | - m.isPackageInit() and exists(m.getPackage().getSubModule(name)) - or - not m.declaredInAll(_) and - exists(PythonModuleObjectInternal mod | - mod.getSourceModule() = m and - InterModulePointsTo::ofInterestInExports(mod, name) - ) - ) - } + /** + * Holds if the attribute name of the implicit '$' variable refers to `value` at the start of the scope. + * Since it cannot refer to any actual value, it is set to "undefined" for sub module names. + */ + pragma[noinline] + private predicate scopeEntryPointsTo( + ScopeEntryDefinition def, string name, ObjectInternal value, CfgOrigin origin + ) { + def.getVariable().isMetaVariable() and + exists(Module m | + def.getScope() = m and + not exists(EssaVariable named | named.getName() = name and named.getScope() = m) and + value = ObjectInternal::undefined() and + origin = CfgOrigin::unknown() + | + m.isPackageInit() and exists(m.getPackage().getSubModule(name)) + or + not m.declaredInAll(_) and + exists(PythonModuleObjectInternal mod | + mod.getSourceModule() = m and + InterModulePointsTo::ofInterestInExports(mod, name) + ) + ) + } } diff --git a/python/ql/src/semmle/python/pointsto/PointsToContext.qll b/python/ql/src/semmle/python/pointsto/PointsToContext.qll index 4be7f812a6b..8b4178c796f 100644 --- a/python/ql/src/semmle/python/pointsto/PointsToContext.qll +++ b/python/ql/src/semmle/python/pointsto/PointsToContext.qll @@ -9,67 +9,67 @@ private import semmle.python.objects.ObjectInternal */ private int given_cost() { - exists(string depth | - py_flags_versioned("context.cost", depth, _) and - result = depth.toInt() - ) + exists(string depth | + py_flags_versioned("context.cost", depth, _) and + result = depth.toInt() + ) } pragma[noinline] private int max_context_cost() { - not py_flags_versioned("context.cost", _, _) and result = 7 - or - result = max(int cost | cost = given_cost() | cost) + not py_flags_versioned("context.cost", _, _) and result = 7 + or + result = max(int cost | cost = given_cost() | cost) } private int syntactic_call_count(Scope s) { - exists(Function f | f = s and f.getName() != "__init__" | - result = - count(CallNode call | - call.getFunction().(NameNode).getId() = f.getName() - or - call.getFunction().(AttrNode).getName() = f.getName() - ) - ) - or - s.getName() = "__init__" and result = 1 - or - not s instanceof Function and result = 0 + exists(Function f | f = s and f.getName() != "__init__" | + result = + count(CallNode call | + call.getFunction().(NameNode).getId() = f.getName() + or + call.getFunction().(AttrNode).getName() = f.getName() + ) + ) + or + s.getName() = "__init__" and result = 1 + or + not s instanceof Function and result = 0 } private int incoming_call_cost(Scope s) { - /* - * Syntactic call count will often be a considerable overestimate - * of the actual number of calls, so we use the square root. - * Cost = log(sqrt(call-count)) - */ + /* + * Syntactic call count will often be a considerable overestimate + * of the actual number of calls, so we use the square root. + * Cost = log(sqrt(call-count)) + */ - result = ((syntactic_call_count(s) + 1).log(2) * 0.5).floor() + result = ((syntactic_call_count(s) + 1).log(2) * 0.5).floor() } private int context_cost(TPointsToContext ctx) { - ctx = TMainContext() and result = 0 - or - ctx = TRuntimeContext() and result = 0 - or - ctx = TImportContext() and result = 0 - or - ctx = TCallContext(_, _, result) + ctx = TMainContext() and result = 0 + or + ctx = TRuntimeContext() and result = 0 + or + ctx = TImportContext() and result = 0 + or + ctx = TCallContext(_, _, result) } private int call_cost(CallNode call) { - if call.getScope().inSource() then result = 2 else result = 3 + if call.getScope().inSource() then result = 2 else result = 3 } private int outgoing_calls(Scope s) { result = strictcount(CallNode call | call.getScope() = s) } predicate super_method_call(CallNode call) { - call.getFunction().(AttrNode).getObject().(CallNode).getFunction().(NameNode).getId() = "super" + call.getFunction().(AttrNode).getObject().(CallNode).getFunction().(NameNode).getId() = "super" } private int outgoing_call_cost(CallNode c) { - /* Cost = log(outgoing-call-count) */ - result = outgoing_calls(c.getScope()).log(2).floor() + /* Cost = log(outgoing-call-count) */ + result = outgoing_calls(c.getScope()).log(2).floor() } /** @@ -79,46 +79,46 @@ private int outgoing_call_cost(CallNode c) { * in the number of contexts while retaining good results. */ private int splay_cost(CallNode c) { - if super_method_call(c) - then result = 0 - else result = outgoing_call_cost(c) + incoming_call_cost(c.getScope()) + if super_method_call(c) + then result = 0 + else result = outgoing_call_cost(c) + incoming_call_cost(c.getScope()) } private predicate call_to_init_or_del(CallNode call) { - exists(string mname | mname = "__init__" or mname = "__del__" | - mname = call.getFunction().(AttrNode).getName() - ) + exists(string mname | mname = "__init__" or mname = "__del__" | + mname = call.getFunction().(AttrNode).getName() + ) } /** Total cost estimate */ private int total_call_cost(CallNode call) { - /* - * We want to always follow __init__ and __del__ calls as they tell us about object construction, - * but we need to be aware of cycles, so they must have a non-zero cost. - */ + /* + * We want to always follow __init__ and __del__ calls as they tell us about object construction, + * but we need to be aware of cycles, so they must have a non-zero cost. + */ - if call_to_init_or_del(call) then result = 1 else result = call_cost(call) + splay_cost(call) + if call_to_init_or_del(call) then result = 1 else result = call_cost(call) + splay_cost(call) } pragma[noinline] private int total_cost(CallNode call, PointsToContext ctx) { - ctx.appliesTo(call) and - result = total_call_cost(call) + context_cost(ctx) + ctx.appliesTo(call) and + result = total_call_cost(call) + context_cost(ctx) } cached private newtype TPointsToContext = - TMainContext() or - TRuntimeContext() or - TImportContext() or - TCallContext(ControlFlowNode call, PointsToContext outerContext, int cost) { - total_cost(call, outerContext) = cost and - cost <= max_context_cost() - } or - TObjectContext(SelfInstanceInternal object) + TMainContext() or + TRuntimeContext() or + TImportContext() or + TCallContext(ControlFlowNode call, PointsToContext outerContext, int cost) { + total_cost(call, outerContext) = cost and + cost <= max_context_cost() + } or + TObjectContext(SelfInstanceInternal object) module Context { - PointsToContext forObject(ObjectInternal object) { result = TObjectContext(object) } + PointsToContext forObject(ObjectInternal object) { result = TObjectContext(object) } } /** @@ -129,109 +129,109 @@ module Context { * * All other contexts are call contexts and consist of a pair of call-site and caller context. */ class PointsToContext extends TPointsToContext { - /** Gets a textual representation of this element. */ - cached - string toString() { - this = TMainContext() and result = "main" - or - this = TRuntimeContext() and result = "runtime" - or - this = TImportContext() and result = "import" - or - exists(CallNode callsite, PointsToContext outerContext | - this = TCallContext(callsite, outerContext, _) and - result = callsite.getLocation() + " from " + outerContext.toString() - ) - } + /** Gets a textual representation of this element. */ + cached + string toString() { + this = TMainContext() and result = "main" + or + this = TRuntimeContext() and result = "runtime" + or + this = TImportContext() and result = "import" + or + exists(CallNode callsite, PointsToContext outerContext | + this = TCallContext(callsite, outerContext, _) and + result = callsite.getLocation() + " from " + outerContext.toString() + ) + } - /** Holds if `call` is the call-site from which this context was entered and `outer` is the caller's context. */ - predicate fromCall(CallNode call, PointsToContext caller) { - caller.appliesTo(call) and - this = TCallContext(call, caller, _) - } + /** Holds if `call` is the call-site from which this context was entered and `outer` is the caller's context. */ + predicate fromCall(CallNode call, PointsToContext caller) { + caller.appliesTo(call) and + this = TCallContext(call, caller, _) + } - /** Holds if `call` is the call-site from which this context was entered and `caller` is the caller's context. */ - predicate fromCall(CallNode call, PythonFunctionObjectInternal callee, PointsToContext caller) { - call = callee.getACall(caller) and - this = TCallContext(call, caller, _) - } + /** Holds if `call` is the call-site from which this context was entered and `caller` is the caller's context. */ + predicate fromCall(CallNode call, PythonFunctionObjectInternal callee, PointsToContext caller) { + call = callee.getACall(caller) and + this = TCallContext(call, caller, _) + } - /** Gets the caller context for this callee context. */ - PointsToContext getOuter() { this = TCallContext(_, result, _) } + /** Gets the caller context for this callee context. */ + PointsToContext getOuter() { this = TCallContext(_, result, _) } - /** Holds if this context is relevant to the given scope. */ - predicate appliesToScope(Scope s) { - /* Scripts */ - this = TMainContext() and maybe_main(s) - or - /* Modules and classes evaluated at import */ - s instanceof ImportTimeScope and this = TImportContext() - or - this = TRuntimeContext() and executes_in_runtime_context(s) - or - /* Called functions, regardless of their name */ - exists( - PythonFunctionObjectInternal callable, ControlFlowNode call, TPointsToContext outerContext - | - call = callable.getACall(outerContext) and - this = TCallContext(call, outerContext, _) - | - s = callable.getScope() - ) - or - InterProceduralPointsTo::callsite_calls_function(_, _, s, this, _) - } + /** Holds if this context is relevant to the given scope. */ + predicate appliesToScope(Scope s) { + /* Scripts */ + this = TMainContext() and maybe_main(s) + or + /* Modules and classes evaluated at import */ + s instanceof ImportTimeScope and this = TImportContext() + or + this = TRuntimeContext() and executes_in_runtime_context(s) + or + /* Called functions, regardless of their name */ + exists( + PythonFunctionObjectInternal callable, ControlFlowNode call, TPointsToContext outerContext + | + call = callable.getACall(outerContext) and + this = TCallContext(call, outerContext, _) + | + s = callable.getScope() + ) + or + InterProceduralPointsTo::callsite_calls_function(_, _, s, this, _) + } - /** Holds if this context can apply to the CFG node `n`. */ - pragma[inline] - predicate appliesTo(ControlFlowNode n) { this.appliesToScope(n.getScope()) } + /** Holds if this context can apply to the CFG node `n`. */ + pragma[inline] + predicate appliesTo(ControlFlowNode n) { this.appliesToScope(n.getScope()) } - /** Holds if this context is a call context. */ - predicate isCall() { this = TCallContext(_, _, _) } + /** Holds if this context is a call context. */ + predicate isCall() { this = TCallContext(_, _, _) } - /** Holds if this is the "main" context. */ - predicate isMain() { this = TMainContext() } + /** Holds if this is the "main" context. */ + predicate isMain() { this = TMainContext() } - /** Holds if this is the "import" context. */ - predicate isImport() { this = TImportContext() } + /** Holds if this is the "import" context. */ + predicate isImport() { this = TImportContext() } - /** Holds if this is the "default" context. */ - predicate isRuntime() { this = TRuntimeContext() } + /** Holds if this is the "default" context. */ + predicate isRuntime() { this = TRuntimeContext() } - /** Holds if this context or one of its caller contexts is the default context. */ - predicate fromRuntime() { - this.isRuntime() - or - this.getOuter().fromRuntime() - } + /** Holds if this context or one of its caller contexts is the default context. */ + predicate fromRuntime() { + this.isRuntime() + or + this.getOuter().fromRuntime() + } - /** Gets the depth (number of calls) for this context. */ - int getDepth() { - not exists(this.getOuter()) and result = 0 - or - result = this.getOuter().getDepth() + 1 - } + /** Gets the depth (number of calls) for this context. */ + int getDepth() { + not exists(this.getOuter()) and result = 0 + or + result = this.getOuter().getDepth() + 1 + } - int getCost() { result = context_cost(this) } + int getCost() { result = context_cost(this) } - CallNode getCall() { this = TCallContext(result, _, _) } + CallNode getCall() { this = TCallContext(result, _, _) } - /** Holds if a call would be too expensive to create a new context for */ - pragma[nomagic] - predicate untrackableCall(CallNode call) { total_cost(call, this) > max_context_cost() } + /** Holds if a call would be too expensive to create a new context for */ + pragma[nomagic] + predicate untrackableCall(CallNode call) { total_cost(call, this) > max_context_cost() } - CallNode getRootCall() { - this = TCallContext(result, TImportContext(), _) - or - result = this.getOuter().getRootCall() - } + CallNode getRootCall() { + this = TCallContext(result, TImportContext(), _) + or + result = this.getOuter().getRootCall() + } - /** Gets a version of Python that this context includes */ - pragma[inline] - Version getAVersion() { - /* Currently contexts do not include any version information, but may do in the future */ - result = major_version() - } + /** Gets a version of Python that this context includes */ + pragma[inline] + Version getAVersion() { + /* Currently contexts do not include any version information, but may do in the future */ + result = major_version() + } } private predicate in_source(Scope s) { exists(s.getEnclosingModule().getFile().getRelativePath()) } @@ -242,15 +242,15 @@ private predicate in_source(Scope s) { exists(s.getEnclosingModule().getFile().g * all "public" functions and methods, including those invoked by the VM. */ predicate executes_in_runtime_context(Function f) { - /* "Public" scope, i.e. functions whose name starts not with an underscore, or special methods */ - (f.getName().charAt(0) != "_" or f.isSpecialMethod() or f.isInitMethod()) and - in_source(f) + /* "Public" scope, i.e. functions whose name starts not with an underscore, or special methods */ + (f.getName().charAt(0) != "_" or f.isSpecialMethod() or f.isInitMethod()) and + in_source(f) } private predicate maybe_main(Module m) { - exists(If i, Compare cmp, Name name, StrConst main | m.getAStmt() = i and i.getTest() = cmp | - cmp.compares(name, any(Eq eq), main) and - name.getId() = "__name__" and - main.getText() = "__main__" - ) + exists(If i, Compare cmp, Name name, StrConst main | m.getAStmt() = i and i.getTest() = cmp | + cmp.compares(name, any(Eq eq), main) and + name.getId() = "__name__" and + main.getText() = "__main__" + ) } diff --git a/python/ql/src/semmle/python/regex.qll b/python/ql/src/semmle/python/regex.qll index cc33b2b347c..aaaf13ed823 100644 --- a/python/ql/src/semmle/python/regex.qll +++ b/python/ql/src/semmle/python/regex.qll @@ -2,21 +2,21 @@ import python import semmle.python.objects.ObjectInternal private predicate re_module_function(string name, int flags) { - name = "compile" and flags = 1 - or - name = "search" and flags = 2 - or - name = "match" and flags = 2 - or - name = "split" and flags = 3 - or - name = "findall" and flags = 2 - or - name = "finditer" and flags = 2 - or - name = "sub" and flags = 4 - or - name = "subn" and flags = 4 + name = "compile" and flags = 1 + or + name = "search" and flags = 2 + or + name = "match" and flags = 2 + or + name = "split" and flags = 3 + or + name = "findall" and flags = 2 + or + name = "finditer" and flags = 2 + or + name = "sub" and flags = 4 + or + name = "subn" and flags = 4 } /** @@ -24,701 +24,699 @@ private predicate re_module_function(string name, int flags) { * If regex mode is not known, `mode` will be `"None"`. */ predicate used_as_regex(Expr s, string mode) { - (s instanceof Bytes or s instanceof Unicode) and - /* Call to re.xxx(regex, ... [mode]) */ - exists(CallNode call, string name | - call.getArg(0).refersTo(_, _, s.getAFlowNode()) and - call.getFunction().pointsTo(Module::named("re").attr(name)) and - not name = "escape" - | - mode = "None" - or - exists(Value obj | mode = mode_from_mode_object(obj) | - exists(int flags_arg | - re_module_function(name, flags_arg) and - call.getArg(flags_arg).pointsTo(obj) - ) - or - call.getArgByName("flags").pointsTo(obj) - ) + (s instanceof Bytes or s instanceof Unicode) and + /* Call to re.xxx(regex, ... [mode]) */ + exists(CallNode call, string name | + call.getArg(0).refersTo(_, _, s.getAFlowNode()) and + call.getFunction().pointsTo(Module::named("re").attr(name)) and + not name = "escape" + | + mode = "None" + or + exists(Value obj | mode = mode_from_mode_object(obj) | + exists(int flags_arg | + re_module_function(name, flags_arg) and + call.getArg(flags_arg).pointsTo(obj) + ) + or + call.getArgByName("flags").pointsTo(obj) ) + ) } string mode_from_mode_object(Value obj) { - ( - result = "DEBUG" or - result = "IGNORECASE" or - result = "LOCALE" or - result = "MULTILINE" or - result = "DOTALL" or - result = "UNICODE" or - result = "VERBOSE" - ) and - exists(int flag | - flag = Value::named("sre_constants.SRE_FLAG_" + result).(ObjectInternal).intValue() and - obj.(ObjectInternal).intValue().bitAnd(flag) = flag - ) + ( + result = "DEBUG" or + result = "IGNORECASE" or + result = "LOCALE" or + result = "MULTILINE" or + result = "DOTALL" or + result = "UNICODE" or + result = "VERBOSE" + ) and + exists(int flag | + flag = Value::named("sre_constants.SRE_FLAG_" + result).(ObjectInternal).intValue() and + obj.(ObjectInternal).intValue().bitAnd(flag) = flag + ) } /** A StrConst used as a regular expression */ abstract class RegexString extends Expr { - RegexString() { (this instanceof Bytes or this instanceof Unicode) } + RegexString() { (this instanceof Bytes or this instanceof Unicode) } - predicate char_set_start(int start, int end) { - this.nonEscapedCharAt(start) = "[" and - ( - this.getChar(start + 1) = "^" and end = start + 2 - or - not this.getChar(start + 1) = "^" and end = start + 1 - ) - } + predicate char_set_start(int start, int end) { + this.nonEscapedCharAt(start) = "[" and + ( + this.getChar(start + 1) = "^" and end = start + 2 + or + not this.getChar(start + 1) = "^" and end = start + 1 + ) + } - /** Whether there is a character class, between start (inclusive) and end (exclusive) */ - predicate charSet(int start, int end) { - exists(int inner_start, int inner_end | - this.char_set_start(start, inner_start) and - not this.char_set_start(_, start) - | - end = inner_end + 1 and - inner_end > inner_start and - this.nonEscapedCharAt(inner_end) = "]" and - not exists(int mid | this.nonEscapedCharAt(mid) = "]" | mid > inner_start and mid < inner_end) - ) - } + /** Whether there is a character class, between start (inclusive) and end (exclusive) */ + predicate charSet(int start, int end) { + exists(int inner_start, int inner_end | + this.char_set_start(start, inner_start) and + not this.char_set_start(_, start) + | + end = inner_end + 1 and + inner_end > inner_start and + this.nonEscapedCharAt(inner_end) = "]" and + not exists(int mid | this.nonEscapedCharAt(mid) = "]" | mid > inner_start and mid < inner_end) + ) + } - predicate escapingChar(int pos) { this.escaping(pos) = true } + predicate escapingChar(int pos) { this.escaping(pos) = true } - private boolean escaping(int pos) { - pos = -1 and result = false + private boolean escaping(int pos) { + pos = -1 and result = false + or + this.getChar(pos) = "\\" and result = this.escaping(pos - 1).booleanNot() + or + this.getChar(pos) != "\\" and result = false + } + + /** Gets the text of this regex */ + string getText() { + result = this.(Unicode).getS() + or + result = this.(Bytes).getS() + } + + string getChar(int i) { result = this.getText().charAt(i) } + + string nonEscapedCharAt(int i) { + result = this.getText().charAt(i) and + not this.escapingChar(i - 1) + } + + private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" } + + private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" } + + private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" } + + predicate failedToParse(int i) { + exists(this.getChar(i)) and + not exists(int start, int end | + this.top_level(start, end) and + start <= i and + end > i + ) + } + + /** Named unicode characters, eg \N{degree sign} */ + private predicate escapedName(int start, int end) { + this.escapingChar(start) and + this.getChar(start + 1) = "N" and + this.getChar(start + 2) = "{" and + this.getChar(end - 1) = "}" and + end > start and + not exists(int i | start + 2 < i and i < end - 1 | this.getChar(i) = "}") + } + + private predicate escapedCharacter(int start, int end) { + this.escapingChar(start) and + not exists(this.getText().substring(start + 1, end + 1).toInt()) and + ( + // hex value \xhh + this.getChar(start + 1) = "x" and end = start + 4 + or + // octal value \ooo + end in [start + 2 .. start + 4] and + exists(this.getText().substring(start + 1, end).toInt()) + or + // 16-bit hex value \uhhhh + this.getChar(start + 1) = "u" and end = start + 6 + or + // 32-bit hex value \Uhhhhhhhh + this.getChar(start + 1) = "U" and end = start + 10 + or + escapedName(start, end) + or + // escape not handled above, update when adding a new case + not this.getChar(start + 1) in ["x", "u", "U", "N"] and + end = start + 2 + ) + } + + private predicate inCharSet(int index) { + exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2]) + } + + /* + * 'simple' characters are any that don't alter the parsing of the regex. + */ + + private predicate simpleCharacter(int start, int end) { + end = start + 1 and + not this.charSet(start, _) and + not this.charSet(_, start + 1) and + exists(string c | c = this.getChar(start) | + exists(int x, int y, int z | + this.charSet(x, z) and + this.char_set_start(x, y) + | + start = y or - this.getChar(pos) = "\\" and result = this.escaping(pos - 1).booleanNot() + start = z - 2 or - this.getChar(pos) != "\\" and result = false - } + start > y and start < z - 2 and not c = "-" + ) + or + not this.inCharSet(start) and + not c = "(" and + not c = "[" and + not c = ")" and + not c = "|" and + not this.qualifier(start, _, _) + ) + } - /** Gets the text of this regex */ - string getText() { - result = this.(Unicode).getS() + predicate character(int start, int end) { + ( + this.simpleCharacter(start, end) and + not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end) + or + this.escapedCharacter(start, end) + ) and + not exists(int x, int y | this.group_start(x, y) and x <= start and y >= end) + } + + predicate normalCharacter(int start, int end) { + this.character(start, end) and + not this.specialCharacter(start, end, _) + } + + predicate specialCharacter(int start, int end, string char) { + this.character(start, end) and + end = start + 1 and + char = this.getChar(start) and + (char = "$" or char = "^" or char = ".") and + not this.inCharSet(start) + } + + /** Whether the text in the range start,end is a group */ + predicate group(int start, int end) { + this.groupContents(start, end, _, _) + or + this.emptyGroup(start, end) + } + + /** Gets the number of the group in start,end */ + int getGroupNumber(int start, int end) { + this.group(start, end) and + result = + count(int i | this.group(i, _) and i < start and not this.non_capturing_group_start(i, _)) + 1 + } + + /** Gets the name, if it has one, of the group in start,end */ + string getGroupName(int start, int end) { + this.group(start, end) and + exists(int name_end | + this.named_group_start(start, name_end) and + result = this.getText().substring(start + 4, name_end - 1) + ) + } + + /** Whether the text in the range start, end is a group and can match the empty string. */ + predicate zeroWidthMatch(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + positiveLookaheadAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + private predicate emptyGroup(int start, int end) { + exists(int endm1 | end = endm1 + 1 | + this.group_start(start, endm1) and + this.isGroupEnd(endm1) + ) + } + + private predicate emptyMatchAtStartGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookaheadAssertionGroup(start, end) + } + + private predicate emptyMatchAtEndGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + private predicate negativeAssertionGroup(int start, int end) { + exists(int in_start | + this.negative_lookahead_assertion_start(start, in_start) + or + this.negative_lookbehind_assertion_start(start, in_start) + | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate positiveLookaheadAssertionGroup(int start, int end) { + exists(int in_start | this.lookahead_assertion_start(start, in_start) | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate positiveLookbehindAssertionGroup(int start, int end) { + exists(int in_start | this.lookbehind_assertion_start(start, in_start) | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate group_start(int start, int end) { + this.non_capturing_group_start(start, end) + or + this.flag_group_start(start, end, _) + or + this.named_group_start(start, end) + or + this.named_backreference_start(start, end) + or + this.lookahead_assertion_start(start, end) + or + this.negative_lookahead_assertion_start(start, end) + or + this.lookbehind_assertion_start(start, end) + or + this.negative_lookbehind_assertion_start(start, end) + or + this.comment_group_start(start, end) + or + this.simple_group_start(start, end) + } + + private predicate non_capturing_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = ":" and + end = start + 3 + } + + private predicate simple_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) != "?" and + end = start + 1 + } + + private predicate named_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "P" and + this.getChar(start + 3) = "<" and + not this.getChar(start + 4) = "=" and + not this.getChar(start + 4) = "!" and + exists(int name_end | + name_end = min(int i | i > start + 4 and this.getChar(i) = ">") and + end = name_end + 1 + ) + } + + private predicate named_backreference_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "P" and + this.getChar(start + 3) = "=" and + end = min(int i | i > start + 4 and this.getChar(i) = "?") + } + + private predicate flag_group_start(int start, int end, string c) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + end = start + 3 and + c = this.getChar(start + 2) and + ( + c = "i" or + c = "L" or + c = "m" or + c = "s" or + c = "u" or + c = "x" + ) + } + + /** + * Gets the mode of this regular expression string if + * it is defined by a prefix. + */ + string getModeFromPrefix() { + exists(string c | this.flag_group_start(_, _, c) | + c = "i" and result = "IGNORECASE" + or + c = "L" and result = "LOCALE" + or + c = "m" and result = "MULTILINE" + or + c = "s" and result = "DOTALL" + or + c = "u" and result = "UNICODE" + or + c = "x" and result = "VERBOSE" + ) + } + + private predicate lookahead_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "=" and + end = start + 3 + } + + private predicate negative_lookahead_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "!" and + end = start + 3 + } + + private predicate lookbehind_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "<" and + this.getChar(start + 3) = "=" and + end = start + 4 + } + + private predicate negative_lookbehind_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "<" and + this.getChar(start + 3) = "!" and + end = start + 4 + } + + private predicate comment_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "#" and + end = start + 3 + } + + predicate groupContents(int start, int end, int in_start, int in_end) { + this.group_start(start, in_start) and + end = in_end + 1 and + this.top_level(in_start, in_end) and + this.isGroupEnd(in_end) + } + + private predicate named_backreference(int start, int end, string name) { + this.named_backreference_start(start, start + 4) and + end = min(int i | i > start + 4 and this.getChar(i) = ")") + 1 and + name = this.getText().substring(start + 4, end - 2) + } + + private predicate numbered_backreference(int start, int end, int value) { + this.escapingChar(start) and + exists(string text, string svalue, int len | + end = start + len and + text = this.getText() and + len in [2 .. 3] + | + svalue = text.substring(start + 1, start + len) and + value = svalue.toInt() and + not exists(text.substring(start + 1, start + len + 1).toInt()) and + value != 0 + ) + } + + /** Whether the text in the range start,end is a back reference */ + predicate backreference(int start, int end) { + this.numbered_backreference(start, end, _) + or + this.named_backreference(start, end, _) + } + + /** Gets the number of the back reference in start,end */ + int getBackrefNumber(int start, int end) { this.numbered_backreference(start, end, result) } + + /** Gets the name, if it has one, of the back reference in start,end */ + string getBackrefName(int start, int end) { this.named_backreference(start, end, result) } + + private predicate baseItem(int start, int end) { + this.character(start, end) and + not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end) + or + this.group(start, end) + or + this.charSet(start, end) + } + + private predicate qualifier(int start, int end, boolean maybe_empty) { + this.short_qualifier(start, end, maybe_empty) and not this.getChar(end) = "?" + or + exists(int short_end | this.short_qualifier(start, short_end, maybe_empty) | + if this.getChar(short_end) = "?" then end = short_end + 1 else end = short_end + ) + } + + private predicate short_qualifier(int start, int end, boolean maybe_empty) { + ( + this.getChar(start) = "+" and maybe_empty = false + or + this.getChar(start) = "*" and maybe_empty = true + or + this.getChar(start) = "?" and maybe_empty = true + ) and + end = start + 1 + or + exists(int endin | end = endin + 1 | + this.getChar(start) = "{" and + this.getChar(endin) = "}" and + end > start and + exists(string multiples | multiples = this.getText().substring(start + 1, endin) | + multiples.regexpMatch("0+") and maybe_empty = true or - result = this.(Bytes).getS() - } - - string getChar(int i) { result = this.getText().charAt(i) } - - string nonEscapedCharAt(int i) { - result = this.getText().charAt(i) and - not this.escapingChar(i - 1) - } - - private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" } - - private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" } - - private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" } - - predicate failedToParse(int i) { - exists(this.getChar(i)) and - not exists(int start, int end | - this.top_level(start, end) and - start <= i and - end > i - ) - } - - /** Named unicode characters, eg \N{degree sign} */ - private predicate escapedName(int start, int end) { - this.escapingChar(start) and - this.getChar(start + 1) = "N" and - this.getChar(start + 2) = "{" and - this.getChar(end - 1) = "}" and - end > start and - not exists(int i | start + 2 < i and i < end - 1 | - this.getChar(i) = "}" - ) - } - - private predicate escapedCharacter(int start, int end) { - this.escapingChar(start) and - not exists(this.getText().substring(start + 1, end + 1).toInt()) and - ( - // hex value \xhh - this.getChar(start + 1) = "x" and end = start + 4 - or - // octal value \ooo - end in [start + 2 .. start + 4] and - exists(this.getText().substring(start + 1, end).toInt()) - or - // 16-bit hex value \uhhhh - this.getChar(start + 1) = "u" and end = start + 6 - or - // 32-bit hex value \Uhhhhhhhh - this.getChar(start + 1) = "U" and end = start + 10 - or - escapedName(start, end) - or - // escape not handled above, update when adding a new case - not this.getChar(start + 1) in ["x", "u", "U", "N"] and - end = start + 2 - ) - } - - private predicate inCharSet(int index) { - exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2]) - } - - /* - * 'simple' characters are any that don't alter the parsing of the regex. - */ - - private predicate simpleCharacter(int start, int end) { - end = start + 1 and - not this.charSet(start, _) and - not this.charSet(_, start + 1) and - exists(string c | c = this.getChar(start) | - exists(int x, int y, int z | - this.charSet(x, z) and - this.char_set_start(x, y) - | - start = y - or - start = z - 2 - or - start > y and start < z - 2 and not c = "-" - ) - or - not this.inCharSet(start) and - not c = "(" and - not c = "[" and - not c = ")" and - not c = "|" and - not this.qualifier(start, _, _) - ) - } - - predicate character(int start, int end) { - ( - this.simpleCharacter(start, end) and - not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end) - or - this.escapedCharacter(start, end) - ) and - not exists(int x, int y | this.group_start(x, y) and x <= start and y >= end) - } - - predicate normalCharacter(int start, int end) { - this.character(start, end) and - not this.specialCharacter(start, end, _) - } - - predicate specialCharacter(int start, int end, string char) { - this.character(start, end) and - end = start + 1 and - char = this.getChar(start) and - (char = "$" or char = "^" or char = ".") and - not this.inCharSet(start) - } - - /** Whether the text in the range start,end is a group */ - predicate group(int start, int end) { - this.groupContents(start, end, _, _) + multiples.regexpMatch("0*,[0-9]*") and maybe_empty = true or - this.emptyGroup(start, end) - } - - /** Gets the number of the group in start,end */ - int getGroupNumber(int start, int end) { - this.group(start, end) and - result = - count(int i | this.group(i, _) and i < start and not this.non_capturing_group_start(i, _)) + 1 - } - - /** Gets the name, if it has one, of the group in start,end */ - string getGroupName(int start, int end) { - this.group(start, end) and - exists(int name_end | - this.named_group_start(start, name_end) and - result = this.getText().substring(start + 4, name_end - 1) - ) - } - - /** Whether the text in the range start, end is a group and can match the empty string. */ - predicate zeroWidthMatch(int start, int end) { - this.emptyGroup(start, end) + multiples.regexpMatch("0*[1-9][0-9]*") and maybe_empty = false or - this.negativeAssertionGroup(start, end) - or - positiveLookaheadAssertionGroup(start, end) - or - this.positiveLookbehindAssertionGroup(start, end) - } + multiples.regexpMatch("0*[1-9][0-9]*,[0-9]*") and maybe_empty = false + ) and + not exists(int mid | + this.getChar(mid) = "}" and + mid > start and + mid < endin + ) + ) + } - private predicate emptyGroup(int start, int end) { - exists(int endm1 | end = endm1 + 1 | - this.group_start(start, endm1) and - this.isGroupEnd(endm1) - ) - } + /** + * Whether the text in the range start,end is a qualified item, where item is a character, + * a character set or a group. + */ + predicate qualifiedItem(int start, int end, boolean maybe_empty) { + this.qualifiedPart(start, _, end, maybe_empty) + } - private predicate emptyMatchAtStartGroup(int start, int end) { - this.emptyGroup(start, end) - or - this.negativeAssertionGroup(start, end) - or - this.positiveLookaheadAssertionGroup(start, end) - } + private predicate qualifiedPart(int start, int part_end, int end, boolean maybe_empty) { + this.baseItem(start, part_end) and + this.qualifier(part_end, end, maybe_empty) + } - private predicate emptyMatchAtEndGroup(int start, int end) { - this.emptyGroup(start, end) - or - this.negativeAssertionGroup(start, end) - or - this.positiveLookbehindAssertionGroup(start, end) - } + private predicate item(int start, int end) { + this.qualifiedItem(start, end, _) + or + this.baseItem(start, end) and not this.qualifier(end, _, _) + } - private predicate negativeAssertionGroup(int start, int end) { - exists(int in_start | - this.negative_lookahead_assertion_start(start, in_start) - or - this.negative_lookbehind_assertion_start(start, in_start) - | - this.groupContents(start, end, in_start, _) - ) - } + private predicate subsequence(int start, int end) { + ( + start = 0 or + this.group_start(_, start) or + this.isOptionDivider(start - 1) + ) and + this.item(start, end) + or + exists(int mid | + this.subsequence(start, mid) and + this.item(mid, end) + ) + } - private predicate positiveLookaheadAssertionGroup(int start, int end) { - exists(int in_start | this.lookahead_assertion_start(start, in_start) | - this.groupContents(start, end, in_start, _) - ) - } + /** + * Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character, + * a character set or a group. + */ + predicate sequence(int start, int end) { + this.sequenceOrQualified(start, end) and + not this.qualifiedItem(start, end, _) + } - private predicate positiveLookbehindAssertionGroup(int start, int end) { - exists(int in_start | this.lookbehind_assertion_start(start, in_start) | - this.groupContents(start, end, in_start, _) - ) - } + private predicate sequenceOrQualified(int start, int end) { + this.subsequence(start, end) and + not this.item_start(end) + } - private predicate group_start(int start, int end) { - this.non_capturing_group_start(start, end) - or - this.flag_group_start(start, end, _) - or - this.named_group_start(start, end) - or - this.named_backreference_start(start, end) - or - this.lookahead_assertion_start(start, end) - or - this.negative_lookahead_assertion_start(start, end) - or - this.lookbehind_assertion_start(start, end) - or - this.negative_lookbehind_assertion_start(start, end) - or - this.comment_group_start(start, end) - or - this.simple_group_start(start, end) - } + private predicate item_start(int start) { + this.character(start, _) or + this.isGroupStart(start) or + this.charSet(start, _) + } - private predicate non_capturing_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = ":" and - end = start + 3 - } + private predicate item_end(int end) { + this.character(_, end) + or + exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1) + or + this.charSet(_, end) + or + this.qualifier(_, end, _) + } - private predicate simple_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) != "?" and - end = start + 1 - } + private predicate top_level(int start, int end) { + this.subalternation(start, end, _) and + not this.isOptionDivider(end) + } - private predicate named_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "P" and - this.getChar(start + 3) = "<" and - not this.getChar(start + 4) = "=" and - not this.getChar(start + 4) = "!" and - exists(int name_end | - name_end = min(int i | i > start + 4 and this.getChar(i) = ">") and - end = name_end + 1 - ) - } + private predicate subalternation(int start, int end, int item_start) { + this.sequenceOrQualified(start, end) and + not this.isOptionDivider(start - 1) and + item_start = start + or + start = end and + not this.item_end(start) and + this.isOptionDivider(end) and + item_start = start + or + exists(int mid | + this.subalternation(start, mid, _) and + this.isOptionDivider(mid) and + item_start = mid + 1 + | + this.sequenceOrQualified(item_start, end) + or + not this.item_start(end) and end = item_start + ) + } - private predicate named_backreference_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "P" and - this.getChar(start + 3) = "=" and - end = min(int i | i > start + 4 and this.getChar(i) = "?") - } + /** + * Whether the text in the range start,end is an alternation + */ + predicate alternation(int start, int end) { + this.top_level(start, end) and + exists(int less | this.subalternation(start, less, _) and less < end) + } - private predicate flag_group_start(int start, int end, string c) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - end = start + 3 and - c = this.getChar(start + 2) and - ( - c = "i" or - c = "L" or - c = "m" or - c = "s" or - c = "u" or - c = "x" - ) - } + /** + * Whether the text in the range start,end is an alternation and the text in part_start, part_end is one of the + * options in that alternation. + */ + predicate alternationOption(int start, int end, int part_start, int part_end) { + this.alternation(start, end) and + this.subalternation(start, part_end, part_start) + } - /** - * Gets the mode of this regular expression string if - * it is defined by a prefix. - */ - string getModeFromPrefix() { - exists(string c | this.flag_group_start(_, _, c) | - c = "i" and result = "IGNORECASE" - or - c = "L" and result = "LOCALE" - or - c = "m" and result = "MULTILINE" - or - c = "s" and result = "DOTALL" - or - c = "u" and result = "UNICODE" - or - c = "x" and result = "VERBOSE" - ) - } + /** A part of the regex that may match the start of the string. */ + private predicate firstPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int x | this.firstPart(x, end) | + this.emptyMatchAtStartGroup(x, start) or + this.qualifiedItem(x, start, true) or + this.specialCharacter(x, start, "^") + ) + or + exists(int y | this.firstPart(start, y) | + this.item(start, end) + or + this.qualifiedPart(start, end, y, _) + ) + or + exists(int x, int y | this.firstPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } - private predicate lookahead_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "=" and - end = start + 3 - } + /** A part of the regex that may match the end of the string. */ + private predicate lastPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int y | this.lastPart(start, y) | + this.emptyMatchAtEndGroup(end, y) + or + this.qualifiedItem(end, y, true) + or + this.specialCharacter(end, y, "$") + or + y = end + 2 and this.escapingChar(end) and this.getChar(end + 1) = "Z" + ) + or + exists(int x | + this.lastPart(x, end) and + this.item(start, end) + ) + or + exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _)) + or + exists(int x, int y | this.lastPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } - private predicate negative_lookahead_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "!" and - end = start + 3 - } + /** + * Whether the item at [start, end) is one of the first items + * to be matched. + */ + predicate firstItem(int start, int end) { + ( + this.character(start, end) + or + this.qualifiedItem(start, end, _) + or + this.charSet(start, end) + ) and + this.firstPart(start, end) + } - private predicate lookbehind_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "<" and - this.getChar(start + 3) = "=" and - end = start + 4 - } - - private predicate negative_lookbehind_assertion_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "<" and - this.getChar(start + 3) = "!" and - end = start + 4 - } - - private predicate comment_group_start(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "#" and - end = start + 3 - } - - predicate groupContents(int start, int end, int in_start, int in_end) { - this.group_start(start, in_start) and - end = in_end + 1 and - this.top_level(in_start, in_end) and - this.isGroupEnd(in_end) - } - - private predicate named_backreference(int start, int end, string name) { - this.named_backreference_start(start, start + 4) and - end = min(int i | i > start + 4 and this.getChar(i) = ")") + 1 and - name = this.getText().substring(start + 4, end - 2) - } - - private predicate numbered_backreference(int start, int end, int value) { - this.escapingChar(start) and - exists(string text, string svalue, int len | - end = start + len and - text = this.getText() and - len in [2 .. 3] - | - svalue = text.substring(start + 1, start + len) and - value = svalue.toInt() and - not exists(text.substring(start + 1, start + len + 1).toInt()) and - value != 0 - ) - } - - /** Whether the text in the range start,end is a back reference */ - predicate backreference(int start, int end) { - this.numbered_backreference(start, end, _) - or - this.named_backreference(start, end, _) - } - - /** Gets the number of the back reference in start,end */ - int getBackrefNumber(int start, int end) { this.numbered_backreference(start, end, result) } - - /** Gets the name, if it has one, of the back reference in start,end */ - string getBackrefName(int start, int end) { this.named_backreference(start, end, result) } - - private predicate baseItem(int start, int end) { - this.character(start, end) and - not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end) - or - this.group(start, end) - or - this.charSet(start, end) - } - - private predicate qualifier(int start, int end, boolean maybe_empty) { - this.short_qualifier(start, end, maybe_empty) and not this.getChar(end) = "?" - or - exists(int short_end | this.short_qualifier(start, short_end, maybe_empty) | - if this.getChar(short_end) = "?" then end = short_end + 1 else end = short_end - ) - } - - private predicate short_qualifier(int start, int end, boolean maybe_empty) { - ( - this.getChar(start) = "+" and maybe_empty = false - or - this.getChar(start) = "*" and maybe_empty = true - or - this.getChar(start) = "?" and maybe_empty = true - ) and - end = start + 1 - or - exists(int endin | end = endin + 1 | - this.getChar(start) = "{" and - this.getChar(endin) = "}" and - end > start and - exists(string multiples | multiples = this.getText().substring(start + 1, endin) | - multiples.regexpMatch("0+") and maybe_empty = true - or - multiples.regexpMatch("0*,[0-9]*") and maybe_empty = true - or - multiples.regexpMatch("0*[1-9][0-9]*") and maybe_empty = false - or - multiples.regexpMatch("0*[1-9][0-9]*,[0-9]*") and maybe_empty = false - ) and - not exists(int mid | - this.getChar(mid) = "}" and - mid > start and - mid < endin - ) - ) - } - - /** - * Whether the text in the range start,end is a qualified item, where item is a character, - * a character set or a group. - */ - predicate qualifiedItem(int start, int end, boolean maybe_empty) { - this.qualifiedPart(start, _, end, maybe_empty) - } - - private predicate qualifiedPart(int start, int part_end, int end, boolean maybe_empty) { - this.baseItem(start, part_end) and - this.qualifier(part_end, end, maybe_empty) - } - - private predicate item(int start, int end) { - this.qualifiedItem(start, end, _) - or - this.baseItem(start, end) and not this.qualifier(end, _, _) - } - - private predicate subsequence(int start, int end) { - ( - start = 0 or - this.group_start(_, start) or - this.isOptionDivider(start - 1) - ) and - this.item(start, end) - or - exists(int mid | - this.subsequence(start, mid) and - this.item(mid, end) - ) - } - - /** - * Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character, - * a character set or a group. - */ - predicate sequence(int start, int end) { - this.sequenceOrQualified(start, end) and - not this.qualifiedItem(start, end, _) - } - - private predicate sequenceOrQualified(int start, int end) { - this.subsequence(start, end) and - not this.item_start(end) - } - - private predicate item_start(int start) { - this.character(start, _) or - this.isGroupStart(start) or - this.charSet(start, _) - } - - private predicate item_end(int end) { - this.character(_, end) - or - exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1) - or - this.charSet(_, end) - or - this.qualifier(_, end, _) - } - - private predicate top_level(int start, int end) { - this.subalternation(start, end, _) and - not this.isOptionDivider(end) - } - - private predicate subalternation(int start, int end, int item_start) { - this.sequenceOrQualified(start, end) and - not this.isOptionDivider(start - 1) and - item_start = start - or - start = end and - not this.item_end(start) and - this.isOptionDivider(end) and - item_start = start - or - exists(int mid | - this.subalternation(start, mid, _) and - this.isOptionDivider(mid) and - item_start = mid + 1 - | - this.sequenceOrQualified(item_start, end) - or - not this.item_start(end) and end = item_start - ) - } - - /** - * Whether the text in the range start,end is an alternation - */ - predicate alternation(int start, int end) { - this.top_level(start, end) and - exists(int less | this.subalternation(start, less, _) and less < end) - } - - /** - * Whether the text in the range start,end is an alternation and the text in part_start, part_end is one of the - * options in that alternation. - */ - predicate alternationOption(int start, int end, int part_start, int part_end) { - this.alternation(start, end) and - this.subalternation(start, part_end, part_start) - } - - /** A part of the regex that may match the start of the string. */ - private predicate firstPart(int start, int end) { - start = 0 and end = this.getText().length() - or - exists(int x | this.firstPart(x, end) | - this.emptyMatchAtStartGroup(x, start) or - this.qualifiedItem(x, start, true) or - this.specialCharacter(x, start, "^") - ) - or - exists(int y | this.firstPart(start, y) | - this.item(start, end) - or - this.qualifiedPart(start, end, y, _) - ) - or - exists(int x, int y | this.firstPart(x, y) | - this.groupContents(x, y, start, end) - or - this.alternationOption(x, y, start, end) - ) - } - - /** A part of the regex that may match the end of the string. */ - private predicate lastPart(int start, int end) { - start = 0 and end = this.getText().length() - or - exists(int y | this.lastPart(start, y) | - this.emptyMatchAtEndGroup(end, y) - or - this.qualifiedItem(end, y, true) - or - this.specialCharacter(end, y, "$") - or - y = end + 2 and this.escapingChar(end) and this.getChar(end + 1) = "Z" - ) - or - exists(int x | - this.lastPart(x, end) and - this.item(start, end) - ) - or - exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _)) - or - exists(int x, int y | this.lastPart(x, y) | - this.groupContents(x, y, start, end) - or - this.alternationOption(x, y, start, end) - ) - } - - /** - * Whether the item at [start, end) is one of the first items - * to be matched. - */ - predicate firstItem(int start, int end) { - ( - this.character(start, end) - or - this.qualifiedItem(start, end, _) - or - this.charSet(start, end) - ) and - this.firstPart(start, end) - } - - /** - * Whether the item at [start, end) is one of the last items - * to be matched. - */ - predicate lastItem(int start, int end) { - ( - this.character(start, end) - or - this.qualifiedItem(start, end, _) - or - this.charSet(start, end) - ) and - this.lastPart(start, end) - } + /** + * Whether the item at [start, end) is one of the last items + * to be matched. + */ + predicate lastItem(int start, int end) { + ( + this.character(start, end) + or + this.qualifiedItem(start, end, _) + or + this.charSet(start, end) + ) and + this.lastPart(start, end) + } } /** A StrConst used as a regular expression */ class Regex extends RegexString { - Regex() { used_as_regex(this, _) } + Regex() { used_as_regex(this, _) } - /** - * Gets a mode (if any) of this regular expression. Can be any of: - * DEBUG - * IGNORECASE - * LOCALE - * MULTILINE - * DOTALL - * UNICODE - * VERBOSE - */ - string getAMode() { - result != "None" and - used_as_regex(this, result) - or - result = this.getModeFromPrefix() - } + /** + * Gets a mode (if any) of this regular expression. Can be any of: + * DEBUG + * IGNORECASE + * LOCALE + * MULTILINE + * DOTALL + * UNICODE + * VERBOSE + */ + string getAMode() { + result != "None" and + used_as_regex(this, result) + or + result = this.getModeFromPrefix() + } } diff --git a/python/ql/src/semmle/python/security/ClearText.qll b/python/ql/src/semmle/python/security/ClearText.qll index a26e33218dd..8e964d19386 100644 --- a/python/ql/src/semmle/python/security/ClearText.qll +++ b/python/ql/src/semmle/python/security/ClearText.qll @@ -5,54 +5,54 @@ import semmle.python.dataflow.Files import semmle.python.web.Http module ClearTextStorage { - abstract class Sink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } - } + abstract class Sink extends TaintSink { + override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } + } - class CookieStorageSink extends Sink { - CookieStorageSink() { any(CookieSet cookie).getValue() = this } - } + class CookieStorageSink extends Sink { + CookieStorageSink() { any(CookieSet cookie).getValue() = this } + } - class FileStorageSink extends Sink { - FileStorageSink() { - exists(CallNode call, AttrNode meth, string name | - any(OpenFile fd).taints(meth.getObject(name)) and - call.getFunction() = meth and - call.getAnArg() = this - | - name = "write" - ) - } + class FileStorageSink extends Sink { + FileStorageSink() { + exists(CallNode call, AttrNode meth, string name | + any(OpenFile fd).taints(meth.getObject(name)) and + call.getFunction() = meth and + call.getAnArg() = this + | + name = "write" + ) } + } } module ClearTextLogging { - abstract class Sink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } - } + abstract class Sink extends TaintSink { + override predicate sinks(TaintKind kind) { kind instanceof SensitiveData } + } - class PrintSink extends Sink { - PrintSink() { - exists(CallNode call | - call.getAnArg() = this and - call = Value::named("print").getACall() - ) - } + class PrintSink extends Sink { + PrintSink() { + exists(CallNode call | + call.getAnArg() = this and + call = Value::named("print").getACall() + ) } + } - class LoggingSink extends Sink { - LoggingSink() { - exists(CallNode call, AttrNode meth, string name | - call.getFunction() = meth and - meth.getObject(name).(NameNode).getId().matches("logg%") and - call.getAnArg() = this - | - name = "error" or - name = "warn" or - name = "warning" or - name = "debug" or - name = "info" - ) - } + class LoggingSink extends Sink { + LoggingSink() { + exists(CallNode call, AttrNode meth, string name | + call.getFunction() = meth and + meth.getObject(name).(NameNode).getId().matches("logg%") and + call.getAnArg() = this + | + name = "error" or + name = "warn" or + name = "warning" or + name = "debug" or + name = "info" + ) } + } } diff --git a/python/ql/src/semmle/python/security/Crypto.qll b/python/ql/src/semmle/python/security/Crypto.qll index 98ec8ecb2f1..65ec8f13a6e 100644 --- a/python/ql/src/semmle/python/security/Crypto.qll +++ b/python/ql/src/semmle/python/security/Crypto.qll @@ -4,136 +4,136 @@ private import semmle.python.security.SensitiveData private import semmle.crypto.Crypto as CryptoLib abstract class WeakCryptoSink extends TaintSink { - override predicate sinks(TaintKind taint) { taint instanceof SensitiveData } + override predicate sinks(TaintKind taint) { taint instanceof SensitiveData } } /** Modeling the 'pycrypto' package https://github.com/dlitz/pycrypto (latest release 2013) */ module Pycrypto { - ModuleValue cipher(string name) { result = Module::named("Crypto.Cipher").attr(name) } + ModuleValue cipher(string name) { result = Module::named("Crypto.Cipher").attr(name) } - class CipherInstance extends TaintKind { - string name; + class CipherInstance extends TaintKind { + string name; - CipherInstance() { - this = "Crypto.Cipher." + name and - exists(cipher(name)) - } - - string getName() { result = name } - - CryptoLib::CryptographicAlgorithm getAlgorithm() { result.getName() = name } - - predicate isWeak() { this.getAlgorithm().isWeak() } + CipherInstance() { + this = "Crypto.Cipher." + name and + exists(cipher(name)) } - class CipherInstanceSource extends TaintSource { - CipherInstance instance; + string getName() { result = name } - CipherInstanceSource() { - exists(AttrNode attr | - this.(CallNode).getFunction() = attr and - attr.getObject("new").pointsTo(cipher(instance.getName())) - ) - } + CryptoLib::CryptographicAlgorithm getAlgorithm() { result.getName() = name } - override string toString() { result = "Source of " + instance } + predicate isWeak() { this.getAlgorithm().isWeak() } + } - override predicate isSourceOf(TaintKind kind) { kind = instance } + class CipherInstanceSource extends TaintSource { + CipherInstance instance; + + CipherInstanceSource() { + exists(AttrNode attr | + this.(CallNode).getFunction() = attr and + attr.getObject("new").pointsTo(cipher(instance.getName())) + ) } - class PycryptoWeakCryptoSink extends WeakCryptoSink { - string name; + override string toString() { result = "Source of " + instance } - PycryptoWeakCryptoSink() { - exists(CallNode call, AttrNode method, CipherInstance cipher | - call.getAnArg() = this and - call.getFunction() = method and - cipher.taints(method.getObject("encrypt")) and - cipher.isWeak() and - cipher.getName() = name - ) - } + override predicate isSourceOf(TaintKind kind) { kind = instance } + } - override string toString() { result = "Use of weak crypto algorithm " + name } + class PycryptoWeakCryptoSink extends WeakCryptoSink { + string name; + + PycryptoWeakCryptoSink() { + exists(CallNode call, AttrNode method, CipherInstance cipher | + call.getAnArg() = this and + call.getFunction() = method and + cipher.taints(method.getObject("encrypt")) and + cipher.isWeak() and + cipher.getName() = name + ) } + + override string toString() { result = "Use of weak crypto algorithm " + name } + } } module Cryptography { - ModuleValue ciphers() { - result = Module::named("cryptography.hazmat.primitives.ciphers") and - result.isPackage() + ModuleValue ciphers() { + result = Module::named("cryptography.hazmat.primitives.ciphers") and + result.isPackage() + } + + class CipherClass extends ClassValue { + CipherClass() { ciphers().attr("Cipher") = this } + } + + class AlgorithmClass extends ClassValue { + AlgorithmClass() { ciphers().attr("algorithms").attr(_) = this } + + string getAlgorithmName() { result = this.declaredAttribute("name").(StringValue).getText() } + + predicate isWeak() { + exists(CryptoLib::CryptographicAlgorithm algo | + algo.getName() = this.getAlgorithmName() and + algo.isWeak() + ) + } + } + + class CipherInstance extends TaintKind { + AlgorithmClass cls; + + CipherInstance() { this = "cryptography.Cipher." + cls.getAlgorithmName() } + + AlgorithmClass getAlgorithm() { result = cls } + + predicate isWeak() { cls.isWeak() } + + override TaintKind getTaintOfMethodResult(string name) { + name = "encryptor" and + result.(Encryptor).getAlgorithm() = this.getAlgorithm() + } + } + + class CipherSource extends TaintSource { + CipherSource() { this.(CallNode).getFunction().pointsTo(any(CipherClass cls)) } + + override predicate isSourceOf(TaintKind kind) { + this.(CallNode).getArg(0).pointsTo().getClass() = kind.(CipherInstance).getAlgorithm() } - class CipherClass extends ClassValue { - CipherClass() { ciphers().attr("Cipher") = this } + override string toString() { result = "cryptography.Cipher.source" } + } + + class Encryptor extends TaintKind { + AlgorithmClass cls; + + Encryptor() { this = "cryptography.encryptor." + cls.getAlgorithmName() } + + AlgorithmClass getAlgorithm() { result = cls } + } + + class CryptographyWeakCryptoSink extends WeakCryptoSink { + CryptographyWeakCryptoSink() { + exists(CallNode call, AttrNode method, Encryptor encryptor | + call.getAnArg() = this and + call.getFunction() = method and + encryptor.taints(method.getObject("update")) and + encryptor.getAlgorithm().isWeak() + ) } - class AlgorithmClass extends ClassValue { - AlgorithmClass() { ciphers().attr("algorithms").attr(_) = this } - - string getAlgorithmName() { result = this.declaredAttribute("name").(StringValue).getText() } - - predicate isWeak() { - exists(CryptoLib::CryptographicAlgorithm algo | - algo.getName() = this.getAlgorithmName() and - algo.isWeak() - ) - } - } - - class CipherInstance extends TaintKind { - AlgorithmClass cls; - - CipherInstance() { this = "cryptography.Cipher." + cls.getAlgorithmName() } - - AlgorithmClass getAlgorithm() { result = cls } - - predicate isWeak() { cls.isWeak() } - - override TaintKind getTaintOfMethodResult(string name) { - name = "encryptor" and - result.(Encryptor).getAlgorithm() = this.getAlgorithm() - } - } - - class CipherSource extends TaintSource { - CipherSource() { this.(CallNode).getFunction().pointsTo(any(CipherClass cls)) } - - override predicate isSourceOf(TaintKind kind) { - this.(CallNode).getArg(0).pointsTo().getClass() = kind.(CipherInstance).getAlgorithm() - } - - override string toString() { result = "cryptography.Cipher.source" } - } - - class Encryptor extends TaintKind { - AlgorithmClass cls; - - Encryptor() { this = "cryptography.encryptor." + cls.getAlgorithmName() } - - AlgorithmClass getAlgorithm() { result = cls } - } - - class CryptographyWeakCryptoSink extends WeakCryptoSink { - CryptographyWeakCryptoSink() { - exists(CallNode call, AttrNode method, Encryptor encryptor | - call.getAnArg() = this and - call.getFunction() = method and - encryptor.taints(method.getObject("update")) and - encryptor.getAlgorithm().isWeak() - ) - } - - override string toString() { result = "Use of weak crypto algorithm" } - } + override string toString() { result = "Use of weak crypto algorithm" } + } } private class CipherConfig extends TaintTracking::Configuration { - CipherConfig() { this = "Crypto cipher config" } + CipherConfig() { this = "Crypto cipher config" } - override predicate isSource(TaintTracking::Source source) { - source instanceof Pycrypto::CipherInstanceSource - or - source instanceof Cryptography::CipherSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof Pycrypto::CipherInstanceSource + or + source instanceof Cryptography::CipherSource + } } diff --git a/python/ql/src/semmle/python/security/Exceptions.qll b/python/ql/src/semmle/python/security/Exceptions.qll index 4288761c565..25b0592c931 100644 --- a/python/ql/src/semmle/python/security/Exceptions.qll +++ b/python/ql/src/semmle/python/security/Exceptions.qll @@ -14,9 +14,9 @@ private Value traceback_function(string name) { result = Module::named("tracebac * message, arguments or parts of the exception traceback. */ class ExceptionInfo extends StringKind { - ExceptionInfo() { this = "exception.info" } + ExceptionInfo() { this = "exception.info" } - override string repr() { result = "exception info" } + override string repr() { result = "exception info" } } /** @@ -29,15 +29,15 @@ abstract class ErrorInfoSource extends TaintSource { } * This kind represents exceptions themselves. */ class ExceptionKind extends TaintKind { - ExceptionKind() { this = "exception.kind" } + ExceptionKind() { this = "exception.kind" } - override string repr() { result = "exception" } + override string repr() { result = "exception" } - override TaintKind getTaintOfAttribute(string name) { - name = "args" and result instanceof ExceptionInfoSequence - or - name = "message" and result instanceof ExceptionInfo - } + override TaintKind getTaintOfAttribute(string name) { + name = "args" and result instanceof ExceptionInfoSequence + or + name = "message" and result instanceof ExceptionInfo + } } /** @@ -45,18 +45,18 @@ class ExceptionKind extends TaintKind { * `except` statement. */ class ExceptionSource extends ErrorInfoSource { - ExceptionSource() { - exists(ClassValue cls | - cls.getASuperType() = ClassValue::baseException() and - this.(ControlFlowNode).pointsTo().getClass() = cls - ) - or - this = any(ExceptStmt s).getName().getAFlowNode() - } + ExceptionSource() { + exists(ClassValue cls | + cls.getASuperType() = ClassValue::baseException() and + this.(ControlFlowNode).pointsTo().getClass() = cls + ) + or + this = any(ExceptStmt s).getName().getAFlowNode() + } - override string toString() { result = "exception.source" } + override string toString() { result = "exception.source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionKind } } /** @@ -64,7 +64,7 @@ class ExceptionSource extends ErrorInfoSource { * for instance the contents of the `args` attribute, or the stack trace. */ class ExceptionInfoSequence extends SequenceKind { - ExceptionInfoSequence() { this.getItem() instanceof ExceptionInfo } + ExceptionInfoSequence() { this.getItem() instanceof ExceptionInfo } } /** @@ -72,23 +72,23 @@ class ExceptionInfoSequence extends SequenceKind { * sequences of exception information. */ class CallToTracebackFunction extends ErrorInfoSource { - CallToTracebackFunction() { - exists(string name | - name = "extract_tb" or - name = "extract_stack" or - name = "format_list" or - name = "format_exception_only" or - name = "format_exception" or - name = "format_tb" or - name = "format_stack" - | - this = traceback_function(name).getACall() - ) - } + CallToTracebackFunction() { + exists(string name | + name = "extract_tb" or + name = "extract_stack" or + name = "format_list" or + name = "format_exception_only" or + name = "format_exception" or + name = "format_tb" or + name = "format_stack" + | + this = traceback_function(name).getACall() + ) + } - override string toString() { result = "exception.info.sequence.source" } + override string toString() { result = "exception.info.sequence.source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfoSequence } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfoSequence } } /** @@ -96,9 +96,9 @@ class CallToTracebackFunction extends ErrorInfoSource { * string of information about an exception. */ class FormattedTracebackSource extends ErrorInfoSource { - FormattedTracebackSource() { this = traceback_function("format_exc").getACall() } + FormattedTracebackSource() { this = traceback_function("format_exc").getACall() } - override string toString() { result = "exception.info.source" } + override string toString() { result = "exception.info.source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } } diff --git a/python/ql/src/semmle/python/security/Paths.qll b/python/ql/src/semmle/python/security/Paths.qll index 3c7bc657948..8f7cba4b969 100644 --- a/python/ql/src/semmle/python/security/Paths.qll +++ b/python/ql/src/semmle/python/security/Paths.qll @@ -1,16 +1,16 @@ import semmle.python.dataflow.Implementation module TaintTrackingPaths { - predicate edge(TaintTrackingNode src, TaintTrackingNode dest, string label) { - exists(TaintTrackingNode source, TaintTrackingNode sink | - source.getConfiguration().hasFlowPath(source, sink) and - source.getASuccessor*() = src and - src.getASuccessor(label) = dest and - dest.getASuccessor*() = sink - ) - } + predicate edge(TaintTrackingNode src, TaintTrackingNode dest, string label) { + exists(TaintTrackingNode source, TaintTrackingNode sink | + source.getConfiguration().hasFlowPath(source, sink) and + source.getASuccessor*() = src and + src.getASuccessor(label) = dest and + dest.getASuccessor*() = sink + ) + } } query predicate edges(TaintTrackingNode fromnode, TaintTrackingNode tonode) { - TaintTrackingPaths::edge(fromnode, tonode, _) + TaintTrackingPaths::edge(fromnode, tonode, _) } diff --git a/python/ql/src/semmle/python/security/SensitiveData.qll b/python/ql/src/semmle/python/security/SensitiveData.qll index 18e52423d19..b616c960940 100644 --- a/python/ql/src/semmle/python/security/SensitiveData.qll +++ b/python/ql/src/semmle/python/security/SensitiveData.qll @@ -20,143 +20,143 @@ import semmle.python.web.HttpRequest * This is copied from the javascript library, but should be language independent. */ private module HeuristicNames { - /** - * Gets a regular expression that identifies strings that may indicate the presence of secret - * or trusted data. - */ - string maybeSecret() { result = "(?is).*((? 0)) - or - this = call.getArgByName(any(string s | not s = "task")) - ) - } + FabricExecuteExtension() { + call = Value::named("fabric.api.execute").getACall() and + ( + this = call.getArg(any(int i | i > 0)) + or + this = call.getArgByName(any(string s | not s = "task")) + ) + } - override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { - tokind = fromkind and - exists(CallableValue func | - ( - call.getArg(0).pointsTo(func) - or - call.getArgByName("task").pointsTo(func) - ) and - exists(int i | - // execute(func, arg0, arg1) => func(arg0, arg1) - this = call.getArg(i) and - result = func.getParameter(i - 1) - ) - or - exists(string name | - this = call.getArgByName(name) and - result = func.getParameterByName(name) - ) - ) - } + override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { + tokind = fromkind and + exists(CallableValue func | + ( + call.getArg(0).pointsTo(func) + or + call.getArgByName("task").pointsTo(func) + ) and + exists(int i | + // execute(func, arg0, arg1) => func(arg0, arg1) + this = call.getArg(i) and + result = func.getParameter(i - 1) + ) + or + exists(string name | + this = call.getArgByName(name) and + result = func.getParameterByName(name) + ) + ) + } } diff --git a/python/ql/src/semmle/python/security/injection/Deserialization.qll b/python/ql/src/semmle/python/security/injection/Deserialization.qll index 1f73ede22f2..029705cd807 100644 --- a/python/ql/src/semmle/python/security/injection/Deserialization.qll +++ b/python/ql/src/semmle/python/security/injection/Deserialization.qll @@ -3,6 +3,6 @@ import semmle.python.dataflow.TaintTracking /** `pickle.loads(untrusted)` vulnerability. */ abstract class DeserializationSink extends TaintSink { - bindingset[this] - DeserializationSink() { this = this } + bindingset[this] + DeserializationSink() { this = this } } diff --git a/python/ql/src/semmle/python/security/injection/Exec.qll b/python/ql/src/semmle/python/security/injection/Exec.qll index 462847e7d3e..b5008a94e3b 100644 --- a/python/ql/src/semmle/python/security/injection/Exec.qll +++ b/python/ql/src/semmle/python/security/injection/Exec.qll @@ -15,15 +15,15 @@ import semmle.python.security.strings.Untrusted * The `vuln` in `exec(vuln)` or similar. */ class StringEvaluationNode extends TaintSink { - override string toString() { result = "exec or eval" } + override string toString() { result = "exec or eval" } - StringEvaluationNode() { - exists(Exec exec | exec.getASubExpression().getAFlowNode() = this) - or - Value::named("exec").getACall().getAnArg() = this - or - Value::named("eval").getACall().getAnArg() = this - } + StringEvaluationNode() { + exists(Exec exec | exec.getASubExpression().getAFlowNode() = this) + or + Value::named("exec").getACall().getAnArg() = this + or + Value::named("eval").getACall().getAnArg() = this + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Marshal.qll b/python/ql/src/semmle/python/security/injection/Marshal.qll index 7ae77e597a5..a77c7cd6278 100644 --- a/python/ql/src/semmle/python/security/injection/Marshal.qll +++ b/python/ql/src/semmle/python/security/injection/Marshal.qll @@ -18,14 +18,14 @@ private FunctionObject marshalLoads() { result = ModuleObject::named("marshal"). * The `vuln` in `marshal.loads(vuln)`. */ class UnmarshalingNode extends DeserializationSink { - override string toString() { result = "unmarshaling vulnerability" } + override string toString() { result = "unmarshaling vulnerability" } - UnmarshalingNode() { - exists(CallNode call | - marshalLoads().getACall() = call and - call.getAnArg() = this - ) - } + UnmarshalingNode() { + exists(CallNode call | + marshalLoads().getACall() = call and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Path.qll b/python/ql/src/semmle/python/security/injection/Path.qll index e871d11cf2b..ee470932749 100644 --- a/python/ql/src/semmle/python/security/injection/Path.qll +++ b/python/ql/src/semmle/python/security/injection/Path.qll @@ -7,52 +7,52 @@ import semmle.python.security.strings.Untrusted * NormalizedPath below handles that case. */ class PathSanitizer extends Sanitizer { - PathSanitizer() { this = "path.sanitizer" } + PathSanitizer() { this = "path.sanitizer" } - override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { - taint instanceof ExternalStringKind and - abspath_call(node, _) - } + override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { + taint instanceof ExternalStringKind and + abspath_call(node, _) + } } private FunctionObject abspath() { - exists(ModuleObject os_path | ModuleObject::named("os").attr("path") = os_path | - os_path.attr("abspath") = result - or - os_path.attr("normpath") = result - ) + exists(ModuleObject os_path | ModuleObject::named("os").attr("path") = os_path | + os_path.attr("abspath") = result + or + os_path.attr("normpath") = result + ) } /** A path that has been normalized, but not verified to be safe */ class NormalizedPath extends TaintKind { - NormalizedPath() { this = "normalized.path.injection" } + NormalizedPath() { this = "normalized.path.injection" } - override string repr() { result = "normalized path" } + override string repr() { result = "normalized path" } } private predicate abspath_call(CallNode call, ControlFlowNode arg) { - call.getFunction().refersTo(abspath()) and - arg = call.getArg(0) + call.getFunction().refersTo(abspath()) and + arg = call.getArg(0) } class AbsPath extends DataFlowExtension::DataFlowNode { - AbsPath() { abspath_call(_, this) } + AbsPath() { abspath_call(_, this) } - override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { - abspath_call(result, this) and - tokind instanceof NormalizedPath and - fromkind instanceof ExternalStringKind - } + override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { + abspath_call(result, this) and + tokind instanceof NormalizedPath and + fromkind instanceof ExternalStringKind + } } class NormalizedPathSanitizer extends Sanitizer { - NormalizedPathSanitizer() { this = "normalized.path.sanitizer" } + NormalizedPathSanitizer() { this = "normalized.path.sanitizer" } - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof NormalizedPath and - test.getTest().(CallNode).getFunction().(AttrNode).getName() = "startswith" and - test.getSense() = true - } + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof NormalizedPath and + test.getTest().(CallNode).getFunction().(AttrNode).getName() = "startswith" and + test.getSense() = true + } } /** @@ -60,22 +60,22 @@ class NormalizedPathSanitizer extends Sanitizer { * The `vuln` in `open(vuln)` and similar. */ class OpenNode extends TaintSink { - override string toString() { result = "argument to open()" } + override string toString() { result = "argument to open()" } - OpenNode() { - exists(CallNode call | - call = Value::named("open").getACall() and - ( - call.getArg(0) = this - or - call.getArgByName("file") = this - ) - ) - } - - override predicate sinks(TaintKind kind) { - kind instanceof ExternalStringKind + OpenNode() { + exists(CallNode call | + call = Value::named("open").getACall() and + ( + call.getArg(0) = this or - kind instanceof NormalizedPath - } + call.getArgByName("file") = this + ) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + or + kind instanceof NormalizedPath + } } diff --git a/python/ql/src/semmle/python/security/injection/Pickle.qll b/python/ql/src/semmle/python/security/injection/Pickle.qll index 1135587df50..f668c7011fe 100644 --- a/python/ql/src/semmle/python/security/injection/Pickle.qll +++ b/python/ql/src/semmle/python/security/injection/Pickle.qll @@ -12,25 +12,25 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.injection.Deserialization private ModuleObject pickleModule() { - result.getName() = "pickle" - or - result.getName() = "cPickle" - or - result.getName() = "dill" + result.getName() = "pickle" + or + result.getName() = "cPickle" + or + result.getName() = "dill" } private FunctionObject pickleLoads() { result = pickleModule().attr("loads") } /** `pickle.loads(untrusted)` vulnerability. */ class UnpicklingNode extends DeserializationSink { - override string toString() { result = "unpickling untrusted data" } + override string toString() { result = "unpickling untrusted data" } - UnpicklingNode() { - exists(CallNode call | - pickleLoads().getACall() = call and - call.getAnArg() = this - ) - } + UnpicklingNode() { + exists(CallNode call | + pickleLoads().getACall() = call and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Sql.qll b/python/ql/src/semmle/python/security/injection/Sql.qll index 7fc9515c08b..5ded218fc9e 100644 --- a/python/ql/src/semmle/python/security/injection/Sql.qll +++ b/python/ql/src/semmle/python/security/injection/Sql.qll @@ -12,27 +12,27 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.SQL private StringObject first_part(ControlFlowNode command) { - command.(BinaryExprNode).getOp() instanceof Add and - command.(BinaryExprNode).getLeft().refersTo(result) - or - exists(CallNode call, SequenceObject seq | call = command | - call = theStrType().lookupAttribute("join") and - call.getArg(0).refersTo(seq) and - seq.getInferredElement(0) = result - ) - or - command.(BinaryExprNode).getOp() instanceof Mod and - command.getNode().(StrConst).getLiteralObject() = result + command.(BinaryExprNode).getOp() instanceof Add and + command.(BinaryExprNode).getLeft().refersTo(result) + or + exists(CallNode call, SequenceObject seq | call = command | + call = theStrType().lookupAttribute("join") and + call.getArg(0).refersTo(seq) and + seq.getInferredElement(0) = result + ) + or + command.(BinaryExprNode).getOp() instanceof Mod and + command.getNode().(StrConst).getLiteralObject() = result } /** Holds if `command` appears to be a SQL command string of which `inject` is a part. */ predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject) { - exists(string prefix | - inject = command.getAChild*() and - first_part(command).getText().regexpMatch(" *" + prefix + ".*") - | - prefix = "CREATE" or prefix = "SELECT" - ) + exists(string prefix | + inject = command.getAChild*() and + first_part(command).getText().regexpMatch(" *" + prefix + ".*") + | + prefix = "CREATE" or prefix = "SELECT" + ) } /** @@ -40,10 +40,10 @@ predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject) * This will be overridden to provide specific kinds of DB cursor. */ abstract class DbCursor extends TaintKind { - bindingset[this] - DbCursor() { any() } + bindingset[this] + DbCursor() { any() } - string getExecuteMethodName() { result = "execute" } + string getExecuteMethodName() { result = "execute" } } /** @@ -51,11 +51,11 @@ abstract class DbCursor extends TaintKind { * vulnerable to malicious input. */ class SimpleSqlStringInjection extends SqlInjectionSink { - override string toString() { result = "simple SQL string injection" } + override string toString() { result = "simple SQL string injection" } - SimpleSqlStringInjection() { probable_sql_command(_, this) } + SimpleSqlStringInjection() { probable_sql_command(_, this) } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } /** @@ -69,15 +69,15 @@ abstract class DbConnectionSource extends TaintSource { } * The `vuln` in `db.connection.execute(vuln)` and similar. */ class DbConnectionExecuteArgument extends SqlInjectionSink { - override string toString() { result = "db.connection.execute" } + override string toString() { result = "db.connection.execute" } - DbConnectionExecuteArgument() { - exists(CallNode call, DbCursor cursor, string name | - cursor.taints(call.getFunction().(AttrNode).getObject(name)) and - cursor.getExecuteMethodName() = name and - call.getArg(0) = this - ) - } + DbConnectionExecuteArgument() { + exists(CallNode call, DbCursor cursor, string name | + cursor.taints(call.getFunction().(AttrNode).getObject(name)) and + cursor.getExecuteMethodName() = name and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/injection/Xml.qll b/python/ql/src/semmle/python/security/injection/Xml.qll index 3a4e6ebc552..07241096b44 100644 --- a/python/ql/src/semmle/python/security/injection/Xml.qll +++ b/python/ql/src/semmle/python/security/injection/Xml.qll @@ -20,34 +20,34 @@ private ModuleObject xmlPullDomModule() { result.getName() = "xml.dom.pulldom" } private ModuleObject xmlSaxModule() { result.getName() = "xml.sax" } private class ExpatParser extends TaintKind { - ExpatParser() { this = "expat.parser" } + ExpatParser() { this = "expat.parser" } } private FunctionObject expatCreateParseFunction() { - result = ModuleObject::named("xml.parsers.expat").attr("ParserCreate") + result = ModuleObject::named("xml.parsers.expat").attr("ParserCreate") } private class ExpatCreateParser extends TaintSource { - ExpatCreateParser() { expatCreateParseFunction().getACall() = this } + ExpatCreateParser() { expatCreateParseFunction().getACall() = this } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExpatParser } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExpatParser } - string toString() { result = "expat.create.parser" } + string toString() { result = "expat.create.parser" } } private FunctionObject xmlFromString() { - result = xmlElementTreeModule().attr("fromstring") - or - result = xmlMiniDomModule().attr("parseString") - or - result = xmlPullDomModule().attr("parseString") - or - result = xmlSaxModule().attr("parseString") + result = xmlElementTreeModule().attr("fromstring") + or + result = xmlMiniDomModule().attr("parseString") + or + result = xmlPullDomModule().attr("parseString") + or + result = xmlSaxModule().attr("parseString") } /** A (potentially) malicious XML string. */ class ExternalXmlString extends ExternalStringKind { - ExternalXmlString() { this = "external xml encoded object" } + ExternalXmlString() { this = "external xml encoded object" } } /** @@ -55,14 +55,14 @@ class ExternalXmlString extends ExternalStringKind { * specially crafted XML string. */ class XmlLoadNode extends DeserializationSink { - override string toString() { result = "xml.load vulnerability" } + override string toString() { result = "xml.load vulnerability" } - XmlLoadNode() { - exists(CallNode call | call.getAnArg() = this | - xmlFromString().getACall() = call or - any(ExpatParser parser).taints(call.getFunction().(AttrNode).getObject("Parse")) - ) - } + XmlLoadNode() { + exists(CallNode call | call.getAnArg() = this | + xmlFromString().getACall() = call or + any(ExpatParser parser).taints(call.getFunction().(AttrNode).getObject("Parse")) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlString } + override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlString } } diff --git a/python/ql/src/semmle/python/security/injection/Yaml.qll b/python/ql/src/semmle/python/security/injection/Yaml.qll index 0799d9b9160..f8f92fff609 100644 --- a/python/ql/src/semmle/python/security/injection/Yaml.qll +++ b/python/ql/src/semmle/python/security/injection/Yaml.qll @@ -15,14 +15,14 @@ private FunctionObject yamlLoad() { result = ModuleObject::named("yaml").attr("l /** `yaml.load(untrusted)` vulnerability. */ class YamlLoadNode extends DeserializationSink { - override string toString() { result = "yaml.load vulnerability" } + override string toString() { result = "yaml.load vulnerability" } - YamlLoadNode() { - exists(CallNode call | - yamlLoad().getACall() = call and - call.getAnArg() = this - ) - } + YamlLoadNode() { + exists(CallNode call | + yamlLoad().getACall() = call and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/security/strings/Basic.qll b/python/ql/src/semmle/python/security/strings/Basic.qll index 9fb17b074a6..345f7d0b82b 100755 --- a/python/ql/src/semmle/python/security/strings/Basic.qll +++ b/python/ql/src/semmle/python/security/strings/Basic.qll @@ -4,107 +4,107 @@ import semmle.python.dataflow.TaintTracking /** An extensible kind of taint representing any kind of string. */ abstract class StringKind extends TaintKind { - bindingset[this] - StringKind() { this = this } + bindingset[this] + StringKind() { this = this } - override TaintKind getTaintOfMethodResult(string name) { - name in ["capitalize", "casefold", "center", "expandtabs", "format", "format_map", "ljust", - "lstrip", "lower", "replace", "rjust", "rstrip", "strip", "swapcase", "title", "upper", - "zfill", - /* encode/decode is technically not correct, but close enough */ - "encode", "decode"] and - result = this - or - name in ["partition", "rpartition", "rsplit", "split", "splitlines"] and - result.(SequenceKind).getItem() = this - } + override TaintKind getTaintOfMethodResult(string name) { + name in ["capitalize", "casefold", "center", "expandtabs", "format", "format_map", "ljust", + "lstrip", "lower", "replace", "rjust", "rstrip", "strip", "swapcase", "title", "upper", + "zfill", + /* encode/decode is technically not correct, but close enough */ + "encode", "decode"] and + result = this + or + name in ["partition", "rpartition", "rsplit", "split", "splitlines"] and + result.(SequenceKind).getItem() = this + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = this and - ( - slice(fromnode, tonode) or - tonode.(BinaryExprNode).getAnOperand() = fromnode or - os_path_join(fromnode, tonode) or - str_format(fromnode, tonode) or - encode_decode(fromnode, tonode) or - to_str(fromnode, tonode) - ) - or - result = this and copy_call(fromnode, tonode) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = this and + ( + slice(fromnode, tonode) or + tonode.(BinaryExprNode).getAnOperand() = fromnode or + os_path_join(fromnode, tonode) or + str_format(fromnode, tonode) or + encode_decode(fromnode, tonode) or + to_str(fromnode, tonode) + ) + or + result = this and copy_call(fromnode, tonode) + } - override ClassValue getType() { - result = Value::named("bytes") or - result = Value::named("str") or - result = Value::named("unicode") - } + override ClassValue getType() { + result = Value::named("bytes") or + result = Value::named("str") or + result = Value::named("unicode") + } } private class StringEqualitySanitizer extends Sanitizer { - StringEqualitySanitizer() { this = "string equality sanitizer" } + StringEqualitySanitizer() { this = "string equality sanitizer" } - /** The test `if untrusted == "KNOWN_VALUE":` sanitizes `untrusted` on its `true` edge. */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof StringKind and - exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | - ( - test.getTest().(CompareNode).operands(const, op, _) - or - test.getTest().(CompareNode).operands(_, op, const) - ) and - ( - op instanceof Eq and test.getSense() = true - or - op instanceof NotEq and test.getSense() = false - ) - ) - } + /** The test `if untrusted == "KNOWN_VALUE":` sanitizes `untrusted` on its `true` edge. */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof StringKind and + exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | + ( + test.getTest().(CompareNode).operands(const, op, _) + or + test.getTest().(CompareNode).operands(_, op, const) + ) and + ( + op instanceof Eq and test.getSense() = true + or + op instanceof NotEq and test.getSense() = false + ) + ) + } } /* tonode = ....format(fromnode) */ private predicate str_format(ControlFlowNode fromnode, CallNode tonode) { - tonode.getFunction().(AttrNode).getName() = "format" and - tonode.getAnArg() = fromnode + tonode.getFunction().(AttrNode).getName() = "format" and + tonode.getAnArg() = fromnode } /* tonode = codec.[en|de]code(fromnode)*/ private predicate encode_decode(ControlFlowNode fromnode, CallNode tonode) { - exists(FunctionObject func, string name | - not func.getFunction().isMethod() and - func.getACall() = tonode and - tonode.getAnArg() = fromnode and - func.getName() = name - | - name = "encode" or - name = "decode" or - name = "decodestring" - ) + exists(FunctionObject func, string name | + not func.getFunction().isMethod() and + func.getACall() = tonode and + tonode.getAnArg() = fromnode and + func.getName() = name + | + name = "encode" or + name = "decode" or + name = "decodestring" + ) } /* tonode = str(fromnode)*/ private predicate to_str(ControlFlowNode fromnode, CallNode tonode) { - tonode.getAnArg() = fromnode and - ( - tonode = ClassValue::bytes().getACall() - or - tonode = ClassValue::unicode().getACall() - ) + tonode.getAnArg() = fromnode and + ( + tonode = ClassValue::bytes().getACall() + or + tonode = ClassValue::unicode().getACall() + ) } /* tonode = fromnode[:] */ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) { - exists(Slice all | - all = tonode.getIndex().getNode() and - not exists(all.getStart()) and - not exists(all.getStop()) and - tonode.getObject() = fromnode - ) + exists(Slice all | + all = tonode.getIndex().getNode() and + not exists(all.getStart()) and + not exists(all.getStop()) and + tonode.getObject() = fromnode + ) } /* tonode = os.path.join(..., fromnode, ...) */ private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) { - tonode = Value::named("os.path.join").getACall() and - tonode.getAnArg() = fromnode + tonode = Value::named("os.path.join").getACall() and + tonode.getAnArg() = fromnode } /** @@ -113,5 +113,5 @@ private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) { * DEPRECATED: Use `ExternalStringDictKind` instead. */ deprecated class StringDictKind extends DictKind { - StringDictKind() { this.getValue() instanceof StringKind } + StringDictKind() { this.getValue() instanceof StringKind } } diff --git a/python/ql/src/semmle/python/security/strings/Common.qll b/python/ql/src/semmle/python/security/strings/Common.qll index 404da271d78..bb0af8b8aec 100644 --- a/python/ql/src/semmle/python/security/strings/Common.qll +++ b/python/ql/src/semmle/python/security/strings/Common.qll @@ -2,13 +2,13 @@ import python /* A call that returns a copy (or similar) of the argument */ predicate copy_call(ControlFlowNode fromnode, CallNode tonode) { - tonode.getFunction().(AttrNode).getObject("copy") = fromnode - or - exists(ModuleValue copy, string name | name = "copy" or name = "deepcopy" | - copy.attr(name).(FunctionValue).getACall() = tonode and - tonode.getArg(0) = fromnode - ) - or - tonode.getFunction().pointsTo(Value::named("reversed")) and + tonode.getFunction().(AttrNode).getObject("copy") = fromnode + or + exists(ModuleValue copy, string name | name = "copy" or name = "deepcopy" | + copy.attr(name).(FunctionValue).getACall() = tonode and tonode.getArg(0) = fromnode + ) + or + tonode.getFunction().pointsTo(Value::named("reversed")) and + tonode.getArg(0) = fromnode } diff --git a/python/ql/src/semmle/python/security/strings/External.qll b/python/ql/src/semmle/python/security/strings/External.qll index 59c4a7373f0..76a74a66e9b 100644 --- a/python/ql/src/semmle/python/security/strings/External.qll +++ b/python/ql/src/semmle/python/security/strings/External.qll @@ -6,32 +6,32 @@ private import Common * An extensible kind of taint representing an externally controlled string. */ abstract class ExternalStringKind extends StringKind { - bindingset[this] - ExternalStringKind() { this = this } + bindingset[this] + ExternalStringKind() { this = this } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = StringKind.super.getTaintForFlowStep(fromnode, tonode) - or - tonode.(SequenceNode).getElement(_) = fromnode and - result.(ExternalStringSequenceKind).getItem() = this - or - json_load(fromnode, tonode) and result.(ExternalJsonKind).getValue() = this - or - tonode.(DictNode).getAValue() = fromnode and result.(ExternalStringDictKind).getValue() = this - or - urlsplit(fromnode, tonode) and result.(ExternalUrlSplitResult).getItem() = this - or - urlparse(fromnode, tonode) and result.(ExternalUrlParseResult).getItem() = this - or - parse_qs(fromnode, tonode) and result.(ExternalStringDictKind).getValue() = this - or - parse_qsl(fromnode, tonode) and result.(SequenceKind).getItem().(SequenceKind).getItem() = this - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = StringKind.super.getTaintForFlowStep(fromnode, tonode) + or + tonode.(SequenceNode).getElement(_) = fromnode and + result.(ExternalStringSequenceKind).getItem() = this + or + json_load(fromnode, tonode) and result.(ExternalJsonKind).getValue() = this + or + tonode.(DictNode).getAValue() = fromnode and result.(ExternalStringDictKind).getValue() = this + or + urlsplit(fromnode, tonode) and result.(ExternalUrlSplitResult).getItem() = this + or + urlparse(fromnode, tonode) and result.(ExternalUrlParseResult).getItem() = this + or + parse_qs(fromnode, tonode) and result.(ExternalStringDictKind).getValue() = this + or + parse_qsl(fromnode, tonode) and result.(SequenceKind).getItem().(SequenceKind).getItem() = this + } } /** A kind of "taint", representing a sequence, with a "taint" member */ class ExternalStringSequenceKind extends SequenceKind { - ExternalStringSequenceKind() { this.getItem() instanceof ExternalStringKind } + ExternalStringSequenceKind() { this.getItem() instanceof ExternalStringKind } } /** @@ -39,30 +39,30 @@ class ExternalStringSequenceKind extends SequenceKind { * This is typically a parsed JSON object. */ class ExternalJsonKind extends TaintKind { - ExternalJsonKind() { this = "json[" + any(ExternalStringKind key) + "]" } + ExternalJsonKind() { this = "json[" + any(ExternalStringKind key) + "]" } - /** Gets the taint kind for item in this sequence */ - TaintKind getValue() { - this = "json[" + result + "]" - or - result = this - } + /** Gets the taint kind for item in this sequence */ + TaintKind getValue() { + this = "json[" + result + "]" + or + result = this + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - this.taints(fromnode) and - json_subscript_taint(tonode, fromnode, this, result) - or - result = this and copy_call(fromnode, tonode) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + this.taints(fromnode) and + json_subscript_taint(tonode, fromnode, this, result) + or + result = this and copy_call(fromnode, tonode) + } - override TaintKind getTaintOfMethodResult(string name) { - name = "get" and result = this.getValue() - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result = this.getValue() + } } /** A kind of "taint", representing a dictionary mapping keys to tainted strings. */ class ExternalStringDictKind extends DictKind { - ExternalStringDictKind() { this.getValue() instanceof ExternalStringKind } + ExternalStringDictKind() { this.getValue() instanceof ExternalStringKind } } /** @@ -70,189 +70,189 @@ class ExternalStringDictKind extends DictKind { * tainted strings. */ class ExternalStringSequenceDictKind extends DictKind { - ExternalStringSequenceDictKind() { this.getValue() instanceof ExternalStringSequenceKind } + ExternalStringSequenceDictKind() { this.getValue() instanceof ExternalStringSequenceKind } } /** TaintKind for the result of `urlsplit(tainted_string)` */ class ExternalUrlSplitResult extends ExternalStringSequenceKind { - // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlsplit - override TaintKind getTaintOfAttribute(string name) { - result = super.getTaintOfAttribute(name) - or - ( - // namedtuple field names - name = "scheme" or - name = "netloc" or - name = "path" or - name = "query" or - name = "fragment" or - // class methods - name = "username" or - name = "password" or - name = "hostname" - ) and - result instanceof ExternalStringKind - } + // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlsplit + override TaintKind getTaintOfAttribute(string name) { + result = super.getTaintOfAttribute(name) + or + ( + // namedtuple field names + name = "scheme" or + name = "netloc" or + name = "path" or + name = "query" or + name = "fragment" or + // class methods + name = "username" or + name = "password" or + name = "hostname" + ) and + result instanceof ExternalStringKind + } - override TaintKind getTaintOfMethodResult(string name) { - result = super.getTaintOfMethodResult(name) - or - name = "geturl" and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + result = super.getTaintOfMethodResult(name) + or + name = "geturl" and + result instanceof ExternalStringKind + } } /** TaintKind for the result of `urlparse(tainted_string)` */ class ExternalUrlParseResult extends ExternalStringSequenceKind { - // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse - override TaintKind getTaintOfAttribute(string name) { - result = super.getTaintOfAttribute(name) - or - ( - // namedtuple field names - name = "scheme" or - name = "netloc" or - name = "path" or - name = "params" or - name = "query" or - name = "fragment" or - // class methods - name = "username" or - name = "password" or - name = "hostname" - ) and - result instanceof ExternalStringKind - } + // https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse + override TaintKind getTaintOfAttribute(string name) { + result = super.getTaintOfAttribute(name) + or + ( + // namedtuple field names + name = "scheme" or + name = "netloc" or + name = "path" or + name = "params" or + name = "query" or + name = "fragment" or + // class methods + name = "username" or + name = "password" or + name = "hostname" + ) and + result instanceof ExternalStringKind + } - override TaintKind getTaintOfMethodResult(string name) { - result = super.getTaintOfMethodResult(name) - or - name = "geturl" and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + result = super.getTaintOfMethodResult(name) + or + name = "geturl" and + result instanceof ExternalStringKind + } } /* Helper for getTaintForStep() */ pragma[noinline] private predicate json_subscript_taint( - SubscriptNode sub, ControlFlowNode obj, ExternalJsonKind seq, TaintKind key + SubscriptNode sub, ControlFlowNode obj, ExternalJsonKind seq, TaintKind key ) { - sub.isLoad() and - sub.getObject() = obj and - key = seq.getValue() + sub.isLoad() and + sub.getObject() = obj and + key = seq.getValue() } private predicate json_load(ControlFlowNode fromnode, CallNode tonode) { - tonode = Value::named("json.loads").getACall() and - tonode.getArg(0) = fromnode + tonode = Value::named("json.loads").getACall() and + tonode.getArg(0) = fromnode } private predicate urlsplit(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value urlsplit | - ( - urlsplit = Value::named("six.moves.urllib.parse.urlsplit") - or - // Python 2 - urlsplit = Value::named("urlparse.urlsplit") - or - // Python 3 - urlsplit = Value::named("urllib.parse.urlsplit") - ) and - tonode = urlsplit.getACall() and - tonode.getArg(0) = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value urlsplit | + ( + urlsplit = Value::named("six.moves.urllib.parse.urlsplit") + or + // Python 2 + urlsplit = Value::named("urlparse.urlsplit") + or + // Python 3 + urlsplit = Value::named("urllib.parse.urlsplit") + ) and + tonode = urlsplit.getACall() and + tonode.getArg(0) = fromnode + ) } private predicate urlparse(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value urlparse | - ( - urlparse = Value::named("six.moves.urllib.parse.urlparse") - or - // Python 2 - urlparse = Value::named("urlparse.urlparse") - or - // Python 3 - urlparse = Value::named("urllib.parse.urlparse") - ) and - tonode = urlparse.getACall() and - tonode.getArg(0) = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value urlparse | + ( + urlparse = Value::named("six.moves.urllib.parse.urlparse") + or + // Python 2 + urlparse = Value::named("urlparse.urlparse") + or + // Python 3 + urlparse = Value::named("urllib.parse.urlparse") + ) and + tonode = urlparse.getACall() and + tonode.getArg(0) = fromnode + ) } private predicate parse_qs(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value parse_qs | - ( - parse_qs = Value::named("six.moves.urllib.parse.parse_qs") - or - // Python 2 - parse_qs = Value::named("urlparse.parse_qs") - or - // Python 2 deprecated version of `urlparse.parse_qs` - parse_qs = Value::named("cgi.parse_qs") - or - // Python 3 - parse_qs = Value::named("urllib.parse.parse_qs") - ) and - tonode = parse_qs.getACall() and - ( - tonode.getArg(0) = fromnode - or - tonode.getArgByName("qs") = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value parse_qs | + ( + parse_qs = Value::named("six.moves.urllib.parse.parse_qs") + or + // Python 2 + parse_qs = Value::named("urlparse.parse_qs") + or + // Python 2 deprecated version of `urlparse.parse_qs` + parse_qs = Value::named("cgi.parse_qs") + or + // Python 3 + parse_qs = Value::named("urllib.parse.parse_qs") + ) and + tonode = parse_qs.getACall() and + ( + tonode.getArg(0) = fromnode + or + tonode.getArgByName("qs") = fromnode ) + ) } private predicate parse_qsl(ControlFlowNode fromnode, CallNode tonode) { - // This could be implemented as `exists(FunctionValue` without the explicit six part, - // but then our tests will need to import +100 modules, so for now this slightly - // altered version gets to live on. - exists(Value parse_qsl | - ( - parse_qsl = Value::named("six.moves.urllib.parse.parse_qsl") - or - // Python 2 - parse_qsl = Value::named("urlparse.parse_qsl") - or - // Python 2 deprecated version of `urlparse.parse_qsl` - parse_qsl = Value::named("cgi.parse_qsl") - or - // Python 3 - parse_qsl = Value::named("urllib.parse.parse_qsl") - ) and - tonode = parse_qsl.getACall() and - ( - tonode.getArg(0) = fromnode - or - tonode.getArgByName("qs") = fromnode - ) + // This could be implemented as `exists(FunctionValue` without the explicit six part, + // but then our tests will need to import +100 modules, so for now this slightly + // altered version gets to live on. + exists(Value parse_qsl | + ( + parse_qsl = Value::named("six.moves.urllib.parse.parse_qsl") + or + // Python 2 + parse_qsl = Value::named("urlparse.parse_qsl") + or + // Python 2 deprecated version of `urlparse.parse_qsl` + parse_qsl = Value::named("cgi.parse_qsl") + or + // Python 3 + parse_qsl = Value::named("urllib.parse.parse_qsl") + ) and + tonode = parse_qsl.getACall() and + ( + tonode.getArg(0) = fromnode + or + tonode.getArgByName("qs") = fromnode ) + ) } /** A kind of "taint", representing an open file-like object from an external source. */ class ExternalFileObject extends TaintKind { - ExternalStringKind valueKind; + ExternalStringKind valueKind; - ExternalFileObject() { this = "file[" + valueKind + "]" } + ExternalFileObject() { this = "file[" + valueKind + "]" } - /** Gets the taint kind for the contents of this file */ - TaintKind getValue() { result = valueKind } + /** Gets the taint kind for the contents of this file */ + TaintKind getValue() { result = valueKind } - override TaintKind getTaintOfMethodResult(string name) { - name in ["read", "readline"] and result = this.getValue() - or - name = "readlines" and result.(SequenceKind).getItem() = this.getValue() - } + override TaintKind getTaintOfMethodResult(string name) { + name in ["read", "readline"] and result = this.getValue() + or + name = "readlines" and result.(SequenceKind).getItem() = this.getValue() + } - override TaintKind getTaintForIteration() { result = this.getValue() } + override TaintKind getTaintForIteration() { result = this.getValue() } } /** @@ -267,65 +267,65 @@ class ExternalFileObject extends TaintKind { * - `if splitres[0] == "KNOWN_VALUE"` */ class UrlsplitUrlparseTempSanitizer extends Sanitizer { - // TODO: remove this once we have better support for named tuples - UrlsplitUrlparseTempSanitizer() { this = "UrlsplitUrlparseTempSanitizer" } + // TODO: remove this once we have better support for named tuples + UrlsplitUrlparseTempSanitizer() { this = "UrlsplitUrlparseTempSanitizer" } - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - ( - taint instanceof ExternalUrlSplitResult - or - taint instanceof ExternalUrlParseResult - ) and - exists(ControlFlowNode full_use | - full_use.(SubscriptNode).getObject() = test.getInput().getAUse() - or - full_use.(AttrNode).getObject() = test.getInput().getAUse() - | - clears_taint(full_use, test.getTest(), test.getSense()) - ) - } + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + ( + taint instanceof ExternalUrlSplitResult + or + taint instanceof ExternalUrlParseResult + ) and + exists(ControlFlowNode full_use | + full_use.(SubscriptNode).getObject() = test.getInput().getAUse() + or + full_use.(AttrNode).getObject() = test.getInput().getAUse() + | + clears_taint(full_use, test.getTest(), test.getSense()) + ) + } - private predicate clears_taint(ControlFlowNode tainted, ControlFlowNode test, boolean sense) { - test_equality_with_const(test, tainted, sense) + private predicate clears_taint(ControlFlowNode tainted, ControlFlowNode test, boolean sense) { + test_equality_with_const(test, tainted, sense) + or + test_in_const_seq(test, tainted, sense) + or + test.(UnaryExprNode).getNode().getOp() instanceof Not and + exists(ControlFlowNode nested_test | + nested_test = test.(UnaryExprNode).getOperand() and + clears_taint(tainted, nested_test, sense.booleanNot()) + ) + } + + /** holds for `== "KNOWN_VALUE"` on `true` edge, and `!= "KNOWN_VALUE"` on `false` edge */ + private predicate test_equality_with_const(CompareNode cmp, ControlFlowNode tainted, boolean sense) { + exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | + ( + cmp.operands(const, op, tainted) or - test_in_const_seq(test, tainted, sense) + cmp.operands(tainted, op, const) + ) and + ( + op instanceof Eq and sense = true or - test.(UnaryExprNode).getNode().getOp() instanceof Not and - exists(ControlFlowNode nested_test | - nested_test = test.(UnaryExprNode).getOperand() and - clears_taint(tainted, nested_test, sense.booleanNot()) - ) - } + op instanceof NotEq and sense = false + ) + ) + } - /** holds for `== "KNOWN_VALUE"` on `true` edge, and `!= "KNOWN_VALUE"` on `false` edge */ - private predicate test_equality_with_const(CompareNode cmp, ControlFlowNode tainted, boolean sense) { - exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst | - ( - cmp.operands(const, op, tainted) - or - cmp.operands(tainted, op, const) - ) and - ( - op instanceof Eq and sense = true - or - op instanceof NotEq and sense = false - ) - ) - } - - /** holds for `in ["KNOWN_VALUE", ...]` on `true` edge, and `not in ["KNOWN_VALUE", ...]` on `false` edge */ - private predicate test_in_const_seq(CompareNode cmp, ControlFlowNode tainted, boolean sense) { - exists(SequenceNode const_seq, Cmpop op | - forall(ControlFlowNode elem | elem = const_seq.getAnElement() | - elem.getNode() instanceof StrConst - ) - | - cmp.operands(tainted, op, const_seq) and - ( - op instanceof In and sense = true - or - op instanceof NotIn and sense = false - ) - ) - } + /** holds for `in ["KNOWN_VALUE", ...]` on `true` edge, and `not in ["KNOWN_VALUE", ...]` on `false` edge */ + private predicate test_in_const_seq(CompareNode cmp, ControlFlowNode tainted, boolean sense) { + exists(SequenceNode const_seq, Cmpop op | + forall(ControlFlowNode elem | elem = const_seq.getAnElement() | + elem.getNode() instanceof StrConst + ) + | + cmp.operands(tainted, op, const_seq) and + ( + op instanceof In and sense = true + or + op instanceof NotIn and sense = false + ) + ) + } } diff --git a/python/ql/src/semmle/python/security/strings/Untrusted.qll b/python/ql/src/semmle/python/security/strings/Untrusted.qll index 6fa12668ada..da8c23e7bde 100644 --- a/python/ql/src/semmle/python/security/strings/Untrusted.qll +++ b/python/ql/src/semmle/python/security/strings/Untrusted.qll @@ -6,5 +6,5 @@ import External * This class is a simple sub-class of `ExternalStringKind`. */ class UntrustedStringKind extends ExternalStringKind { - UntrustedStringKind() { this = "externally controlled string" } + UntrustedStringKind() { this = "externally controlled string" } } diff --git a/python/ql/src/semmle/python/strings.qll b/python/ql/src/semmle/python/strings.qll index e366d191669..6c4b3ded1ce 100644 --- a/python/ql/src/semmle/python/strings.qll +++ b/python/ql/src/semmle/python/strings.qll @@ -1,11 +1,11 @@ import python predicate format_string(StrConst e) { - exists(BinaryExpr b | b.getOp() instanceof Mod and b.getLeft() = e) + exists(BinaryExpr b | b.getOp() instanceof Mod and b.getLeft() = e) } predicate mapping_format(StrConst e) { - conversion_specifier(e, _).regexpMatch("%\\([A-Z_a-z0-9]+\\).*") + conversion_specifier(e, _).regexpMatch("%\\([A-Z_a-z0-9]+\\).*") } /* @@ -18,36 +18,36 @@ predicate mapping_format(StrConst e) { */ private string conversion_specifier_string(StrConst e, int number, int position) { - exists(string s, string REGEX | s = e.getText() | - REGEX = "%(\\([^)]*\\))?[#0\\- +]*(\\*|[0-9]*)(\\.(\\*|[0-9]*))?(h|H|l|L)?[badiouxXeEfFgGcrs%]" and - result = s.regexpFind(REGEX, number, position) - ) + exists(string s, string REGEX | s = e.getText() | + REGEX = "%(\\([^)]*\\))?[#0\\- +]*(\\*|[0-9]*)(\\.(\\*|[0-9]*))?(h|H|l|L)?[badiouxXeEfFgGcrs%]" and + result = s.regexpFind(REGEX, number, position) + ) } private string conversion_specifier(StrConst e, int number) { - result = conversion_specifier_string(e, number, _) and result != "%%" + result = conversion_specifier_string(e, number, _) and result != "%%" } int illegal_conversion_specifier(StrConst e) { - format_string(e) and - "%" = e.getText().charAt(result) and - // not the start of a conversion specifier or the second % of a %% - not exists(conversion_specifier_string(e, _, result)) and - not exists(conversion_specifier_string(e, _, result - 1)) + format_string(e) and + "%" = e.getText().charAt(result) and + // not the start of a conversion specifier or the second % of a %% + not exists(conversion_specifier_string(e, _, result)) and + not exists(conversion_specifier_string(e, _, result - 1)) } /** Gets the number of format items in a format string */ int format_items(StrConst e) { - result = - count(int i | | conversion_specifier(e, i)) + - // a conversion specifier uses an extra item for each * - count(int i, int j | conversion_specifier(e, i).charAt(j) = "*") + result = + count(int i | | conversion_specifier(e, i)) + + // a conversion specifier uses an extra item for each * + count(int i, int j | conversion_specifier(e, i).charAt(j) = "*") } private string str(Expr e) { - result = e.(Num).getN() - or - result = "'" + e.(StrConst).getText() + "'" + result = e.(Num).getN() + or + result = "'" + e.(StrConst).getText() + "'" } /** Gets a string representation of an expression more suited for embedding in message strings than .toString() */ diff --git a/python/ql/src/semmle/python/templates/PyxlTags.qll b/python/ql/src/semmle/python/templates/PyxlTags.qll index 07f4aa44e03..f0e663cdad0 100644 --- a/python/ql/src/semmle/python/templates/PyxlTags.qll +++ b/python/ql/src/semmle/python/templates/PyxlTags.qll @@ -4,68 +4,68 @@ import python * A Tag in Pyxl (which gets converted to a call in Python). */ class PyxlTag extends Call { - PyxlTag() { pyxl_tag(this, _) } + PyxlTag() { pyxl_tag(this, _) } - string getPyxlTagName() { pyxl_tag(this, result) } + string getPyxlTagName() { pyxl_tag(this, result) } - /** Gets the pyxl or Python node that is enclosed by this one in the pyxl source */ - Expr getEnclosedNode() { none() } + /** Gets the pyxl or Python node that is enclosed by this one in the pyxl source */ + Expr getEnclosedNode() { none() } - /** Gets the Python code (if any) that is contained in this pyxl node */ - Expr getEnclosedPythonCode() { - result = this.getEnclosedNode() and not result instanceof PyxlTag - or - result = this.getEnclosedNode().(PyxlTag).getEnclosedPythonCode() - } + /** Gets the Python code (if any) that is contained in this pyxl node */ + Expr getEnclosedPythonCode() { + result = this.getEnclosedNode() and not result instanceof PyxlTag + or + result = this.getEnclosedNode().(PyxlTag).getEnclosedPythonCode() + } } private predicate pyxl_tag(Call c, string name) { - exists(Attribute tag, Name html | - tag = c.getFunc() and - html = tag.getObject() and - name = tag.getName() and - html.getId() = "html" - ) + exists(Attribute tag, Name html | + tag = c.getFunc() and + html = tag.getObject() and + name = tag.getName() and + html.getId() = "html" + ) } class PyxlHtmlTag extends PyxlTag { - PyxlHtmlTag() { this.getPyxlTagName().prefix(2) = "x_" } + PyxlHtmlTag() { this.getPyxlTagName().prefix(2) = "x_" } - string getTagName() { result = this.getPyxlTagName().suffix(2) } + string getTagName() { result = this.getPyxlTagName().suffix(2) } - /** Html tags get transformed into a call. This node is the callee function and the enclosed node is an argument. */ - override Expr getEnclosedNode() { - exists(Call c | - c.getFunc() = this and - result = c.getAnArg() - ) - } + /** Html tags get transformed into a call. This node is the callee function and the enclosed node is an argument. */ + override Expr getEnclosedNode() { + exists(Call c | + c.getFunc() = this and + result = c.getAnArg() + ) + } } class PyxlIfTag extends PyxlTag { - PyxlIfTag() { this.getPyxlTagName() = "_push_condition" } + PyxlIfTag() { this.getPyxlTagName() = "_push_condition" } - override Expr getEnclosedNode() { result = this.getAnArg() } + override Expr getEnclosedNode() { result = this.getAnArg() } } class PyxlEndIfTag extends PyxlTag { - PyxlEndIfTag() { this.getPyxlTagName() = "_leave_if" } + PyxlEndIfTag() { this.getPyxlTagName() = "_leave_if" } - override Expr getEnclosedNode() { result = this.getAnArg() } + override Expr getEnclosedNode() { result = this.getAnArg() } } class PyxlRawHtml extends PyxlTag { - PyxlRawHtml() { this.getPyxlTagName() = "rawhtml" } + PyxlRawHtml() { this.getPyxlTagName() = "rawhtml" } - /** The text for this raw html, if it is simple text. */ - string getText() { - exists(Unicode text | - text = this.getValue() and - result = text.getS() - ) - } + /** The text for this raw html, if it is simple text. */ + string getText() { + exists(Unicode text | + text = this.getValue() and + result = text.getS() + ) + } - Expr getValue() { result = this.getArg(0) } + Expr getValue() { result = this.getArg(0) } - override Expr getEnclosedNode() { result = this.getAnArg() } + override Expr getEnclosedNode() { result = this.getAnArg() } } diff --git a/python/ql/src/semmle/python/templates/Templates.qll b/python/ql/src/semmle/python/templates/Templates.qll index 00aacd232a0..fa33aa33a7f 100644 --- a/python/ql/src/semmle/python/templates/Templates.qll +++ b/python/ql/src/semmle/python/templates/Templates.qll @@ -3,13 +3,13 @@ import python abstract class Template extends Module { } class SpitfireTemplate extends Template { - SpitfireTemplate() { this.getKind() = "Spitfire template" } + SpitfireTemplate() { this.getKind() = "Spitfire template" } } class PyxlModule extends Template { - PyxlModule() { - exists(Comment c | c.getLocation().getFile() = this.getFile() | - c.getText().regexpMatch("# *coding.*pyxl.*") - ) - } + PyxlModule() { + exists(Comment c | c.getLocation().getFile() = this.getFile() | + c.getText().regexpMatch("# *coding.*pyxl.*") + ) + } } diff --git a/python/ql/src/semmle/python/types/Builtins.qll b/python/ql/src/semmle/python/types/Builtins.qll index 062d552a887..b5a03686bdd 100644 --- a/python/ql/src/semmle/python/types/Builtins.qll +++ b/python/ql/src/semmle/python/types/Builtins.qll @@ -1,126 +1,126 @@ import python class Builtin extends @py_cobject { - Builtin() { - not ( - /* @py_cobjects for modules which have a corresponding Python module */ - exists(@py_cobject mod_type | - py_special_objects(mod_type, "ModuleType") and py_cobjecttypes(this, mod_type) - ) and - exists(Module m | py_cobjectnames(this, m.getName())) - ) and - ( - /* Exclude unmatched builtin objects in the library trap files */ - py_cobjectnames(this, _) or - py_cobjecttypes(this, _) or - py_special_objects(this, _) - ) - } + Builtin() { + not ( + /* @py_cobjects for modules which have a corresponding Python module */ + exists(@py_cobject mod_type | + py_special_objects(mod_type, "ModuleType") and py_cobjecttypes(this, mod_type) + ) and + exists(Module m | py_cobjectnames(this, m.getName())) + ) and + ( + /* Exclude unmatched builtin objects in the library trap files */ + py_cobjectnames(this, _) or + py_cobjecttypes(this, _) or + py_special_objects(this, _) + ) + } - /** Gets a textual representation of this element. */ - string toString() { - not this = undefinedVariable().asBuiltin() and - not this = Builtin::unknown() and - exists(Builtin type, string typename, string objname | - py_cobjecttypes(this, type) and py_cobjectnames(this, objname) and typename = type.getName() - | - result = typename + " " + objname - ) - } + /** Gets a textual representation of this element. */ + string toString() { + not this = undefinedVariable().asBuiltin() and + not this = Builtin::unknown() and + exists(Builtin type, string typename, string objname | + py_cobjecttypes(this, type) and py_cobjectnames(this, objname) and typename = type.getName() + | + result = typename + " " + objname + ) + } - Builtin getClass() { - py_cobjecttypes(this, result) and not this = Builtin::unknown() - or - this = Builtin::unknown() and result = Builtin::unknownType() - } + Builtin getClass() { + py_cobjecttypes(this, result) and not this = Builtin::unknown() + or + this = Builtin::unknown() and result = Builtin::unknownType() + } - Builtin getMember(string name) { - not name = ".super." and - py_cmembers_versioned(this, name, result, major_version().toString()) - } + Builtin getMember(string name) { + not name = ".super." and + py_cmembers_versioned(this, name, result, major_version().toString()) + } - Builtin getItem(int index) { py_citems(this, index, result) } + Builtin getItem(int index) { py_citems(this, index, result) } - Builtin getBaseClass() { - /* The extractor uses the special name ".super." to indicate the super class of a builtin class */ - py_cmembers_versioned(this, ".super.", result, major_version().toString()) - } + Builtin getBaseClass() { + /* The extractor uses the special name ".super." to indicate the super class of a builtin class */ + py_cmembers_versioned(this, ".super.", result, major_version().toString()) + } - predicate inheritsFromType() { - this = Builtin::special("type") - or - this.getBaseClass().inheritsFromType() - } + predicate inheritsFromType() { + this = Builtin::special("type") + or + this.getBaseClass().inheritsFromType() + } - string getName() { if this.isStr() then result = "str" else py_cobjectnames(this, result) } + string getName() { if this.isStr() then result = "str" else py_cobjectnames(this, result) } - private predicate isStr() { - major_version() = 2 and this = Builtin::special("bytes") - or - major_version() = 3 and this = Builtin::special("unicode") - } + private predicate isStr() { + major_version() = 2 and this = Builtin::special("bytes") + or + major_version() = 3 and this = Builtin::special("unicode") + } - predicate isClass() { - py_cobjecttypes(_, this) - or - this = Builtin::unknownType() - or - exists(Builtin meta | meta.inheritsFromType() and py_cobjecttypes(this, meta)) - } + predicate isClass() { + py_cobjecttypes(_, this) + or + this = Builtin::unknownType() + or + exists(Builtin meta | meta.inheritsFromType() and py_cobjecttypes(this, meta)) + } - predicate isFunction() { - this.getClass() = Builtin::special("BuiltinFunctionType") and - exists(Builtin mod | - mod.isModule() and - mod.getMember(_) = this - ) - } + predicate isFunction() { + this.getClass() = Builtin::special("BuiltinFunctionType") and + exists(Builtin mod | + mod.isModule() and + mod.getMember(_) = this + ) + } - predicate isModule() { this.getClass() = Builtin::special("ModuleType") } + predicate isModule() { this.getClass() = Builtin::special("ModuleType") } - predicate isMethod() { - this.getClass() = Builtin::special("MethodDescriptorType") - or - this.getClass().getName() = "wrapper_descriptor" - } + predicate isMethod() { + this.getClass() = Builtin::special("MethodDescriptorType") + or + this.getClass().getName() = "wrapper_descriptor" + } - int intValue() { - ( - this.getClass() = Builtin::special("int") or - this.getClass() = Builtin::special("long") - ) and - result = this.getName().toInt() - } + int intValue() { + ( + this.getClass() = Builtin::special("int") or + this.getClass() = Builtin::special("long") + ) and + result = this.getName().toInt() + } - float floatValue() { - this.getClass() = Builtin::special("float") and - result = this.getName().toFloat() - } + float floatValue() { + this.getClass() = Builtin::special("float") and + result = this.getName().toFloat() + } - string strValue() { - ( - this.getClass() = Builtin::special("unicode") or - this.getClass() = Builtin::special("bytes") - ) and - exists(string quoted_string | - quoted_string = this.getName() and - result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) - ) - } + string strValue() { + ( + this.getClass() = Builtin::special("unicode") or + this.getClass() = Builtin::special("bytes") + ) and + exists(string quoted_string | + quoted_string = this.getName() and + result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) + ) + } } module Builtin { - Builtin builtinModule() { - py_special_objects(result, "builtin_module_2") and major_version() = 2 - or - py_special_objects(result, "builtin_module_3") and major_version() = 3 - } + Builtin builtinModule() { + py_special_objects(result, "builtin_module_2") and major_version() = 2 + or + py_special_objects(result, "builtin_module_3") and major_version() = 3 + } - Builtin builtin(string name) { result = builtinModule().getMember(name) } + Builtin builtin(string name) { result = builtinModule().getMember(name) } - Builtin special(string name) { py_special_objects(result, name) } + Builtin special(string name) { py_special_objects(result, name) } - Builtin unknown() { py_special_objects(result, "_1") } + Builtin unknown() { py_special_objects(result, "_1") } - Builtin unknownType() { py_special_objects(result, "_semmle_unknown_type") } + Builtin unknownType() { py_special_objects(result, "_semmle_unknown_type") } } diff --git a/python/ql/src/semmle/python/types/ClassObject.qll b/python/ql/src/semmle/python/types/ClassObject.qll index d48a9bddf22..8bcfb2ff92d 100644 --- a/python/ql/src/semmle/python/types/ClassObject.qll +++ b/python/ql/src/semmle/python/types/ClassObject.qll @@ -21,339 +21,339 @@ private import semmle.python.objects.ObjectInternal * running program. */ class ClassObject extends Object { - private ClassObjectInternal theClass() { - result.getOrigin() = this - or - result.getBuiltin() = this - } + private ClassObjectInternal theClass() { + result.getOrigin() = this + or + result.getBuiltin() = this + } - ClassObject() { - this.getOrigin() instanceof ClassExpr or - this.asBuiltin().isClass() - } + ClassObject() { + this.getOrigin() instanceof ClassExpr or + this.asBuiltin().isClass() + } - /** Gets the short (unqualified) name of this class */ - string getName() { result = theClass().getName() } + /** Gets the short (unqualified) name of this class */ + string getName() { result = theClass().getName() } - /** - * Gets the qualified name for this class. - * Should return the same name as the `__qualname__` attribute on classes in Python 3. + /** + * Gets the qualified name for this class. + * Should return the same name as the `__qualname__` attribute on classes in Python 3. + */ + string getQualifiedName() { + result = theClass().getBuiltin().getName() + or + result = theClass().(PythonClassObjectInternal).getScope().getQualifiedName() + } + + /** Gets the nth base class of this class */ + Object getBaseType(int n) { result = Types::getBase(theClass(), n).getSource() } + + /** Gets a base class of this class */ + Object getABaseType() { result = this.getBaseType(_) } + + /** Whether this class has a base class */ + predicate hasABase() { exists(Types::getBase(theClass(), _)) } + + /** Gets a super class of this class (includes transitive super classes) */ + ClassObject getASuperType() { + result = Types::getMro(theClass()).getTail().getAnItem().getSource() + } + + /** Gets a super class of this class (includes transitive super classes) or this class */ + ClassObject getAnImproperSuperType() { result = this.getABaseType*() } + + /** + * Whether this class is a new style class. + * A new style class is one that implicitly or explicitly inherits from `object`. + */ + predicate isNewStyle() { Types::isNewStyle(theClass()) } + + /** + * Whether this class is an old style class. + * An old style class is one that does not inherit from `object`. + */ + predicate isOldStyle() { Types::isOldStyle(theClass()) } + + /** + * Whether this class is a legal exception class. + * What constitutes a legal exception class differs between major versions + */ + predicate isLegalExceptionType() { + not this.isNewStyle() + or + this.getAnImproperSuperType() = theBaseExceptionType() + or + major_version() = 2 and this = theTupleType() + } + + /** Gets the scope associated with this class, if it is not a builtin class */ + Class getPyClass() { result.getClassObject() = this } + + /** Returns an attribute declared on this class (not on a super-class) */ + Object declaredAttribute(string name) { + exists(ObjectInternal val | + Types::declaredAttribute(theClass(), name, val, _) and + result = val.getSource() + ) + } + + /** Returns an attribute declared on this class (not on a super-class) */ + predicate declaresAttribute(string name) { + theClass().getClassDeclaration().declaresAttribute(name) + } + + /** + * Returns an attribute as it would be when looked up at runtime on this class. + * Will include attributes of super-classes + */ + Object lookupAttribute(string name) { + exists(ObjectInternal val | + theClass().lookup(name, val, _) and + result = val.getSource() + ) + } + + ClassList getMro() { result = Types::getMro(theClass()) } + + /** Looks up an attribute by searching this class' MRO starting at `start` */ + Object lookupMro(ClassObject start, string name) { + exists(ClassObjectInternal other, ClassObjectInternal decl, ObjectInternal val | + other.getSource() = start and + decl = Types::getMro(theClass()).startingAt(other).findDeclaringClass(name) and + Types::declaredAttribute(decl, name, val, _) and + result = val.getSource() + ) + } + + /** Whether the named attribute refers to the object and origin */ + predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { + this.attributeRefersTo(name, obj, _, origin) + } + + /** Whether the named attribute refers to the object, class and origin */ + predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { + exists(ObjectInternal val, CfgOrigin valorig | + theClass().lookup(name, val, valorig) and + obj = val.getSource() and + cls = val.getClass().getSource() and + origin = valorig.toCfgNode() + ) + } + + /** Whether this class has a attribute named `name`, either declared or inherited. */ + predicate hasAttribute(string name) { theClass().hasAttribute(name) } + + /** + * Whether it is impossible to know all the attributes of this class. Usually because it is + * impossible to calculate the full class hierarchy or because some attribute is too dynamic. + */ + predicate unknowableAttributes() { + /* True for a class with undeterminable superclasses, unanalysable metaclasses, or other confusions */ + this.failedInference() + or + this.getMetaClass().failedInference() + or + exists(Object base | base = this.getABaseType() | + base.(ClassObject).unknowableAttributes() + or + not base instanceof ClassObject + ) + } + + /** Gets the metaclass for this class */ + ClassObject getMetaClass() { + result = theClass().getClass().getSource() and + not this.failedInference() + } + + /* Whether this class is abstract. */ + predicate isAbstract() { + this.getMetaClass() = theAbcMetaClassObject() + or + exists(FunctionObject f, Raise r, Name ex | + f = this.lookupAttribute(_) and + r.getScope() = f.getFunction() + | + (r.getException() = ex or r.getException().(Call).getFunc() = ex) and + (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") + ) + } + + ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() } + + /** Has type inference failed to compute the full class hierarchy for this class for the reason given. */ + predicate failedInference(string reason) { Types::failedInference(theClass(), reason) } + + /** Has type inference failed to compute the full class hierarchy for this class */ + predicate failedInference() { this.failedInference(_) } + + /** + * Gets an object which is the sole instance of this class, if this class is probably a singleton. + * Note the 'probable' in the name; there is no guarantee that this class is in fact a singleton. + * It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject. + */ + Object getProbableSingletonInstance() { + exists(ControlFlowNode use, Expr origin | use.refersTo(result, this, origin.getAFlowNode()) | + this.hasStaticallyUniqueInstance() and + /* Ensure that original expression will be executed only one. */ + origin.getScope() instanceof ImportTimeScope and + not exists(Expr outer | outer.getASubExpression+() = origin) + ) + or + this = theNoneType() and result = theNoneObject() + } + + /** This class is only instantiated at one place in the code */ + private predicate hasStaticallyUniqueInstance() { + strictcount(SpecificInstanceInternal inst | inst.getClass() = theClass()) = 1 + } + + ImportTimeScope getImportTimeScope() { result = this.getPyClass() } + + override string toString() { + this.isC() and result = "builtin-class " + this.getName() and not this = theUnknownType() + or + not this.isC() and result = "class " + this.getName() + } + + /* Method Resolution Order */ + /** Returns the next class in the MRO of 'this' after 'sup' */ + ClassObject nextInMro(ClassObject sup) { + exists(ClassObjectInternal other | + other.getSource() = sup and + result = Types::getMro(theClass()).startingAt(other).getTail().getHead().getSource() + ) and + not this.failedInference() + } + + /** + * Gets the MRO for this class. ClassObject `sup` occurs at `index` in the list of classes. + * `this` has an index of `1`, the next class in the MRO has an index of `2`, and so on. + */ + ClassObject getMroItem(int index) { result = this.getMro().getItem(index).getSource() } + + /** Holds if this class has duplicate base classes */ + predicate hasDuplicateBases() { + exists(ClassObject base, int i, int j | + i != j and base = this.getBaseType(i) and base = this.getBaseType(j) + ) + } + + /** Holds if this class is an iterable. */ + predicate isIterable() { this.hasAttribute("__iter__") or this.hasAttribute("__getitem__") } + + /** Holds if this class is an iterator. */ + predicate isIterator() { + this.hasAttribute("__iter__") and + ( + major_version() = 3 and this.hasAttribute("__next__") + or + /* + * Because 'next' is a common method name we need to check that an __iter__ + * method actually returns this class. This is not needed for Py3 as the + * '__next__' method exists to define a class as an iterator. + */ + + major_version() = 2 and + this.hasAttribute("next") and + exists(ClassObject other, FunctionObject iter | other.declaredAttribute("__iter__") = iter | + iter.getAnInferredReturnType() = this + ) + ) + or + /* This will be redundant when we have C class information */ + this = theGeneratorType() + } + + /** + * Holds if this class is an improper subclass of the other class. + * True if this is a sub-class of other or this is the same class as other. + * + * Equivalent to the Python builtin function issubclass(). + */ + predicate isSubclassOf(ClassObject other) { this = other or this.getASuperType() = other } + + /** Synonymous with isContainer(), retained for backwards compatibility. */ + predicate isCollection() { this.isContainer() } + + /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ + predicate isContainer() { exists(this.lookupAttribute("__getitem__")) } + + /** Holds if this class is a mapping. */ + predicate isMapping() { + exists(this.lookupAttribute("__getitem__")) and + not this.isSequence() + } + + /** Holds if this class is probably a sequence. */ + predicate isSequence() { + /* + * To determine whether something is a sequence or a mapping is not entirely clear, + * so we need to guess a bit. */ - string getQualifiedName() { - result = theClass().getBuiltin().getName() - or - result = theClass().(PythonClassObjectInternal).getScope().getQualifiedName() - } - /** Gets the nth base class of this class */ - Object getBaseType(int n) { result = Types::getBase(theClass(), n).getSource() } + this.getAnImproperSuperType() = theTupleType() + or + this.getAnImproperSuperType() = theListType() + or + this.getAnImproperSuperType() = theRangeType() + or + this.getAnImproperSuperType() = theBytesType() + or + this.getAnImproperSuperType() = theUnicodeType() + or + /* Does this inherit from abc.Sequence? */ + this.getASuperType().getName() = "Sequence" + or + /* Does it have an index or __reversed__ method? */ + this.isContainer() and + ( + this.hasAttribute("index") or + this.hasAttribute("__reversed__") + ) + } - /** Gets a base class of this class */ - Object getABaseType() { result = this.getBaseType(_) } + predicate isCallable() { this.hasAttribute("__call__") } - /** Whether this class has a base class */ - predicate hasABase() { exists(Types::getBase(theClass(), _)) } + predicate isContextManager() { this.hasAttribute("__enter__") and this.hasAttribute("__exit__") } - /** Gets a super class of this class (includes transitive super classes) */ - ClassObject getASuperType() { - result = Types::getMro(theClass()).getTail().getAnItem().getSource() - } + predicate assignedInInit(string name) { + exists(FunctionObject init | init = this.lookupAttribute("__init__") | + attribute_assigned_in_method(init, name) + ) + } - /** Gets a super class of this class (includes transitive super classes) or this class */ - ClassObject getAnImproperSuperType() { result = this.getABaseType*() } + /** Holds if this class is unhashable */ + predicate unhashable() { + this.lookupAttribute("__hash__") = theNoneObject() + or + this.lookupAttribute("__hash__").(FunctionObject).neverReturns() + } - /** - * Whether this class is a new style class. - * A new style class is one that implicitly or explicitly inherits from `object`. - */ - predicate isNewStyle() { Types::isNewStyle(theClass()) } + /** Holds if this class is a descriptor */ + predicate isDescriptorType() { this.hasAttribute("__get__") } - /** - * Whether this class is an old style class. - * An old style class is one that does not inherit from `object`. - */ - predicate isOldStyle() { Types::isOldStyle(theClass()) } + /** Holds if this class is an overriding descriptor */ + predicate isOverridingDescriptorType() { + this.hasAttribute("__get__") and this.hasAttribute("__set__") + } - /** - * Whether this class is a legal exception class. - * What constitutes a legal exception class differs between major versions - */ - predicate isLegalExceptionType() { - not this.isNewStyle() - or - this.getAnImproperSuperType() = theBaseExceptionType() - or - major_version() = 2 and this = theTupleType() - } + FunctionObject getAMethodCalledFromInit() { + exists(FunctionObject init | + init = this.lookupAttribute("__init__") and + init.getACallee*() = result + ) + } - /** Gets the scope associated with this class, if it is not a builtin class */ - Class getPyClass() { result.getClassObject() = this } + override boolean booleanValue() { result = true } - /** Returns an attribute declared on this class (not on a super-class) */ - Object declaredAttribute(string name) { - exists(ObjectInternal val | - Types::declaredAttribute(theClass(), name, val, _) and - result = val.getSource() - ) - } + /** + * Gets a call to this class. Note that the call may not create a new instance of + * this class, as that depends on the `__new__` method of this class. + */ + CallNode getACall() { result.getFunction().refersTo(this) } - /** Returns an attribute declared on this class (not on a super-class) */ - predicate declaresAttribute(string name) { - theClass().getClassDeclaration().declaresAttribute(name) - } - - /** - * Returns an attribute as it would be when looked up at runtime on this class. - * Will include attributes of super-classes - */ - Object lookupAttribute(string name) { - exists(ObjectInternal val | - theClass().lookup(name, val, _) and - result = val.getSource() - ) - } - - ClassList getMro() { result = Types::getMro(theClass()) } - - /** Looks up an attribute by searching this class' MRO starting at `start` */ - Object lookupMro(ClassObject start, string name) { - exists(ClassObjectInternal other, ClassObjectInternal decl, ObjectInternal val | - other.getSource() = start and - decl = Types::getMro(theClass()).startingAt(other).findDeclaringClass(name) and - Types::declaredAttribute(decl, name, val, _) and - result = val.getSource() - ) - } - - /** Whether the named attribute refers to the object and origin */ - predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { - this.attributeRefersTo(name, obj, _, origin) - } - - /** Whether the named attribute refers to the object, class and origin */ - predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { - exists(ObjectInternal val, CfgOrigin valorig | - theClass().lookup(name, val, valorig) and - obj = val.getSource() and - cls = val.getClass().getSource() and - origin = valorig.toCfgNode() - ) - } - - /** Whether this class has a attribute named `name`, either declared or inherited. */ - predicate hasAttribute(string name) { theClass().hasAttribute(name) } - - /** - * Whether it is impossible to know all the attributes of this class. Usually because it is - * impossible to calculate the full class hierarchy or because some attribute is too dynamic. - */ - predicate unknowableAttributes() { - /* True for a class with undeterminable superclasses, unanalysable metaclasses, or other confusions */ - this.failedInference() - or - this.getMetaClass().failedInference() - or - exists(Object base | base = this.getABaseType() | - base.(ClassObject).unknowableAttributes() - or - not base instanceof ClassObject - ) - } - - /** Gets the metaclass for this class */ - ClassObject getMetaClass() { - result = theClass().getClass().getSource() and - not this.failedInference() - } - - /* Whether this class is abstract. */ - predicate isAbstract() { - this.getMetaClass() = theAbcMetaClassObject() - or - exists(FunctionObject f, Raise r, Name ex | - f = this.lookupAttribute(_) and - r.getScope() = f.getFunction() - | - (r.getException() = ex or r.getException().(Call).getFunc() = ex) and - (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") - ) - } - - ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() } - - /** Has type inference failed to compute the full class hierarchy for this class for the reason given. */ - predicate failedInference(string reason) { Types::failedInference(theClass(), reason) } - - /** Has type inference failed to compute the full class hierarchy for this class */ - predicate failedInference() { this.failedInference(_) } - - /** - * Gets an object which is the sole instance of this class, if this class is probably a singleton. - * Note the 'probable' in the name; there is no guarantee that this class is in fact a singleton. - * It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject. - */ - Object getProbableSingletonInstance() { - exists(ControlFlowNode use, Expr origin | use.refersTo(result, this, origin.getAFlowNode()) | - this.hasStaticallyUniqueInstance() and - /* Ensure that original expression will be executed only one. */ - origin.getScope() instanceof ImportTimeScope and - not exists(Expr outer | outer.getASubExpression+() = origin) - ) - or - this = theNoneType() and result = theNoneObject() - } - - /** This class is only instantiated at one place in the code */ - private predicate hasStaticallyUniqueInstance() { - strictcount(SpecificInstanceInternal inst | inst.getClass() = theClass()) = 1 - } - - ImportTimeScope getImportTimeScope() { result = this.getPyClass() } - - override string toString() { - this.isC() and result = "builtin-class " + this.getName() and not this = theUnknownType() - or - not this.isC() and result = "class " + this.getName() - } - - /* Method Resolution Order */ - /** Returns the next class in the MRO of 'this' after 'sup' */ - ClassObject nextInMro(ClassObject sup) { - exists(ClassObjectInternal other | - other.getSource() = sup and - result = Types::getMro(theClass()).startingAt(other).getTail().getHead().getSource() - ) and - not this.failedInference() - } - - /** - * Gets the MRO for this class. ClassObject `sup` occurs at `index` in the list of classes. - * `this` has an index of `1`, the next class in the MRO has an index of `2`, and so on. - */ - ClassObject getMroItem(int index) { result = this.getMro().getItem(index).getSource() } - - /** Holds if this class has duplicate base classes */ - predicate hasDuplicateBases() { - exists(ClassObject base, int i, int j | - i != j and base = this.getBaseType(i) and base = this.getBaseType(j) - ) - } - - /** Holds if this class is an iterable. */ - predicate isIterable() { this.hasAttribute("__iter__") or this.hasAttribute("__getitem__") } - - /** Holds if this class is an iterator. */ - predicate isIterator() { - this.hasAttribute("__iter__") and - ( - major_version() = 3 and this.hasAttribute("__next__") - or - /* - * Because 'next' is a common method name we need to check that an __iter__ - * method actually returns this class. This is not needed for Py3 as the - * '__next__' method exists to define a class as an iterator. - */ - - major_version() = 2 and - this.hasAttribute("next") and - exists(ClassObject other, FunctionObject iter | other.declaredAttribute("__iter__") = iter | - iter.getAnInferredReturnType() = this - ) - ) - or - /* This will be redundant when we have C class information */ - this = theGeneratorType() - } - - /** - * Holds if this class is an improper subclass of the other class. - * True if this is a sub-class of other or this is the same class as other. - * - * Equivalent to the Python builtin function issubclass(). - */ - predicate isSubclassOf(ClassObject other) { this = other or this.getASuperType() = other } - - /** Synonymous with isContainer(), retained for backwards compatibility. */ - predicate isCollection() { this.isContainer() } - - /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ - predicate isContainer() { exists(this.lookupAttribute("__getitem__")) } - - /** Holds if this class is a mapping. */ - predicate isMapping() { - exists(this.lookupAttribute("__getitem__")) and - not this.isSequence() - } - - /** Holds if this class is probably a sequence. */ - predicate isSequence() { - /* - * To determine whether something is a sequence or a mapping is not entirely clear, - * so we need to guess a bit. - */ - - this.getAnImproperSuperType() = theTupleType() - or - this.getAnImproperSuperType() = theListType() - or - this.getAnImproperSuperType() = theRangeType() - or - this.getAnImproperSuperType() = theBytesType() - or - this.getAnImproperSuperType() = theUnicodeType() - or - /* Does this inherit from abc.Sequence? */ - this.getASuperType().getName() = "Sequence" - or - /* Does it have an index or __reversed__ method? */ - this.isContainer() and - ( - this.hasAttribute("index") or - this.hasAttribute("__reversed__") - ) - } - - predicate isCallable() { this.hasAttribute("__call__") } - - predicate isContextManager() { this.hasAttribute("__enter__") and this.hasAttribute("__exit__") } - - predicate assignedInInit(string name) { - exists(FunctionObject init | init = this.lookupAttribute("__init__") | - attribute_assigned_in_method(init, name) - ) - } - - /** Holds if this class is unhashable */ - predicate unhashable() { - this.lookupAttribute("__hash__") = theNoneObject() - or - this.lookupAttribute("__hash__").(FunctionObject).neverReturns() - } - - /** Holds if this class is a descriptor */ - predicate isDescriptorType() { this.hasAttribute("__get__") } - - /** Holds if this class is an overriding descriptor */ - predicate isOverridingDescriptorType() { - this.hasAttribute("__get__") and this.hasAttribute("__set__") - } - - FunctionObject getAMethodCalledFromInit() { - exists(FunctionObject init | - init = this.lookupAttribute("__init__") and - init.getACallee*() = result - ) - } - - override boolean booleanValue() { result = true } - - /** - * Gets a call to this class. Note that the call may not create a new instance of - * this class, as that depends on the `__new__` method of this class. - */ - CallNode getACall() { result.getFunction().refersTo(this) } - - override predicate notClass() { none() } + override predicate notClass() { none() } } /** @@ -361,17 +361,17 @@ class ClassObject extends Object { * Python 2 and the 'unicode' class for Python 3 */ ClassObject theStrType() { - if major_version() = 2 then result = theBytesType() else result = theUnicodeType() + if major_version() = 2 then result = theBytesType() else result = theUnicodeType() } private Module theAbcModule() { result.getName() = "abc" } ClassObject theAbcMetaClassObject() { - /* Avoid using points-to and thus negative recursion */ - exists(Class abcmeta | result.getPyClass() = abcmeta | - abcmeta.getName() = "ABCMeta" and - abcmeta.getScope() = theAbcModule() - ) + /* Avoid using points-to and thus negative recursion */ + exists(Class abcmeta | result.getPyClass() = abcmeta | + abcmeta.getName() = "ABCMeta" and + abcmeta.getScope() = theAbcModule() + ) } /* Common builtin classes */ @@ -422,9 +422,9 @@ ClassObject theUnicodeType() { result.asBuiltin() = Builtin::special("unicode") /** The builtin class '(x)range' */ ClassObject theRangeType() { - result = Object::builtin("xrange") - or - major_version() = 3 and result = Object::builtin("range") + result = Object::builtin("xrange") + or + major_version() = 3 and result = Object::builtin("range") } /** The builtin class for bytes. str in Python2, bytes in Python3 */ @@ -441,7 +441,7 @@ ClassObject theBaseExceptionType() { result.asBuiltin() = Builtin::special("Base /** The class of builtin-functions */ ClassObject theBuiltinFunctionType() { - result.asBuiltin() = Builtin::special("BuiltinFunctionType") + result.asBuiltin() = Builtin::special("BuiltinFunctionType") } /** The class of Python functions */ @@ -474,19 +474,19 @@ ClassObject theBoundMethodType() { result.asBuiltin() = Builtin::special("Method /** The builtin class of builtin properties */ ClassObject theGetSetDescriptorType() { - result.asBuiltin() = Builtin::special("GetSetDescriptorType") + result.asBuiltin() = Builtin::special("GetSetDescriptorType") } /** The method descriptor class */ ClassObject theMethodDescriptorType() { - result.asBuiltin() = Builtin::special("MethodDescriptorType") + result.asBuiltin() = Builtin::special("MethodDescriptorType") } /** The class of builtin properties */ ClassObject theBuiltinPropertyType() { - /* This is CPython specific */ - result.isC() and - result.getName() = "getset_descriptor" + /* This is CPython specific */ + result.isC() and + result.getName() = "getset_descriptor" } /** The builtin class 'IOError' */ diff --git a/python/ql/src/semmle/python/types/Descriptors.qll b/python/ql/src/semmle/python/types/Descriptors.qll index f190f9a6434..6a5743c444a 100644 --- a/python/ql/src/semmle/python/types/Descriptors.qll +++ b/python/ql/src/semmle/python/types/Descriptors.qll @@ -3,26 +3,26 @@ private import semmle.python.objects.ObjectInternal /** A class method object. Either a decorated function or an explicit call to classmethod(f) */ class ClassMethodObject extends Object { - ClassMethodObject() { any(ClassMethodObjectInternal cm).getOrigin() = this } + ClassMethodObject() { any(ClassMethodObjectInternal cm).getOrigin() = this } - FunctionObject getFunction() { - exists(ClassMethodObjectInternal cm | - cm.getOrigin() = this and - result = cm.getFunction().getSource() - ) - } + FunctionObject getFunction() { + exists(ClassMethodObjectInternal cm | + cm.getOrigin() = this and + result = cm.getFunction().getSource() + ) + } - CallNode getACall() { result = this.getFunction().getACall() } + CallNode getACall() { result = this.getFunction().getACall() } } /** A static method object. Either a decorated function or an explicit call to staticmethod(f) */ class StaticMethodObject extends Object { - StaticMethodObject() { any(StaticMethodObjectInternal sm).getOrigin() = this } + StaticMethodObject() { any(StaticMethodObjectInternal sm).getOrigin() = this } - FunctionObject getFunction() { - exists(StaticMethodObjectInternal sm | - sm.getOrigin() = this and - result = sm.getFunction().getSource() - ) - } + FunctionObject getFunction() { + exists(StaticMethodObjectInternal sm | + sm.getOrigin() = this and + result = sm.getFunction().getSource() + ) + } } diff --git a/python/ql/src/semmle/python/types/Exceptions.qll b/python/ql/src/semmle/python/types/Exceptions.qll index 7fe1b274664..7383b44e742 100644 --- a/python/ql/src/semmle/python/types/Exceptions.qll +++ b/python/ql/src/semmle/python/types/Exceptions.qll @@ -15,432 +15,432 @@ import python /** Subset of ControlFlowNodes which might raise an exception */ class RaisingNode extends ControlFlowNode { - RaisingNode() { - exists(this.getAnExceptionalSuccessor()) - or - this.isExceptionalExit(_) - } + RaisingNode() { + exists(this.getAnExceptionalSuccessor()) + or + this.isExceptionalExit(_) + } - /** Gets the CFG node for the exception, if and only if this RaisingNode is an explicit raise */ - ControlFlowNode getExceptionNode() { - exists(Raise r | - r = this.getNode() and - result.getNode() = r.getRaised() and - result.getBasicBlock().dominates(this.getBasicBlock()) - ) - } + /** Gets the CFG node for the exception, if and only if this RaisingNode is an explicit raise */ + ControlFlowNode getExceptionNode() { + exists(Raise r | + r = this.getNode() and + result.getNode() = r.getRaised() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } - private predicate quits() { this.(CallNode).getFunction().refersTo(Object::quitter(_)) } + private predicate quits() { this.(CallNode).getFunction().refersTo(Object::quitter(_)) } - /** - * Gets the type of an exception that may be raised - * at this control flow node - */ - ClassObject getARaisedType_objectapi() { - result = this.localRaisedType_objectapi() - or - exists(FunctionObject func | this = func.getACall() | result = func.getARaisedType()) - or - result = systemExitRaise_objectapi() - } + /** + * Gets the type of an exception that may be raised + * at this control flow node + */ + ClassObject getARaisedType_objectapi() { + result = this.localRaisedType_objectapi() + or + exists(FunctionObject func | this = func.getACall() | result = func.getARaisedType()) + or + result = systemExitRaise_objectapi() + } - /** - * Gets the type of an exception that may be raised - * at this control flow node - */ - ClassValue getARaisedType() { - result = this.localRaisedType() - or - exists(FunctionValue func | this = func.getACall() | result = func.getARaisedType()) - or - result = systemExitRaise() - } + /** + * Gets the type of an exception that may be raised + * at this control flow node + */ + ClassValue getARaisedType() { + result = this.localRaisedType() + or + exists(FunctionValue func | this = func.getACall() | result = func.getARaisedType()) + or + result = systemExitRaise() + } - pragma[noinline] - private ClassObject systemExitRaise_objectapi() { - this.quits() and result = Object::builtin("SystemExit") - } + pragma[noinline] + private ClassObject systemExitRaise_objectapi() { + this.quits() and result = Object::builtin("SystemExit") + } - pragma[noinline] - private ClassValue systemExitRaise() { this.quits() and result = ClassValue::systemExit() } + pragma[noinline] + private ClassValue systemExitRaise() { this.quits() and result = ClassValue::systemExit() } - pragma[noinline, nomagic] - private ClassObject localRaisedType_objectapi() { - result.isSubclassOf(theBaseExceptionType()) and - ( - exists(ControlFlowNode ex | - ex = this.getExceptionNode() and - (ex.refersTo(result) or ex.refersTo(_, result, _)) - ) - or - this.getNode() instanceof ImportExpr and result = Object::builtin("ImportError") - or - this.getNode() instanceof Print and result = theIOErrorType() - or - exists(ExceptFlowNode except | - except = this.getAnExceptionalSuccessor() and - except.handles_objectapi(result) and - result = this.innateException_objectapi() - ) - or - not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and - sequence_or_mapping(this) and - result = theLookupErrorType() - or - this.read_write_call() and result = theIOErrorType() - ) - } + pragma[noinline, nomagic] + private ClassObject localRaisedType_objectapi() { + result.isSubclassOf(theBaseExceptionType()) and + ( + exists(ControlFlowNode ex | + ex = this.getExceptionNode() and + (ex.refersTo(result) or ex.refersTo(_, result, _)) + ) + or + this.getNode() instanceof ImportExpr and result = Object::builtin("ImportError") + or + this.getNode() instanceof Print and result = theIOErrorType() + or + exists(ExceptFlowNode except | + except = this.getAnExceptionalSuccessor() and + except.handles_objectapi(result) and + result = this.innateException_objectapi() + ) + or + not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and + sequence_or_mapping(this) and + result = theLookupErrorType() + or + this.read_write_call() and result = theIOErrorType() + ) + } - pragma[noinline, nomagic] - private ClassValue localRaisedType() { - result.getASuperType() = ClassValue::baseException() and - ( - exists(ControlFlowNode ex | - ex = this.getExceptionNode() and - (ex.pointsTo(result) or ex.pointsTo().getClass() = result) - ) - or - this.getNode() instanceof ImportExpr and result = ClassValue::importError() - or - this.getNode() instanceof Print and result = ClassValue::ioError() - or - exists(ExceptFlowNode except | - except = this.getAnExceptionalSuccessor() and - except.handles(result) and - result = this.innateException() - ) - or - not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and - sequence_or_mapping(this) and - result = ClassValue::lookupError() - or - this.read_write_call() and result = ClassValue::ioError() - ) - } + pragma[noinline, nomagic] + private ClassValue localRaisedType() { + result.getASuperType() = ClassValue::baseException() and + ( + exists(ControlFlowNode ex | + ex = this.getExceptionNode() and + (ex.pointsTo(result) or ex.pointsTo().getClass() = result) + ) + or + this.getNode() instanceof ImportExpr and result = ClassValue::importError() + or + this.getNode() instanceof Print and result = ClassValue::ioError() + or + exists(ExceptFlowNode except | + except = this.getAnExceptionalSuccessor() and + except.handles(result) and + result = this.innateException() + ) + or + not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and + sequence_or_mapping(this) and + result = ClassValue::lookupError() + or + this.read_write_call() and result = ClassValue::ioError() + ) + } - /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ - pragma[noinline] - ClassObject innateException_objectapi() { - this.getNode() instanceof Attribute and result = theAttributeErrorType() - or - this.getNode() instanceof Name and result = theNameErrorType() - or - this.getNode() instanceof Subscript and result = theIndexErrorType() - or - this.getNode() instanceof Subscript and result = theKeyErrorType() - } + /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ + pragma[noinline] + ClassObject innateException_objectapi() { + this.getNode() instanceof Attribute and result = theAttributeErrorType() + or + this.getNode() instanceof Name and result = theNameErrorType() + or + this.getNode() instanceof Subscript and result = theIndexErrorType() + or + this.getNode() instanceof Subscript and result = theKeyErrorType() + } - /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ - pragma[noinline] - ClassValue innateException() { - this.getNode() instanceof Attribute and result = ClassValue::attributeError() - or - this.getNode() instanceof Name and result = ClassValue::nameError() - or - this.getNode() instanceof Subscript and result = ClassValue::indexError() - or - this.getNode() instanceof Subscript and result = ClassValue::keyError() - } + /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ + pragma[noinline] + ClassValue innateException() { + this.getNode() instanceof Attribute and result = ClassValue::attributeError() + or + this.getNode() instanceof Name and result = ClassValue::nameError() + or + this.getNode() instanceof Subscript and result = ClassValue::indexError() + or + this.getNode() instanceof Subscript and result = ClassValue::keyError() + } - /** - * Whether this control flow node raises an exception, - * but the type of the exception it raises cannot be inferred. - */ - predicate raisesUnknownType() { - /* read/write calls are assumed to raise IOError (OSError for Py3) */ - not this.read_write_call() and - ( - /* Call to an unknown object */ - this.getNode() instanceof Call and - not exists(FunctionObject func | this = func.getACall()) and - not exists(ClassObject known | this.(CallNode).getFunction().refersTo(known)) - or - this.getNode() instanceof Exec - or - /* Call to a function raising an unknown type */ - exists(FunctionObject func | this = func.getACall() | func.raisesUnknownType()) - ) - } + /** + * Whether this control flow node raises an exception, + * but the type of the exception it raises cannot be inferred. + */ + predicate raisesUnknownType() { + /* read/write calls are assumed to raise IOError (OSError for Py3) */ + not this.read_write_call() and + ( + /* Call to an unknown object */ + this.getNode() instanceof Call and + not exists(FunctionObject func | this = func.getACall()) and + not exists(ClassObject known | this.(CallNode).getFunction().refersTo(known)) + or + this.getNode() instanceof Exec + or + /* Call to a function raising an unknown type */ + exists(FunctionObject func | this = func.getACall() | func.raisesUnknownType()) + ) + } - private predicate read_write_call() { - exists(string mname | mname = this.(CallNode).getFunction().(AttrNode).getName() | - mname = "read" or mname = "write" - ) - } + private predicate read_write_call() { + exists(string mname | mname = this.(CallNode).getFunction().(AttrNode).getName() | + mname = "read" or mname = "write" + ) + } - /** Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. */ - predicate unlikelySuccessor(ControlFlowNode succ) { - succ = this.getAnExceptionalSuccessor() and - not this.viableExceptionEdge_objectapi(succ, _) and - not this.raisesUnknownType() - or - exists(FunctionObject func | - func.getACall() = this and - func.neverReturns() and - succ = this.getASuccessor() and - not succ = this.getAnExceptionalSuccessor() and - // If result is yielded then func is likely to be some form of coroutine. - not succ.getNode() instanceof Yield - ) - or - this.quits() and - succ = this.getASuccessor() and - not succ = this.getAnExceptionalSuccessor() - } + /** Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. */ + predicate unlikelySuccessor(ControlFlowNode succ) { + succ = this.getAnExceptionalSuccessor() and + not this.viableExceptionEdge_objectapi(succ, _) and + not this.raisesUnknownType() + or + exists(FunctionObject func | + func.getACall() = this and + func.neverReturns() and + succ = this.getASuccessor() and + not succ = this.getAnExceptionalSuccessor() and + // If result is yielded then func is likely to be some form of coroutine. + not succ.getNode() instanceof Yield + ) + or + this.quits() and + succ = this.getASuccessor() and + not succ = this.getAnExceptionalSuccessor() + } - /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ - predicate viableExceptionEdge_objectapi(ControlFlowNode succ, ClassObject raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType_objectapi() and - succ = this.getAnExceptionalSuccessor() and - ( - /* An 'except' that handles raised and there is no more previous handler */ - succ.(ExceptFlowNode).handles_objectapi(raised) and - not exists(ExceptFlowNode other, StmtList s, int i, int j | - not other = succ and - other.handles_objectapi(raised) and - s.getItem(i) = succ.getNode() and - s.getItem(j) = other.getNode() - | - j < i - ) - or - /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) and - not succ instanceof ExceptFlowNode - ) - } + /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ + predicate viableExceptionEdge_objectapi(ControlFlowNode succ, ClassObject raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType_objectapi() and + succ = this.getAnExceptionalSuccessor() and + ( + /* An 'except' that handles raised and there is no more previous handler */ + succ.(ExceptFlowNode).handles_objectapi(raised) and + not exists(ExceptFlowNode other, StmtList s, int i, int j | + not other = succ and + other.handles_objectapi(raised) and + s.getItem(i) = succ.getNode() and + s.getItem(j) = other.getNode() + | + j < i + ) + or + /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) and + not succ instanceof ExceptFlowNode + ) + } - /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ - predicate viableExceptionEdge(ControlFlowNode succ, ClassValue raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType() and - succ = this.getAnExceptionalSuccessor() and - ( - /* An 'except' that handles raised and there is no more previous handler */ - succ.(ExceptFlowNode).handles(raised) and - not exists(ExceptFlowNode other, StmtList s, int i, int j | - not other = succ and - other.handles(raised) and - s.getItem(i) = succ.getNode() and - s.getItem(j) = other.getNode() - | - j < i - ) - or - /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) and - not succ instanceof ExceptFlowNode - ) - } + /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ + predicate viableExceptionEdge(ControlFlowNode succ, ClassValue raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType() and + succ = this.getAnExceptionalSuccessor() and + ( + /* An 'except' that handles raised and there is no more previous handler */ + succ.(ExceptFlowNode).handles(raised) and + not exists(ExceptFlowNode other, StmtList s, int i, int j | + not other = succ and + other.handles(raised) and + s.getItem(i) = succ.getNode() and + s.getItem(j) = other.getNode() + | + j < i + ) + or + /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) and + not succ instanceof ExceptFlowNode + ) + } - /** - * Whether this exceptional exit is viable. That is, is it - * plausible that the scope `s` can be exited with exception `raised` - * at this point. - */ - predicate viableExceptionalExit_objectapi(Scope s, ClassObject raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType_objectapi() and - this.isExceptionalExit(s) and - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) - } + /** + * Whether this exceptional exit is viable. That is, is it + * plausible that the scope `s` can be exited with exception `raised` + * at this point. + */ + predicate viableExceptionalExit_objectapi(Scope s, ClassObject raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType_objectapi() and + this.isExceptionalExit(s) and + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) + } - /** - * Whether this exceptional exit is viable. That is, is it - * plausible that the scope `s` can be exited with exception `raised` - * at this point. - */ - predicate viableExceptionalExit(Scope s, ClassValue raised) { - raised.isLegalExceptionType() and - raised = this.getARaisedType() and - this.isExceptionalExit(s) and - not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) - } + /** + * Whether this exceptional exit is viable. That is, is it + * plausible that the scope `s` can be exited with exception `raised` + * at this point. + */ + predicate viableExceptionalExit(Scope s, ClassValue raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType() and + this.isExceptionalExit(s) and + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) + } } /** Is this a sequence or mapping subscript x[i]? */ private predicate sequence_or_mapping(RaisingNode r) { r.getNode() instanceof Subscript } private predicate current_exception_objectapi(ClassObject ex, BasicBlock b) { - exists(RaisingNode r | - r.viableExceptionEdge_objectapi(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode - ) - or - exists(BasicBlock prev | - current_exception_objectapi(ex, prev) and - exists(ControlFlowNode pred, ControlFlowNode succ | - pred = prev.getLastNode() and succ = b.getNode(0) - | - pred.getASuccessor() = succ and - ( - /* Normal control flow */ - not pred.getAnExceptionalSuccessor() = succ - or - /* Re-raise the current exception, propagating to the successor */ - pred instanceof ReraisingNode - ) - ) + exists(RaisingNode r | + r.viableExceptionEdge_objectapi(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + current_exception_objectapi(ex, prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and + ( + /* Normal control flow */ + not pred.getAnExceptionalSuccessor() = succ + or + /* Re-raise the current exception, propagating to the successor */ + pred instanceof ReraisingNode + ) ) + ) } private predicate current_exception(ClassValue ex, BasicBlock b) { - exists(RaisingNode r | - r.viableExceptionEdge(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode - ) - or - exists(BasicBlock prev | - current_exception(ex, prev) and - exists(ControlFlowNode pred, ControlFlowNode succ | - pred = prev.getLastNode() and succ = b.getNode(0) - | - pred.getASuccessor() = succ and - ( - /* Normal control flow */ - not pred.getAnExceptionalSuccessor() = succ - or - /* Re-raise the current exception, propagating to the successor */ - pred instanceof ReraisingNode - ) - ) + exists(RaisingNode r | + r.viableExceptionEdge(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + current_exception(ex, prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and + ( + /* Normal control flow */ + not pred.getAnExceptionalSuccessor() = succ + or + /* Re-raise the current exception, propagating to the successor */ + pred instanceof ReraisingNode + ) ) + ) } private predicate unknown_current_exception(BasicBlock b) { - exists(RaisingNode r | - r.raisesUnknownType() and - r.getAnExceptionalSuccessor() = b.getNode(0) and - not b.getNode(0) instanceof ExceptFlowNode - ) - or - exists(BasicBlock prev | - unknown_current_exception(prev) and - exists(ControlFlowNode pred, ControlFlowNode succ | - pred = prev.getLastNode() and succ = b.getNode(0) - | - pred.getASuccessor() = succ and - (not pred.getAnExceptionalSuccessor() = succ or pred instanceof ReraisingNode) - ) + exists(RaisingNode r | + r.raisesUnknownType() and + r.getAnExceptionalSuccessor() = b.getNode(0) and + not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + unknown_current_exception(prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and + (not pred.getAnExceptionalSuccessor() = succ or pred instanceof ReraisingNode) ) + ) } /** INTERNAL -- Use FunctionObject.getARaisedType() instead */ predicate scope_raises_objectapi(ClassObject ex, Scope s) { - exists(BasicBlock b | - current_exception_objectapi(ex, b) and - b.getLastNode().isExceptionalExit(s) - | - b.getLastNode() instanceof ReraisingNode - ) - or - exists(RaisingNode r | r.viableExceptionalExit_objectapi(s, ex)) + exists(BasicBlock b | + current_exception_objectapi(ex, b) and + b.getLastNode().isExceptionalExit(s) + | + b.getLastNode() instanceof ReraisingNode + ) + or + exists(RaisingNode r | r.viableExceptionalExit_objectapi(s, ex)) } /** INTERNAL -- Use FunctionObject.getARaisedType() instead */ predicate scope_raises(ClassValue ex, Scope s) { - exists(BasicBlock b | - current_exception(ex, b) and - b.getLastNode().isExceptionalExit(s) - | - b.getLastNode() instanceof ReraisingNode - ) - or - exists(RaisingNode r | r.viableExceptionalExit(s, ex)) + exists(BasicBlock b | + current_exception(ex, b) and + b.getLastNode().isExceptionalExit(s) + | + b.getLastNode() instanceof ReraisingNode + ) + or + exists(RaisingNode r | r.viableExceptionalExit(s, ex)) } /** INTERNAL -- Use FunctionObject.raisesUnknownType() instead */ predicate scope_raises_unknown(Scope s) { - exists(BasicBlock b | - b.getLastNode() instanceof ReraisingNode and - b.getLastNode().isExceptionalExit(s) - | - unknown_current_exception(b) - ) - or - exists(RaisingNode r | - r.raisesUnknownType() and - r.isExceptionalExit(s) - ) + exists(BasicBlock b | + b.getLastNode() instanceof ReraisingNode and + b.getLastNode().isExceptionalExit(s) + | + unknown_current_exception(b) + ) + or + exists(RaisingNode r | + r.raisesUnknownType() and + r.isExceptionalExit(s) + ) } /** ControlFlowNode for an 'except' statement. */ class ExceptFlowNode extends ControlFlowNode { - ExceptFlowNode() { this.getNode() instanceof ExceptStmt } + ExceptFlowNode() { this.getNode() instanceof ExceptStmt } - ControlFlowNode getType() { - exists(ExceptStmt ex | - this.getBasicBlock().dominates(result.getBasicBlock()) and - ex = this.getNode() and - result = ex.getType().getAFlowNode() - ) - } + ControlFlowNode getType() { + exists(ExceptStmt ex | + this.getBasicBlock().dominates(result.getBasicBlock()) and + ex = this.getNode() and + result = ex.getType().getAFlowNode() + ) + } - ControlFlowNode getName() { - exists(ExceptStmt ex | - this.getBasicBlock().dominates(result.getBasicBlock()) and - ex = this.getNode() and - result = ex.getName().getAFlowNode() - ) - } + ControlFlowNode getName() { + exists(ExceptStmt ex | + this.getBasicBlock().dominates(result.getBasicBlock()) and + ex = this.getNode() and + result = ex.getName().getAFlowNode() + ) + } - private predicate handledObject_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { - this.getType().refersTo(obj, cls, origin) - or - exists(Object tup | this.handledObject_objectapi(tup, theTupleType(), _) | - element_from_tuple_objectapi(tup).refersTo(obj, cls, origin) - ) - } + private predicate handledObject_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { + this.getType().refersTo(obj, cls, origin) + or + exists(Object tup | this.handledObject_objectapi(tup, theTupleType(), _) | + element_from_tuple_objectapi(tup).refersTo(obj, cls, origin) + ) + } - private predicate handledObject(Value val, ClassValue cls, ControlFlowNode origin) { - val.getClass() = cls and - ( - this.getType().pointsTo(val, origin) - or - exists(TupleValue tup | this.handledObject(tup, ClassValue::tuple(), _) | - val = tup.getItem(_) and origin = val.getOrigin() - ) - ) - } + private predicate handledObject(Value val, ClassValue cls, ControlFlowNode origin) { + val.getClass() = cls and + ( + this.getType().pointsTo(val, origin) + or + exists(TupleValue tup | this.handledObject(tup, ClassValue::tuple(), _) | + val = tup.getItem(_) and origin = val.getOrigin() + ) + ) + } - /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ - pragma[noinline] - predicate handledException_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { - this.handledObject_objectapi(obj, cls, origin) and not cls = theTupleType() - or - not exists(this.getNode().(ExceptStmt).getType()) and - obj = theBaseExceptionType() and - cls = theTypeType() and - origin = this - } + /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ + pragma[noinline] + predicate handledException_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { + this.handledObject_objectapi(obj, cls, origin) and not cls = theTupleType() + or + not exists(this.getNode().(ExceptStmt).getType()) and + obj = theBaseExceptionType() and + cls = theTypeType() and + origin = this + } - /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ - pragma[noinline] - predicate handledException(Value val, ClassValue cls, ControlFlowNode origin) { - this.handledObject(val, cls, origin) and not cls = ClassValue::tuple() - or - not exists(this.getNode().(ExceptStmt).getType()) and - val = ClassValue::baseException() and - cls = ClassValue::type() and - origin = this - } + /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ + pragma[noinline] + predicate handledException(Value val, ClassValue cls, ControlFlowNode origin) { + this.handledObject(val, cls, origin) and not cls = ClassValue::tuple() + or + not exists(this.getNode().(ExceptStmt).getType()) and + val = ClassValue::baseException() and + cls = ClassValue::type() and + origin = this + } - /** Whether this `except` handles `cls` */ - predicate handles_objectapi(ClassObject cls) { - exists(ClassObject handled | this.handledException_objectapi(handled, _, _) | - cls.getAnImproperSuperType() = handled - ) - } + /** Whether this `except` handles `cls` */ + predicate handles_objectapi(ClassObject cls) { + exists(ClassObject handled | this.handledException_objectapi(handled, _, _) | + cls.getAnImproperSuperType() = handled + ) + } - /** Whether this `except` handles `cls` */ - predicate handles(ClassValue cls) { - exists(ClassValue handled | this.handledException(handled, _, _) | - cls.getASuperType() = handled - ) - } + /** Whether this `except` handles `cls` */ + predicate handles(ClassValue cls) { + exists(ClassValue handled | this.handledException(handled, _, _) | + cls.getASuperType() = handled + ) + } } private ControlFlowNode element_from_tuple_objectapi(Object tuple) { - exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode()) + exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode()) } /** @@ -448,35 +448,35 @@ private ControlFlowNode element_from_tuple_objectapi(Object tuple) { * that reraises the current exception. */ class ReraisingNode extends RaisingNode { - ReraisingNode() { - not this.getNode() instanceof Raise and - in_finally(this) and - forall(ControlFlowNode succ | succ = this.getASuccessor() | - succ = this.getAnExceptionalSuccessor() - ) - } + ReraisingNode() { + not this.getNode() instanceof Raise and + in_finally(this) and + forall(ControlFlowNode succ | succ = this.getASuccessor() | + succ = this.getAnExceptionalSuccessor() + ) + } - /** Gets a class that may be raised by this node */ - override ClassObject getARaisedType_objectapi() { - exists(BasicBlock b | - current_exception_objectapi(result, b) and - b.getNode(_) = this - ) - } + /** Gets a class that may be raised by this node */ + override ClassObject getARaisedType_objectapi() { + exists(BasicBlock b | + current_exception_objectapi(result, b) and + b.getNode(_) = this + ) + } - /** Gets a class that may be raised by this node */ - override ClassValue getARaisedType() { - exists(BasicBlock b | - current_exception(result, b) and - b.getNode(_) = this - ) - } + /** Gets a class that may be raised by this node */ + override ClassValue getARaisedType() { + exists(BasicBlock b | + current_exception(result, b) and + b.getNode(_) = this + ) + } } private predicate in_finally(ControlFlowNode n) { - exists(Stmt f | exists(Try t | f = t.getAFinalstmt()) | - f = n.getNode() - or - f.containsInScope(n.getNode()) - ) + exists(Stmt f | exists(Try t | f = t.getAFinalstmt()) | + f = n.getNode() + or + f.containsInScope(n.getNode()) + ) } diff --git a/python/ql/src/semmle/python/types/Extensions.qll b/python/ql/src/semmle/python/types/Extensions.qll index 19e05875826..9c067ed7e4a 100644 --- a/python/ql/src/semmle/python/types/Extensions.qll +++ b/python/ql/src/semmle/python/types/Extensions.qll @@ -19,9 +19,9 @@ private import semmle.python.web.HttpConstants import semmle.python.objects.ObjectInternal abstract class PointsToExtension extends @py_flow_node { - string toString() { result = "PointsToExtension with missing toString" } + string toString() { result = "PointsToExtension with missing toString" } - abstract predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin); + abstract predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin); } /* Legacy API */ @@ -32,160 +32,160 @@ abstract class PointsToExtension extends @py_flow_node { /** DEPRECATED -- Use PointsToExtension instead */ abstract deprecated class CustomPointsToFact extends @py_flow_node { - string toString() { result = "CustomPointsToFact with missing toString" } + string toString() { result = "CustomPointsToFact with missing toString" } - abstract predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin); + abstract predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin); } /** DEPRECATED -- Use PointsToExtension instead */ deprecated class FinalCustomPointsToFact = CustomPointsToFact; abstract deprecated class CustomPointsToOriginFact extends CustomPointsToFact { - abstract predicate pointsTo(Object value, ClassObject cls); + abstract predicate pointsTo(Object value, ClassObject cls); - override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { - this.pointsTo(value, cls) and origin = this and context.appliesTo(this) - } + override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { + this.pointsTo(value, cls) and origin = this and context.appliesTo(this) + } } /* Custom points-to fact with inferred class */ abstract deprecated class CustomPointsToObjectFact extends CustomPointsToFact { - abstract predicate pointsTo(Object value); + abstract predicate pointsTo(Object value); - override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { - this.pointsTo(value) and cls = simple_types(value) and origin = this and context.appliesTo(this) - } + override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { + this.pointsTo(value) and cls = simple_types(value) and origin = this and context.appliesTo(this) + } } /** DEPRECATED -- Unsupported; do not use */ abstract deprecated class CustomPointsToAttribute extends Object { - abstract predicate attributePointsTo( - string name, Object value, ClassObject cls, ControlFlowNode origin - ); + abstract predicate attributePointsTo( + string name, Object value, ClassObject cls, ControlFlowNode origin + ); } /* An example */ /** Any variable iterating over range or xrange must be an integer */ class RangeIterationVariableFact extends PointsToExtension { - RangeIterationVariableFact() { - exists(For f, ControlFlowNode iterable | - iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and - f.getIter().getAFlowNode() = iterable and - f.getTarget().getAFlowNode() = this and - exists(ObjectInternal range | - PointsTo::pointsTo(iterable, _, range, _) and - range.getClass() = ObjectInternal::builtin("range") - ) - ) - } + RangeIterationVariableFact() { + exists(For f, ControlFlowNode iterable | + iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and + f.getIter().getAFlowNode() = iterable and + f.getTarget().getAFlowNode() = this and + exists(ObjectInternal range | + PointsTo::pointsTo(iterable, _, range, _) and + range.getClass() = ObjectInternal::builtin("range") + ) + ) + } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - value = TUnknownInstance(ObjectInternal::builtin("int")) and - origin = this and - context.appliesTo(this) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + value = TUnknownInstance(ObjectInternal::builtin("int")) and + origin = this and + context.appliesTo(this) + } } /* bottle module route constants */ class BottleRoutePointToExtension extends PointsToExtension { - string name; + string name; - BottleRoutePointToExtension() { - exists(DefinitionNode defn | - defn.getScope().(Module).getName() = "bottle" and - this = defn.getValue() and - name = defn.(NameNode).getId() - | - name = "route" or - name = httpVerbLower() - ) - } + BottleRoutePointToExtension() { + exists(DefinitionNode defn | + defn.getScope().(Module).getName() = "bottle" and + this = defn.getValue() and + name = defn.(NameNode).getId() + | + name = "route" or + name = httpVerbLower() + ) + } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - context.isImport() and - exists(CfgOrigin orig | - Module::named("bottle").attr("Bottle").(ClassObjectInternal).attribute(name, value, orig) and - origin = orig.asCfgNodeOrHere(this) - ) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + context.isImport() and + exists(CfgOrigin orig | + Module::named("bottle").attr("Bottle").(ClassObjectInternal).attribute(name, value, orig) and + origin = orig.asCfgNodeOrHere(this) + ) + } } /* Python 3.6+ regex module constants */ string short_flag(string flag) { - ( - flag = "ASCII" or - flag = "IGNORECASE" or - flag = "LOCALE" or - flag = "UNICODE" or - flag = "MULTILINE" or - flag = "TEMPLATE" - ) and - result = flag.prefix(1) - or - flag = "DOTALL" and result = "S" - or - flag = "VERBOSE" and result = "X" + ( + flag = "ASCII" or + flag = "IGNORECASE" or + flag = "LOCALE" or + flag = "UNICODE" or + flag = "MULTILINE" or + flag = "TEMPLATE" + ) and + result = flag.prefix(1) + or + flag = "DOTALL" and result = "S" + or + flag = "VERBOSE" and result = "X" } class ReModulePointToExtension extends PointsToExtension { - string name; + string name; - ReModulePointToExtension() { - exists(ModuleObjectInternal re | - re.getName() = "re" and - PointsTo::pointsTo(this.(AttrNode).getObject(name), _, re, _) - ) - } + ReModulePointToExtension() { + exists(ModuleObjectInternal re | + re.getName() = "re" and + PointsTo::pointsTo(this.(AttrNode).getObject(name), _, re, _) + ) + } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - exists(ModuleObjectInternal sre_constants, CfgOrigin orig, string flag | - (name = flag or name = short_flag(flag)) and - sre_constants.getName() = "sre_constants" and - sre_constants.attribute("SRE_FLAG_" + flag, value, orig) and - origin = orig.asCfgNodeOrHere(this) - ) and - pointsTo_helper(context) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + exists(ModuleObjectInternal sre_constants, CfgOrigin orig, string flag | + (name = flag or name = short_flag(flag)) and + sre_constants.getName() = "sre_constants" and + sre_constants.attribute("SRE_FLAG_" + flag, value, orig) and + origin = orig.asCfgNodeOrHere(this) + ) and + pointsTo_helper(context) + } - pragma[noinline] - private predicate pointsTo_helper(Context context) { context.appliesTo(this) } + pragma[noinline] + private predicate pointsTo_helper(Context context) { context.appliesTo(this) } } deprecated private class BackwardCompatiblePointToExtension extends PointsToExtension { - BackwardCompatiblePointToExtension() { this instanceof CustomPointsToFact } + BackwardCompatiblePointToExtension() { this instanceof CustomPointsToFact } - override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { - exists(Object obj, ClassObject cls | - this.(CustomPointsToFact).pointsTo(context, obj, cls, origin) - | - value.getBuiltin() = obj - or - obj instanceof ControlFlowNode and - exists(ClassObjectInternal c | - c.getSource() = cls and - value = TUnknownInstance(c) - ) - ) - or - exists(ObjectInternal owner, string name | - PointsTo::pointsTo(this.(AttrNode).getObject(name), context, owner, _) and - additionalAttribute(owner, name, value, origin) - ) - } + override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) { + exists(Object obj, ClassObject cls | + this.(CustomPointsToFact).pointsTo(context, obj, cls, origin) + | + value.getBuiltin() = obj + or + obj instanceof ControlFlowNode and + exists(ClassObjectInternal c | + c.getSource() = cls and + value = TUnknownInstance(c) + ) + ) + or + exists(ObjectInternal owner, string name | + PointsTo::pointsTo(this.(AttrNode).getObject(name), context, owner, _) and + additionalAttribute(owner, name, value, origin) + ) + } } deprecated private predicate additionalAttribute( - ObjectInternal owner, string name, ObjectInternal value, ControlFlowNode origin + ObjectInternal owner, string name, ObjectInternal value, ControlFlowNode origin ) { - exists(Object obj, ClassObject cls | - owner.getSource().(CustomPointsToAttribute).attributePointsTo(name, obj, cls, origin) - | - value.getBuiltin() = obj - or - obj instanceof ControlFlowNode and - exists(ClassObjectInternal c | - c.getSource() = cls and - value = TUnknownInstance(c) - ) + exists(Object obj, ClassObject cls | + owner.getSource().(CustomPointsToAttribute).attributePointsTo(name, obj, cls, origin) + | + value.getBuiltin() = obj + or + obj instanceof ControlFlowNode and + exists(ClassObjectInternal c | + c.getSource() = cls and + value = TUnknownInstance(c) ) + ) } diff --git a/python/ql/src/semmle/python/types/FunctionObject.qll b/python/ql/src/semmle/python/types/FunctionObject.qll index 5d3a81363db..c293a43d675 100644 --- a/python/ql/src/semmle/python/types/FunctionObject.qll +++ b/python/ql/src/semmle/python/types/FunctionObject.qll @@ -9,306 +9,306 @@ private import semmle.python.types.Builtins /** A function object, whether written in Python or builtin */ abstract class FunctionObject extends Object { - CallableValue theCallable() { result.(ObjectInternal).getSource() = this } + CallableValue theCallable() { result.(ObjectInternal).getSource() = this } - predicate isOverridingMethod() { exists(Object f | this.overrides(f)) } + predicate isOverridingMethod() { exists(Object f | this.overrides(f)) } - predicate isOverriddenMethod() { exists(Object f | f.overrides(this)) } + predicate isOverriddenMethod() { exists(Object f | f.overrides(this)) } - Function getFunction() { result = this.getOrigin().(CallableExpr).getInnerScope() } + Function getFunction() { result = this.getOrigin().(CallableExpr).getInnerScope() } - /** This function always returns None, meaning that its return value should be disregarded */ - abstract predicate isProcedure(); + /** This function always returns None, meaning that its return value should be disregarded */ + abstract predicate isProcedure(); - /** Gets the name of this function */ - abstract string getName(); + /** Gets the name of this function */ + abstract string getName(); - /** Gets a class that may be raised by this function */ - abstract ClassObject getARaisedType(); + /** Gets a class that may be raised by this function */ + abstract ClassObject getARaisedType(); - /** Whether this function raises an exception, the class of which cannot be inferred */ - abstract predicate raisesUnknownType(); + /** Whether this function raises an exception, the class of which cannot be inferred */ + abstract predicate raisesUnknownType(); - /** Use descriptiveString() instead. */ - deprecated string prettyString() { result = this.descriptiveString() } + /** Use descriptiveString() instead. */ + deprecated string prettyString() { result = this.descriptiveString() } - /** Gets a longer, more descriptive version of toString() */ - abstract string descriptiveString(); + /** Gets a longer, more descriptive version of toString() */ + abstract string descriptiveString(); - /** Gets a call-site from where this function is called as a function */ - CallNode getAFunctionCall() { result.getFunction().inferredValue() = theCallable() } + /** Gets a call-site from where this function is called as a function */ + CallNode getAFunctionCall() { result.getFunction().inferredValue() = theCallable() } - /** Gets a call-site from where this function is called as a method */ - CallNode getAMethodCall() { - exists(BoundMethodObjectInternal bm | - result.getFunction().inferredValue() = bm and - bm.getFunction() = theCallable() - ) - } + /** Gets a call-site from where this function is called as a method */ + CallNode getAMethodCall() { + exists(BoundMethodObjectInternal bm | + result.getFunction().inferredValue() = bm and + bm.getFunction() = theCallable() + ) + } - /** Gets a call-site from where this function is called */ - ControlFlowNode getACall() { result = theCallable().getACall() } + /** Gets a call-site from where this function is called */ + ControlFlowNode getACall() { result = theCallable().getACall() } - /** Gets a call-site from where this function is called, given the `context` */ - ControlFlowNode getACall(Context caller_context) { - result = theCallable().getACall(caller_context) - } + /** Gets a call-site from where this function is called, given the `context` */ + ControlFlowNode getACall(Context caller_context) { + result = theCallable().getACall(caller_context) + } - /** - * Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`. - * This predicate will correctly handle `x.y()`, treating `x` as the zeroth argument. - */ - ControlFlowNode getArgumentForCall(CallNode call, int n) { - result = theCallable().getArgumentForCall(call, n) - } + /** + * Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`. + * This predicate will correctly handle `x.y()`, treating `x` as the zeroth argument. + */ + ControlFlowNode getArgumentForCall(CallNode call, int n) { + result = theCallable().getArgumentForCall(call, n) + } - /** - * Gets the `ControlFlowNode` that will be passed as the named argument to `this` when called at `call`. - * This predicate will correctly handle `x.y()`, treating `x` as the self argument. - */ - ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { - result = theCallable().getNamedArgumentForCall(call, name) - } + /** + * Gets the `ControlFlowNode` that will be passed as the named argument to `this` when called at `call`. + * This predicate will correctly handle `x.y()`, treating `x` as the self argument. + */ + ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { + result = theCallable().getNamedArgumentForCall(call, name) + } - /** Whether this function never returns. This is an approximation. */ - predicate neverReturns() { theCallable().neverReturns() } + /** Whether this function never returns. This is an approximation. */ + predicate neverReturns() { theCallable().neverReturns() } - /** - * Whether this is a "normal" method, that is, it is exists as a class attribute - * which is not wrapped and not the __new__ method. - */ - predicate isNormalMethod() { - exists(ClassObject cls, string name | - cls.declaredAttribute(name) = this and - name != "__new__" and - not this.getOrigin() instanceof Lambda - ) - } + /** + * Whether this is a "normal" method, that is, it is exists as a class attribute + * which is not wrapped and not the __new__ method. + */ + predicate isNormalMethod() { + exists(ClassObject cls, string name | + cls.declaredAttribute(name) = this and + name != "__new__" and + not this.getOrigin() instanceof Lambda + ) + } - /** Gets the minimum number of parameters that can be correctly passed to this function */ - abstract int minParameters(); + /** Gets the minimum number of parameters that can be correctly passed to this function */ + abstract int minParameters(); - /** Gets the maximum number of parameters that can be correctly passed to this function */ - abstract int maxParameters(); + /** Gets the maximum number of parameters that can be correctly passed to this function */ + abstract int maxParameters(); - /** Gets a function that this function (directly) calls */ - FunctionObject getACallee() { - exists(ControlFlowNode node | - node.getScope() = this.getFunction() and - result.getACall() = node - ) - } + /** Gets a function that this function (directly) calls */ + FunctionObject getACallee() { + exists(ControlFlowNode node | + node.getScope() = this.getFunction() and + result.getACall() = node + ) + } - /** - * Gets the qualified name for this function object. - * Should return the same name as the `__qualname__` attribute on functions in Python 3. - */ - abstract string getQualifiedName(); + /** + * Gets the qualified name for this function object. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ + abstract string getQualifiedName(); - /** Whether `name` is a legal argument name for this function */ - bindingset[name] - predicate isLegalArgumentName(string name) { - this.getFunction().getAnArg().asName().getId() = name - or - this.getFunction().getAKeywordOnlyArg().getId() = name - or - this.getFunction().hasKwArg() - } + /** Whether `name` is a legal argument name for this function */ + bindingset[name] + predicate isLegalArgumentName(string name) { + this.getFunction().getAnArg().asName().getId() = name + or + this.getFunction().getAKeywordOnlyArg().getId() = name + or + this.getFunction().hasKwArg() + } - /** Gets a class that this function may return */ - ClassObject getAnInferredReturnType() { result = this.(BuiltinCallable).getAReturnType() } + /** Gets a class that this function may return */ + ClassObject getAnInferredReturnType() { result = this.(BuiltinCallable).getAReturnType() } - predicate isAbstract() { this.getARaisedType() = theNotImplementedErrorType() } + predicate isAbstract() { this.getARaisedType() = theNotImplementedErrorType() } } class PyFunctionObject extends FunctionObject { - PyFunctionObject() { any(PythonFunctionObjectInternal f).getOrigin() = this } + PyFunctionObject() { any(PythonFunctionObjectInternal f).getOrigin() = this } - override string toString() { result = "Function " + this.getName() } + override string toString() { result = "Function " + this.getName() } - override string getName() { - result = this.getOrigin().(FunctionExpr).getName() - or - this.getOrigin() instanceof Lambda and result = "lambda" - } + override string getName() { + result = this.getOrigin().(FunctionExpr).getName() + or + this.getOrigin() instanceof Lambda and result = "lambda" + } - /** Whether this function is a procedure, that is, it has no explicit return statement and is not a generator function */ - override predicate isProcedure() { this.getFunction().isProcedure() } + /** Whether this function is a procedure, that is, it has no explicit return statement and is not a generator function */ + override predicate isProcedure() { this.getFunction().isProcedure() } - override ClassObject getARaisedType() { scope_raises_objectapi(result, this.getFunction()) } + override ClassObject getARaisedType() { scope_raises_objectapi(result, this.getFunction()) } - override predicate raisesUnknownType() { scope_raises_unknown(this.getFunction()) } + override predicate raisesUnknownType() { scope_raises_unknown(this.getFunction()) } - /** Gets a control flow node corresponding to the value of a return statement */ - ControlFlowNode getAReturnedNode() { result = this.getFunction().getAReturnValueFlowNode() } + /** Gets a control flow node corresponding to the value of a return statement */ + ControlFlowNode getAReturnedNode() { result = this.getFunction().getAReturnValueFlowNode() } - override string descriptiveString() { - if this.getFunction().isMethod() - then - exists(Class cls | this.getFunction().getScope() = cls | - result = "method " + this.getQualifiedName() - ) - else result = "function " + this.getQualifiedName() - } + override string descriptiveString() { + if this.getFunction().isMethod() + then + exists(Class cls | this.getFunction().getScope() = cls | + result = "method " + this.getQualifiedName() + ) + else result = "function " + this.getQualifiedName() + } - override int minParameters() { - exists(Function f | - f = this.getFunction() and - result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) - ) - } + override int minParameters() { + exists(Function f | + f = this.getFunction() and + result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) + ) + } - override int maxParameters() { - exists(Function f | - f = this.getFunction() and - if exists(f.getVararg()) - then result = 2147483647 // INT_MAX - else result = count(f.getAnArg()) - ) - } + override int maxParameters() { + exists(Function f | + f = this.getFunction() and + if exists(f.getVararg()) + then result = 2147483647 // INT_MAX + else result = count(f.getAnArg()) + ) + } - override string getQualifiedName() { result = this.getFunction().getQualifiedName() } + override string getQualifiedName() { result = this.getFunction().getQualifiedName() } - predicate unconditionallyReturnsParameter(int n) { - exists(SsaVariable pvar | - exists(Parameter p | p = this.getFunction().getArg(n) | - p.asName().getAFlowNode() = pvar.getDefinition() - ) and - exists(NameNode rval | - rval = pvar.getAUse() and - exists(Return r | r.getValue() = rval.getNode()) and - rval.strictlyDominates(rval.getScope().getANormalExit()) - ) - ) - } + predicate unconditionallyReturnsParameter(int n) { + exists(SsaVariable pvar | + exists(Parameter p | p = this.getFunction().getArg(n) | + p.asName().getAFlowNode() = pvar.getDefinition() + ) and + exists(NameNode rval | + rval = pvar.getAUse() and + exists(Return r | r.getValue() = rval.getNode()) and + rval.strictlyDominates(rval.getScope().getANormalExit()) + ) + ) + } - /** Factored out to help join ordering */ - private predicate implicitlyReturns(Object none_, ClassObject noneType) { - noneType = theNoneType() and - not this.getFunction().isGenerator() and - none_ = theNoneObject() and - ( - not exists(this.getAReturnedNode()) and exists(this.getFunction().getANormalExit()) - or - exists(Return ret | ret.getScope() = this.getFunction() and not exists(ret.getValue())) - ) - } + /** Factored out to help join ordering */ + private predicate implicitlyReturns(Object none_, ClassObject noneType) { + noneType = theNoneType() and + not this.getFunction().isGenerator() and + none_ = theNoneObject() and + ( + not exists(this.getAReturnedNode()) and exists(this.getFunction().getANormalExit()) + or + exists(Return ret | ret.getScope() = this.getFunction() and not exists(ret.getValue())) + ) + } - /** Gets a class that this function may return */ - override ClassObject getAnInferredReturnType() { - this.getFunction().isGenerator() and result = theGeneratorType() - or - not this.neverReturns() and - not this.getFunction().isGenerator() and - ( - this.(PyFunctionObject).getAReturnedNode().refersTo(_, result, _) - or - this.implicitlyReturns(_, result) - ) - } + /** Gets a class that this function may return */ + override ClassObject getAnInferredReturnType() { + this.getFunction().isGenerator() and result = theGeneratorType() + or + not this.neverReturns() and + not this.getFunction().isGenerator() and + ( + this.(PyFunctionObject).getAReturnedNode().refersTo(_, result, _) + or + this.implicitlyReturns(_, result) + ) + } - ParameterDefinition getParameter(int n) { - result.getDefiningNode().getNode() = this.getFunction().getArg(n) - } + ParameterDefinition getParameter(int n) { + result.getDefiningNode().getNode() = this.getFunction().getArg(n) + } } abstract class BuiltinCallable extends FunctionObject { - abstract ClassObject getAReturnType(); + abstract ClassObject getAReturnType(); - override predicate isProcedure() { - forex(ClassObject rt | rt = this.getAReturnType() | rt = theNoneType()) - } + override predicate isProcedure() { + forex(ClassObject rt | rt = this.getAReturnType() | rt = theNoneType()) + } - abstract override string getQualifiedName(); + abstract override string getQualifiedName(); - override ControlFlowNode getArgumentForCall(CallNode call, int n) { - call = this.getACall() and result = call.getArg(n) - } + override ControlFlowNode getArgumentForCall(CallNode call, int n) { + call = this.getACall() and result = call.getArg(n) + } } class BuiltinMethodObject extends BuiltinCallable { - BuiltinMethodObject() { any(BuiltinMethodObjectInternal m).getBuiltin() = this } + BuiltinMethodObject() { any(BuiltinMethodObjectInternal m).getBuiltin() = this } - override string getQualifiedName() { - exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin() | - result = cls.getName() + "." + this.getName() - ) - or - not exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin()) and - result = this.getName() - } + override string getQualifiedName() { + exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin() | + result = cls.getName() + "." + this.getName() + ) + or + not exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin()) and + result = this.getName() + } - override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } + override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - override string toString() { result = "Builtin-method " + this.getName() } + override string toString() { result = "Builtin-method " + this.getName() } - override ClassObject getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassObject getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override predicate raisesUnknownType() { - /* Information is unavailable for C code in general */ - any() - } + override predicate raisesUnknownType() { + /* Information is unavailable for C code in general */ + any() + } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } - override ClassObject getAReturnType() { ext_rettype(this.asBuiltin(), result.asBuiltin()) } + override ClassObject getAReturnType() { ext_rettype(this.asBuiltin(), result.asBuiltin()) } } class BuiltinFunctionObject extends BuiltinCallable { - BuiltinFunctionObject() { any(BuiltinFunctionObjectInternal f).getBuiltin() = this } + BuiltinFunctionObject() { any(BuiltinFunctionObjectInternal f).getBuiltin() = this } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - override string getQualifiedName() { result = this.getName() } + override string getQualifiedName() { result = this.getName() } - override string toString() { result = "Builtin-function " + this.getName() } + override string toString() { result = "Builtin-function " + this.getName() } - override string descriptiveString() { result = "builtin-function " + this.getName() } + override string descriptiveString() { result = "builtin-function " + this.getName() } - override ClassObject getARaisedType() { - /* Information is unavailable for C code in general */ - none() - } + override ClassObject getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } - override predicate raisesUnknownType() { - /* Information is unavailable for C code in general */ - any() - } + override predicate raisesUnknownType() { + /* Information is unavailable for C code in general */ + any() + } - override ClassObject getAReturnType() { - /* - * Enumerate the types of a few builtin functions, that the CPython analysis misses. - */ + override ClassObject getAReturnType() { + /* + * Enumerate the types of a few builtin functions, that the CPython analysis misses. + */ - this = Object::builtin("hex") and result = theStrType() - or - this = Object::builtin("oct") and result = theStrType() - or - this = Object::builtin("intern") and result = theStrType() - or - /* Fix a few minor inaccuracies in the CPython analysis */ - ext_rettype(this.asBuiltin(), result.asBuiltin()) and - not ( - this = Object::builtin("__import__") and result = theNoneType() - or - this = Object::builtin("compile") and result = theNoneType() - or - this = Object::builtin("sum") - or - this = Object::builtin("filter") - ) - } + this = Object::builtin("hex") and result = theStrType() + or + this = Object::builtin("oct") and result = theStrType() + or + this = Object::builtin("intern") and result = theStrType() + or + /* Fix a few minor inaccuracies in the CPython analysis */ + ext_rettype(this.asBuiltin(), result.asBuiltin()) and + not ( + this = Object::builtin("__import__") and result = theNoneType() + or + this = Object::builtin("compile") and result = theNoneType() + or + this = Object::builtin("sum") + or + this = Object::builtin("filter") + ) + } - override int minParameters() { none() } + override int minParameters() { none() } - override int maxParameters() { none() } + override int maxParameters() { none() } } /** DEPRECATED -- Use `Object::builtin("apply")` instead. */ diff --git a/python/ql/src/semmle/python/types/ImportTime.qll b/python/ql/src/semmle/python/types/ImportTime.qll index a35ed9d122a..a520f84dcb7 100644 --- a/python/ql/src/semmle/python/types/ImportTime.qll +++ b/python/ql/src/semmle/python/types/ImportTime.qll @@ -7,28 +7,28 @@ import python * This is an artificial approximation, which is necessary for static analysis. */ class ImportTimeScope extends Scope { - ImportTimeScope() { not this.getEnclosingScope*() instanceof Function } + ImportTimeScope() { not this.getEnclosingScope*() instanceof Function } - /** - * Whether this scope explicitly defines 'name'. - * Does not cover implicit definitions be import * - */ - pragma[nomagic] - predicate definesName(string name) { - exists(SsaVariable var | name = var.getId() and var.getAUse() = this.getANormalExit()) - } + /** + * Whether this scope explicitly defines 'name'. + * Does not cover implicit definitions be import * + */ + pragma[nomagic] + predicate definesName(string name) { + exists(SsaVariable var | name = var.getId() and var.getAUse() = this.getANormalExit()) + } - /** Holds if the control flow passes from `outer` to `inner` when this scope starts executing */ - predicate entryEdge(ControlFlowNode outer, ControlFlowNode inner) { - inner = this.getEntryNode() and - outer.getNode().(ClassExpr).getInnerScope() = this - } + /** Holds if the control flow passes from `outer` to `inner` when this scope starts executing */ + predicate entryEdge(ControlFlowNode outer, ControlFlowNode inner) { + inner = this.getEntryNode() and + outer.getNode().(ClassExpr).getInnerScope() = this + } - /** Gets the global variable that is used during lookup, should `var` be undefined. */ - GlobalVariable getOuterVariable(LocalVariable var) { - this instanceof Class and - var.getScope() = this and - result.getScope() = this.getEnclosingModule() and - var.getId() = result.getId() - } + /** Gets the global variable that is used during lookup, should `var` be undefined. */ + GlobalVariable getOuterVariable(LocalVariable var) { + this instanceof Class and + var.getScope() = this and + result.getScope() = this.getEnclosingModule() and + var.getId() = result.getId() + } } diff --git a/python/ql/src/semmle/python/types/ModuleKind.qll b/python/ql/src/semmle/python/types/ModuleKind.qll index 1509bac24e2..edb582b3627 100644 --- a/python/ql/src/semmle/python/types/ModuleKind.qll +++ b/python/ql/src/semmle/python/types/ModuleKind.qll @@ -1,34 +1,34 @@ import python private predicate is_normal_module(ModuleObject m) { - m instanceof BuiltinModuleObject - or - m instanceof PackageObject - or - exists(ImportingStmt i | m.importedAs(i.getAnImportedModuleName())) - or - m.getName().matches("%\\_\\_init\\_\\_") + m instanceof BuiltinModuleObject + or + m instanceof PackageObject + or + exists(ImportingStmt i | m.importedAs(i.getAnImportedModuleName())) + or + m.getName().matches("%\\_\\_init\\_\\_") } private predicate is_script(ModuleObject m) { - not is_normal_module(m) and - ( - m.getModule().getFile().getExtension() != ".py" - or - exists(If i, Name name, StrConst main, Cmpop op | - i.getScope() = m.getModule() and - op instanceof Eq and - i.getTest().(Compare).compares(name, op, main) and - name.getId() = "__name__" and - main.getText() = "__main__" - ) + not is_normal_module(m) and + ( + m.getModule().getFile().getExtension() != ".py" + or + exists(If i, Name name, StrConst main, Cmpop op | + i.getScope() = m.getModule() and + op instanceof Eq and + i.getTest().(Compare).compares(name, op, main) and + name.getId() = "__name__" and + main.getText() = "__main__" ) + ) } private predicate is_plugin(ModuleObject m) { - // This needs refining but is sufficient for our present needs. - not is_normal_module(m) and - not is_script(m) + // This needs refining but is sufficient for our present needs. + not is_normal_module(m) and + not is_script(m) } /** @@ -36,9 +36,9 @@ private predicate is_plugin(ModuleObject m) { * "module", "script" or "plugin" */ string getKindForModule(ModuleObject m) { - is_normal_module(m) and result = "module" - or - is_script(m) and result = "script" - or - is_plugin(m) and result = "plugin" + is_normal_module(m) and result = "module" + or + is_script(m) and result = "script" + or + is_plugin(m) and result = "plugin" } diff --git a/python/ql/src/semmle/python/types/ModuleObject.qll b/python/ql/src/semmle/python/types/ModuleObject.qll index 644d4e60244..ea62c57fff0 100644 --- a/python/ql/src/semmle/python/types/ModuleObject.qll +++ b/python/ql/src/semmle/python/types/ModuleObject.qll @@ -4,145 +4,145 @@ private import semmle.python.objects.ObjectInternal private import semmle.python.types.ModuleKind abstract class ModuleObject extends Object { - ModuleValue theModule() { - result.(PythonModuleObjectInternal).getSourceModule() = this.getModule() - or - result.(PackageObjectInternal).getFolder() = this.(PackageObject).getPath() - or - result.(BuiltinModuleObjectInternal).getBuiltin() = this - } + ModuleValue theModule() { + result.(PythonModuleObjectInternal).getSourceModule() = this.getModule() + or + result.(PackageObjectInternal).getFolder() = this.(PackageObject).getPath() + or + result.(BuiltinModuleObjectInternal).getBuiltin() = this + } - /** Gets the scope corresponding to this module, if this is a Python module */ - Module getModule() { none() } + /** Gets the scope corresponding to this module, if this is a Python module */ + Module getModule() { none() } - /** Gets the source scope corresponding to this module, if this is a Python module */ - Module getSourceModule() { none() } + /** Gets the source scope corresponding to this module, if this is a Python module */ + Module getSourceModule() { none() } - Container getPath() { none() } + Container getPath() { none() } - /** Gets the name of this scope */ - abstract string getName(); + /** Gets the name of this scope */ + abstract string getName(); - override string toString() { - result = "Module " + this.getName() - or - not exists(this.getName()) and - result = this.getModule().toString() - } + override string toString() { + result = "Module " + this.getName() + or + not exists(this.getName()) and + result = this.getModule().toString() + } - /** - * Gets the named attribute of this module. Using attributeRefersTo() instead - * may provide better results for presentation. - */ - Object getAttribute(string name) { this.attributeRefersTo(name, result, _) } + /** + * Gets the named attribute of this module. Using attributeRefersTo() instead + * may provide better results for presentation. + */ + Object getAttribute(string name) { this.attributeRefersTo(name, result, _) } - /** - * Gets the named attribute of this module. - * Synonym for `getAttribute(name)` - */ - pragma[inline] - final Object attr(string name) { result = this.getAttribute(name) } + /** + * Gets the named attribute of this module. + * Synonym for `getAttribute(name)` + */ + pragma[inline] + final Object attr(string name) { result = this.getAttribute(name) } - predicate hasAttribute(string name) { theModule().hasAttribute(name) } + predicate hasAttribute(string name) { theModule().hasAttribute(name) } - predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { - exists(ObjectInternal val, CfgOrigin valorig | - theModule().(ModuleObjectInternal).attribute(name, val, valorig) and - obj = val.getSource() and - origin = valorig.toCfgNode() - ) - } + predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { + exists(ObjectInternal val, CfgOrigin valorig | + theModule().(ModuleObjectInternal).attribute(name, val, valorig) and + obj = val.getSource() and + origin = valorig.toCfgNode() + ) + } - predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { - exists(ObjectInternal val, CfgOrigin valorig | - theModule().(ModuleObjectInternal).attribute(name, val, valorig) and - obj = val.getSource() and - cls = val.getClass().getSource() and - origin = valorig.toCfgNode() - ) - } + predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { + exists(ObjectInternal val, CfgOrigin valorig | + theModule().(ModuleObjectInternal).attribute(name, val, valorig) and + obj = val.getSource() and + cls = val.getClass().getSource() and + origin = valorig.toCfgNode() + ) + } - /** Gets the package for this module. */ - PackageObject getPackage() { - this.getName().matches("%.%") and - result.getName() = this.getName().regexpReplaceAll("\\.[^.]*$", "") - } + /** Gets the package for this module. */ + PackageObject getPackage() { + this.getName().matches("%.%") and + result.getName() = this.getName().regexpReplaceAll("\\.[^.]*$", "") + } - /** - * Whether this module "exports" `name`. That is, whether using `import *` on this module - * will result in `name` being added to the namespace. - */ - predicate exports(string name) { theModule().exports(name) } + /** + * Whether this module "exports" `name`. That is, whether using `import *` on this module + * will result in `name` being added to the namespace. + */ + predicate exports(string name) { theModule().exports(name) } - /** - * Whether the complete set of names "exported" by this module can be accurately determined - * - * DEPRECATED: Use ModuleValue::hasCompleteExportInfo instead - */ - abstract deprecated predicate exportsComplete(); + /** + * Whether the complete set of names "exported" by this module can be accurately determined + * + * DEPRECATED: Use ModuleValue::hasCompleteExportInfo instead + */ + abstract deprecated predicate exportsComplete(); - /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ - string getShortName() { - result = this.getName().suffix(this.getPackage().getName().length() + 1) - or - result = this.getName() and not exists(this.getPackage()) - } + /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ + string getShortName() { + result = this.getName().suffix(this.getPackage().getName().length() + 1) + or + result = this.getName() and not exists(this.getPackage()) + } - /** - * Whether this module is imported by 'import name'. For example on a linux system, - * the module 'posixpath' is imported as 'os.path' or as 'posixpath' - */ - predicate importedAs(string name) { PointsToInternal::module_imported_as(theModule(), name) } + /** + * Whether this module is imported by 'import name'. For example on a linux system, + * the module 'posixpath' is imported as 'os.path' or as 'posixpath' + */ + predicate importedAs(string name) { PointsToInternal::module_imported_as(theModule(), name) } - ModuleObject getAnImportedModule() { - result.importedAs(this.getModule().getAnImportedModuleName()) - } + ModuleObject getAnImportedModule() { + result.importedAs(this.getModule().getAnImportedModuleName()) + } - /** - * Gets the kind for this module. Will be one of - * "module", "script" or "plugin". - */ - string getKind() { result = getKindForModule(this) } + /** + * Gets the kind for this module. Will be one of + * "module", "script" or "plugin". + */ + string getKind() { result = getKindForModule(this) } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } } class BuiltinModuleObject extends ModuleObject { - BuiltinModuleObject() { this.asBuiltin().getClass() = theModuleType().asBuiltin() } + BuiltinModuleObject() { this.asBuiltin().getClass() = theModuleType().asBuiltin() } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - override Object getAttribute(string name) { - result.asBuiltin() = this.asBuiltin().getMember(name) - } + override Object getAttribute(string name) { + result.asBuiltin() = this.asBuiltin().getMember(name) + } - override predicate hasAttribute(string name) { exists(this.asBuiltin().getMember(name)) } + override predicate hasAttribute(string name) { exists(this.asBuiltin().getMember(name)) } - deprecated override predicate exportsComplete() { any() } + deprecated override predicate exportsComplete() { any() } } class PythonModuleObject extends ModuleObject { - PythonModuleObject() { exists(Module m | m.getEntryNode() = this | not m.isPackage()) } + PythonModuleObject() { exists(Module m | m.getEntryNode() = this | not m.isPackage()) } - override string getName() { result = this.getModule().getName() } + override string getName() { result = this.getModule().getName() } - override Module getModule() { result = this.getOrigin() } + override Module getModule() { result = this.getOrigin() } - override Module getSourceModule() { result = this.getOrigin() } + override Module getSourceModule() { result = this.getOrigin() } - override Container getPath() { result = this.getModule().getFile() } + override Container getPath() { result = this.getModule().getFile() } - deprecated override predicate exportsComplete() { - exists(Module m | m = this.getModule() | - not exists(Call modify, Attribute attr, GlobalVariable all | - modify.getScope() = m and - modify.getFunc() = attr and - all.getId() = "__all__" - | - attr.getObject().(Name).uses(all) - ) - ) - } + deprecated override predicate exportsComplete() { + exists(Module m | m = this.getModule() | + not exists(Call modify, Attribute attr, GlobalVariable all | + modify.getScope() = m and + modify.getFunc() = attr and + all.getId() = "__all__" + | + attr.getObject().(Name).uses(all) + ) + ) + } } /** @@ -153,74 +153,74 @@ class PythonModuleObject extends ModuleObject { * for each module name, with the name b'text' or u'text' (including the quotes). */ Object object_for_string(string text) { - result.asBuiltin().getClass() = theStrType().asBuiltin() and - exists(string repr | - repr = result.asBuiltin().getName() and - repr.charAt(1) = "'" - | - /* Strip quotes off repr */ - text = repr.substring(2, repr.length() - 1) - ) + result.asBuiltin().getClass() = theStrType().asBuiltin() and + exists(string repr | + repr = result.asBuiltin().getName() and + repr.charAt(1) = "'" + | + /* Strip quotes off repr */ + text = repr.substring(2, repr.length() - 1) + ) } class PackageObject extends ModuleObject { - PackageObject() { exists(Module p | p.getEntryNode() = this | p.isPackage()) } + PackageObject() { exists(Module p | p.getEntryNode() = this | p.isPackage()) } - override string getName() { result = this.getModule().getName() } + override string getName() { result = this.getModule().getName() } - override Module getModule() { result = this.getOrigin() } + override Module getModule() { result = this.getOrigin() } - override Module getSourceModule() { result = this.getModule().getInitModule() } + override Module getSourceModule() { result = this.getModule().getInitModule() } - override Container getPath() { result = this.getModule().getPath() } + override Container getPath() { result = this.getModule().getPath() } - ModuleObject submodule(string name) { - result.getPackage() = this and - name = result.getShortName() - } + ModuleObject submodule(string name) { + result.getPackage() = this and + name = result.getShortName() + } - override Object getAttribute(string name) { - exists(ObjectInternal val | - theModule().(PackageObjectInternal).attribute(name, val, _) and - result = val.getSource() - ) - } + override Object getAttribute(string name) { + exists(ObjectInternal val | + theModule().(PackageObjectInternal).attribute(name, val, _) and + result = val.getSource() + ) + } - PythonModuleObject getInitModule() { result.getModule() = this.getModule().getInitModule() } + PythonModuleObject getInitModule() { result.getModule() = this.getModule().getInitModule() } - /** Holds if this package has no `__init__.py` file. */ - predicate hasNoInitModule() { - not exists(Module m | - m.isPackageInit() and - m.getFile().getParent() = this.getPath() - ) - } + /** Holds if this package has no `__init__.py` file. */ + predicate hasNoInitModule() { + not exists(Module m | + m.isPackageInit() and + m.getFile().getParent() = this.getPath() + ) + } - deprecated override predicate exportsComplete() { - not exists(this.getInitModule()) - or - this.getInitModule().exportsComplete() - } + deprecated override predicate exportsComplete() { + not exists(this.getInitModule()) + or + this.getInitModule().exportsComplete() + } - override predicate hasAttribute(string name) { - exists(this.submodule(name)) - or - this.getInitModule().hasAttribute(name) - } + override predicate hasAttribute(string name) { + exists(this.submodule(name)) + or + this.getInitModule().hasAttribute(name) + } - Location getLocation() { none() } + Location getLocation() { none() } - override predicate hasLocationInfo(string path, int bl, int bc, int el, int ec) { - path = this.getPath().getName() and - bl = 0 and - bc = 0 and - el = 0 and - ec = 0 - } + override predicate hasLocationInfo(string path, int bl, int bc, int el, int ec) { + path = this.getPath().getName() and + bl = 0 and + bc = 0 and + el = 0 and + ec = 0 + } } /** Utility module for predicates relevant to the `ModuleObject` class. */ module ModuleObject { - /** Gets a `ModuleObject` called `name`, if it exists. */ - ModuleObject named(string name) { result.getName() = name } + /** Gets a `ModuleObject` called `name`, if it exists. */ + ModuleObject named(string name) { result.getName() = name } } diff --git a/python/ql/src/semmle/python/types/Object.qll b/python/ql/src/semmle/python/types/Object.qll index 0bfc7dd0059..7f6bc9f0e36 100644 --- a/python/ql/src/semmle/python/types/Object.qll +++ b/python/ql/src/semmle/python/types/Object.qll @@ -5,12 +5,12 @@ private import semmle.python.types.Builtins cached private predicate is_an_object(@py_object obj) { - /* CFG nodes for numeric literals, all of which have a @py_cobject for the value of that literal */ - obj instanceof ControlFlowNode and - not obj.(ControlFlowNode).getNode() instanceof IntegerLiteral and - not obj.(ControlFlowNode).getNode() instanceof StrConst - or - obj instanceof Builtin + /* CFG nodes for numeric literals, all of which have a @py_cobject for the value of that literal */ + obj instanceof ControlFlowNode and + not obj.(ControlFlowNode).getNode() instanceof IntegerLiteral and + not obj.(ControlFlowNode).getNode() instanceof StrConst + or + obj instanceof Builtin } /** @@ -30,186 +30,191 @@ private predicate is_an_object(@py_object obj) { * there is a one-to-one relation. */ class Object extends @py_object { - Object() { is_an_object(this) } + Object() { is_an_object(this) } - /** - * Gets an inferred type for this object, without using inter-procedural analysis. - * WARNING: The lack of context makes this less accurate than f.refersTo(this, result, _) - * for a control flow node 'f' - */ - ClassObject getAnInferredType() { - exists(ControlFlowNode somewhere | somewhere.refersTo(this, result, _)) - or - this.asBuiltin().getClass() = result.asBuiltin() and not this = unknownValue() - or - this = unknownValue() and result = theUnknownType() - } + /** + * Gets an inferred type for this object, without using inter-procedural analysis. + * WARNING: The lack of context makes this less accurate than f.refersTo(this, result, _) + * for a control flow node 'f' + */ + ClassObject getAnInferredType() { + exists(ControlFlowNode somewhere | somewhere.refersTo(this, result, _)) + or + this.asBuiltin().getClass() = result.asBuiltin() and not this = unknownValue() + or + this = unknownValue() and result = theUnknownType() + } - /** - * Whether this is a builtin object. A builtin object is one defined by the implementation, - * such as the integer 4 or by a native extension, such as a NumPy array class. - */ - predicate isBuiltin() { exists(this.asBuiltin()) } + /** + * Whether this is a builtin object. A builtin object is one defined by the implementation, + * such as the integer 4 or by a native extension, such as a NumPy array class. + */ + predicate isBuiltin() { exists(this.asBuiltin()) } - /** Retained for backwards compatibility. See Object.isBuiltin() */ - predicate isC() { this.isBuiltin() } + /** Retained for backwards compatibility. See Object.isBuiltin() */ + predicate isC() { this.isBuiltin() } - /** - * Gets the point in the source code from which this object "originates". - * - * WARNING: The lack of context makes this less accurate than f.refersTo(this, _, result) - * for a control flow node 'f'. - */ - AstNode getOrigin() { py_flow_bb_node(this, result, _, _) } + /** + * Gets the point in the source code from which this object "originates". + * + * WARNING: The lack of context makes this less accurate than f.refersTo(this, _, result) + * for a control flow node 'f'. + */ + AstNode getOrigin() { py_flow_bb_node(this, result, _, _) } - private predicate hasOrigin() { py_flow_bb_node(this, _, _, _) } + private predicate hasOrigin() { py_flow_bb_node(this, _, _, _) } - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). - */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { this.hasOrigin() and this.getOrigin().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - not this.hasOrigin() and - filepath = ":Compiled Code" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.hasOrigin() and + this + .getOrigin() + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + or + not this.hasOrigin() and + filepath = ":Compiled Code" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } - /** INTERNAL -- Do not use */ - Builtin asBuiltin() { result = this } + /** INTERNAL -- Do not use */ + Builtin asBuiltin() { result = this } - /** Gets a textual representation of this element. */ - string toString() { - not this = undefinedVariable() and - not this = unknownValue() and - exists(ClassObject type | type.asBuiltin() = this.asBuiltin().getClass() | - result = type.getName() + " " + this.asBuiltin().getName() - ) - or - result = this.getOrigin().toString() - } + /** Gets a textual representation of this element. */ + string toString() { + not this = undefinedVariable() and + not this = unknownValue() and + exists(ClassObject type | type.asBuiltin() = this.asBuiltin().getClass() | + result = type.getName() + " " + this.asBuiltin().getName() + ) + or + result = this.getOrigin().toString() + } - /** - * Gets the class of this object for simple cases, namely constants, functions, - * comprehensions and built-in objects. - * - * This exists primarily for internal use. Use getAnInferredType() instead. - */ - cached - ClassObject simpleClass() { - result = comprehension(this.getOrigin()) - or - result = collection_literal(this.getOrigin()) - or - result = string_literal(this.getOrigin()) - or - this.getOrigin() instanceof CallableExpr and result = thePyFunctionType() - or - this.getOrigin() instanceof Module and result = theModuleType() - or - result.(Object).asBuiltin() = this.asBuiltin().getClass() - } + /** + * Gets the class of this object for simple cases, namely constants, functions, + * comprehensions and built-in objects. + * + * This exists primarily for internal use. Use getAnInferredType() instead. + */ + cached + ClassObject simpleClass() { + result = comprehension(this.getOrigin()) + or + result = collection_literal(this.getOrigin()) + or + result = string_literal(this.getOrigin()) + or + this.getOrigin() instanceof CallableExpr and result = thePyFunctionType() + or + this.getOrigin() instanceof Module and result = theModuleType() + or + result.(Object).asBuiltin() = this.asBuiltin().getClass() + } - private ClassObject declaringClass(string name) { result.declaredAttribute(name) = this } + private ClassObject declaringClass(string name) { result.declaredAttribute(name) = this } - /** - * Whether this overrides o. In this context, "overrides" means that this object - * is a named attribute of a some class C and `o` is a named attribute of another - * class S, both attributes having the same name, and S is a super class of C. - */ - predicate overrides(Object o) { - exists(string name | declaringClass(name).getASuperType() = o.declaringClass(name)) - } + /** + * Whether this overrides o. In this context, "overrides" means that this object + * is a named attribute of a some class C and `o` is a named attribute of another + * class S, both attributes having the same name, and S is a super class of C. + */ + predicate overrides(Object o) { + exists(string name | declaringClass(name).getASuperType() = o.declaringClass(name)) + } - private boolean booleanFromValue() { - exists(ObjectInternal obj | obj.getSource() = this | result = obj.booleanValue()) - } + private boolean booleanFromValue() { + exists(ObjectInternal obj | obj.getSource() = this | result = obj.booleanValue()) + } - /** - * The Boolean value of this object if it always evaluates to true or false. - * For example: - * false for None, true for 7 and no result for int(x) - */ - boolean booleanValue() { - result = this.booleanFromValue() and - not this.maybe() - } + /** + * The Boolean value of this object if it always evaluates to true or false. + * For example: + * false for None, true for 7 and no result for int(x) + */ + boolean booleanValue() { + result = this.booleanFromValue() and + not this.maybe() + } - final predicate maybe() { - booleanFromValue() = true and - booleanFromValue() = false - } + final predicate maybe() { + booleanFromValue() = true and + booleanFromValue() = false + } - predicate notClass() { any() } + predicate notClass() { any() } - /** - * Holds if this object can be referred to by `longName` - * For example, the modules `dict` in the `sys` module - * has the long name `sys.modules` and the name `os.path.join` - * will refer to the path joining function even though it might - * be declared in the `posix` or `nt` modules. - * Long names can have no more than three dots after the module name. - */ - cached - predicate hasLongName(string longName) { - this = findByName0(longName) - or - this = findByName1(longName) - or - this = findByName2(longName) - or - this = findByName3(longName) - or - exists(ClassMethodObject cm | - cm.hasLongName(longName) and - cm.getFunction() = this - ) - or - exists(StaticMethodObject cm | - cm.hasLongName(longName) and - cm.getFunction() = this - ) - } + /** + * Holds if this object can be referred to by `longName` + * For example, the modules `dict` in the `sys` module + * has the long name `sys.modules` and the name `os.path.join` + * will refer to the path joining function even though it might + * be declared in the `posix` or `nt` modules. + * Long names can have no more than three dots after the module name. + */ + cached + predicate hasLongName(string longName) { + this = findByName0(longName) + or + this = findByName1(longName) + or + this = findByName2(longName) + or + this = findByName3(longName) + or + exists(ClassMethodObject cm | + cm.hasLongName(longName) and + cm.getFunction() = this + ) + or + exists(StaticMethodObject cm | + cm.hasLongName(longName) and + cm.getFunction() = this + ) + } } private Object findByName0(string longName) { result.(ModuleObject).getName() = longName } private Object findByName1(string longName) { - exists(string owner, string attrname | longName = owner + "." + attrname | - result = findByName0(owner).(ModuleObject).attr(attrname) - or - result = findByName0(owner).(ClassObject).lookupAttribute(attrname) - ) and - not result = findByName0(_) + exists(string owner, string attrname | longName = owner + "." + attrname | + result = findByName0(owner).(ModuleObject).attr(attrname) + or + result = findByName0(owner).(ClassObject).lookupAttribute(attrname) + ) and + not result = findByName0(_) } private Object findByName2(string longName) { - exists(string owner, string attrname | longName = owner + "." + attrname | - result = findByName1(owner).(ModuleObject).attr(attrname) - or - result = findByName1(owner).(ClassObject).lookupAttribute(attrname) - ) and - not result = findByName0(_) and - not result = findByName1(_) + exists(string owner, string attrname | longName = owner + "." + attrname | + result = findByName1(owner).(ModuleObject).attr(attrname) + or + result = findByName1(owner).(ClassObject).lookupAttribute(attrname) + ) and + not result = findByName0(_) and + not result = findByName1(_) } private Object findByName3(string longName) { - exists(string owner, string attrname | longName = owner + "." + attrname | - result = findByName2(owner).(ModuleObject).attr(attrname) - or - result = findByName2(owner).(ClassObject).lookupAttribute(attrname) - ) and - not result = findByName0(_) and - not result = findByName1(_) and - not result = findByName2(_) + exists(string owner, string attrname | longName = owner + "." + attrname | + result = findByName2(owner).(ModuleObject).attr(attrname) + or + result = findByName2(owner).(ClassObject).lookupAttribute(attrname) + ) and + not result = findByName0(_) and + not result = findByName1(_) and + not result = findByName2(_) } /** @@ -218,50 +223,50 @@ private Object findByName3(string longName) { * or in a builtin module as a value. */ class NumericObject extends Object { - NumericObject() { - this.asBuiltin().getClass() = theIntType().asBuiltin() or - this.asBuiltin().getClass() = theLongType().asBuiltin() or - this.asBuiltin().getClass() = theFloatType().asBuiltin() - } + NumericObject() { + this.asBuiltin().getClass() = theIntType().asBuiltin() or + this.asBuiltin().getClass() = theLongType().asBuiltin() or + this.asBuiltin().getClass() = theFloatType().asBuiltin() + } - /** - * Gets the Boolean value that this object - * would evaluate to in a Boolean context, - * such as `bool(x)` or `if x: ...` - */ - override boolean booleanValue() { - this.intValue() != 0 and result = true - or - this.intValue() = 0 and result = false - or - this.floatValue() != 0 and result = true - or - this.floatValue() = 0 and result = false - } + /** + * Gets the Boolean value that this object + * would evaluate to in a Boolean context, + * such as `bool(x)` or `if x: ...` + */ + override boolean booleanValue() { + this.intValue() != 0 and result = true + or + this.intValue() = 0 and result = false + or + this.floatValue() != 0 and result = true + or + this.floatValue() = 0 and result = false + } - /** Gets the value of this object if it is a constant integer and it fits in a QL int */ - int intValue() { - ( - this.asBuiltin().getClass() = theIntType().asBuiltin() or - this.asBuiltin().getClass() = theLongType().asBuiltin() - ) and - result = this.asBuiltin().getName().toInt() - } + /** Gets the value of this object if it is a constant integer and it fits in a QL int */ + int intValue() { + ( + this.asBuiltin().getClass() = theIntType().asBuiltin() or + this.asBuiltin().getClass() = theLongType().asBuiltin() + ) and + result = this.asBuiltin().getName().toInt() + } - /** Gets the value of this object if it is a constant float */ - float floatValue() { - this.asBuiltin().getClass() = theFloatType().asBuiltin() and - result = this.asBuiltin().getName().toFloat() - } + /** Gets the value of this object if it is a constant float */ + float floatValue() { + this.asBuiltin().getClass() = theFloatType().asBuiltin() and + result = this.asBuiltin().getName().toFloat() + } - /** Gets the string representation of this object, equivalent to calling repr() in Python */ - string repr() { - exists(string s | s = this.asBuiltin().getName() | - if this.asBuiltin().getClass() = theLongType().asBuiltin() - then result = s + "L" - else result = s - ) - } + /** Gets the string representation of this object, equivalent to calling repr() in Python */ + string repr() { + exists(string s | s = this.asBuiltin().getName() | + if this.asBuiltin().getClass() = theLongType().asBuiltin() + then result = s + "L" + else result = s + ) + } } /** @@ -270,28 +275,28 @@ class NumericObject extends Object { * or in a builtin module as a value. */ class StringObject extends Object { - StringObject() { - this.asBuiltin().getClass() = theUnicodeType().asBuiltin() or - this.asBuiltin().getClass() = theBytesType().asBuiltin() - } + StringObject() { + this.asBuiltin().getClass() = theUnicodeType().asBuiltin() or + this.asBuiltin().getClass() = theBytesType().asBuiltin() + } - /** Whether this string is composed entirely of ascii encodable characters */ - predicate isAscii() { this.getText().regexpMatch("^\\p{ASCII}*$") } + /** Whether this string is composed entirely of ascii encodable characters */ + predicate isAscii() { this.getText().regexpMatch("^\\p{ASCII}*$") } - override boolean booleanValue() { - this.getText() = "" and result = false - or - this.getText() != "" and result = true - } + override boolean booleanValue() { + this.getText() = "" and result = false + or + this.getText() != "" and result = true + } - /** Gets the text for this string */ - cached - string getText() { - exists(string quoted_string | - quoted_string = this.asBuiltin().getName() and - result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) - ) - } + /** Gets the text for this string */ + cached + string getText() { + exists(string quoted_string | + quoted_string = this.asBuiltin().getName() and + result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) + ) + } } /** @@ -300,58 +305,58 @@ class StringObject extends Object { * or in a builtin module as a value. */ abstract class SequenceObject extends Object { - /** Gets the length of this sequence */ - int getLength() { - result = strictcount(this.getBuiltinElement(_)) - or - result = strictcount(this.getSourceElement(_)) - } + /** Gets the length of this sequence */ + int getLength() { + result = strictcount(this.getBuiltinElement(_)) + or + result = strictcount(this.getSourceElement(_)) + } - /** Gets the nth item of this builtin sequence */ - Object getBuiltinElement(int n) { result.asBuiltin() = this.asBuiltin().getItem(n) } + /** Gets the nth item of this builtin sequence */ + Object getBuiltinElement(int n) { result.asBuiltin() = this.asBuiltin().getItem(n) } - /** Gets the nth source element of this sequence */ - ControlFlowNode getSourceElement(int n) { result = this.(SequenceNode).getElement(n) } + /** Gets the nth source element of this sequence */ + ControlFlowNode getSourceElement(int n) { result = this.(SequenceNode).getElement(n) } - Object getInferredElement(int n) { - result = this.getBuiltinElement(n) - or - this.getSourceElement(n).refersTo(result) - } + Object getInferredElement(int n) { + result = this.getBuiltinElement(n) + or + this.getSourceElement(n).refersTo(result) + } } class TupleObject extends SequenceObject { - TupleObject() { - this.asBuiltin().getClass() = theTupleType().asBuiltin() - or - this instanceof TupleNode - or - exists(Function func | func.getVararg().getAFlowNode() = this) - } + TupleObject() { + this.asBuiltin().getClass() = theTupleType().asBuiltin() + or + this instanceof TupleNode + or + exists(Function func | func.getVararg().getAFlowNode() = this) + } } module TupleObject { - TupleObject empty() { - exists(Builtin empty | - empty = result.asBuiltin() and - empty.getClass() = theTupleType().asBuiltin() and - not exists(empty.getItem(_)) - ) - } + TupleObject empty() { + exists(Builtin empty | + empty = result.asBuiltin() and + empty.getClass() = theTupleType().asBuiltin() and + not exists(empty.getItem(_)) + ) + } } class NonEmptyTupleObject extends TupleObject { - NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) } + NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) } - override boolean booleanValue() { result = true } + override boolean booleanValue() { result = true } } class ListObject extends SequenceObject { - ListObject() { - this.asBuiltin().getClass() = theListType().asBuiltin() - or - this instanceof ListNode - } + ListObject() { + this.asBuiltin().getClass() = theListType().asBuiltin() + or + this instanceof ListNode + } } /** The `builtin` module */ @@ -394,62 +399,62 @@ deprecated Object theNotImplementedObject() { result = Object::builtin("NotImple deprecated Object theEmptyTupleObject() { result = TupleObject::empty() } module Object { - Object builtin(string name) { result.asBuiltin() = Builtin::builtin(name) } + Object builtin(string name) { result.asBuiltin() = Builtin::builtin(name) } - /** The named quitter object (quit or exit) in the builtin namespace */ - Object quitter(string name) { - (name = "quit" or name = "exit") and - result = builtin(name) - } + /** The named quitter object (quit or exit) in the builtin namespace */ + Object quitter(string name) { + (name = "quit" or name = "exit") and + result = builtin(name) + } - /** The builtin object `NotImplemented`. Not be confused with `NotImplementedError`. */ - Object notImplemented() { result = builtin("NotImplemented") } + /** The builtin object `NotImplemented`. Not be confused with `NotImplementedError`. */ + Object notImplemented() { result = builtin("NotImplemented") } } private ClassObject comprehension(Expr e) { - e instanceof ListComp and result = theListType() - or - e instanceof SetComp and result = theSetType() - or - e instanceof DictComp and result = theDictType() - or - e instanceof GeneratorExp and result = theGeneratorType() + e instanceof ListComp and result = theListType() + or + e instanceof SetComp and result = theSetType() + or + e instanceof DictComp and result = theDictType() + or + e instanceof GeneratorExp and result = theGeneratorType() } private ClassObject collection_literal(Expr e) { - e instanceof List and result = theListType() - or - e instanceof Set and result = theSetType() - or - e instanceof Dict and result = theDictType() - or - e instanceof Tuple and result = theTupleType() + e instanceof List and result = theListType() + or + e instanceof Set and result = theSetType() + or + e instanceof Dict and result = theDictType() + or + e instanceof Tuple and result = theTupleType() } private ClassObject string_literal(Expr e) { - e instanceof Bytes and result = theBytesType() - or - e instanceof Unicode and result = theUnicodeType() + e instanceof Bytes and result = theBytesType() + or + e instanceof Unicode and result = theUnicodeType() } Object theUnknownType() { result.asBuiltin() = Builtin::unknownType() } /* For backwards compatibility */ class SuperBoundMethod extends Object { - string name; + string name; - SuperBoundMethod() { - this.(AttrNode).getObject(name).inferredValue().getClass() = Value::named("super") - } + SuperBoundMethod() { + this.(AttrNode).getObject(name).inferredValue().getClass() = Value::named("super") + } - override string toString() { result = "super()." + name } + override string toString() { result = "super()." + name } - Object getFunction(string fname) { - fname = name and - exists(SuperInstance sup, BoundMethodObjectInternal m | - sup = this.(AttrNode).getObject(name).inferredValue() and - sup.attribute(name, m, _) and - result = m.getFunction().getSource() - ) - } + Object getFunction(string fname) { + fname = name and + exists(SuperInstance sup, BoundMethodObjectInternal m | + sup = this.(AttrNode).getObject(name).inferredValue() and + sup.attribute(name, m, _) and + result = m.getFunction().getSource() + ) + } } diff --git a/python/ql/src/semmle/python/types/Properties.qll b/python/ql/src/semmle/python/types/Properties.qll index 207562c63c6..09bd08b6c15 100644 --- a/python/ql/src/semmle/python/types/Properties.qll +++ b/python/ql/src/semmle/python/types/Properties.qll @@ -9,100 +9,100 @@ import python * Also any instances of types.GetSetDescriptorType (which are equivalent, but implemented in C) */ abstract class PropertyObject extends Object { - PropertyObject() { - property_getter(this, _) - or - this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() - } + PropertyObject() { + property_getter(this, _) + or + this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() + } - /** Gets the name of this property */ - abstract string getName(); + /** Gets the name of this property */ + abstract string getName(); - /** Gets the getter of this property */ - abstract Object getGetter(); + /** Gets the getter of this property */ + abstract Object getGetter(); - /** Gets the setter of this property */ - abstract Object getSetter(); + /** Gets the setter of this property */ + abstract Object getSetter(); - /** Gets the deleter of this property */ - abstract Object getDeleter(); + /** Gets the deleter of this property */ + abstract Object getDeleter(); - override string toString() { result = "Property " + this.getName() } + override string toString() { result = "Property " + this.getName() } - /** Whether this property is read-only. */ - predicate isReadOnly() { not exists(this.getSetter()) } + /** Whether this property is read-only. */ + predicate isReadOnly() { not exists(this.getSetter()) } - /** - * Gets an inferred type of this property. - * That is the type returned by its getter function, - * not the type of the property object which is types.PropertyType. - */ - abstract ClassObject getInferredPropertyType(); + /** + * Gets an inferred type of this property. + * That is the type returned by its getter function, + * not the type of the property object which is types.PropertyType. + */ + abstract ClassObject getInferredPropertyType(); } class PythonPropertyObject extends PropertyObject { - PythonPropertyObject() { property_getter(this, _) } + PythonPropertyObject() { property_getter(this, _) } - override string getName() { result = this.getGetter().getName() } + override string getName() { result = this.getGetter().getName() } - /** Gets the getter function of this property */ - override FunctionObject getGetter() { property_getter(this, result) } + /** Gets the getter function of this property */ + override FunctionObject getGetter() { property_getter(this, result) } - override ClassObject getInferredPropertyType() { - result = this.getGetter().getAnInferredReturnType() - } + override ClassObject getInferredPropertyType() { + result = this.getGetter().getAnInferredReturnType() + } - /** Gets the setter function of this property */ - override FunctionObject getSetter() { property_setter(this, result) } + /** Gets the setter function of this property */ + override FunctionObject getSetter() { property_setter(this, result) } - /** Gets the deleter function of this property */ - override FunctionObject getDeleter() { property_deleter(this, result) } + /** Gets the deleter function of this property */ + override FunctionObject getDeleter() { property_deleter(this, result) } } class BuiltinPropertyObject extends PropertyObject { - BuiltinPropertyObject() { this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() } + BuiltinPropertyObject() { this.asBuiltin().getClass() = theBuiltinPropertyType().asBuiltin() } - override string getName() { result = this.asBuiltin().getName() } + override string getName() { result = this.asBuiltin().getName() } - /** Gets the getter method wrapper of this property */ - override Object getGetter() { result.asBuiltin() = this.asBuiltin().getMember("__get__") } + /** Gets the getter method wrapper of this property */ + override Object getGetter() { result.asBuiltin() = this.asBuiltin().getMember("__get__") } - override ClassObject getInferredPropertyType() { none() } + override ClassObject getInferredPropertyType() { none() } - /** Gets the setter method wrapper of this property */ - override Object getSetter() { result.asBuiltin() = this.asBuiltin().getMember("__set__") } + /** Gets the setter method wrapper of this property */ + override Object getSetter() { result.asBuiltin() = this.asBuiltin().getMember("__set__") } - /** Gets the deleter method wrapper of this property */ - override Object getDeleter() { result.asBuiltin() = this.asBuiltin().getMember("__delete__") } + /** Gets the deleter method wrapper of this property */ + override Object getDeleter() { result.asBuiltin() = this.asBuiltin().getMember("__delete__") } } private predicate property_getter(CallNode decorated, FunctionObject getter) { - decorated.getFunction().refersTo(thePropertyType()) and - decorated.getArg(0).refersTo(getter) + decorated.getFunction().refersTo(thePropertyType()) and + decorated.getArg(0).refersTo(getter) } private predicate property_setter(CallNode decorated, FunctionObject setter) { - property_getter(decorated, _) and - exists(CallNode setter_call, AttrNode prop_setter | - prop_setter.getObject("setter").refersTo(decorated.(Object)) - | - setter_call.getArg(0).refersTo(setter) and - setter_call.getFunction() = prop_setter - ) - or - decorated.getFunction().refersTo(thePropertyType()) and - decorated.getArg(1).refersTo(setter) + property_getter(decorated, _) and + exists(CallNode setter_call, AttrNode prop_setter | + prop_setter.getObject("setter").refersTo(decorated.(Object)) + | + setter_call.getArg(0).refersTo(setter) and + setter_call.getFunction() = prop_setter + ) + or + decorated.getFunction().refersTo(thePropertyType()) and + decorated.getArg(1).refersTo(setter) } private predicate property_deleter(CallNode decorated, FunctionObject deleter) { - property_getter(decorated, _) and - exists(CallNode deleter_call, AttrNode prop_deleter | - prop_deleter.getObject("deleter").refersTo(decorated.(Object)) - | - deleter_call.getArg(0).refersTo(deleter) and - deleter_call.getFunction() = prop_deleter - ) - or - decorated.getFunction().refersTo(thePropertyType()) and - decorated.getArg(2).refersTo(deleter) + property_getter(decorated, _) and + exists(CallNode deleter_call, AttrNode prop_deleter | + prop_deleter.getObject("deleter").refersTo(decorated.(Object)) + | + deleter_call.getArg(0).refersTo(deleter) and + deleter_call.getFunction() = prop_deleter + ) + or + decorated.getFunction().refersTo(thePropertyType()) and + decorated.getArg(2).refersTo(deleter) } diff --git a/python/ql/src/semmle/python/types/Version.qll b/python/ql/src/semmle/python/types/Version.qll index 26ab46e970a..ee34387ba8c 100644 --- a/python/ql/src/semmle/python/types/Version.qll +++ b/python/ql/src/semmle/python/types/Version.qll @@ -5,12 +5,12 @@ import python * Currently only 2.7 or 3.x but may include different sets of versions in the future. */ class Version extends int { - Version() { this = 2 or this = 3 } + Version() { this = 2 or this = 3 } - /** Holds if this version (or set of versions) includes the version `major`.`minor` */ - predicate includes(int major, int minor) { - this = 2 and major = 2 and minor = 7 - or - this = 3 and major = 3 and minor in [4 .. 8] - } + /** Holds if this version (or set of versions) includes the version `major`.`minor` */ + predicate includes(int major, int minor) { + this = 2 and major = 2 and minor = 7 + or + this = 3 and major = 3 and minor in [4 .. 8] + } } diff --git a/python/ql/src/semmle/python/values/StringAttributes.qll b/python/ql/src/semmle/python/values/StringAttributes.qll index 1209313eaf1..a7a8ef00f00 100644 --- a/python/ql/src/semmle/python/values/StringAttributes.qll +++ b/python/ql/src/semmle/python/values/StringAttributes.qll @@ -1,82 +1,82 @@ import python predicate string_attribute_all(ControlFlowNode n, string attr) { - (n.getNode() instanceof Unicode or n.getNode() instanceof Bytes) and - attr = "const" - or - exists(Object s | - n.refersTo(s, theBytesType(), _) and - attr = "bytes" and - // We are only interested in bytes if they may cause an exception if - // implicitly converted to unicode. ASCII is safe. - not s.(StringObject).isAscii() - ) + (n.getNode() instanceof Unicode or n.getNode() instanceof Bytes) and + attr = "const" + or + exists(Object s | + n.refersTo(s, theBytesType(), _) and + attr = "bytes" and + // We are only interested in bytes if they may cause an exception if + // implicitly converted to unicode. ASCII is safe. + not s.(StringObject).isAscii() + ) } predicate tracked_object(ControlFlowNode obj, string attr) { - tracked_object_all(obj, attr) - or - tracked_object_any(obj, attr) + tracked_object_all(obj, attr) + or + tracked_object_any(obj, attr) } predicate open_file(Object obj) { obj.(CallNode).getFunction().refersTo(Object::builtin("open")) } predicate string_attribute_any(ControlFlowNode n, string attr) { - attr = "user-input" and - exists(Object input | n.(CallNode).getFunction().refersTo(input) | - if major_version() = 2 - then input = Object::builtin("raw_input") - else input = Object::builtin("input") - ) - or - attr = "file-input" and - exists(Object fd | n.(CallNode).getFunction().(AttrNode).getObject("read").refersTo(fd) | - open_file(fd) - ) - or - n.refersTo(_, theUnicodeType(), _) and attr = "unicode" + attr = "user-input" and + exists(Object input | n.(CallNode).getFunction().refersTo(input) | + if major_version() = 2 + then input = Object::builtin("raw_input") + else input = Object::builtin("input") + ) + or + attr = "file-input" and + exists(Object fd | n.(CallNode).getFunction().(AttrNode).getObject("read").refersTo(fd) | + open_file(fd) + ) + or + n.refersTo(_, theUnicodeType(), _) and attr = "unicode" } predicate tracked_object_any(ControlFlowNode obj, string attr) { - string_attribute_any(obj, attr) - or - exists(ControlFlowNode other | tracking_step(other, obj) | tracked_object_any(other, attr)) + string_attribute_any(obj, attr) + or + exists(ControlFlowNode other | tracking_step(other, obj) | tracked_object_any(other, attr)) } predicate tracked_object_all(ControlFlowNode obj, string attr) { - string_attribute_all(obj, attr) - or - forex(ControlFlowNode other | tracking_step(other, obj) | tracked_object_all(other, attr)) + string_attribute_all(obj, attr) + or + forex(ControlFlowNode other | tracking_step(other, obj) | tracked_object_all(other, attr)) } predicate tracked_call_step(ControlFlowNode ret, ControlFlowNode call) { - exists(FunctionObject func, Return r | - func.getACall() = call and - func.getFunction() = r.getScope() and - r.getValue() = ret.getNode() - ) + exists(FunctionObject func, Return r | + func.getACall() = call and + func.getFunction() = r.getScope() and + r.getValue() = ret.getNode() + ) } ControlFlowNode sequence_for_iterator(ControlFlowNode f) { - exists(For for | f.getNode() = for.getTarget() | - result.getNode() = for.getIter() and - result.getBasicBlock().dominates(f.getBasicBlock()) - ) + exists(For for | f.getNode() = for.getTarget() | + result.getNode() = for.getIter() and + result.getBasicBlock().dominates(f.getBasicBlock()) + ) } pragma[noinline] private predicate tracking_step(ControlFlowNode src, ControlFlowNode dest) { - src = dest.(BinaryExprNode).getAnOperand() - or - src = dest.(UnaryExprNode).getOperand() - or - src = sequence_for_iterator(dest) - or - src = dest.(AttrNode).getObject() - or - src = dest.(SubscriptNode).getObject() - or - tracked_call_step(src, dest) - or - dest.refersTo(src.(Object)) + src = dest.(BinaryExprNode).getAnOperand() + or + src = dest.(UnaryExprNode).getOperand() + or + src = sequence_for_iterator(dest) + or + src = dest.(AttrNode).getObject() + or + src = dest.(SubscriptNode).getObject() + or + tracked_call_step(src, dest) + or + dest.refersTo(src.(Object)) } diff --git a/python/ql/src/semmle/python/web/Http.qll b/python/ql/src/semmle/python/web/Http.qll index f8724554fc2..527a050d814 100644 --- a/python/ql/src/semmle/python/web/Http.qll +++ b/python/ql/src/semmle/python/web/Http.qll @@ -11,32 +11,32 @@ abstract class HttpRequestTaintSource extends TaintSource { } * As specified in PEP 3333. https://www.python.org/dev/peps/pep-3333/#environ-variables */ class WsgiEnvironment extends TaintKind { - WsgiEnvironment() { this = "wsgi.environment" } + WsgiEnvironment() { this = "wsgi.environment" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = this and Implementation::copyCall(fromnode, tonode) - or - result = this and - tonode.(CallNode).getFunction().pointsTo(ClassValue::dict()) and - tonode.(CallNode).getArg(0) = fromnode - or - exists(Value key, string text | - tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and - tonode.(CallNode).getArg(0).pointsTo(key) - or - tonode.(SubscriptNode).getObject() = fromnode and - tonode.isLoad() and - tonode.(SubscriptNode).getIndex().pointsTo(key) - | - key = Value::forString(text) and - result instanceof ExternalStringKind and - ( - text = "QUERY_STRING" or - text = "PATH_INFO" or - text.prefix(5) = "HTTP_" - ) - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = this and Implementation::copyCall(fromnode, tonode) + or + result = this and + tonode.(CallNode).getFunction().pointsTo(ClassValue::dict()) and + tonode.(CallNode).getArg(0) = fromnode + or + exists(Value key, string text | + tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and + tonode.(CallNode).getArg(0).pointsTo(key) + or + tonode.(SubscriptNode).getObject() = fromnode and + tonode.isLoad() and + tonode.(SubscriptNode).getIndex().pointsTo(key) + | + key = Value::forString(text) and + result instanceof ExternalStringKind and + ( + text = "QUERY_STRING" or + text = "PATH_INFO" or + text.prefix(5) = "HTTP_" + ) + ) + } } /** @@ -44,31 +44,31 @@ class WsgiEnvironment extends TaintKind { * typically an instance of `http.cookies.Morsel` */ class UntrustedMorsel extends TaintKind { - UntrustedMorsel() { this = "http.Morsel" } + UntrustedMorsel() { this = "http.Morsel" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringKind and - name = "value" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringKind and + name = "value" + } } /** A standard cookie object from a HTTP request, typically an instance of `http.cookies.SimpleCookie` */ class UntrustedCookie extends TaintKind { - UntrustedCookie() { this = "http.Cookie" } + UntrustedCookie() { this = "http.Cookie" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(SubscriptNode).getObject() = fromnode and - result instanceof UntrustedMorsel - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(SubscriptNode).getObject() = fromnode and + result instanceof UntrustedMorsel + } } abstract class CookieOperation extends @py_flow_node { - /** Gets a textual representation of this element. */ - abstract string toString(); + /** Gets a textual representation of this element. */ + abstract string toString(); - abstract ControlFlowNode getKey(); + abstract ControlFlowNode getKey(); - abstract ControlFlowNode getValue(); + abstract ControlFlowNode getValue(); } abstract class CookieGet extends CookieOperation { } @@ -77,43 +77,43 @@ abstract class CookieSet extends CookieOperation { } /** Generic taint sink in a http response */ abstract class HttpResponseTaintSink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } abstract class HttpRedirectTaintSink extends TaintSink { - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } module Client { - // TODO: user-input in other than URL: - // - `data`, `json` for `requests.post` - // - `body` for `HTTPConnection.request` - // - headers? - // TODO: Add more library support - // - urllib3 https://github.com/urllib3/urllib3 - // - httpx https://github.com/encode/httpx + // TODO: user-input in other than URL: + // - `data`, `json` for `requests.post` + // - `body` for `HTTPConnection.request` + // - headers? + // TODO: Add more library support + // - urllib3 https://github.com/urllib3/urllib3 + // - httpx https://github.com/encode/httpx + /** + * An outgoing http request + * + * For example: + * conn = HTTPConnection('example.com') + * conn.request('GET', '/path') + */ + abstract class HttpRequest extends ControlFlowNode { /** - * An outgoing http request + * Get any ControlFlowNode that is used to construct the final URL. * - * For example: - * conn = HTTPConnection('example.com') - * conn.request('GET', '/path') + * In the HTTPConnection example, there is a result for both `'example.com'` and for `'/path'`. */ - abstract class HttpRequest extends ControlFlowNode { - /** - * Get any ControlFlowNode that is used to construct the final URL. - * - * In the HTTPConnection example, there is a result for both `'example.com'` and for `'/path'`. - */ - abstract ControlFlowNode getAUrlPart(); + abstract ControlFlowNode getAUrlPart(); - abstract string getMethodUpper(); - } + abstract string getMethodUpper(); + } - /** Taint sink for the URL-part of an outgoing http request */ - class HttpRequestUrlTaintSink extends TaintSink { - HttpRequestUrlTaintSink() { this = any(HttpRequest r).getAUrlPart() } + /** Taint sink for the URL-part of an outgoing http request */ + class HttpRequestUrlTaintSink extends TaintSink { + HttpRequestUrlTaintSink() { this = any(HttpRequest r).getAUrlPart() } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + } } diff --git a/python/ql/src/semmle/python/web/HttpConstants.qll b/python/ql/src/semmle/python/web/HttpConstants.qll index 41f3905b887..5d39d517fc9 100644 --- a/python/ql/src/semmle/python/web/HttpConstants.qll +++ b/python/ql/src/semmle/python/web/HttpConstants.qll @@ -1,12 +1,12 @@ /** Gets an http verb */ string httpVerb() { - result = "GET" or - result = "POST" or - result = "PUT" or - result = "PATCH" or - result = "DELETE" or - result = "OPTIONS" or - result = "HEAD" + result = "GET" or + result = "POST" or + result = "PUT" or + result = "PATCH" or + result = "DELETE" or + result = "OPTIONS" or + result = "HEAD" } /** Gets an http verb, in lower case */ diff --git a/python/ql/src/semmle/python/web/bottle/General.qll b/python/ql/src/semmle/python/web/bottle/General.qll index d368a1f27b5..99aacf0948d 100644 --- a/python/ql/src/semmle/python/web/bottle/General.qll +++ b/python/ql/src/semmle/python/web/bottle/General.qll @@ -13,34 +13,34 @@ ClassValue theBottleClass() { result = theBottleModule().attr("Bottle") } * by decorating `func` with `app.route(route)` or `route(route)` */ predicate bottle_route(CallNode route_call, ControlFlowNode route, Function func) { - exists(CallNode decorator_call, string name | - route_call.getFunction().(AttrNode).getObject(name).pointsTo().getClass() = theBottleClass() or - route_call.getFunction().pointsTo(theBottleModule().attr(name)) - | - (name = "route" or name = httpVerbLower()) and - decorator_call.getFunction() = route_call and - route_call.getArg(0) = route and - decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func - ) + exists(CallNode decorator_call, string name | + route_call.getFunction().(AttrNode).getObject(name).pointsTo().getClass() = theBottleClass() or + route_call.getFunction().pointsTo(theBottleModule().attr(name)) + | + (name = "route" or name = httpVerbLower()) and + decorator_call.getFunction() = route_call and + route_call.getArg(0) = route and + decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func + ) } class BottleRoute extends ControlFlowNode { - BottleRoute() { bottle_route(this, _, _) } + BottleRoute() { bottle_route(this, _, _) } - string getUrl() { - exists(StrConst url | - bottle_route(this, url.getAFlowNode(), _) and - result = url.getText() - ) - } + string getUrl() { + exists(StrConst url | + bottle_route(this, url.getAFlowNode(), _) and + result = url.getText() + ) + } - Function getFunction() { bottle_route(this, _, result) } + Function getFunction() { bottle_route(this, _, result) } - Parameter getANamedArgument() { - exists(string name, Function func | - func = this.getFunction() and - func.getArgByName(name) = result and - this.getUrl().matches("%<" + name + ">%") - ) - } + Parameter getANamedArgument() { + exists(string name, Function func | + func = this.getFunction() and + func.getArgByName(name) = result and + this.getUrl().matches("%<" + name + ">%") + ) + } } diff --git a/python/ql/src/semmle/python/web/bottle/Redirect.qll b/python/ql/src/semmle/python/web/bottle/Redirect.qll index be4c552fea2..714468d6b45 100644 --- a/python/ql/src/semmle/python/web/bottle/Redirect.qll +++ b/python/ql/src/semmle/python/web/bottle/Redirect.qll @@ -15,14 +15,14 @@ FunctionValue bottle_redirect() { result = theBottleModule().attr("redirect") } * Represents an argument to the `bottle.redirect` function. */ class BottleRedirect extends TaintSink { - override string toString() { result = "bottle.redirect" } + override string toString() { result = "bottle.redirect" } - BottleRedirect() { - exists(CallNode call | - bottle_redirect().getACall() = call and - this = call.getAnArg() - ) - } + BottleRedirect() { + exists(CallNode call | + bottle_redirect().getACall() = call and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/bottle/Request.qll b/python/ql/src/semmle/python/web/bottle/Request.qll index 91f04dde16d..67b5b78bfdf 100644 --- a/python/ql/src/semmle/python/web/bottle/Request.qll +++ b/python/ql/src/semmle/python/web/bottle/Request.qll @@ -7,61 +7,61 @@ import semmle.python.web.bottle.General private Value theBottleRequestObject() { result = theBottleModule().attr("request") } class BottleRequestKind extends TaintKind { - BottleRequestKind() { this = "bottle.request" } + BottleRequestKind() { this = "bottle.request" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof BottleFormsDict and - (name = "cookies" or name = "query" or name = "form") - or - result instanceof ExternalStringKind and - (name = "query_string" or name = "url_args") - or - result.(DictKind).getValue() instanceof FileUpload and - name = "files" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof BottleFormsDict and + (name = "cookies" or name = "query" or name = "form") + or + result instanceof ExternalStringKind and + (name = "query_string" or name = "url_args") + or + result.(DictKind).getValue() instanceof FileUpload and + name = "files" + } } private class RequestSource extends HttpRequestTaintSource { - RequestSource() { this.(ControlFlowNode).pointsTo(theBottleRequestObject()) } + RequestSource() { this.(ControlFlowNode).pointsTo(theBottleRequestObject()) } - override predicate isSourceOf(TaintKind kind) { kind instanceof BottleRequestKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof BottleRequestKind } } class BottleFormsDict extends TaintKind { - BottleFormsDict() { this = "bottle.FormsDict" } + BottleFormsDict() { this = "bottle.FormsDict" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - /* Cannot use `getTaintOfAttribute(name)` as it wouldn't bind `name` */ - exists(string name | - fromnode = tonode.(AttrNode).getObject(name) and - result instanceof ExternalStringKind - | - name != "get" and name != "getunicode" and name != "getall" - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + /* Cannot use `getTaintOfAttribute(name)` as it wouldn't bind `name` */ + exists(string name | + fromnode = tonode.(AttrNode).getObject(name) and + result instanceof ExternalStringKind + | + name != "get" and name != "getunicode" and name != "getall" + ) + } - override TaintKind getTaintOfMethodResult(string name) { - (name = "get" or name = "getunicode") and - result instanceof ExternalStringKind - or - name = "getall" and result.(SequenceKind).getItem() instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + (name = "get" or name = "getunicode") and + result instanceof ExternalStringKind + or + name = "getall" and result.(SequenceKind).getItem() instanceof ExternalStringKind + } } class FileUpload extends TaintKind { - FileUpload() { this = "bottle.FileUpload" } + FileUpload() { this = "bottle.FileUpload" } - override TaintKind getTaintOfAttribute(string name) { - name = "filename" and result instanceof ExternalStringKind - or - name = "raw_filename" and result instanceof ExternalStringKind - or - name = "file" and result instanceof UntrustedFile - } + override TaintKind getTaintOfAttribute(string name) { + name = "filename" and result instanceof ExternalStringKind + or + name = "raw_filename" and result instanceof ExternalStringKind + or + name = "file" and result instanceof UntrustedFile + } } class UntrustedFile extends TaintKind { - UntrustedFile() { this = "Untrusted file" } + UntrustedFile() { this = "Untrusted file" } } // @@ -70,11 +70,11 @@ class UntrustedFile extends TaintKind { // /** Parameter to a bottle request handler function */ class BottleRequestParameter extends HttpRequestTaintSource { - BottleRequestParameter() { - exists(BottleRoute route | route.getANamedArgument() = this.(ControlFlowNode).getNode()) - } + BottleRequestParameter() { + exists(BottleRoute route | route.getANamedArgument() = this.(ControlFlowNode).getNode()) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "bottle handler function argument" } + override string toString() { result = "bottle handler function argument" } } diff --git a/python/ql/src/semmle/python/web/bottle/Response.qll b/python/ql/src/semmle/python/web/bottle/Response.qll index dede231c27d..285e83c8685 100644 --- a/python/ql/src/semmle/python/web/bottle/Response.qll +++ b/python/ql/src/semmle/python/web/bottle/Response.qll @@ -10,43 +10,43 @@ import semmle.python.web.bottle.General * track the flow of response objects. */ class BottleResponse extends TaintKind { - BottleResponse() { this = "bottle.response" } + BottleResponse() { this = "bottle.response" } } private Value theBottleResponseObject() { result = theBottleModule().attr("response") } class BottleResponseBodyAssignment extends HttpResponseTaintSink { - BottleResponseBodyAssignment() { - exists(DefinitionNode lhs | - lhs.getValue() = this and - lhs.(AttrNode).getObject("body").pointsTo(theBottleResponseObject()) - ) - } + BottleResponseBodyAssignment() { + exists(DefinitionNode lhs | + lhs.getValue() = this and + lhs.(AttrNode).getObject("body").pointsTo(theBottleResponseObject()) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } class BottleHandlerFunctionResult extends HttpResponseTaintSink { - BottleHandlerFunctionResult() { - exists(BottleRoute route, Return ret | - ret.getScope() = route.getFunction() and - ret.getValue().getAFlowNode() = this - ) - } + BottleHandlerFunctionResult() { + exists(BottleRoute route, Return ret | + ret.getScope() = route.getFunction() and + ret.getValue().getAFlowNode() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "bottle handler function result" } + override string toString() { result = "bottle handler function result" } } class BottleCookieSet extends CookieSet, CallNode { - BottleCookieSet() { - any(BottleResponse r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) - } + BottleCookieSet() { + any(BottleResponse r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/cherrypy/General.qll b/python/ql/src/semmle/python/web/cherrypy/General.qll index 5a8984d98d3..718c1486bc4 100644 --- a/python/ql/src/semmle/python/web/cherrypy/General.qll +++ b/python/ql/src/semmle/python/web/cherrypy/General.qll @@ -2,43 +2,43 @@ import python import semmle.python.web.Http module CherryPy { - FunctionValue expose() { result = Value::named("cherrypy.expose") } + FunctionValue expose() { result = Value::named("cherrypy.expose") } } class CherryPyExposedFunction extends Function { - CherryPyExposedFunction() { - this.getADecorator().pointsTo(CherryPy::expose()) - or - this.getADecorator().(Call).getFunc().pointsTo(CherryPy::expose()) - } + CherryPyExposedFunction() { + this.getADecorator().pointsTo(CherryPy::expose()) + or + this.getADecorator().(Call).getFunc().pointsTo(CherryPy::expose()) + } } class CherryPyRoute extends CallNode { - CherryPyRoute() { - /* cherrypy.quickstart(root, script_name, config) */ - Value::named("cherrypy.quickstart").(FunctionValue).getACall() = this - or - /* cherrypy.tree.mount(root, script_name, config) */ - this.getFunction().(AttrNode).getObject("mount").pointsTo(Value::named("cherrypy.tree")) - } + CherryPyRoute() { + /* cherrypy.quickstart(root, script_name, config) */ + Value::named("cherrypy.quickstart").(FunctionValue).getACall() = this + or + /* cherrypy.tree.mount(root, script_name, config) */ + this.getFunction().(AttrNode).getObject("mount").pointsTo(Value::named("cherrypy.tree")) + } - ClassValue getAppClass() { - this.getArg(0).pointsTo().getClass() = result - or - this.getArgByName("root").pointsTo().getClass() = result - } + ClassValue getAppClass() { + this.getArg(0).pointsTo().getClass() = result + or + this.getArgByName("root").pointsTo().getClass() = result + } - string getPath() { - exists(Value path | path = Value::forString(result) | - this.getArg(1).pointsTo(path) - or - this.getArgByName("script_name").pointsTo(path) - ) - } + string getPath() { + exists(Value path | path = Value::forString(result) | + this.getArg(1).pointsTo(path) + or + this.getArgByName("script_name").pointsTo(path) + ) + } - ClassValue getConfig() { - this.getArg(2).pointsTo().getClass() = result - or - this.getArgByName("config").pointsTo().getClass() = result - } + ClassValue getConfig() { + this.getArg(2).pointsTo().getClass() = result + or + this.getArgByName("config").pointsTo().getClass() = result + } } diff --git a/python/ql/src/semmle/python/web/cherrypy/Request.qll b/python/ql/src/semmle/python/web/cherrypy/Request.qll index 309d51f5539..094474b8915 100644 --- a/python/ql/src/semmle/python/web/cherrypy/Request.qll +++ b/python/ql/src/semmle/python/web/cherrypy/Request.qll @@ -6,41 +6,41 @@ import semmle.python.web.cherrypy.General /** The cherrypy.request local-proxy object */ class CherryPyRequest extends TaintKind { - CherryPyRequest() { this = "cherrypy.request" } + CherryPyRequest() { this = "cherrypy.request" } - override TaintKind getTaintOfAttribute(string name) { - name = "params" and result instanceof ExternalStringDictKind - or - name = "cookie" and result instanceof UntrustedCookie - } + override TaintKind getTaintOfAttribute(string name) { + name = "params" and result instanceof ExternalStringDictKind + or + name = "cookie" and result instanceof UntrustedCookie + } - override TaintKind getTaintOfMethodResult(string name) { - ( - name = "getHeader" or - name = "getCookie" or - name = "getUser" or - name = "getPassword" - ) and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + ( + name = "getHeader" or + name = "getCookie" or + name = "getUser" or + name = "getPassword" + ) and + result instanceof ExternalStringKind + } } class CherryPyExposedFunctionParameter extends HttpRequestTaintSource { - CherryPyExposedFunctionParameter() { - exists(Parameter p | - p = any(CherryPyExposedFunction f).getAnArg() and - not p.isSelf() and - p.asName().getAFlowNode() = this - ) - } + CherryPyExposedFunctionParameter() { + exists(Parameter p | + p = any(CherryPyExposedFunction f).getAnArg() and + not p.isSelf() and + p.asName().getAFlowNode() = this + ) + } - override string toString() { result = "CherryPy handler function parameter" } + override string toString() { result = "CherryPy handler function parameter" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } class CherryPyRequestSource extends HttpRequestTaintSource { - CherryPyRequestSource() { this.(ControlFlowNode).pointsTo(Value::named("cherrypy.request")) } + CherryPyRequestSource() { this.(ControlFlowNode).pointsTo(Value::named("cherrypy.request")) } - override predicate isSourceOf(TaintKind kind) { kind instanceof CherryPyRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof CherryPyRequest } } diff --git a/python/ql/src/semmle/python/web/cherrypy/Response.qll b/python/ql/src/semmle/python/web/cherrypy/Response.qll index 3ed1d0d9b57..6905244ca95 100644 --- a/python/ql/src/semmle/python/web/cherrypy/Response.qll +++ b/python/ql/src/semmle/python/web/cherrypy/Response.qll @@ -5,14 +5,14 @@ import semmle.python.web.Http import semmle.python.web.cherrypy.General class CherryPyExposedFunctionResult extends HttpResponseTaintSink { - CherryPyExposedFunctionResult() { - exists(Return ret | - ret.getScope() instanceof CherryPyExposedFunction and - ret.getValue().getAFlowNode() = this - ) - } + CherryPyExposedFunctionResult() { + exists(Return ret | + ret.getScope() instanceof CherryPyExposedFunction and + ret.getValue().getAFlowNode() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "cherrypy handler function result" } + override string toString() { result = "cherrypy handler function result" } } diff --git a/python/ql/src/semmle/python/web/client/Requests.qll b/python/ql/src/semmle/python/web/client/Requests.qll index 6899c651fa6..a65ea3229d5 100644 --- a/python/ql/src/semmle/python/web/client/Requests.qll +++ b/python/ql/src/semmle/python/web/client/Requests.qll @@ -7,16 +7,16 @@ import python private import semmle.python.web.Http class RequestsHttpRequest extends Client::HttpRequest, CallNode { - CallableValue func; - string method; + CallableValue func; + string method; - RequestsHttpRequest() { - method = httpVerbLower() and - func = Module::named("requests").attr(method) and - this = func.getACall() - } + RequestsHttpRequest() { + method = httpVerbLower() and + func = Module::named("requests").attr(method) and + this = func.getACall() + } - override ControlFlowNode getAUrlPart() { result = func.getNamedArgumentForCall(this, "url") } + override ControlFlowNode getAUrlPart() { result = func.getNamedArgumentForCall(this, "url") } - override string getMethodUpper() { result = method.toUpperCase() } + override string getMethodUpper() { result = method.toUpperCase() } } diff --git a/python/ql/src/semmle/python/web/client/StdLib.qll b/python/ql/src/semmle/python/web/client/StdLib.qll index 9ee089a47f5..459c2db8ec0 100644 --- a/python/ql/src/semmle/python/web/client/StdLib.qll +++ b/python/ql/src/semmle/python/web/client/StdLib.qll @@ -2,54 +2,54 @@ import python private import semmle.python.web.Http ClassValue httpConnectionClass() { - // Python 2 - result = Value::named("httplib.HTTPConnection") - or - result = Value::named("httplib.HTTPSConnection") - or - // Python 3 - result = Value::named("http.client.HTTPConnection") - or - result = Value::named("http.client.HTTPSConnection") - or - // six - result = Value::named("six.moves.http_client.HTTPConnection") - or - result = Value::named("six.moves.http_client.HTTPSConnection") + // Python 2 + result = Value::named("httplib.HTTPConnection") + or + result = Value::named("httplib.HTTPSConnection") + or + // Python 3 + result = Value::named("http.client.HTTPConnection") + or + result = Value::named("http.client.HTTPSConnection") + or + // six + result = Value::named("six.moves.http_client.HTTPConnection") + or + result = Value::named("six.moves.http_client.HTTPSConnection") } class HttpConnectionHttpRequest extends Client::HttpRequest, CallNode { - CallNode constructor_call; - CallableValue func; + CallNode constructor_call; + CallableValue func; - HttpConnectionHttpRequest() { - exists(ClassValue cls, AttrNode call_origin, Value constructor_call_value | - cls = httpConnectionClass() and - func = cls.lookup("request") and - this = func.getACall() and - // since you can do `r = conn.request; r('GET', path)`, we need to find the origin - this.getFunction().pointsTo(_, _, call_origin) and - // Since HTTPSConnection is a subtype of HTTPConnection, up until this point, `cls` could be either class, - // because `HTTPSConnection.request == HTTPConnection.request`. To avoid generating 2 results, we filter - // on the actual class used as the constructor - call_origin.getObject().pointsTo(_, constructor_call_value, constructor_call) and - cls = constructor_call_value.getClass() and - constructor_call = cls.getACall() - ) - } + HttpConnectionHttpRequest() { + exists(ClassValue cls, AttrNode call_origin, Value constructor_call_value | + cls = httpConnectionClass() and + func = cls.lookup("request") and + this = func.getACall() and + // since you can do `r = conn.request; r('GET', path)`, we need to find the origin + this.getFunction().pointsTo(_, _, call_origin) and + // Since HTTPSConnection is a subtype of HTTPConnection, up until this point, `cls` could be either class, + // because `HTTPSConnection.request == HTTPConnection.request`. To avoid generating 2 results, we filter + // on the actual class used as the constructor + call_origin.getObject().pointsTo(_, constructor_call_value, constructor_call) and + cls = constructor_call_value.getClass() and + constructor_call = cls.getACall() + ) + } - override ControlFlowNode getAUrlPart() { - result = func.getNamedArgumentForCall(this, "url") - or - result = constructor_call.getArg(0) - or - result = constructor_call.getArgByName("host") - } + override ControlFlowNode getAUrlPart() { + result = func.getNamedArgumentForCall(this, "url") + or + result = constructor_call.getArg(0) + or + result = constructor_call.getArgByName("host") + } - override string getMethodUpper() { - exists(string method | - result = method.toUpperCase() and - func.getNamedArgumentForCall(this, "method").pointsTo(Value::forString(method)) - ) - } + override string getMethodUpper() { + exists(string method | + result = method.toUpperCase() and + func.getNamedArgumentForCall(this, "method").pointsTo(Value::forString(method)) + ) + } } diff --git a/python/ql/src/semmle/python/web/django/Db.qll b/python/ql/src/semmle/python/web/django/Db.qll index 00a36f5ba76..1dbb52fd13d 100644 --- a/python/ql/src/semmle/python/web/django/Db.qll +++ b/python/ql/src/semmle/python/web/django/Db.qll @@ -5,7 +5,7 @@ import semmle.python.security.injection.Sql * A taint kind representing a django cursor object. */ class DjangoDbCursor extends DbCursor { - DjangoDbCursor() { this = "django.db.connection.cursor" } + DjangoDbCursor() { this = "django.db.connection.cursor" } } private Value theDjangoConnectionObject() { result = Value::named("django.db.connection") } @@ -14,16 +14,16 @@ private Value theDjangoConnectionObject() { result = Value::named("django.db.con * A kind of taint source representing sources of django cursor objects. */ class DjangoDbCursorSource extends DbConnectionSource { - DjangoDbCursorSource() { - exists(AttrNode cursor | - this.(CallNode).getFunction() = cursor and - cursor.getObject("cursor").pointsTo(theDjangoConnectionObject()) - ) - } + DjangoDbCursorSource() { + exists(AttrNode cursor | + this.(CallNode).getFunction() = cursor and + cursor.getObject("cursor").pointsTo(theDjangoConnectionObject()) + ) + } - override string toString() { result = "django.db.connection.cursor" } + override string toString() { result = "django.db.connection.cursor" } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbCursor } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbCursor } } ClassValue theDjangoRawSqlClass() { result = Value::named("django.db.models.expressions.RawSQL") } @@ -33,14 +33,14 @@ ClassValue theDjangoRawSqlClass() { result = Value::named("django.db.models.expr * allows arbitrary SQL statements to be executed, which is a security risk. */ class DjangoRawSqlSink extends SqlInjectionSink { - DjangoRawSqlSink() { - exists(CallNode call | - call = theDjangoRawSqlClass().getACall() and - this = call.getArg(0) - ) - } + DjangoRawSqlSink() { + exists(CallNode call | + call = theDjangoRawSqlClass().getACall() and + this = call.getArg(0) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.db.models.expressions.RawSQL(sink,...)" } + override string toString() { result = "django.db.models.expressions.RawSQL(sink,...)" } } diff --git a/python/ql/src/semmle/python/web/django/General.qll b/python/ql/src/semmle/python/web/django/General.qll index 8d707a3a6e6..0b511cbfbcf 100644 --- a/python/ql/src/semmle/python/web/django/General.qll +++ b/python/ql/src/semmle/python/web/django/General.qll @@ -6,19 +6,19 @@ import semmle.python.web.Http // a FunctionValue, so we can't use `FunctionValue.getArgumentForCall` // https://github.com/django/django/blob/master/django/urls/conf.py#L76 abstract class DjangoRoute extends CallNode { - DjangoViewHandler getViewHandler() { - result = view_handler_from_view_arg(this.getArg(1)) - or - result = view_handler_from_view_arg(this.getArgByName("view")) - } + DjangoViewHandler getViewHandler() { + result = view_handler_from_view_arg(this.getArg(1)) + or + result = view_handler_from_view_arg(this.getArgByName("view")) + } - abstract string getANamedArgument(); + abstract string getANamedArgument(); - /** - * Get the number of positional arguments that will be passed to the view. - * Will only return a result if there are no named arguments. - */ - abstract int getNumPositionalArguments(); + /** + * Get the number of positional arguments that will be passed to the view. + * Will only return a result if there are no named arguments. + */ + abstract int getNumPositionalArguments(); } /** @@ -27,8 +27,8 @@ abstract class DjangoRoute extends CallNode { * https://docs.djangoproject.com/en/3.0/topics/http/views/ */ class DjangoViewHandler extends PythonFunctionValue { - /** Gets the index of the 'request' argument */ - int getRequestArgIndex() { result = 0 } + /** Gets the index of the 'request' argument */ + int getRequestArgIndex() { result = 0 } } /** @@ -37,20 +37,20 @@ class DjangoViewHandler extends PythonFunctionValue { * https://docs.djangoproject.com/en/3.0/topics/class-based-views/ */ private class DjangoViewClass extends ClassValue { - DjangoViewClass() { - Value::named("django.views.generic.View") = this.getASuperType() - or - Value::named("django.views.View") = this.getASuperType() - } + DjangoViewClass() { + Value::named("django.views.generic.View") = this.getASuperType() + or + Value::named("django.views.View") = this.getASuperType() + } } class DjangoClassBasedViewHandler extends DjangoViewHandler { - DjangoClassBasedViewHandler() { exists(DjangoViewClass cls | cls.lookup(httpVerbLower()) = this) } + DjangoClassBasedViewHandler() { exists(DjangoViewClass cls | cls.lookup(httpVerbLower()) = this) } - override int getRequestArgIndex() { - // due to `self` being the first parameter - result = 1 - } + override int getRequestArgIndex() { + // due to `self` being the first parameter + result = 1 + } } /** @@ -58,79 +58,79 @@ class DjangoClassBasedViewHandler extends DjangoViewHandler { * django route. That is, this methods handles Class-based Views and its `as_view()` function. */ private DjangoViewHandler view_handler_from_view_arg(ControlFlowNode view_arg) { - // Function-based view - result = view_arg.pointsTo() - or - // Class-based view - exists(ClassValue cls | - cls = view_arg.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo() and - result = cls.lookup(httpVerbLower()) - ) + // Function-based view + result = view_arg.pointsTo() + or + // Class-based view + exists(ClassValue cls | + cls = view_arg.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo() and + result = cls.lookup(httpVerbLower()) + ) } // We need this "dummy" class, since otherwise the regex argument would not be considered // a regex (RegexString is abstract) class DjangoRouteRegex extends RegexString { - DjangoRouteRegex() { exists(DjangoRegexRoute route | route.getRouteArg() = this.getAFlowNode()) } + DjangoRouteRegex() { exists(DjangoRegexRoute route | route.getRouteArg() = this.getAFlowNode()) } } class DjangoRegexRoute extends DjangoRoute { - ControlFlowNode route; + ControlFlowNode route; - DjangoRegexRoute() { - exists(FunctionValue route_maker | - // Django 1.x: https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url - Value::named("django.conf.urls.url") = route_maker and - route_maker.getArgumentForCall(this, 0) = route - ) - or - // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#re-path - this = Value::named("django.urls.re_path").getACall() and - ( - route = this.getArg(0) - or - route = this.getArgByName("route") - ) - } + DjangoRegexRoute() { + exists(FunctionValue route_maker | + // Django 1.x: https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url + Value::named("django.conf.urls.url") = route_maker and + route_maker.getArgumentForCall(this, 0) = route + ) + or + // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#re-path + this = Value::named("django.urls.re_path").getACall() and + ( + route = this.getArg(0) + or + route = this.getArgByName("route") + ) + } - ControlFlowNode getRouteArg() { result = route } + ControlFlowNode getRouteArg() { result = route } - override string getANamedArgument() { - exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | - result = regex.getGroupName(_, _) - ) - } + override string getANamedArgument() { + exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | + result = regex.getGroupName(_, _) + ) + } - override int getNumPositionalArguments() { - not exists(this.getANamedArgument()) and - exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | - result = count(regex.getGroupNumber(_, _)) - ) - } + override int getNumPositionalArguments() { + not exists(this.getANamedArgument()) and + exists(DjangoRouteRegex regex | regex.getAFlowNode() = route | + result = count(regex.getGroupNumber(_, _)) + ) + } } class DjangoPathRoute extends DjangoRoute { - ControlFlowNode route; + ControlFlowNode route; - DjangoPathRoute() { - // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#path - this = Value::named("django.urls.path").getACall() and - ( - route = this.getArg(0) - or - route = this.getArgByName("route") - ) - } + DjangoPathRoute() { + // Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#path + this = Value::named("django.urls.path").getACall() and + ( + route = this.getArg(0) + or + route = this.getArgByName("route") + ) + } - override string getANamedArgument() { - // regexp taken from django: - // https://github.com/django/django/blob/7d1bf29977bb368d7c28e7c6eb146db3b3009ae7/django/urls/resolvers.py#L199 - exists(StrConst route_str, string match | - route_str = route.getNode() and - match = route_str.getText().regexpFind("<(?:(?[^>:]+):)?(?\\w+)>", _, _) and - result = match.regexpCapture("<(?:(?[^>:]+):)?(?\\w+)>", 2) - ) - } + override string getANamedArgument() { + // regexp taken from django: + // https://github.com/django/django/blob/7d1bf29977bb368d7c28e7c6eb146db3b3009ae7/django/urls/resolvers.py#L199 + exists(StrConst route_str, string match | + route_str = route.getNode() and + match = route_str.getText().regexpFind("<(?:(?[^>:]+):)?(?\\w+)>", _, _) and + result = match.regexpCapture("<(?:(?[^>:]+):)?(?\\w+)>", 2) + ) + } - override int getNumPositionalArguments() { none() } + override int getNumPositionalArguments() { none() } } diff --git a/python/ql/src/semmle/python/web/django/Model.qll b/python/ql/src/semmle/python/web/django/Model.qll index f8a61bda10e..d48fe6e04f9 100644 --- a/python/ql/src/semmle/python/web/django/Model.qll +++ b/python/ql/src/semmle/python/web/django/Model.qll @@ -6,52 +6,52 @@ import semmle.python.security.injection.Sql /** A django model class */ class DjangoModel extends ClassValue { - DjangoModel() { Value::named("django.db.models.Model") = this.getASuperType() } + DjangoModel() { Value::named("django.db.models.Model") = this.getASuperType() } } /** A "taint" for django database tables */ class DjangoDbTableObjects extends TaintKind { - DjangoDbTableObjects() { this = "django.db.models.Model.objects" } + DjangoDbTableObjects() { this = "django.db.models.Model.objects" } - override TaintKind getTaintOfMethodResult(string name) { - result = this and - ( - name = "filter" or - name = "exclude" or - name = "annotate" or - name = "order_by" or - name = "reverse" or - name = "distinct" or - name = "values" or - name = "values_list" or - name = "dates" or - name = "datetimes" or - name = "none" or - name = "all" or - name = "union" or - name = "intersection" or - name = "difference" or - name = "select_related" or - name = "prefetch_related" or - name = "extra" or - name = "defer" or - name = "only" or - name = "using" or - name = "select_for_update" or - name = "raw" - ) - } + override TaintKind getTaintOfMethodResult(string name) { + result = this and + ( + name = "filter" or + name = "exclude" or + name = "annotate" or + name = "order_by" or + name = "reverse" or + name = "distinct" or + name = "values" or + name = "values_list" or + name = "dates" or + name = "datetimes" or + name = "none" or + name = "all" or + name = "union" or + name = "intersection" or + name = "difference" or + name = "select_related" or + name = "prefetch_related" or + name = "extra" or + name = "defer" or + name = "only" or + name = "using" or + name = "select_for_update" or + name = "raw" + ) + } } /** Django model objects, which are sources of django database table "taint" */ class DjangoModelObjects extends TaintSource { - DjangoModelObjects() { - this.(AttrNode).isLoad() and this.(AttrNode).getObject("objects").pointsTo(any(DjangoModel m)) - } + DjangoModelObjects() { + this.(AttrNode).isLoad() and this.(AttrNode).getObject("objects").pointsTo(any(DjangoModel m)) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbTableObjects } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbTableObjects } - override string toString() { result = "django.db.models.Model.objects" } + override string toString() { result = "django.db.models.Model.objects" } } /** @@ -59,16 +59,16 @@ class DjangoModelObjects extends TaintSource { * to be sent to the database, which is a security risk. */ class DjangoModelRawCall extends SqlInjectionSink { - DjangoModelRawCall() { - exists(CallNode raw_call, ControlFlowNode queryset | this = raw_call.getArg(0) | - raw_call.getFunction().(AttrNode).getObject("raw") = queryset and - any(DjangoDbTableObjects objs).taints(queryset) - ) - } + DjangoModelRawCall() { + exists(CallNode raw_call, ControlFlowNode queryset | this = raw_call.getArg(0) | + raw_call.getFunction().(AttrNode).getObject("raw") = queryset and + any(DjangoDbTableObjects objs).taints(queryset) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.models.QuerySet.raw(sink,...)" } + override string toString() { result = "django.models.QuerySet.raw(sink,...)" } } /** @@ -76,14 +76,14 @@ class DjangoModelRawCall extends SqlInjectionSink { * to be sent to the database, which is a security risk. */ class DjangoModelExtraCall extends SqlInjectionSink { - DjangoModelExtraCall() { - exists(CallNode extra_call, ControlFlowNode queryset | this = extra_call.getArg(0) | - extra_call.getFunction().(AttrNode).getObject("extra") = queryset and - any(DjangoDbTableObjects objs).taints(queryset) - ) - } + DjangoModelExtraCall() { + exists(CallNode extra_call, ControlFlowNode queryset | this = extra_call.getArg(0) | + extra_call.getFunction().(AttrNode).getObject("extra") = queryset and + any(DjangoDbTableObjects objs).taints(queryset) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.models.QuerySet.extra(sink,...)" } + override string toString() { result = "django.models.QuerySet.extra(sink,...)" } } diff --git a/python/ql/src/semmle/python/web/django/Redirect.qll b/python/ql/src/semmle/python/web/django/Redirect.qll index d6afbcce7e7..3b0b1f2b50b 100644 --- a/python/ql/src/semmle/python/web/django/Redirect.qll +++ b/python/ql/src/semmle/python/web/django/Redirect.qll @@ -14,11 +14,11 @@ private import semmle.python.web.Http * The URL argument for a call to the `django.shortcuts.redirect` function. */ class DjangoShortcutsRedirectSink extends HttpRedirectTaintSink { - override string toString() { result = "DjangoShortcutsRedirectSink" } + override string toString() { result = "DjangoShortcutsRedirectSink" } - DjangoShortcutsRedirectSink() { - this = Value::named("django.shortcuts.redirect").(FunctionValue).getArgumentForCall(_, 0) - } + DjangoShortcutsRedirectSink() { + this = Value::named("django.shortcuts.redirect").(FunctionValue).getArgumentForCall(_, 0) + } } /** DEPRECATED: Use `DjangoShortcutsRedirectSink` instead. */ @@ -28,13 +28,13 @@ deprecated class DjangoRedirect = DjangoShortcutsRedirectSink; * The URL argument when instantiating a Django Redirect Response. */ class DjangoRedirectResponseSink extends HttpRedirectTaintSink { - DjangoRedirectResponseSink() { - exists(CallNode call | call = any(DjangoRedirectResponseClass cls).getACall() | - this = call.getArg(0) - or - this = call.getArgByName("redirect_to") - ) - } + DjangoRedirectResponseSink() { + exists(CallNode call | call = any(DjangoRedirectResponseClass cls).getACall() | + this = call.getArg(0) + or + this = call.getArgByName("redirect_to") + ) + } - override string toString() { result = "DjangoRedirectResponseSink" } + override string toString() { result = "DjangoRedirectResponseSink" } } diff --git a/python/ql/src/semmle/python/web/django/Request.qll b/python/ql/src/semmle/python/web/django/Request.qll index 503264c2817..291d61b184b 100644 --- a/python/ql/src/semmle/python/web/django/Request.qll +++ b/python/ql/src/semmle/python/web/django/Request.qll @@ -5,74 +5,74 @@ import semmle.python.web.django.General /** A django.request.HttpRequest object */ class DjangoRequest extends TaintKind { - DjangoRequest() { this = "django.request.HttpRequest" } + DjangoRequest() { this = "django.request.HttpRequest" } - override TaintKind getTaintOfAttribute(string name) { - (name = "GET" or name = "POST") and - result instanceof DjangoQueryDict - } + override TaintKind getTaintOfAttribute(string name) { + (name = "GET" or name = "POST") and + result instanceof DjangoQueryDict + } - override TaintKind getTaintOfMethodResult(string name) { - (name = "body" or name = "path") and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + (name = "body" or name = "path") and + result instanceof ExternalStringKind + } } /* Helper for getTaintForStep() */ pragma[noinline] private predicate subscript_taint(SubscriptNode sub, ControlFlowNode obj, TaintKind kind) { - sub.getObject() = obj and - kind instanceof ExternalStringKind + sub.getObject() = obj and + kind instanceof ExternalStringKind } /** A django.request.QueryDict object */ class DjangoQueryDict extends TaintKind { - DjangoQueryDict() { this = "django.http.request.QueryDict" } + DjangoQueryDict() { this = "django.http.request.QueryDict" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - this.taints(fromnode) and - subscript_taint(tonode, fromnode, result) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + this.taints(fromnode) and + subscript_taint(tonode, fromnode, result) + } - override TaintKind getTaintOfMethodResult(string name) { - name = "get" and result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result instanceof ExternalStringKind + } } /** A Django request parameter */ class DjangoRequestSource extends HttpRequestTaintSource { - DjangoRequestSource() { - exists(DjangoRoute route, DjangoViewHandler view, int request_arg_index | - route.getViewHandler() = view and - request_arg_index = view.getRequestArgIndex() and - this = view.getScope().getArg(request_arg_index).asName().getAFlowNode() - ) - } + DjangoRequestSource() { + exists(DjangoRoute route, DjangoViewHandler view, int request_arg_index | + route.getViewHandler() = view and + request_arg_index = view.getRequestArgIndex() and + this = view.getScope().getArg(request_arg_index).asName().getAFlowNode() + ) + } - override string toString() { result = "Django request source" } + override string toString() { result = "Django request source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoRequest } } /** An argument specified in a url routing table */ class DjangoRequestParameter extends HttpRequestTaintSource { - DjangoRequestParameter() { - exists(DjangoRoute route, Function f, DjangoViewHandler view, int request_arg_index | - route.getViewHandler() = view and - request_arg_index = view.getRequestArgIndex() and - f = view.getScope() - | - this.(ControlFlowNode).getNode() = f.getArgByName(route.getANamedArgument()) - or - exists(int i | i >= 0 | - i < route.getNumPositionalArguments() and - // +1 because first argument is always the request - this.(ControlFlowNode).getNode() = f.getArg(request_arg_index + 1 + i) - ) - ) - } + DjangoRequestParameter() { + exists(DjangoRoute route, Function f, DjangoViewHandler view, int request_arg_index | + route.getViewHandler() = view and + request_arg_index = view.getRequestArgIndex() and + f = view.getScope() + | + this.(ControlFlowNode).getNode() = f.getArgByName(route.getANamedArgument()) + or + exists(int i | i >= 0 | + i < route.getNumPositionalArguments() and + // +1 because first argument is always the request + this.(ControlFlowNode).getNode() = f.getArg(request_arg_index + 1 + i) + ) + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "django.http.request.parameter" } + override string toString() { result = "django.http.request.parameter" } } diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll index 649a503cc4b..ea1fe234693 100644 --- a/python/ql/src/semmle/python/web/django/Response.qll +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -16,74 +16,74 @@ deprecated class DjangoResponse = DjangoResponseKind; /** INTERNAL class used for tracking a django response object. */ private class DjangoResponseKind extends TaintKind { - DjangoResponseKind() { this = "django.response.HttpResponse" } + DjangoResponseKind() { this = "django.response.HttpResponse" } } /** INTERNAL taint-source used for tracking a django response object. */ private class DjangoResponseSource extends TaintSource { - DjangoResponseSource() { exists(DjangoContentResponseClass cls | cls.getACall() = this) } + DjangoResponseSource() { exists(DjangoContentResponseClass cls | cls.getACall() = this) } - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoResponseKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoResponseKind } - override string toString() { result = "django.http.response.HttpResponse" } + override string toString() { result = "django.http.response.HttpResponse" } } /** A write to a django response, which is vulnerable to external data (xss) */ class DjangoResponseWrite extends HttpResponseTaintSink { - DjangoResponseWrite() { - exists(AttrNode meth, CallNode call | - call.getFunction() = meth and - any(DjangoResponseKind response).taints(meth.getObject("write")) and - this = call.getArg(0) - ) - } + DjangoResponseWrite() { + exists(AttrNode meth, CallNode call | + call.getFunction() = meth and + any(DjangoResponseKind response).taints(meth.getObject("write")) and + this = call.getArg(0) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "django.Response.write(...)" } + override string toString() { result = "django.Response.write(...)" } } /** * An argument to initialization of a django response. */ class DjangoResponseContent extends HttpResponseTaintSink { - DjangoContentResponseClass cls; - CallNode call; + DjangoContentResponseClass cls; + CallNode call; - DjangoResponseContent() { - call = cls.getACall() and - this = cls.getContentArg(call) - } + DjangoResponseContent() { + call = cls.getACall() and + this = cls.getContentArg(call) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "django.Response(...)" } + override string toString() { result = "django.Response(...)" } } /** * An argument to initialization of a django response, which is vulnerable to external data (XSS). */ class DjangoResponseContentXSSVulnerable extends DjangoResponseContent { - override DjangoXSSVulnerableResponseClass cls; + override DjangoXSSVulnerableResponseClass cls; - DjangoResponseContentXSSVulnerable() { - not exists(cls.getContentTypeArg(call)) - or - exists(StringValue s | - cls.getContentTypeArg(call).pointsTo(s) and - s.getText().matches("text/html%") - ) - } + DjangoResponseContentXSSVulnerable() { + not exists(cls.getContentTypeArg(call)) + or + exists(StringValue s | + cls.getContentTypeArg(call).pointsTo(s) and + s.getText().matches("text/html%") + ) + } } class DjangoCookieSet extends CookieSet, CallNode { - DjangoCookieSet() { - any(DjangoResponseKind r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) - } + DjangoCookieSet() { + any(DjangoResponseKind r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/django/Shared.qll b/python/ql/src/semmle/python/web/django/Shared.qll index ea856ddf65a..d6b49e22a6f 100644 --- a/python/ql/src/semmle/python/web/django/Shared.qll +++ b/python/ql/src/semmle/python/web/django/Shared.qll @@ -5,26 +5,26 @@ deprecated FunctionValue redirect() { result = Value::named("django.shortcuts.re /** DEPRECATED: Use `DjangoRedirectResponseClass` instead. */ deprecated ClassValue theDjangoHttpRedirectClass() { - // version 1.x - result = Value::named("django.http.response.HttpResponseRedirectBase") - or - // version 2.x - result = Value::named("django.http.HttpResponseRedirectBase") + // version 1.x + result = Value::named("django.http.response.HttpResponseRedirectBase") + or + // version 2.x + result = Value::named("django.http.HttpResponseRedirectBase") } /** A class that is a Django Redirect Response (subclass of `django.http.HttpResponseRedirectBase`). */ class DjangoRedirectResponseClass extends ClassValue { - DjangoRedirectResponseClass() { - exists(ClassValue redirect_base | - // version 1.x - redirect_base = Value::named("django.http.response.HttpResponseRedirectBase") - or - // version 2.x and 3.x - redirect_base = Value::named("django.http.HttpResponseRedirectBase") - | - this.getASuperType() = redirect_base - ) - } + DjangoRedirectResponseClass() { + exists(ClassValue redirect_base | + // version 1.x + redirect_base = Value::named("django.http.response.HttpResponseRedirectBase") + or + // version 2.x and 3.x + redirect_base = Value::named("django.http.HttpResponseRedirectBase") + | + this.getASuperType() = redirect_base + ) + } } /** @@ -32,53 +32,53 @@ class DjangoRedirectResponseClass extends ClassValue { * A subclass of `django.http.HttpResponse` that is not a `DjangoRedirectResponseClass`. */ class DjangoContentResponseClass extends ClassValue { - ClassValue base; + ClassValue base; - DjangoContentResponseClass() { - ( - // version 1.x - base = Value::named("django.http.response.HttpResponse") - or - // version 2.x and 3.x - // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects - base = Value::named("django.http.HttpResponse") - ) and - this.getASuperType() = base - } + DjangoContentResponseClass() { + ( + // version 1.x + base = Value::named("django.http.response.HttpResponse") + or + // version 2.x and 3.x + // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects + base = Value::named("django.http.HttpResponse") + ) and + this.getASuperType() = base + } - // The reason these two methods are defined in this class (and not in the Sink - // definition that uses this class), is that if we were to add support for - // `django.http.response.HttpResponseNotAllowed` it would make much more sense to add - // the custom logic in this class (or subclass), than to handle all of it in the sink - // definition. - /** Gets the `content` argument of a `call` to the constructor */ - ControlFlowNode getContentArg(CallNode call) { none() } + // The reason these two methods are defined in this class (and not in the Sink + // definition that uses this class), is that if we were to add support for + // `django.http.response.HttpResponseNotAllowed` it would make much more sense to add + // the custom logic in this class (or subclass), than to handle all of it in the sink + // definition. + /** Gets the `content` argument of a `call` to the constructor */ + ControlFlowNode getContentArg(CallNode call) { none() } - /** Gets the `content_type` argument of a `call` to the constructor */ - ControlFlowNode getContentTypeArg(CallNode call) { none() } + /** Gets the `content_type` argument of a `call` to the constructor */ + ControlFlowNode getContentTypeArg(CallNode call) { none() } } /** A class that is a Django Response, and is vulnerable to XSS. */ class DjangoXSSVulnerableResponseClass extends DjangoContentResponseClass { - DjangoXSSVulnerableResponseClass() { - // We want to avoid FPs on subclasses that are not exposed to XSS, for example `JsonResponse`. - // The easiest way is to disregard any subclass that has a special `__init__` method. - // It's not guaranteed to remove all FPs, or not to generate FNs, but compared to our - // previous implementation that would treat 0-th argument to _any_ subclass as a sink, - // this gets us much closer to reality. - this.lookup("__init__") = base.lookup("__init__") and - not this instanceof DjangoRedirectResponseClass - } + DjangoXSSVulnerableResponseClass() { + // We want to avoid FPs on subclasses that are not exposed to XSS, for example `JsonResponse`. + // The easiest way is to disregard any subclass that has a special `__init__` method. + // It's not guaranteed to remove all FPs, or not to generate FNs, but compared to our + // previous implementation that would treat 0-th argument to _any_ subclass as a sink, + // this gets us much closer to reality. + this.lookup("__init__") = base.lookup("__init__") and + not this instanceof DjangoRedirectResponseClass + } - override ControlFlowNode getContentArg(CallNode call) { - result = call.getArg(0) - or - result = call.getArgByName("content") - } + override ControlFlowNode getContentArg(CallNode call) { + result = call.getArg(0) + or + result = call.getArgByName("content") + } - override ControlFlowNode getContentTypeArg(CallNode call) { - result = call.getArg(1) - or - result = call.getArgByName("content_type") - } + override ControlFlowNode getContentTypeArg(CallNode call) { + result = call.getArg(1) + or + result = call.getArgByName("content_type") + } } diff --git a/python/ql/src/semmle/python/web/falcon/General.qll b/python/ql/src/semmle/python/web/falcon/General.qll index bee0ad746f6..b08b71cfbd7 100644 --- a/python/ql/src/semmle/python/web/falcon/General.qll +++ b/python/ql/src/semmle/python/web/falcon/General.qll @@ -6,39 +6,39 @@ ClassValue theFalconAPIClass() { result = Value::named("falcon.API") } /** Holds if `route` is routed to `resource` */ private predicate api_route(CallNode route_call, ControlFlowNode route, ClassValue resource) { - route_call.getFunction().(AttrNode).getObject("add_route").pointsTo().getClass() = - theFalconAPIClass() and - route_call.getArg(0) = route and - route_call.getArg(1).pointsTo().getClass() = resource + route_call.getFunction().(AttrNode).getObject("add_route").pointsTo().getClass() = + theFalconAPIClass() and + route_call.getArg(0) = route and + route_call.getArg(1).pointsTo().getClass() = resource } private predicate route(FalconRoute route, Function target, string funcname) { - route.getResourceClass().lookup("on_" + funcname).(FunctionValue).getScope() = target + route.getResourceClass().lookup("on_" + funcname).(FunctionValue).getScope() = target } class FalconRoute extends ControlFlowNode { - FalconRoute() { api_route(this, _, _) } + FalconRoute() { api_route(this, _, _) } - string getUrl() { - exists(StrConst url | - api_route(this, url.getAFlowNode(), _) and - result = url.getText() - ) - } + string getUrl() { + exists(StrConst url | + api_route(this, url.getAFlowNode(), _) and + result = url.getText() + ) + } - ClassValue getResourceClass() { api_route(this, _, result) } + ClassValue getResourceClass() { api_route(this, _, result) } - FalconHandlerFunction getHandlerFunction(string method) { route(this, result, method) } + FalconHandlerFunction getHandlerFunction(string method) { route(this, result, method) } } class FalconHandlerFunction extends Function { - FalconHandlerFunction() { route(_, this, _) } + FalconHandlerFunction() { route(_, this, _) } - private string methodName() { route(_, this, result) } + private string methodName() { route(_, this, result) } - string getMethod() { result = this.methodName().toUpperCase() } + string getMethod() { result = this.methodName().toUpperCase() } - Parameter getRequest() { result = this.getArg(1) } + Parameter getRequest() { result = this.getArg(1) } - Parameter getResponse() { result = this.getArg(2) } + Parameter getResponse() { result = this.getArg(2) } } diff --git a/python/ql/src/semmle/python/web/falcon/Request.qll b/python/ql/src/semmle/python/web/falcon/Request.qll index 66707b01d0c..4b6ceb93fb6 100644 --- a/python/ql/src/semmle/python/web/falcon/Request.qll +++ b/python/ql/src/semmle/python/web/falcon/Request.qll @@ -6,39 +6,39 @@ import semmle.python.security.strings.External /** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */ class FalconRequest extends TaintKind { - FalconRequest() { this = "falcon.request" } + FalconRequest() { this = "falcon.request" } - override TaintKind getTaintOfAttribute(string name) { - name = "env" and result instanceof WsgiEnvironment - or - result instanceof ExternalStringKind and - ( - name = "uri" or - name = "url" or - name = "forwarded_uri" or - name = "relative_uri" or - name = "query_string" - ) - or - result instanceof ExternalStringDictKind and - (name = "cookies" or name = "params") - or - name = "stream" and result instanceof ExternalFileObject - } + override TaintKind getTaintOfAttribute(string name) { + name = "env" and result instanceof WsgiEnvironment + or + result instanceof ExternalStringKind and + ( + name = "uri" or + name = "url" or + name = "forwarded_uri" or + name = "relative_uri" or + name = "query_string" + ) + or + result instanceof ExternalStringDictKind and + (name = "cookies" or name = "params") + or + name = "stream" and result instanceof ExternalFileObject + } - override TaintKind getTaintOfMethodResult(string name) { - name = "get_param" and result instanceof ExternalStringKind - or - name = "get_param_as_json" and result instanceof ExternalJsonKind - or - name = "get_param_as_list" and result instanceof ExternalStringSequenceKind - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get_param" and result instanceof ExternalStringKind + or + name = "get_param_as_json" and result instanceof ExternalJsonKind + or + name = "get_param_as_list" and result instanceof ExternalStringSequenceKind + } } class FalconRequestParameter extends HttpRequestTaintSource { - FalconRequestParameter() { - exists(FalconHandlerFunction f | f.getRequest() = this.(ControlFlowNode).getNode()) - } + FalconRequestParameter() { + exists(FalconHandlerFunction f | f.getRequest() = this.(ControlFlowNode).getNode()) + } - override predicate isSourceOf(TaintKind k) { k instanceof FalconRequest } + override predicate isSourceOf(TaintKind k) { k instanceof FalconRequest } } diff --git a/python/ql/src/semmle/python/web/falcon/Response.qll b/python/ql/src/semmle/python/web/falcon/Response.qll index c66a6315ce5..3ea44fafcd3 100644 --- a/python/ql/src/semmle/python/web/falcon/Response.qll +++ b/python/ql/src/semmle/python/web/falcon/Response.qll @@ -6,24 +6,24 @@ import semmle.python.security.strings.External /** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */ class FalconResponse extends TaintKind { - FalconResponse() { this = "falcon.response" } + FalconResponse() { this = "falcon.response" } } /** Only used internally to track the response parameter */ private class FalconResponseParameter extends TaintSource { - FalconResponseParameter() { - exists(FalconHandlerFunction f | f.getResponse() = this.(ControlFlowNode).getNode()) - } + FalconResponseParameter() { + exists(FalconHandlerFunction f | f.getResponse() = this.(ControlFlowNode).getNode()) + } - override predicate isSourceOf(TaintKind k) { k instanceof FalconResponse } + override predicate isSourceOf(TaintKind k) { k instanceof FalconResponse } } class FalconResponseBodySink extends HttpResponseTaintSink { - FalconResponseBodySink() { - exists(AttrNode attr | any(FalconResponse f).taints(attr.getObject("body")) | - attr.(DefinitionNode).getValue() = this - ) - } + FalconResponseBodySink() { + exists(AttrNode attr | any(FalconResponse f).taints(attr.getObject("body")) | + attr.(DefinitionNode).getValue() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/flask/General.qll b/python/ql/src/semmle/python/web/flask/General.qll index 71eb57fb07b..d5d7e30ec47 100644 --- a/python/ql/src/semmle/python/web/flask/General.qll +++ b/python/ql/src/semmle/python/web/flask/General.qll @@ -15,36 +15,36 @@ ClassValue theFlaskReponseClass() { result = Value::named("flask.Response") } * by decorating `func` with `app.route(route)` */ predicate app_route(ControlFlowNode route, Function func) { - exists(CallNode route_call, CallNode decorator_call | - route_call.getFunction().(AttrNode).getObject("route").pointsTo().getClass() = theFlaskClass() and - decorator_call.getFunction() = route_call and - route_call.getArg(0) = route and - decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func - ) + exists(CallNode route_call, CallNode decorator_call | + route_call.getFunction().(AttrNode).getObject("route").pointsTo().getClass() = theFlaskClass() and + decorator_call.getFunction() = route_call and + route_call.getArg(0) = route and + decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func + ) } /* Helper for add_url_rule */ private predicate add_url_rule_call(ControlFlowNode regex, ControlFlowNode callable) { - exists(CallNode call | - call.getFunction().(AttrNode).getObject("add_url_rule").pointsTo().getClass() = theFlaskClass() and - regex = call.getArg(0) - | - callable = call.getArg(2) or - callable = call.getArgByName("view_func") - ) + exists(CallNode call | + call.getFunction().(AttrNode).getObject("add_url_rule").pointsTo().getClass() = theFlaskClass() and + regex = call.getArg(0) + | + callable = call.getArg(2) or + callable = call.getArgByName("view_func") + ) } /** Holds if urls matching `regex` are routed to `func` */ predicate add_url_rule(ControlFlowNode regex, Function func) { - exists(ControlFlowNode callable | add_url_rule_call(regex, callable) | - exists(PythonFunctionValue f | f.getScope() = func and callable.pointsTo(f)) - or - /* MethodView.as_view() */ - exists(MethodViewClass view_cls | view_cls.asTaint().taints(callable) | - func = view_cls.lookup(httpVerbLower()).(FunctionValue).getScope() - ) - /* TODO: -- Handle Views that aren't MethodViews */ + exists(ControlFlowNode callable | add_url_rule_call(regex, callable) | + exists(PythonFunctionValue f | f.getScope() = func and callable.pointsTo(f)) + or + /* MethodView.as_view() */ + exists(MethodViewClass view_cls | view_cls.asTaint().taints(callable) | + func = view_cls.lookup(httpVerbLower()).(FunctionValue).getScope() ) + /* TODO: -- Handle Views that aren't MethodViews */ + ) } /** @@ -52,53 +52,53 @@ predicate add_url_rule(ControlFlowNode regex, Function func) { * any of flask's routing mechanisms. */ predicate flask_routing(ControlFlowNode regex, Function func) { - app_route(regex, func) - or - add_url_rule(regex, func) + app_route(regex, func) + or + add_url_rule(regex, func) } /** A class that extends flask.views.MethodView */ private class MethodViewClass extends ClassValue { - MethodViewClass() { this.getASuperType() = theFlaskMethodViewClass() } + MethodViewClass() { this.getASuperType() = theFlaskMethodViewClass() } - /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ - string taintString() { result = "flask/" + this.getQualifiedName() + ".as.view" } + /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ + string taintString() { result = "flask/" + this.getQualifiedName() + ".as.view" } - /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ - TaintKind asTaint() { result = this.taintString() } + /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ + TaintKind asTaint() { result = this.taintString() } } private class MethodViewTaint extends TaintKind { - MethodViewTaint() { any(MethodViewClass cls).taintString() = this } + MethodViewTaint() { any(MethodViewClass cls).taintString() = this } } /** A source of method view "taint"s. */ private class AsView extends TaintSource { - AsView() { - exists(ClassValue view_class | - view_class.getASuperType() = theFlaskMethodViewClass() and - this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) - ) - } + AsView() { + exists(ClassValue view_class | + view_class.getASuperType() = theFlaskMethodViewClass() and + this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) + ) + } - override string toString() { result = "flask.MethodView.as_view()" } + override string toString() { result = "flask.MethodView.as_view()" } - override predicate isSourceOf(TaintKind kind) { - exists(MethodViewClass view_class | - kind = view_class.asTaint() and - this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) - ) - } + override predicate isSourceOf(TaintKind kind) { + exists(MethodViewClass view_class | + kind = view_class.asTaint() and + this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class) + ) + } } class FlaskCookieSet extends CookieSet, CallNode { - FlaskCookieSet() { - any(FlaskResponseTaintKind t).taints(this.getFunction().(AttrNode).getObject("set_cookie")) - } + FlaskCookieSet() { + any(FlaskResponseTaintKind t).taints(this.getFunction().(AttrNode).getObject("set_cookie")) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/flask/Redirect.qll b/python/ql/src/semmle/python/web/flask/Redirect.qll index 4c4e289c605..caba59ec2c1 100644 --- a/python/ql/src/semmle/python/web/flask/Redirect.qll +++ b/python/ql/src/semmle/python/web/flask/Redirect.qll @@ -15,12 +15,12 @@ FunctionValue flask_redirect() { result = Value::named("flask.redirect") } * Represents an argument to the `flask.redirect` function. */ class FlaskRedirect extends HttpRedirectTaintSink { - override string toString() { result = "flask.redirect" } + override string toString() { result = "flask.redirect" } - FlaskRedirect() { - exists(CallNode call | - flask_redirect().getACall() = call and - this = call.getAnArg() - ) - } + FlaskRedirect() { + exists(CallNode call | + flask_redirect().getACall() = call and + this = call.getAnArg() + ) + } } diff --git a/python/ql/src/semmle/python/web/flask/Request.qll b/python/ql/src/semmle/python/web/flask/Request.qll index 5548e409c32..ceae5e7a2c6 100644 --- a/python/ql/src/semmle/python/web/flask/Request.qll +++ b/python/ql/src/semmle/python/web/flask/Request.qll @@ -7,52 +7,52 @@ private Value theFlaskRequestObject() { result = Value::named("flask.request") } /** Holds if `attr` is an access of attribute `name` of the flask request object */ private predicate flask_request_attr(AttrNode attr, string name) { - attr.isLoad() and - attr.getObject(name).pointsTo(theFlaskRequestObject()) + attr.isLoad() and + attr.getObject(name).pointsTo(theFlaskRequestObject()) } /** Source of external data from a flask request */ class FlaskRequestData extends HttpRequestTaintSource { - FlaskRequestData() { - not this instanceof FlaskRequestArgs and - exists(string name | flask_request_attr(this, name) | - name = "path" or - name = "full_path" or - name = "base_url" or - name = "url" - ) - } + FlaskRequestData() { + not this instanceof FlaskRequestArgs and + exists(string name | flask_request_attr(this, name) | + name = "path" or + name = "full_path" or + name = "base_url" or + name = "url" + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "flask.request" } + override string toString() { result = "flask.request" } } /** Source of dictionary whose values are externally controlled */ class FlaskRequestArgs extends HttpRequestTaintSource { - FlaskRequestArgs() { - exists(string attr | flask_request_attr(this, attr) | - attr = "args" or - attr = "form" or - attr = "values" or - attr = "files" or - attr = "headers" or - attr = "json" - ) - } + FlaskRequestArgs() { + exists(string attr | flask_request_attr(this, attr) | + attr = "args" or + attr = "form" or + attr = "values" or + attr = "files" or + attr = "headers" or + attr = "json" + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "flask.request.args" } + override string toString() { result = "flask.request.args" } } /** Source of dictionary whose values are externally controlled */ class FlaskRequestJson extends HttpRequestTaintSource { - FlaskRequestJson() { flask_request_attr(this, "json") } + FlaskRequestJson() { flask_request_attr(this, "json") } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalJsonKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalJsonKind } - override string toString() { result = "flask.request.json" } + override string toString() { result = "flask.request.json" } } /** @@ -66,23 +66,23 @@ class FlaskRequestJson extends HttpRequestTaintSource { * ``` */ class FlaskRoutedParameter extends HttpRequestTaintSource { - FlaskRoutedParameter() { - exists(string name, Function func, StrConst url_pattern | - this.(ControlFlowNode).getNode() = func.getArgByName(name) and - flask_routing(url_pattern.getAFlowNode(), func) and - exists(string match | - match = url_pattern.getS().regexpFind(werkzeug_rule_re(), _, _) and - name = match.regexpCapture(werkzeug_rule_re(), 4) - ) - ) - } + FlaskRoutedParameter() { + exists(string name, Function func, StrConst url_pattern | + this.(ControlFlowNode).getNode() = func.getArgByName(name) and + flask_routing(url_pattern.getAFlowNode(), func) and + exists(string match | + match = url_pattern.getS().regexpFind(werkzeug_rule_re(), _, _) and + name = match.regexpCapture(werkzeug_rule_re(), 4) + ) + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } private string werkzeug_rule_re() { - // since flask uses werkzeug internally, we are using its routing rules from - // https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151 - result = - "(?[^<]*)<(?:(?[a-zA-Z_][a-zA-Z0-9_]*)(?:\\((?.*?)\\))?\\:)?(?[a-zA-Z_][a-zA-Z0-9_]*)>" + // since flask uses werkzeug internally, we are using its routing rules from + // https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151 + result = + "(?[^<]*)<(?:(?[a-zA-Z_][a-zA-Z0-9_]*)(?:\\((?.*?)\\))?\\:)?(?[a-zA-Z_][a-zA-Z0-9_]*)>" } diff --git a/python/ql/src/semmle/python/web/flask/Response.qll b/python/ql/src/semmle/python/web/flask/Response.qll index e070f19b1f6..e8166175580 100644 --- a/python/ql/src/semmle/python/web/flask/Response.qll +++ b/python/ql/src/semmle/python/web/flask/Response.qll @@ -8,48 +8,48 @@ import semmle.python.web.flask.General * http response malice. */ class FlaskRoutedResponse extends HttpResponseTaintSink { - FlaskRoutedResponse() { - exists(PythonFunctionValue response | - flask_routing(_, response.getScope()) and - this = response.getAReturnedNode() - ) - } + FlaskRoutedResponse() { + exists(PythonFunctionValue response | + flask_routing(_, response.getScope()) and + this = response.getAReturnedNode() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "flask.routed.response" } + override string toString() { result = "flask.routed.response" } } class FlaskResponseArgument extends HttpResponseTaintSink { - FlaskResponseArgument() { - exists(CallNode call | - ( - call.getFunction().pointsTo(theFlaskReponseClass()) - or - call.getFunction().pointsTo(Value::named("flask.make_response")) - ) and - call.getArg(0) = this - ) - } + FlaskResponseArgument() { + exists(CallNode call | + ( + call.getFunction().pointsTo(theFlaskReponseClass()) + or + call.getFunction().pointsTo(Value::named("flask.make_response")) + ) and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "flask.response.argument" } + override string toString() { result = "flask.response.argument" } } class FlaskResponseTaintKind extends TaintKind { - FlaskResponseTaintKind() { this = "flask.Response" } + FlaskResponseTaintKind() { this = "flask.Response" } } class FlaskResponseConfiguration extends TaintTracking::Configuration { - FlaskResponseConfiguration() { this = "Flask response configuration" } + FlaskResponseConfiguration() { this = "Flask response configuration" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - kind instanceof FlaskResponseTaintKind and - ( - node.asCfgNode().(CallNode).getFunction().pointsTo(theFlaskReponseClass()) - or - node.asCfgNode().(CallNode).getFunction().pointsTo(Value::named("flask.make_response")) - ) - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + kind instanceof FlaskResponseTaintKind and + ( + node.asCfgNode().(CallNode).getFunction().pointsTo(theFlaskReponseClass()) + or + node.asCfgNode().(CallNode).getFunction().pointsTo(Value::named("flask.make_response")) + ) + } } diff --git a/python/ql/src/semmle/python/web/pyramid/Redirect.qll b/python/ql/src/semmle/python/web/pyramid/Redirect.qll index 2ab68b40621..85301256736 100644 --- a/python/ql/src/semmle/python/web/pyramid/Redirect.qll +++ b/python/ql/src/semmle/python/web/pyramid/Redirect.qll @@ -10,24 +10,24 @@ import semmle.python.security.strings.Basic import semmle.python.web.Http private ClassValue redirectClass() { - exists(ModuleValue ex | ex.getName() = "pyramid.httpexceptions" | - ex.attr("HTTPFound") = result - or - ex.attr("HTTPTemporaryRedirect") = result - ) + exists(ModuleValue ex | ex.getName() = "pyramid.httpexceptions" | + ex.attr("HTTPFound") = result + or + ex.attr("HTTPTemporaryRedirect") = result + ) } /** * Represents an argument to the `tornado.redirect` function. */ class PyramidRedirect extends HttpRedirectTaintSink { - override string toString() { result = "pyramid.redirect" } + override string toString() { result = "pyramid.redirect" } - PyramidRedirect() { - exists(CallNode call | call.getFunction().pointsTo(redirectClass()) | - call.getArg(0) = this - or - call.getArgByName("location") = this - ) - } + PyramidRedirect() { + exists(CallNode call | call.getFunction().pointsTo(redirectClass()) | + call.getArg(0) = this + or + call.getArgByName("location") = this + ) + } } diff --git a/python/ql/src/semmle/python/web/pyramid/Request.qll b/python/ql/src/semmle/python/web/pyramid/Request.qll index f3422b682d6..19b1e1af25e 100644 --- a/python/ql/src/semmle/python/web/pyramid/Request.qll +++ b/python/ql/src/semmle/python/web/pyramid/Request.qll @@ -5,21 +5,21 @@ private import semmle.python.web.webob.Request private import semmle.python.web.pyramid.View class PyramidRequest extends BaseWebobRequest { - PyramidRequest() { this = "pyramid.request" } + PyramidRequest() { this = "pyramid.request" } - override ClassValue getType() { result = Value::named("pyramid.request.Request") } + override ClassValue getType() { result = Value::named("pyramid.request.Request") } } /** Source of pyramid request objects */ class PyramidViewArgument extends HttpRequestTaintSource { - PyramidViewArgument() { - exists(Function view_func | - is_pyramid_view_function(view_func) and - this.(ControlFlowNode).getNode() = view_func.getArg(0) - ) - } + PyramidViewArgument() { + exists(Function view_func | + is_pyramid_view_function(view_func) and + this.(ControlFlowNode).getNode() = view_func.getArg(0) + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof PyramidRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof PyramidRequest } - override string toString() { result = "pyramid.view.argument" } + override string toString() { result = "pyramid.view.argument" } } diff --git a/python/ql/src/semmle/python/web/pyramid/Response.qll b/python/ql/src/semmle/python/web/pyramid/Response.qll index c51a437350d..f29832f2d06 100644 --- a/python/ql/src/semmle/python/web/pyramid/Response.qll +++ b/python/ql/src/semmle/python/web/pyramid/Response.qll @@ -10,29 +10,29 @@ private import semmle.python.web.Http * http response malice. */ class PyramidRoutedResponse extends HttpResponseTaintSink { - PyramidRoutedResponse() { - exists(PythonFunctionValue view | - is_pyramid_view_function(view.getScope()) and - this = view.getAReturnedNode() - ) - } + PyramidRoutedResponse() { + exists(PythonFunctionValue view | + is_pyramid_view_function(view.getScope()) and + this = view.getAReturnedNode() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } - override string toString() { result = "pyramid.routed.response" } + override string toString() { result = "pyramid.routed.response" } } class PyramidCookieSet extends CookieSet, CallNode { - PyramidCookieSet() { - exists(ControlFlowNode f | - f = this.getFunction().(AttrNode).getObject("set_cookie") and - f.pointsTo().getClass() = Value::named("pyramid.response.Response") - ) - } + PyramidCookieSet() { + exists(ControlFlowNode f | + f = this.getFunction().(AttrNode).getObject("set_cookie") and + f.pointsTo().getClass() = Value::named("pyramid.response.Response") + ) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/pyramid/View.qll b/python/ql/src/semmle/python/web/pyramid/View.qll index 3bf49de87c3..b4e0dc770fc 100644 --- a/python/ql/src/semmle/python/web/pyramid/View.qll +++ b/python/ql/src/semmle/python/web/pyramid/View.qll @@ -5,5 +5,5 @@ ModuleValue thePyramidViewModule() { result.getName() = "pyramid.view" } Value thePyramidViewConfig() { result = thePyramidViewModule().attr("view_config") } predicate is_pyramid_view_function(Function func) { - func.getADecorator().pointsTo().getClass() = thePyramidViewConfig() + func.getADecorator().pointsTo().getClass() = thePyramidViewConfig() } diff --git a/python/ql/src/semmle/python/web/stdlib/Request.qll b/python/ql/src/semmle/python/web/stdlib/Request.qll index 459a5091389..c1095d811ce 100644 --- a/python/ql/src/semmle/python/web/stdlib/Request.qll +++ b/python/ql/src/semmle/python/web/stdlib/Request.qll @@ -3,122 +3,124 @@ * Specifically, we model `HttpRequestTaintSource`s from instances of `BaseHTTPRequestHandler` * (or subclasses) and form parsing using `cgi.FieldStorage`. */ + import python import semmle.python.dataflow.TaintTracking import semmle.python.web.Http /** Source of BaseHTTPRequestHandler instances. */ class StdLibRequestSource extends HttpRequestTaintSource { - StdLibRequestSource() { - exists(ClassValue cls | - cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") - or - cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") - | - this.(ControlFlowNode).pointsTo().getClass() = cls - ) - } + StdLibRequestSource() { + exists(ClassValue cls | + cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") + or + cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") + | + this.(ControlFlowNode).pointsTo().getClass() = cls + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof BaseHTTPRequestHandlerKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof BaseHTTPRequestHandlerKind } } /** TaintKind for an instance of BaseHTTPRequestHandler. */ class BaseHTTPRequestHandlerKind extends TaintKind { - BaseHTTPRequestHandlerKind() { this = "BaseHTTPRequestHandlerKind" } + BaseHTTPRequestHandlerKind() { this = "BaseHTTPRequestHandlerKind" } - override TaintKind getTaintOfAttribute(string name) { - name in ["requestline", "path"] and - result instanceof ExternalStringKind - or - name = "headers" and - result instanceof HTTPMessageKind - or - name = "rfile" and - result instanceof ExternalFileObject - } + override TaintKind getTaintOfAttribute(string name) { + name in ["requestline", "path"] and + result instanceof ExternalStringKind + or + name = "headers" and + result instanceof HTTPMessageKind + or + name = "rfile" and + result instanceof ExternalFileObject + } } /** TaintKind for headers (instance of HTTPMessage). */ class HTTPMessageKind extends ExternalStringDictKind { - override TaintKind getTaintOfMethodResult(string name) { - result = super.getTaintOfMethodResult(name) - or - name = "get_all" and - result.(SequenceKind).getItem() = this.getValue() - or - name in ["as_bytes", "as_string"] and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + result = super.getTaintOfMethodResult(name) + or + name = "get_all" and + result.(SequenceKind).getItem() = this.getValue() + or + name in ["as_bytes", "as_string"] and + result instanceof ExternalStringKind + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - result = super.getTaintForFlowStep(fromnode, tonode) - or - exists(ClassValue cls | cls = ClassValue::unicode() or cls = ClassValue::bytes() | - tonode = cls.getACall() and - tonode.(CallNode).getArg(0) = fromnode and - result instanceof ExternalStringKind - ) - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = super.getTaintForFlowStep(fromnode, tonode) + or + exists(ClassValue cls | cls = ClassValue::unicode() or cls = ClassValue::bytes() | + tonode = cls.getACall() and + tonode.(CallNode).getArg(0) = fromnode and + result instanceof ExternalStringKind + ) + } } /** Source of parsed HTTP forms (by using the `cgi` module). */ class CgiFieldStorageSource extends HttpRequestTaintSource { - CgiFieldStorageSource() { this = Value::named("cgi.FieldStorage").getACall() } + CgiFieldStorageSource() { this = Value::named("cgi.FieldStorage").getACall() } - override predicate isSourceOf(TaintKind kind) { kind instanceof CgiFieldStorageFormKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof CgiFieldStorageFormKind } } /** TaintKind for a parsed HTTP form. */ class CgiFieldStorageFormKind extends TaintKind { - /* - * There is a slight difference between how we model form/fields and how it is handled by the code. - * In the code - * ``` - * form = cgi.FieldStorage() - * field = form['myfield'] - * ``` - * both `form` and `field` have the type `cgi.FieldStorage`. This allows the code to represent - * nested forms as `form['nested_form']['myfield']`. However, since HTML forms can't be nested - * we ignore that detail since it allows for a more clean modeling. - */ - CgiFieldStorageFormKind() { this = "CgiFieldStorageFormKind" } + /* + * There is a slight difference between how we model form/fields and how it is handled by the code. + * In the code + * ``` + * form = cgi.FieldStorage() + * field = form['myfield'] + * ``` + * both `form` and `field` have the type `cgi.FieldStorage`. This allows the code to represent + * nested forms as `form['nested_form']['myfield']`. However, since HTML forms can't be nested + * we ignore that detail since it allows for a more clean modeling. + */ - override TaintKind getTaintOfAttribute(string name) { - name = "value" and result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind - } + CgiFieldStorageFormKind() { this = "CgiFieldStorageFormKind" } - override TaintKind getTaintOfMethodResult(string name) { - name = "getvalue" and - ( - result instanceof ExternalStringKind - or - result.(SequenceKind).getItem() instanceof ExternalStringKind - ) - or - name = "getfirst" and - result instanceof ExternalStringKind - or - name = "getlist" and - result.(SequenceKind).getItem() instanceof ExternalStringKind - } + override TaintKind getTaintOfAttribute(string name) { + name = "value" and result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind + } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(SubscriptNode).getObject() = fromnode and - ( - result instanceof CgiFieldStorageFieldKind - or - result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind - ) - } + override TaintKind getTaintOfMethodResult(string name) { + name = "getvalue" and + ( + result instanceof ExternalStringKind + or + result.(SequenceKind).getItem() instanceof ExternalStringKind + ) + or + name = "getfirst" and + result instanceof ExternalStringKind + or + name = "getlist" and + result.(SequenceKind).getItem() instanceof ExternalStringKind + } + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(SubscriptNode).getObject() = fromnode and + ( + result instanceof CgiFieldStorageFieldKind + or + result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind + ) + } } /** TaintKind for the field of a parsed HTTP form. */ class CgiFieldStorageFieldKind extends TaintKind { - CgiFieldStorageFieldKind() { this = "CgiFieldStorageFieldKind" } + CgiFieldStorageFieldKind() { this = "CgiFieldStorageFieldKind" } - override TaintKind getTaintOfAttribute(string name) { - name in ["filename", "value"] and result instanceof ExternalStringKind - or - name = "file" and result instanceof ExternalFileObject - } + override TaintKind getTaintOfAttribute(string name) { + name in ["filename", "value"] and result instanceof ExternalStringKind + or + name = "file" and result instanceof ExternalFileObject + } } diff --git a/python/ql/src/semmle/python/web/stdlib/Response.qll b/python/ql/src/semmle/python/web/stdlib/Response.qll index 58949e0a6d9..784690dea5a 100644 --- a/python/ql/src/semmle/python/web/stdlib/Response.qll +++ b/python/ql/src/semmle/python/web/stdlib/Response.qll @@ -7,37 +7,37 @@ import semmle.python.dataflow.TaintTracking import semmle.python.web.Http private predicate is_wfile(AttrNode wfile) { - exists(ClassValue cls | - // Python 2 - cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") - or - // Python 3 - cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") - | - wfile.getObject("wfile").pointsTo().getClass() = cls - ) + exists(ClassValue cls | + // Python 2 + cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") + or + // Python 3 + cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") + | + wfile.getObject("wfile").pointsTo().getClass() = cls + ) } /** Sink for `h.wfile.write` where `h` is an instance of BaseHTTPRequestHandler. */ class StdLibWFileWriteSink extends HttpResponseTaintSink { - StdLibWFileWriteSink() { - exists(CallNode call | - is_wfile(call.getFunction().(AttrNode).getObject("write")) and - call.getArg(0) = this - ) - } + StdLibWFileWriteSink() { + exists(CallNode call | + is_wfile(call.getFunction().(AttrNode).getObject("write")) and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } } /** Sink for `h.wfile.writelines` where `h` is an instance of BaseHTTPRequestHandler. */ class StdLibWFileWritelinesSink extends HttpResponseTaintSink { - StdLibWFileWritelinesSink() { - exists(CallNode call | - is_wfile(call.getFunction().(AttrNode).getObject("writelines")) and - call.getArg(0) = this - ) - } + StdLibWFileWritelinesSink() { + exists(CallNode call | + is_wfile(call.getFunction().(AttrNode).getObject("writelines")) and + call.getArg(0) = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringSequenceKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Redirect.qll b/python/ql/src/semmle/python/web/tornado/Redirect.qll index f846f113816..93875c948ce 100644 --- a/python/ql/src/semmle/python/web/tornado/Redirect.qll +++ b/python/ql/src/semmle/python/web/tornado/Redirect.qll @@ -14,15 +14,15 @@ import Tornado * Represents an argument to the `tornado.redirect` function. */ class TornadoHttpRequestHandlerRedirect extends HttpRedirectTaintSink { - override string toString() { result = "tornado.HttpRequestHandler.redirect" } + override string toString() { result = "tornado.HttpRequestHandler.redirect" } - TornadoHttpRequestHandlerRedirect() { - exists(CallNode call, ControlFlowNode node | - node = call.getFunction().(AttrNode).getObject("redirect") and - isTornadoRequestHandlerInstance(node) and - this = call.getArg(0) - ) - } + TornadoHttpRequestHandlerRedirect() { + exists(CallNode call, ControlFlowNode node | + node = call.getFunction().(AttrNode).getObject("redirect") and + isTornadoRequestHandlerInstance(node) and + this = call.getArg(0) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Request.qll b/python/ql/src/semmle/python/web/tornado/Request.qll index cfb7bfa7b04..1dde3379117 100644 --- a/python/ql/src/semmle/python/web/tornado/Request.qll +++ b/python/ql/src/semmle/python/web/tornado/Request.qll @@ -5,68 +5,68 @@ import Tornado /** A tornado.request.HttpRequest object */ class TornadoRequest extends TaintKind { - TornadoRequest() { this = "tornado.request.HttpRequest" } + TornadoRequest() { this = "tornado.request.HttpRequest" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringDictKind and - ( - name = "headers" or - name = "cookies" - ) - or - result instanceof ExternalStringKind and - ( - name = "uri" or - name = "query" or - name = "body" - ) - or - result instanceof ExternalStringSequenceDictKind and - ( - name = "arguments" or - name = "query_arguments" or - name = "body_arguments" - ) - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringDictKind and + ( + name = "headers" or + name = "cookies" + ) + or + result instanceof ExternalStringKind and + ( + name = "uri" or + name = "query" or + name = "body" + ) + or + result instanceof ExternalStringSequenceDictKind and + ( + name = "arguments" or + name = "query_arguments" or + name = "body_arguments" + ) + } } class TornadoRequestSource extends HttpRequestTaintSource { - TornadoRequestSource() { isTornadoRequestHandlerInstance(this.(AttrNode).getObject("request")) } + TornadoRequestSource() { isTornadoRequestHandlerInstance(this.(AttrNode).getObject("request")) } - override string toString() { result = "Tornado request source" } + override string toString() { result = "Tornado request source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoRequest } } class TornadoExternalInputSource extends HttpRequestTaintSource { - TornadoExternalInputSource() { - exists(string name | - name = "get_argument" or - name = "get_query_argument" or - name = "get_body_argument" or - name = "decode_argument" - | - this = callToNamedTornadoRequestHandlerMethod(name) - ) - } + TornadoExternalInputSource() { + exists(string name | + name = "get_argument" or + name = "get_query_argument" or + name = "get_body_argument" or + name = "decode_argument" + | + this = callToNamedTornadoRequestHandlerMethod(name) + ) + } - override string toString() { result = "Tornado request method" } + override string toString() { result = "Tornado request method" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } class TornadoExternalInputListSource extends HttpRequestTaintSource { - TornadoExternalInputListSource() { - exists(string name | - name = "get_arguments" or - name = "get_query_arguments" or - name = "get_body_arguments" - | - this = callToNamedTornadoRequestHandlerMethod(name) - ) - } + TornadoExternalInputListSource() { + exists(string name | + name = "get_arguments" or + name = "get_query_arguments" or + name = "get_body_arguments" + | + this = callToNamedTornadoRequestHandlerMethod(name) + ) + } - override string toString() { result = "Tornado request method" } + override string toString() { result = "Tornado request method" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Response.qll b/python/ql/src/semmle/python/web/tornado/Response.qll index b9213ac8446..8ef3762ab2d 100644 --- a/python/ql/src/semmle/python/web/tornado/Response.qll +++ b/python/ql/src/semmle/python/web/tornado/Response.qll @@ -5,43 +5,43 @@ private import semmle.python.web.Http import Tornado class TornadoConnection extends TaintKind { - TornadoConnection() { this = "tornado.http.connection" } + TornadoConnection() { this = "tornado.http.connection" } } class TornadoConnectionSource extends TaintSource { - TornadoConnectionSource() { - isTornadoRequestHandlerInstance(this.(AttrNode).getObject("connection")) - } + TornadoConnectionSource() { + isTornadoRequestHandlerInstance(this.(AttrNode).getObject("connection")) + } - override string toString() { result = "Tornado http connection source" } + override string toString() { result = "Tornado http connection source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoConnection } + override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoConnection } } class TornadoConnectionWrite extends HttpResponseTaintSink { - override string toString() { result = "tornado.connection.write" } + override string toString() { result = "tornado.connection.write" } - TornadoConnectionWrite() { - exists(CallNode call, ControlFlowNode conn | - conn = call.getFunction().(AttrNode).getObject("write") and - this = call.getAnArg() and - exists(TornadoConnection tc | tc.taints(conn)) - ) - } + TornadoConnectionWrite() { + exists(CallNode call, ControlFlowNode conn | + conn = call.getFunction().(AttrNode).getObject("write") and + this = call.getAnArg() and + exists(TornadoConnection tc | tc.taints(conn)) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } class TornadoHttpRequestHandlerWrite extends HttpResponseTaintSink { - override string toString() { result = "tornado.HttpRequestHandler.write" } + override string toString() { result = "tornado.HttpRequestHandler.write" } - TornadoHttpRequestHandlerWrite() { - exists(CallNode call, ControlFlowNode node | - node = call.getFunction().(AttrNode).getObject("write") and - this = call.getAnArg() and - isTornadoRequestHandlerInstance(node) - ) - } + TornadoHttpRequestHandlerWrite() { + exists(CallNode call, ControlFlowNode node | + node = call.getFunction().(AttrNode).getObject("write") and + this = call.getAnArg() and + isTornadoRequestHandlerInstance(node) + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } diff --git a/python/ql/src/semmle/python/web/tornado/Tornado.qll b/python/ql/src/semmle/python/web/tornado/Tornado.qll index d9f6ab823b9..40523b15261 100644 --- a/python/ql/src/semmle/python/web/tornado/Tornado.qll +++ b/python/ql/src/semmle/python/web/tornado/Tornado.qll @@ -3,11 +3,11 @@ import semmle.python.dataflow.TaintTracking import semmle.python.web.Http private ClassValue theTornadoRequestHandlerClass() { - result = Value::named("tornado.web.RequestHandler") + result = Value::named("tornado.web.RequestHandler") } ClassValue aTornadoRequestHandlerClass() { - result.getABaseType+() = theTornadoRequestHandlerClass() + result.getABaseType+() = theTornadoRequestHandlerClass() } /** @@ -15,36 +15,36 @@ ClassValue aTornadoRequestHandlerClass() { * `RequestHandler` class. */ predicate isTornadoRequestHandlerInstance(ControlFlowNode node) { - node.pointsTo().getClass() = aTornadoRequestHandlerClass() - or - /* - * In some cases, the points-to analysis won't capture all instances we care - * about. For these, we use the following syntactic check. First, that - * `node` appears inside a method of a subclass of - * `tornado.web.RequestHandler`: - */ + node.pointsTo().getClass() = aTornadoRequestHandlerClass() + or + /* + * In some cases, the points-to analysis won't capture all instances we care + * about. For these, we use the following syntactic check. First, that + * `node` appears inside a method of a subclass of + * `tornado.web.RequestHandler`: + */ - node.getScope().getEnclosingScope() = aTornadoRequestHandlerClass().getScope() and - /* Secondly, that `node` refers to the `self` argument: */ - node.isLoad() and - node.(NameNode).isSelf() + node.getScope().getEnclosingScope() = aTornadoRequestHandlerClass().getScope() and + /* Secondly, that `node` refers to the `self` argument: */ + node.isLoad() and + node.(NameNode).isSelf() } CallNode callToNamedTornadoRequestHandlerMethod(string name) { - isTornadoRequestHandlerInstance(result.getFunction().(AttrNode).getObject(name)) + isTornadoRequestHandlerInstance(result.getFunction().(AttrNode).getObject(name)) } class TornadoCookieSet extends CookieSet, CallNode { - TornadoCookieSet() { - exists(ControlFlowNode f | - f = this.getFunction().(AttrNode).getObject("set_cookie") and - isTornadoRequestHandlerInstance(f) - ) - } + TornadoCookieSet() { + exists(ControlFlowNode f | + f = this.getFunction().(AttrNode).getObject("set_cookie") and + isTornadoRequestHandlerInstance(f) + ) + } - override string toString() { result = CallNode.super.toString() } + override string toString() { result = CallNode.super.toString() } - override ControlFlowNode getKey() { result = this.getArg(0) } + override ControlFlowNode getKey() { result = this.getArg(0) } - override ControlFlowNode getValue() { result = this.getArg(1) } + override ControlFlowNode getValue() { result = this.getArg(1) } } diff --git a/python/ql/src/semmle/python/web/turbogears/Request.qll b/python/ql/src/semmle/python/web/turbogears/Request.qll index 19d9be06c52..806d85bafc5 100644 --- a/python/ql/src/semmle/python/web/turbogears/Request.qll +++ b/python/ql/src/semmle/python/web/turbogears/Request.qll @@ -4,23 +4,23 @@ import semmle.python.web.Http import TurboGears private class ValidatedMethodParameter extends Parameter { - ValidatedMethodParameter() { - exists(string name, TurboGearsControllerMethod method | - method.getArgByName(name) = this and - method.getValidationDict().getItem(_).(KeyValuePair).getKey().(StrConst).getText() = name - ) - } + ValidatedMethodParameter() { + exists(string name, TurboGearsControllerMethod method | + method.getArgByName(name) = this and + method.getValidationDict().getItem(_).(KeyValuePair).getKey().(StrConst).getText() = name + ) + } } class UnvalidatedControllerMethodParameter extends HttpRequestTaintSource { - UnvalidatedControllerMethodParameter() { - exists(Parameter p | - any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and - not p instanceof ValidatedMethodParameter and - not p.isSelf() and - p.(Name).getAFlowNode() = this - ) - } + UnvalidatedControllerMethodParameter() { + exists(Parameter p | + any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and + not p instanceof ValidatedMethodParameter and + not p.isSelf() and + p.(Name).getAFlowNode() = this + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } } diff --git a/python/ql/src/semmle/python/web/turbogears/Response.qll b/python/ql/src/semmle/python/web/turbogears/Response.qll index b6345d3755a..a095057e7d2 100644 --- a/python/ql/src/semmle/python/web/turbogears/Response.qll +++ b/python/ql/src/semmle/python/web/turbogears/Response.qll @@ -5,27 +5,27 @@ import semmle.python.web.Http import TurboGears class ControllerMethodReturnValue extends HttpResponseTaintSink { - override string toString() { result = "TurboGears ControllerMethodReturnValue" } + override string toString() { result = "TurboGears ControllerMethodReturnValue" } - ControllerMethodReturnValue() { - exists(TurboGearsControllerMethod m | - m.getAReturnValueFlowNode() = this and - not m.isTemplated() - ) - } + ControllerMethodReturnValue() { + exists(TurboGearsControllerMethod m | + m.getAReturnValueFlowNode() = this and + not m.isTemplated() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof StringKind } + override predicate sinks(TaintKind kind) { kind instanceof StringKind } } class ControllerMethodTemplatedReturnValue extends HttpResponseTaintSink { - override string toString() { result = "TurboGears ControllerMethodTemplatedReturnValue" } + override string toString() { result = "TurboGears ControllerMethodTemplatedReturnValue" } - ControllerMethodTemplatedReturnValue() { - exists(TurboGearsControllerMethod m | - m.getAReturnValueFlowNode() = this and - m.isTemplated() - ) - } + ControllerMethodTemplatedReturnValue() { + exists(TurboGearsControllerMethod m | + m.getAReturnValueFlowNode() = this and + m.isTemplated() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringDictKind } } diff --git a/python/ql/src/semmle/python/web/turbogears/TurboGears.qll b/python/ql/src/semmle/python/web/turbogears/TurboGears.qll index 547a6c0e505..0c53dcc17bc 100644 --- a/python/ql/src/semmle/python/web/turbogears/TurboGears.qll +++ b/python/ql/src/semmle/python/web/turbogears/TurboGears.qll @@ -6,28 +6,28 @@ private ClassValue theTurboGearsControllerClass() { result = Value::named("tg.TG ClassValue aTurboGearsControllerClass() { result.getABaseType+() = theTurboGearsControllerClass() } class TurboGearsControllerMethod extends Function { - ControlFlowNode decorator; + ControlFlowNode decorator; - TurboGearsControllerMethod() { - aTurboGearsControllerClass().getScope() = this.getScope() and - decorator = this.getADecorator().getAFlowNode() and - /* Is decorated with @expose() or @expose(path) */ - ( - decorator.(CallNode).getFunction().(NameNode).getId() = "expose" - or - decorator.pointsTo().getClass() = Value::named("tg.expose") - ) - } + TurboGearsControllerMethod() { + aTurboGearsControllerClass().getScope() = this.getScope() and + decorator = this.getADecorator().getAFlowNode() and + /* Is decorated with @expose() or @expose(path) */ + ( + decorator.(CallNode).getFunction().(NameNode).getId() = "expose" + or + decorator.pointsTo().getClass() = Value::named("tg.expose") + ) + } - private ControlFlowNode templateName() { result = decorator.(CallNode).getArg(0) } + private ControlFlowNode templateName() { result = decorator.(CallNode).getArg(0) } - predicate isTemplated() { exists(templateName()) } + predicate isTemplated() { exists(templateName()) } - Dict getValidationDict() { - exists(Call call, Value dict | - call = this.getADecorator() and - call.getFunc().(Name).getId() = "validate" and - call.getArg(0).pointsTo(dict, result) - ) - } + Dict getValidationDict() { + exists(Call call, Value dict | + call = this.getADecorator() and + call.getFunc().(Name).getId() = "validate" and + call.getArg(0).pointsTo(dict, result) + ) + } } diff --git a/python/ql/src/semmle/python/web/twisted/Request.qll b/python/ql/src/semmle/python/web/twisted/Request.qll index 0be6fc78f2c..e164de585d1 100644 --- a/python/ql/src/semmle/python/web/twisted/Request.qll +++ b/python/ql/src/semmle/python/web/twisted/Request.qll @@ -5,31 +5,31 @@ import Twisted /** A twisted.web.http.Request object */ class TwistedRequest extends TaintKind { - TwistedRequest() { this = "twisted.request.http.Request" } + TwistedRequest() { this = "twisted.request.http.Request" } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringSequenceDictKind and - name = "args" - or - result instanceof ExternalStringKind and - name = "uri" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringSequenceDictKind and + name = "args" + or + result instanceof ExternalStringKind and + name = "uri" + } - override TaintKind getTaintOfMethodResult(string name) { - ( - name = "getHeader" or - name = "getCookie" or - name = "getUser" or - name = "getPassword" - ) and - result instanceof ExternalStringKind - } + override TaintKind getTaintOfMethodResult(string name) { + ( + name = "getHeader" or + name = "getCookie" or + name = "getUser" or + name = "getPassword" + ) and + result instanceof ExternalStringKind + } } class TwistedRequestSource extends HttpRequestTaintSource { - TwistedRequestSource() { isTwistedRequestInstance(this) } + TwistedRequestSource() { isTwistedRequestInstance(this) } - override string toString() { result = "Twisted request source" } + override string toString() { result = "Twisted request source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TwistedRequest } + override predicate isSourceOf(TaintKind kind) { kind instanceof TwistedRequest } } diff --git a/python/ql/src/semmle/python/web/twisted/Response.qll b/python/ql/src/semmle/python/web/twisted/Response.qll index be32ba08188..4817ec762d6 100644 --- a/python/ql/src/semmle/python/web/twisted/Response.qll +++ b/python/ql/src/semmle/python/web/twisted/Response.qll @@ -6,18 +6,18 @@ import Twisted import Request class TwistedResponse extends HttpResponseTaintSink { - TwistedResponse() { - exists(PythonFunctionValue func, string name | - isKnownRequestHandlerMethodName(name) and - name = func.getName() and - func = getTwistedRequestHandlerMethod(name) and - this = func.getAReturnedNode() - ) - } + TwistedResponse() { + exists(PythonFunctionValue func, string name | + isKnownRequestHandlerMethodName(name) and + name = func.getName() and + func = getTwistedRequestHandlerMethod(name) and + this = func.getAReturnedNode() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "Twisted response" } + override string toString() { result = "Twisted response" } } /** @@ -26,20 +26,20 @@ class TwistedResponse extends HttpResponseTaintSink { * request. */ class TwistedRequestSetter extends HttpResponseTaintSink { - TwistedRequestSetter() { - exists(CallNode call, ControlFlowNode node, string name | - ( - name = "setHeader" or - name = "addCookie" or - name = "write" - ) and - any(TwistedRequest t).taints(node) and - node = call.getFunction().(AttrNode).getObject(name) and - this = call.getAnArg() - ) - } + TwistedRequestSetter() { + exists(CallNode call, ControlFlowNode node, string name | + ( + name = "setHeader" or + name = "addCookie" or + name = "write" + ) and + any(TwistedRequest t).taints(node) and + node = call.getFunction().(AttrNode).getObject(name) and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "Twisted request setter" } + override string toString() { result = "Twisted request setter" } } diff --git a/python/ql/src/semmle/python/web/twisted/Twisted.qll b/python/ql/src/semmle/python/web/twisted/Twisted.qll index 9ecd12b9620..98cb60e60dd 100644 --- a/python/ql/src/semmle/python/web/twisted/Twisted.qll +++ b/python/ql/src/semmle/python/web/twisted/Twisted.qll @@ -2,23 +2,23 @@ import python import semmle.python.dataflow.TaintTracking private ClassValue theTwistedHttpRequestClass() { - result = Value::named("twisted.web.http.Request") + result = Value::named("twisted.web.http.Request") } private ClassValue theTwistedHttpResourceClass() { - result = Value::named("twisted.web.resource.Resource") + result = Value::named("twisted.web.resource.Resource") } ClassValue aTwistedRequestHandlerClass() { result.getABaseType+() = theTwistedHttpResourceClass() } FunctionValue getTwistedRequestHandlerMethod(string name) { - result = aTwistedRequestHandlerClass().declaredAttribute(name) + result = aTwistedRequestHandlerClass().declaredAttribute(name) } bindingset[name] predicate isKnownRequestHandlerMethodName(string name) { - name = "render" or - name.matches("render_%") + name = "render" or + name.matches("render_%") } /** @@ -26,25 +26,25 @@ predicate isKnownRequestHandlerMethodName(string name) { * `Request` class. */ predicate isTwistedRequestInstance(NameNode node) { - node.pointsTo().getClass() = theTwistedHttpRequestClass() - or - /* - * In points-to analysis cannot infer that a given object is an instance of - * the `twisted.web.http.Request` class, we also include any parameter - * called `request` that appears inside a subclass of a request handler - * class, and the appropriate arguments of known request handler methods. - */ + node.pointsTo().getClass() = theTwistedHttpRequestClass() + or + /* + * In points-to analysis cannot infer that a given object is an instance of + * the `twisted.web.http.Request` class, we also include any parameter + * called `request` that appears inside a subclass of a request handler + * class, and the appropriate arguments of known request handler methods. + */ - exists(Function func | - func = node.getScope() and - func.getEnclosingScope() = aTwistedRequestHandlerClass().getScope() - | - /* Any parameter called `request` */ - node.getId() = "request" and - node.isParameter() - or - /* Any request parameter of a known request handler method */ - isKnownRequestHandlerMethodName(func.getName()) and - node.getNode() = func.getArg(1) - ) + exists(Function func | + func = node.getScope() and + func.getEnclosingScope() = aTwistedRequestHandlerClass().getScope() + | + /* Any parameter called `request` */ + node.getId() = "request" and + node.isParameter() + or + /* Any request parameter of a known request handler method */ + isKnownRequestHandlerMethodName(func.getName()) and + node.getNode() = func.getArg(1) + ) } diff --git a/python/ql/src/semmle/python/web/webob/Request.qll b/python/ql/src/semmle/python/web/webob/Request.qll index 4d6e98bb2e9..6aafa730ca1 100644 --- a/python/ql/src/semmle/python/web/webob/Request.qll +++ b/python/ql/src/semmle/python/web/webob/Request.qll @@ -3,36 +3,36 @@ import semmle.python.dataflow.TaintTracking import semmle.python.web.Http abstract class BaseWebobRequest extends TaintKind { - bindingset[this] - BaseWebobRequest() { any() } + bindingset[this] + BaseWebobRequest() { any() } - override TaintKind getTaintOfAttribute(string name) { - result instanceof ExternalStringDictKind and - ( - name = "GET" or - name = "POST" or - name = "headers" - ) - or - result instanceof ExternalStringKind and - name = "body" - } + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringDictKind and + ( + name = "GET" or + name = "POST" or + name = "headers" + ) + or + result instanceof ExternalStringKind and + name = "body" + } - override TaintKind getTaintOfMethodResult(string name) { - result = this and - ( - name = "copy" or - name = "copy_get" or - name = "copy_body" - ) - or - result instanceof ExternalStringKind and - name = "as_bytes" - } + override TaintKind getTaintOfMethodResult(string name) { + result = this and + ( + name = "copy" or + name = "copy_get" or + name = "copy_body" + ) + or + result instanceof ExternalStringKind and + name = "as_bytes" + } } class WebobRequest extends BaseWebobRequest { - WebobRequest() { this = "webob.Request" } + WebobRequest() { this = "webob.Request" } - override ClassValue getType() { result = Value::named("webob.request.Request") } + override ClassValue getType() { result = Value::named("webob.request.Request") } } diff --git a/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql b/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql index 80831a9ca54..f8e4a4b7dac 100644 --- a/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql +++ b/python/ql/test/2/library-tests/ControlFlow/Exceptions/Likely.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode r, ControlFlowNode s where - s = r.getAnExceptionalSuccessor() and - not r.(RaisingNode).unlikelySuccessor(s) + s = r.getAnExceptionalSuccessor() and + not r.(RaisingNode).unlikelySuccessor(s) select r.getLocation().getStartLine(), r.toString(), s.getLocation().getStartLine(), s.toString() diff --git a/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql b/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql index 8594fc33ae2..3281b8d26e6 100644 --- a/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql +++ b/python/ql/test/2/library-tests/PointsTo/class_properties/ClassValues.ql @@ -2,19 +2,19 @@ import python from ClassValue cls, string res where - exists(CallNode call | - call.getFunction().(NameNode).getId() = "test" and - call.getAnArg().pointsTo(cls) - ) and - ( - cls.isSequence() and - cls.isMapping() and - res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." - or - cls.isSequence() and not cls.isMapping() and res = "sequence" - or - not cls.isSequence() and cls.isMapping() and res = "mapping" - or - not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" - ) + exists(CallNode call | + call.getFunction().(NameNode).getId() = "test" and + call.getAnArg().pointsTo(cls) + ) and + ( + cls.isSequence() and + cls.isMapping() and + res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." + or + cls.isSequence() and not cls.isMapping() and res = "sequence" + or + not cls.isSequence() and cls.isMapping() and res = "mapping" + or + not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" + ) select res, cls.toString() diff --git a/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql b/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql index 94a1db9b83d..e77849860c7 100644 --- a/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql +++ b/python/ql/test/2/library-tests/PointsTo/import_time/Pruned.ql @@ -4,9 +4,9 @@ import semmle.python.pointsto.PointsToContext from ControlFlowNode f, Location l, Context c where - not PointsToInternal::reachableBlock(f.getBasicBlock(), c) and - c.isImport() and - (f.getNode() instanceof FunctionExpr or f.getNode() instanceof ClassExpr) and - l = f.getLocation() and - l.getFile().getShortName() = "test.py" + not PointsToInternal::reachableBlock(f.getBasicBlock(), c) and + c.isImport() and + (f.getNode() instanceof FunctionExpr or f.getNode() instanceof ClassExpr) and + l = f.getLocation() and + l.getFile().getShortName() = "test.py" select l.getStartLine() diff --git a/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql b/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql index 44a35b27b27..7a46cc8cad1 100644 --- a/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql +++ b/python/ql/test/2/library-tests/PointsTo/imports/Runtime.ql @@ -2,9 +2,9 @@ import python from int line, ControlFlowNode f, Object o, ControlFlowNode orig where - not f.getLocation().getFile().inStdlib() and - f.refersTo(o, orig) and - line = f.getLocation().getStartLine() and - line != 0 and - not o instanceof NumericObject // Omit sys.hexversion as it will change between machines + not f.getLocation().getFile().inStdlib() and + f.refersTo(o, orig) and + line = f.getLocation().getStartLine() and + line != 0 and + not o instanceof NumericObject // Omit sys.hexversion as it will change between machines select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), orig.toString() diff --git a/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql b/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql index 6cd800ac399..4c7a4fff358 100644 --- a/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql +++ b/python/ql/test/2/library-tests/PointsTo/origin_uniqueness/Origin.ql @@ -4,8 +4,8 @@ string short_loc(Location l) { result = l.getFile().getShortName() + ":" + l.get from ControlFlowNode use, Object obj, ControlFlowNode orig, int line where - use.refersTo(obj, orig) and - use.getLocation().getFile().getShortName() = "test.py" and - line = use.getLocation().getStartLine() and - not line = 0 + use.refersTo(obj, orig) and + use.getLocation().getFile().getShortName() = "test.py" and + line = use.getLocation().getStartLine() and + not line = 0 select line, use.toString(), obj.toString(), short_loc(orig.getLocation()) diff --git a/python/ql/test/2/library-tests/classes/attr/class_attr.ql b/python/ql/test/2/library-tests/classes/attr/class_attr.ql index 3b7bf8b3ba0..197ab1a1e5e 100644 --- a/python/ql/test/2/library-tests/classes/attr/class_attr.ql +++ b/python/ql/test/2/library-tests/classes/attr/class_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql b/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql index 2f16aa4ca97..be8272d1bd6 100644 --- a/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql +++ b/python/ql/test/2/library-tests/classes/attr/class_has_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.hasAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.hasAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/2/library-tests/classes/attr/list_attr.ql b/python/ql/test/2/library-tests/classes/attr/list_attr.ql index aad2d9489c3..c38694d5883 100644 --- a/python/ql/test/2/library-tests/classes/attr/list_attr.ql +++ b/python/ql/test/2/library-tests/classes/attr/list_attr.ql @@ -8,9 +8,9 @@ import python from ClassObject cls, string name, Object what where - ( - cls.getName() = "list" or - cls.getASuperType().getName() = "list" - ) and - cls.lookupAttribute(name) = what + ( + cls.getName() = "list" or + cls.getASuperType().getName() = "list" + ) and + cls.lookupAttribute(name) = what select cls.toString(), name, what.toString() diff --git a/python/ql/test/2/library-tests/classes/mro/C3.ql b/python/ql/test/2/library-tests/classes/mro/C3.ql index c4b0dd896d6..6807f223f91 100644 --- a/python/ql/test/2/library-tests/classes/mro/C3.ql +++ b/python/ql/test/2/library-tests/classes/mro/C3.ql @@ -4,7 +4,7 @@ import semmle.python.pointsto.PointsTo import semmle.python.objects.ObjectInternal ClassList mro(ClassObjectInternal cls) { - if Types::isNewStyle(cls) then result = Mro::newStyleMro(cls) else result = Mro::oldStyleMro(cls) + if Types::isNewStyle(cls) then result = Mro::newStyleMro(cls) else result = Mro::oldStyleMro(cls) } from ClassObjectInternal cls diff --git a/python/ql/test/2/library-tests/classes/mro/mro.ql b/python/ql/test/2/library-tests/classes/mro/mro.ql index 122d31c4a9b..0c4cf077adb 100644 --- a/python/ql/test/2/library-tests/classes/mro/mro.ql +++ b/python/ql/test/2/library-tests/classes/mro/mro.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject l, ClassObject r where - not cls.isC() and - r = cls.nextInMro(l) + not cls.isC() and + r = cls.nextInMro(l) select cls.toString(), l.toString(), r.toString() diff --git a/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql b/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql index 475505620f4..2f5191fb547 100644 --- a/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql +++ b/python/ql/test/2/library-tests/comprehensions/ConsistencyCheck.ql @@ -5,5 +5,5 @@ import python select count(Comprehension c | - count(c.toString()) != 1 or count(c.getLocation()) != 1 or not exists(c.getAFlowNode()) - ) + count(c.toString()) != 1 or count(c.getLocation()) != 1 or not exists(c.getAFlowNode()) + ) diff --git a/python/ql/test/2/library-tests/locations/general/AllLocations.ql b/python/ql/test/2/library-tests/locations/general/AllLocations.ql index 9e6fcb00a05..e3a84325418 100644 --- a/python/ql/test/2/library-tests/locations/general/AllLocations.ql +++ b/python/ql/test/2/library-tests/locations/general/AllLocations.ql @@ -9,7 +9,7 @@ import python from string classname where - exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) - or - exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + or + exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) select classname diff --git a/python/ql/test/2/library-tests/modules/general/import_test.ql b/python/ql/test/2/library-tests/modules/general/import_test.ql index 94f8c1447ca..cbd8cafb2f7 100644 --- a/python/ql/test/2/library-tests/modules/general/import_test.ql +++ b/python/ql/test/2/library-tests/modules/general/import_test.ql @@ -2,8 +2,8 @@ import python from ImportExpr ie, string m, string t, string r where - m = ie.getImportedModuleName() and - (if ie.isTop() then t = "top" else t = "bottom") and - (if ie.isRelative() then r = "relative" else r = "absolute") + m = ie.getImportedModuleName() and + (if ie.isTop() then t = "top" else t = "bottom") and + (if ie.isRelative() then r = "relative" else r = "absolute") select ie.getScope().toString(), ie.getLocation().getStartLine(), ie.toString(), ie.getLevel(), t, - r, m + r, m diff --git a/python/ql/test/2/library-tests/objects/Literals.ql b/python/ql/test/2/library-tests/objects/Literals.ql index ad6e1181cfd..a7f10b358ff 100644 --- a/python/ql/test/2/library-tests/objects/Literals.ql +++ b/python/ql/test/2/library-tests/objects/Literals.ql @@ -2,9 +2,9 @@ import python string repr(Expr e) { - result = e.(Num).getN() or - result = e.(Bytes).getS() or - result = e.(Unicode).getS() + result = e.(Num).getN() or + result = e.(Bytes).getS() or + result = e.(Unicode).getS() } from ImmutableLiteral l diff --git a/python/ql/test/2/library-tests/six/pointsto.ql b/python/ql/test/2/library-tests/six/pointsto.ql index d44761b1b12..cca7eeede10 100644 --- a/python/ql/test/2/library-tests/six/pointsto.ql +++ b/python/ql/test/2/library-tests/six/pointsto.ql @@ -1,9 +1,9 @@ import python string longname(Expr e) { - result = e.(Name).getId() - or - exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) + result = e.(Name).getId() + or + exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) } from Expr e, Value v diff --git a/python/ql/test/2/library-tests/types/classes/new_style.ql b/python/ql/test/2/library-tests/types/classes/new_style.ql index a0cd38b9e62..2af40565329 100644 --- a/python/ql/test/2/library-tests/types/classes/new_style.ql +++ b/python/ql/test/2/library-tests/types/classes/new_style.ql @@ -2,7 +2,7 @@ import python from ClassObject cls, string style where - not cls.isC() and - not cls.failedInference() and - (if cls.isNewStyle() then style = "new" else style = "old") + not cls.isC() and + not cls.failedInference() and + (if cls.isNewStyle() then style = "new" else style = "old") select cls.toString(), style diff --git a/python/ql/test/2/library-tests/types/exceptions/Raises.ql b/python/ql/test/2/library-tests/types/exceptions/Raises.ql index aa477f718a2..2415c707967 100644 --- a/python/ql/test/2/library-tests/types/exceptions/Raises.ql +++ b/python/ql/test/2/library-tests/types/exceptions/Raises.ql @@ -2,11 +2,11 @@ import python from PyFunctionObject f, string type where - type = f.getARaisedType().toString() - or - type = "Unknown" and f.raisesUnknownType() - or - not exists(f.getARaisedType()) and - not f.raisesUnknownType() and - type = "None" + type = f.getARaisedType().toString() + or + type = "Unknown" and f.raisesUnknownType() + or + not exists(f.getARaisedType()) and + not f.raisesUnknownType() and + type = "None" select f.toString(), type diff --git a/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql b/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql index 6ff0563e787..24766db9f2e 100644 --- a/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql +++ b/python/ql/test/2/library-tests/types/properties/BuiltinProperties.ql @@ -2,7 +2,7 @@ import python from ClassObject cls, string name, BuiltinPropertyObject p where - cls.declaredAttribute(name) = p and - (cls = theObjectType() or cls = theListType() or cls = theTypeType()) + cls.declaredAttribute(name) = p and + (cls = theObjectType() or cls = theListType() or cls = theTypeType()) select cls.toString(), name, p.toString(), p.getGetter().toString(), p.getSetter().toString(), - p.getDeleter().toString() + p.getDeleter().toString() diff --git a/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql b/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql index 80831a9ca54..f8e4a4b7dac 100644 --- a/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql +++ b/python/ql/test/3/library-tests/ControlFlow/Exceptions/Likely.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode r, ControlFlowNode s where - s = r.getAnExceptionalSuccessor() and - not r.(RaisingNode).unlikelySuccessor(s) + s = r.getAnExceptionalSuccessor() and + not r.(RaisingNode).unlikelySuccessor(s) select r.getLocation().getStartLine(), r.toString(), s.getLocation().getStartLine(), s.toString() diff --git a/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql b/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql index 0c6149c38b1..2b4b8a8c70c 100644 --- a/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql +++ b/python/ql/test/3/library-tests/PointsTo/attributes/TestWithType.ql @@ -3,4 +3,4 @@ import python from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where f.refersTo(o, c, x) select f.getLocation().getStartLine(), f.toString(), o.toString(), c.toString(), - x.getLocation().getStartLine() + x.getLocation().getStartLine() diff --git a/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql b/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql index 8594fc33ae2..3281b8d26e6 100644 --- a/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql +++ b/python/ql/test/3/library-tests/PointsTo/class_properties/ClassValues.ql @@ -2,19 +2,19 @@ import python from ClassValue cls, string res where - exists(CallNode call | - call.getFunction().(NameNode).getId() = "test" and - call.getAnArg().pointsTo(cls) - ) and - ( - cls.isSequence() and - cls.isMapping() and - res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." - or - cls.isSequence() and not cls.isMapping() and res = "sequence" - or - not cls.isSequence() and cls.isMapping() and res = "mapping" - or - not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" - ) + exists(CallNode call | + call.getFunction().(NameNode).getId() = "test" and + call.getAnArg().pointsTo(cls) + ) and + ( + cls.isSequence() and + cls.isMapping() and + res = "IS BOTH. SHOULD NOT HAPPEN. THEY ARE MUTUALLY EXCLUSIVE." + or + cls.isSequence() and not cls.isMapping() and res = "sequence" + or + not cls.isSequence() and cls.isMapping() and res = "mapping" + or + not cls.isSequence() and not cls.isMapping() and res = "neither sequence nor mapping" + ) select res, cls.toString() diff --git a/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql b/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql index 6215714a25e..4299e11d660 100644 --- a/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql +++ b/python/ql/test/3/library-tests/PointsTo/consts/BooleanConstants.ql @@ -3,7 +3,7 @@ import semmle.python.pointsto.PointsTo from ControlFlowNode f, Context c, boolean b where - exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and obj.booleanValue() = b) and - not exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and not obj.booleanValue() = b) + exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and obj.booleanValue() = b) and + not exists(Object obj | PointsTo::points_to(f, c, obj, _, _) and not obj.booleanValue() = b) select f.getLocation().getFile().getShortName(), f.getLocation().getStartLine(), f.toString(), - c.toString(), b + c.toString(), b diff --git a/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql b/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql index d07dc65c34f..de3b4a282c2 100644 --- a/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql +++ b/python/ql/test/3/library-tests/PointsTo/import_time/Pruned.ql @@ -3,7 +3,7 @@ import semmle.python.pointsto.PointsTo from ControlFlowNode f, Location l where - not PointsToInternal::reachableBlock(f.getBasicBlock(), _) and - l = f.getLocation() and - l.getFile().getShortName() = "test.py" + not PointsToInternal::reachableBlock(f.getBasicBlock(), _) and + l = f.getLocation() and + l.getFile().getShortName() = "test.py" select l.getStartLine() diff --git a/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql b/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql index e21a864b8bb..bc666b4f206 100644 --- a/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql +++ b/python/ql/test/3/library-tests/PointsTo/regressions/subprocess-assert/ClassValue.ql @@ -3,9 +3,9 @@ import python // as used in semmle.python.filters.Tests from ClassValue c, string base where - c.getScope().getLocation().getFile().getShortName().matches("mwe%.py") and - c.getName() = "MyTest" and - if exists(c.getABaseType()) - then base = c.getABaseType().toString() - else base = "" + c.getScope().getLocation().getFile().getShortName().matches("mwe%.py") and + c.getName() = "MyTest" and + if exists(c.getABaseType()) + then base = c.getABaseType().toString() + else base = "" select c, base diff --git a/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql b/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql index 8716d38f086..192468a2248 100644 --- a/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql +++ b/python/ql/test/3/library-tests/PointsTo/typehints/Values.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode f, Context ctx, Value v, ControlFlowNode origin where - f.pointsTo(ctx, v, origin) and - f.getLocation().getFile().getBaseName() = "test.py" + f.pointsTo(ctx, v, origin) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation(), f.toString(), ctx, v diff --git a/python/ql/test/3/library-tests/classes/attr/class_attr.ql b/python/ql/test/3/library-tests/classes/attr/class_attr.ql index 3b7bf8b3ba0..197ab1a1e5e 100644 --- a/python/ql/test/3/library-tests/classes/attr/class_attr.ql +++ b/python/ql/test/3/library-tests/classes/attr/class_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql b/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql index 2f16aa4ca97..be8272d1bd6 100644 --- a/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql +++ b/python/ql/test/3/library-tests/classes/attr/class_has_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.hasAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.hasAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/3/library-tests/classes/mro/mro.ql b/python/ql/test/3/library-tests/classes/mro/mro.ql index 2c710a18eeb..576d6a75afd 100644 --- a/python/ql/test/3/library-tests/classes/mro/mro.ql +++ b/python/ql/test/3/library-tests/classes/mro/mro.ql @@ -8,6 +8,6 @@ import python from ClassObject cls, ClassObject l, ClassObject r where - not cls.isC() and - r = cls.nextInMro(l) + not cls.isC() and + r = cls.nextInMro(l) select cls.toString(), l.toString(), r.toString() diff --git a/python/ql/test/3/library-tests/classes/mro/mro_index.ql b/python/ql/test/3/library-tests/classes/mro/mro_index.ql index 641667e28f1..da40776044e 100644 --- a/python/ql/test/3/library-tests/classes/mro/mro_index.ql +++ b/python/ql/test/3/library-tests/classes/mro/mro_index.ql @@ -8,6 +8,6 @@ import python from ClassObject cls, ClassObject sup, int index where - sup = cls.getMroItem(index) and - not cls.isC() + sup = cls.getMroItem(index) and + not cls.isC() select cls.toString(), index, sup.toString() diff --git a/python/ql/test/3/library-tests/locations/general/AllLocations.ql b/python/ql/test/3/library-tests/locations/general/AllLocations.ql index 9e6fcb00a05..e3a84325418 100644 --- a/python/ql/test/3/library-tests/locations/general/AllLocations.ql +++ b/python/ql/test/3/library-tests/locations/general/AllLocations.ql @@ -9,7 +9,7 @@ import python from string classname where - exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) - or - exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + exists(AstNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) + or + exists(ControlFlowNode node | not exists(node.getLocation()) and classname = node.getAQlClass()) select classname diff --git a/python/ql/test/3/library-tests/modules/general/import_test.ql b/python/ql/test/3/library-tests/modules/general/import_test.ql index 94f8c1447ca..cbd8cafb2f7 100644 --- a/python/ql/test/3/library-tests/modules/general/import_test.ql +++ b/python/ql/test/3/library-tests/modules/general/import_test.ql @@ -2,8 +2,8 @@ import python from ImportExpr ie, string m, string t, string r where - m = ie.getImportedModuleName() and - (if ie.isTop() then t = "top" else t = "bottom") and - (if ie.isRelative() then r = "relative" else r = "absolute") + m = ie.getImportedModuleName() and + (if ie.isTop() then t = "top" else t = "bottom") and + (if ie.isRelative() then r = "relative" else r = "absolute") select ie.getScope().toString(), ie.getLocation().getStartLine(), ie.toString(), ie.getLevel(), t, - r, m + r, m diff --git a/python/ql/test/3/library-tests/parameters/Special.ql b/python/ql/test/3/library-tests/parameters/Special.ql index 4987599bc72..e26e0797ff6 100644 --- a/python/ql/test/3/library-tests/parameters/Special.ql +++ b/python/ql/test/3/library-tests/parameters/Special.ql @@ -2,9 +2,9 @@ import python from Parameter p, string type where - p.isKwargs() and type = "kwargs" - or - p.isVarargs() and type = "varargs" - or - not p.isKwargs() and not p.isVarargs() and type = "normal" + p.isKwargs() and type = "kwargs" + or + p.isVarargs() and type = "varargs" + or + not p.isKwargs() and not p.isVarargs() and type = "normal" select p.getName(), type diff --git a/python/ql/test/3/library-tests/six/pointsto.ql b/python/ql/test/3/library-tests/six/pointsto.ql index d44761b1b12..cca7eeede10 100644 --- a/python/ql/test/3/library-tests/six/pointsto.ql +++ b/python/ql/test/3/library-tests/six/pointsto.ql @@ -1,9 +1,9 @@ import python string longname(Expr e) { - result = e.(Name).getId() - or - exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) + result = e.(Name).getId() + or + exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName()) } from Expr e, Value v diff --git a/python/ql/test/3/library-tests/taint/unpacking/Taint.qll b/python/ql/test/3/library-tests/taint/unpacking/Taint.qll index 21e16aabac5..010b9738c5c 100644 --- a/python/ql/test/3/library-tests/taint/unpacking/Taint.qll +++ b/python/ql/test/3/library-tests/taint/unpacking/Taint.qll @@ -3,25 +3,25 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } diff --git a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql b/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql +++ b/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/3/library-tests/types/exceptions/Raises.ql b/python/ql/test/3/library-tests/types/exceptions/Raises.ql index aa477f718a2..2415c707967 100644 --- a/python/ql/test/3/library-tests/types/exceptions/Raises.ql +++ b/python/ql/test/3/library-tests/types/exceptions/Raises.ql @@ -2,11 +2,11 @@ import python from PyFunctionObject f, string type where - type = f.getARaisedType().toString() - or - type = "Unknown" and f.raisesUnknownType() - or - not exists(f.getARaisedType()) and - not f.raisesUnknownType() and - type = "None" + type = f.getARaisedType().toString() + or + type = "Unknown" and f.raisesUnknownType() + or + not exists(f.getARaisedType()) and + not f.raisesUnknownType() and + type = "None" select f.toString(), type diff --git a/python/ql/test/3/library-tests/types/exceptions/Viable.ql b/python/ql/test/3/library-tests/types/exceptions/Viable.ql index ed388e2faf2..bf00a0675d6 100644 --- a/python/ql/test/3/library-tests/types/exceptions/Viable.ql +++ b/python/ql/test/3/library-tests/types/exceptions/Viable.ql @@ -3,4 +3,4 @@ import python from RaisingNode r, ControlFlowNode n, ClassObject ex where r.viableExceptionEdge_objectapi(n, ex) select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), - n.getNode().toString(), ex.toString() + n.getNode().toString(), ex.toString() diff --git a/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql b/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql index 4a35fae7e8b..38241e8fabc 100644 --- a/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql +++ b/python/ql/test/3/library-tests/types/namespaces/NameSpace.ql @@ -9,16 +9,16 @@ import python from Scope s, string name, Object val where - name != "__name__" and - ( - exists(ModuleObject m | - m.getModule() = s and - m.attributeRefersTo(name, val, _) - ) - or - exists(ClassObject cls | - cls.getPyClass() = s and - cls.declaredAttribute(name) = val - ) + name != "__name__" and + ( + exists(ModuleObject m | + m.getModule() = s and + m.attributeRefersTo(name, val, _) ) + or + exists(ClassObject cls | + cls.getPyClass() = s and + cls.declaredAttribute(name) = val + ) + ) select s.toString(), name, val.toString() diff --git a/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql b/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql index 6ff0563e787..24766db9f2e 100644 --- a/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql +++ b/python/ql/test/3/library-tests/types/properties/BuiltinProperties.ql @@ -2,7 +2,7 @@ import python from ClassObject cls, string name, BuiltinPropertyObject p where - cls.declaredAttribute(name) = p and - (cls = theObjectType() or cls = theListType() or cls = theTypeType()) + cls.declaredAttribute(name) = p and + (cls = theObjectType() or cls = theListType() or cls = theTypeType()) select cls.toString(), name, p.toString(), p.getGetter().toString(), p.getSetter().toString(), - p.getDeleter().toString() + p.getDeleter().toString() diff --git a/python/ql/test/experimental/dataflow/basic/callGraph.ql b/python/ql/test/experimental/dataflow/basic/callGraph.ql index 0d0a0279891..53747b31739 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraph.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraph.ql @@ -1,9 +1,5 @@ import callGraphConfig -from - DataFlow::Node source, - DataFlow::Node sink -where - exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) -select - source, sink +from DataFlow::Node source, DataFlow::Node sink +where exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) +select source, sink diff --git a/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll b/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll index 9866ac1cdbe..241b7b9478c 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll +++ b/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll @@ -1,7 +1,7 @@ import experimental.dataflow.DataFlow /** - * A configuration to find the call graph edges. + * A configuration to find the call graph edges. */ class CallGraphConfig extends DataFlow::Configuration { CallGraphConfig() { this = "CallGraphConfig" } diff --git a/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql b/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql index ef635f9afa6..020ea245cfd 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql @@ -2,4 +2,4 @@ import callGraphConfig from DataFlow::Node sink where exists(CallGraphConfig cfg | cfg.isSink(sink)) -select sink \ No newline at end of file +select sink diff --git a/python/ql/test/experimental/dataflow/basic/callGraphSources.ql b/python/ql/test/experimental/dataflow/basic/callGraphSources.ql index de58e5a2269..a6bd5538866 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphSources.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraphSources.ql @@ -2,4 +2,4 @@ import callGraphConfig from DataFlow::Node source where exists(CallGraphConfig cfg | cfg.isSource(source)) -select source \ No newline at end of file +select source diff --git a/python/ql/test/experimental/dataflow/basic/global.ql b/python/ql/test/experimental/dataflow/basic/global.ql index 6f57575819a..ba9a302b05b 100644 --- a/python/ql/test/experimental/dataflow/basic/global.ql +++ b/python/ql/test/experimental/dataflow/basic/global.ql @@ -1,10 +1,7 @@ import allFlowsConfig -from - DataFlow::Node source, - DataFlow::Node sink +from DataFlow::Node source, DataFlow::Node sink where - source != sink and + source != sink and exists(AllFlowsConfig cfg | cfg.hasFlow(source, sink)) -select - source, sink +select source, sink diff --git a/python/ql/test/experimental/dataflow/basic/globalStep.ql b/python/ql/test/experimental/dataflow/basic/globalStep.ql index f583a9f1a81..18014b2cc5f 100644 --- a/python/ql/test/experimental/dataflow/basic/globalStep.ql +++ b/python/ql/test/experimental/dataflow/basic/globalStep.ql @@ -1,9 +1,5 @@ import allFlowsConfig -from - DataFlow::PathNode fromNode, - DataFlow::PathNode toNode -where - toNode = fromNode.getASuccessor() -select - fromNode, toNode +from DataFlow::PathNode fromNode, DataFlow::PathNode toNode +where toNode = fromNode.getASuccessor() +select fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/basic/local.ql b/python/ql/test/experimental/dataflow/basic/local.ql index 40aa5c403e1..a4f7519483f 100644 --- a/python/ql/test/experimental/dataflow/basic/local.ql +++ b/python/ql/test/experimental/dataflow/basic/local.ql @@ -1,9 +1,5 @@ import experimental.dataflow.DataFlow -from - DataFlow::Node fromNode, - DataFlow::Node toNode -where - DataFlow::localFlow(fromNode, toNode) -select - fromNode, toNode +from DataFlow::Node fromNode, DataFlow::Node toNode +where DataFlow::localFlow(fromNode, toNode) +select fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/basic/localStep.ql b/python/ql/test/experimental/dataflow/basic/localStep.ql index b8a9941b99b..ee57b07f0c7 100644 --- a/python/ql/test/experimental/dataflow/basic/localStep.ql +++ b/python/ql/test/experimental/dataflow/basic/localStep.ql @@ -1,9 +1,5 @@ import experimental.dataflow.DataFlow -from - DataFlow::Node fromNode, - DataFlow::Node toNode -where - DataFlow::localFlowStep(fromNode, toNode) -select - fromNode, toNode +from DataFlow::Node fromNode, DataFlow::Node toNode +where DataFlow::localFlowStep(fromNode, toNode) +select fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/basic/maximalFlows.ql b/python/ql/test/experimental/dataflow/basic/maximalFlows.ql index 6e183dc393b..ddd673954b9 100644 --- a/python/ql/test/experimental/dataflow/basic/maximalFlows.ql +++ b/python/ql/test/experimental/dataflow/basic/maximalFlows.ql @@ -1,10 +1,7 @@ import maximalFlowsConfig -from - DataFlow::Node source, - DataFlow::Node sink +from DataFlow::Node source, DataFlow::Node sink where - source != sink and + source != sink and exists(MaximalFlowsConfig cfg | cfg.hasFlow(source, sink)) -select - source, sink +select source, sink diff --git a/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll b/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll index 9db8ec67e94..de2d22d7d52 100644 --- a/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll +++ b/python/ql/test/experimental/dataflow/basic/maximalFlowsConfig.qll @@ -11,9 +11,7 @@ class MaximalFlowsConfig extends DataFlow::Configuration { node instanceof DataFlow::ParameterNode or node instanceof DataFlow::EssaNode and - not exists(DataFlow::EssaNode pred | - DataFlow::localFlowStep(pred, node) - ) + not exists(DataFlow::EssaNode pred | DataFlow::localFlowStep(pred, node)) } override predicate isSink(DataFlow::Node node) { diff --git a/python/ql/test/experimental/dataflow/basic/sinks.ql b/python/ql/test/experimental/dataflow/basic/sinks.ql index 9b4534b9870..8560bb99d3d 100644 --- a/python/ql/test/experimental/dataflow/basic/sinks.ql +++ b/python/ql/test/experimental/dataflow/basic/sinks.ql @@ -2,4 +2,4 @@ import allFlowsConfig from DataFlow::Node sink where exists(AllFlowsConfig cfg | cfg.isSink(sink)) -select sink \ No newline at end of file +select sink diff --git a/python/ql/test/experimental/dataflow/basic/sources.ql b/python/ql/test/experimental/dataflow/basic/sources.ql index f47fa31d62e..d079d4db596 100644 --- a/python/ql/test/experimental/dataflow/basic/sources.ql +++ b/python/ql/test/experimental/dataflow/basic/sources.ql @@ -2,4 +2,4 @@ import allFlowsConfig from DataFlow::Node source where exists(AllFlowsConfig cfg | cfg.isSource(source)) -select source \ No newline at end of file +select source diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.ql b/python/ql/test/experimental/dataflow/coverage/dataflow.ql index 2d6e71ea679..65a13d57233 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.ql +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.ql @@ -1,9 +1,5 @@ import experimental.dataflow.testConfig -from - DataFlow::Node source, - DataFlow::Node sink -where - exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) -select - source, sink +from DataFlow::Node source, DataFlow::Node sink +where exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) +select source, sink diff --git a/python/ql/test/experimental/dataflow/regression/dataflow.ql b/python/ql/test/experimental/dataflow/regression/dataflow.ql index 066a42b2c1c..a5a933bdc71 100644 --- a/python/ql/test/experimental/dataflow/regression/dataflow.ql +++ b/python/ql/test/experimental/dataflow/regression/dataflow.ql @@ -7,10 +7,6 @@ import experimental.dataflow.testConfig -from - DataFlow::Node source, - DataFlow::Node sink -where - exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) -select - source, sink +from DataFlow::Node source, DataFlow::Node sink +where exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) +select source, sink diff --git a/python/ql/test/experimental/dataflow/testConfig.qll b/python/ql/test/experimental/dataflow/testConfig.qll index 8d89991d8c2..178f2f4f229 100644 --- a/python/ql/test/experimental/dataflow/testConfig.qll +++ b/python/ql/test/experimental/dataflow/testConfig.qll @@ -9,9 +9,9 @@ * SINK(s) * ``` * `SOURCE` will be a source and the second occurance of `s` will be a sink. - * + * * In order to test literals, alternative sources are defined for each type: - * + * * for | use * ---------- * string | `"source"` @@ -25,7 +25,7 @@ import experimental.dataflow.DataFlow class TestConfiguration extends DataFlow::Configuration { TestConfiguration() { this = "TestConfiguration" } - override predicate isSource(DataFlow::Node node) { + override predicate isSource(DataFlow::Node node) { node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE" or node.(DataFlow::CfgNode).getNode().getNode().(StrConst).getS() = "source" @@ -36,7 +36,7 @@ class TestConfiguration extends DataFlow::Configuration { // No support for complex numbers } - override predicate isSink(DataFlow::Node node) { + override predicate isSink(DataFlow::Node node) { exists(CallNode call | call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and node.(DataFlow::CfgNode).getNode() = call.getAnArg() diff --git a/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql b/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql index 54e7ed36333..8b52244478f 100644 --- a/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql +++ b/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql @@ -2,14 +2,14 @@ import python /*Find any Definition, assigned value pairs that 'valueForDefinition' misses */ Expr assignedValue(Name n) { - exists(Assign a | a.getATarget() = n and result = a.getValue()) - or - exists(Alias a | a.getAsname() = n and result = a.getValue()) + exists(Assign a | a.getATarget() = n and result = a.getValue()) + or + exists(Alias a | a.getAsname() = n and result = a.getValue()) } from Name def, DefinitionNode d where - d = def.getAFlowNode() and - exists(assignedValue(def)) and - not d.getValue().getNode() = assignedValue(def) + d = def.getAFlowNode() and + exists(assignedValue(def)) and + not d.getValue().getNode() = assignedValue(def) select def.toString(), assignedValue(def) diff --git a/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql b/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql index a4b98183c27..ce4f454ab3b 100644 --- a/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql +++ b/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql @@ -4,7 +4,7 @@ int lineof(ControlFlowNode f) { result = f.getNode().getLocation().getStartLine( from ControlFlowNode defn, ControlFlowNode use where - defn.getNode() = use.getNode() and - defn.isStore() and - use.isLoad() + defn.getNode() = use.getNode() and + defn.isStore() and + use.isLoad() select defn.toString(), use.toString(), lineof(defn) diff --git a/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql b/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql index c97f8446345..979a2395941 100644 --- a/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql +++ b/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql @@ -1,15 +1,15 @@ import python string kind(ControlFlowNode f) { - if f.isAugLoad() - then result = "aug load" + if f.isAugLoad() + then result = "aug load" + else ( + if f.isAugStore() + then result = "aug store" else ( - if f.isAugStore() - then result = "aug store" - else ( - if f.isLoad() then result = "load" else (f.isStore() and result = "store") - ) + if f.isLoad() then result = "load" else (f.isStore() and result = "store") ) + ) } from ControlFlowNode cfg diff --git a/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql b/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql index 13e4736e6d9..a14e31c420a 100644 --- a/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql +++ b/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql @@ -9,10 +9,10 @@ import python from CompareNode c, NameNode l, NameNode r, Cmpop op, int line, Variable vl, Variable vr where - c.operands(l, op, r) and - line = c.getLocation().getStartLine() and - line = l.getLocation().getStartLine() and - line = r.getLocation().getStartLine() and - l.uses(vl) and - r.uses(vr) + c.operands(l, op, r) and + line = c.getLocation().getStartLine() and + line = l.getLocation().getStartLine() and + line = r.getLocation().getStartLine() and + l.uses(vl) and + r.uses(vr) select line, c.toString(), vl.getId(), vr.getId(), op.getSymbol() diff --git a/python/ql/test/library-tests/ControlFlow/delete/test.ql b/python/ql/test/library-tests/ControlFlow/delete/test.ql index 2aaa45ea719..ba6ea81fb40 100644 --- a/python/ql/test/library-tests/ControlFlow/delete/test.ql +++ b/python/ql/test/library-tests/ControlFlow/delete/test.ql @@ -3,4 +3,4 @@ import python from ControlFlowNode p, ControlFlowNode s where p.getASuccessor() = s select p.getLocation().getStartLine().toString(), p.toString(), s.getLocation().getStartLine(), - s.toString() + s.toString() diff --git a/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql b/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql index d39328e44c7..680d27e5cd1 100644 --- a/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql +++ b/python/ql/test/library-tests/ControlFlow/dominators/DominatesConsistency.ql @@ -1,8 +1,8 @@ import python select count(BasicBlock b1, BasicBlock b2 | - b1 = b2.getImmediateDominator+() and not b1.strictlyDominates(b2) - ), - count(BasicBlock b1, BasicBlock b2 | - not b1 = b2.getImmediateDominator+() and b1.strictlyDominates(b2) - ) + b1 = b2.getImmediateDominator+() and not b1.strictlyDominates(b2) + ), + count(BasicBlock b1, BasicBlock b2 | + not b1 = b2.getImmediateDominator+() and b1.strictlyDominates(b2) + ) diff --git a/python/ql/test/library-tests/ControlFlow/dominators/idom.ql b/python/ql/test/library-tests/ControlFlow/dominators/idom.ql index cd948b6ff10..44c7da924aa 100644 --- a/python/ql/test/library-tests/ControlFlow/dominators/idom.ql +++ b/python/ql/test/library-tests/ControlFlow/dominators/idom.ql @@ -10,6 +10,6 @@ import python /* This query should *never* produce a result */ from ControlFlowNode f where - not exists(f.getImmediateDominator()) and - not f.getNode() instanceof Scope + not exists(f.getImmediateDominator()) and + not f.getNode() instanceof Scope select f diff --git a/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql b/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql index 66758604be2..917e112b290 100644 --- a/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql +++ b/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql @@ -1,19 +1,19 @@ import python predicate can_reach_from_entry_without_passing(ControlFlowNode target, ControlFlowNode pass) { - target != pass and - target.getScope() = pass.getScope() and - ( - target.isEntryNode() - or - exists(ControlFlowNode pre | - target.getAPredecessor() = pre and can_reach_from_entry_without_passing(pre, pass) - ) + target != pass and + target.getScope() = pass.getScope() and + ( + target.isEntryNode() + or + exists(ControlFlowNode pre | + target.getAPredecessor() = pre and can_reach_from_entry_without_passing(pre, pass) ) + ) } from ControlFlowNode node, ControlFlowNode dom where - dom = node.getImmediateDominator() and - can_reach_from_entry_without_passing(node, dom) + dom = node.getImmediateDominator() and + can_reach_from_entry_without_passing(node, dom) select node.toString(), dom.toString() diff --git a/python/ql/test/library-tests/ControlFlow/general/Lines.ql b/python/ql/test/library-tests/ControlFlow/general/Lines.ql index dabbe2bbf58..ca6e7715538 100644 --- a/python/ql/test/library-tests/ControlFlow/general/Lines.ql +++ b/python/ql/test/library-tests/ControlFlow/general/Lines.ql @@ -2,7 +2,7 @@ import python from Scope s, int n where - exists(Function f | f = s | n = f.getMetrics().getNumberOfLines()) - or - exists(Module m | m = s | n = m.getMetrics().getNumberOfLines()) + exists(Function f | f = s | n = f.getMetrics().getNumberOfLines()) + or + exists(Module m | m = s | n = m.getMetrics().getNumberOfLines()) select s.toString(), n diff --git a/python/ql/test/library-tests/ControlFlow/general/Reaches.ql b/python/ql/test/library-tests/ControlFlow/general/Reaches.ql index 3412e6a99bd..2fecec98f1b 100644 --- a/python/ql/test/library-tests/ControlFlow/general/Reaches.ql +++ b/python/ql/test/library-tests/ControlFlow/general/Reaches.ql @@ -1,10 +1,10 @@ import python predicate reaches_exit(Name u) { - u.uses(_) and - exists(ControlFlowNode f, BasicBlock b | f.getNode() = u and f.getBasicBlock() = b | - b.reachesExit() - ) + u.uses(_) and + exists(ControlFlowNode f, BasicBlock b | f.getNode() = u and f.getBasicBlock() = b | + b.reachesExit() + ) } from Name u diff --git a/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql b/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql index 35e43acaa12..afe95f4c799 100644 --- a/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql +++ b/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql @@ -7,9 +7,9 @@ import python from ControlFlowNode p, ControlFlowNode s, string kind where - p.getASuccessor() = s and - (if s = p.getAnExceptionalSuccessor() then kind = "exception" else kind = " normal ") and - not p.getNode() instanceof Scope and - not s.getNode() instanceof Scope + p.getASuccessor() = s and + (if s = p.getAnExceptionalSuccessor() then kind = "exception" else kind = " normal ") and + not p.getNode() instanceof Scope and + not s.getNode() instanceof Scope select p.getNode().getLocation().getStartLine(), p.toString(), kind, - s.getNode().getLocation().getStartLine(), s + s.getNode().getLocation().getStartLine(), s diff --git a/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql b/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql index c743952f2b1..c51707a65ff 100644 --- a/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql +++ b/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql @@ -2,10 +2,10 @@ import python from AstNode a, Scope s where - not a instanceof Import and - not a instanceof If and - not a instanceof AssignStmt and - not a instanceof ExprStmt and - a.getScope() = s and - s instanceof Function + not a instanceof Import and + not a instanceof If and + not a instanceof AssignStmt and + not a instanceof ExprStmt and + a.getScope() = s and + s instanceof Function select a.getLocation().getStartLine(), s.getName(), a, count(a.getAFlowNode()) diff --git a/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql b/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql index 0941d2f2024..2d7e21b2c93 100644 --- a/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql +++ b/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode p, Scope s where - p.getScope() = s and - (exists(p.getATrueSuccessor()) or exists(p.getAFalseSuccessor())) and - s instanceof Function + p.getScope() = s and + (exists(p.getATrueSuccessor()) or exists(p.getAFalseSuccessor())) and + s instanceof Function select p.getLocation().getStartLine(), s.getName(), p, strictcount(p.getASuccessor()) diff --git a/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql index d4cff3d6122..b07837f8746 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, SsaVariable def where def = var.getAnUltimateDefinition() select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - def, def.getLocation().getStartLine() + def, def.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql index feafac5a6c7..90bd66be7b8 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql @@ -2,12 +2,12 @@ import python from SsaVariable v, string kind, ControlFlowNode use, int line where - use = v.getAUse() and - ( - kind = "delete" and v.getDefinition().isDelete() - or - kind = "other " and not v.getDefinition().isDelete() - ) and - line = use.getLocation().getStartLine() and - line != 0 + use = v.getAUse() and + ( + kind = "delete" and v.getDefinition().isDelete() + or + kind = "other " and not v.getDefinition().isDelete() + ) and + line = use.getLocation().getStartLine() and + line != 0 select line, use.toString(), v.getId(), kind diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql index fb2c8f20da8..b03da6a851d 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, SsaVariable arg, BasicBlock pred where pred = var.getPredecessorBlockForPhiArgument(arg) select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - arg, arg.getLocation().getStartLine(), pred.getLastNode().getLocation().getStartLine() + arg, arg.getLocation().getStartLine(), pred.getLastNode().getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql index a8aef8cc72d..9466ac97061 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, SsaVariable arg where arg = var.getAPhiInput() select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - arg, arg.getLocation().getStartLine() + arg, arg.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql index e120b6e1657..31128317e45 100644 --- a/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql +++ b/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql @@ -3,4 +3,4 @@ import python from ControlFlowNode use, SsaVariable def where def.getAUse() = use select use.getLocation().getFile().getShortName(), use.toString(), use.getLocation().getStartLine(), - def.toString(), def.getLocation().getStartLine() + def.toString(), def.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/successors/Successors.ql b/python/ql/test/library-tests/ControlFlow/successors/Successors.ql index 802ed60962b..4b4c2aa7c30 100644 --- a/python/ql/test/library-tests/ControlFlow/successors/Successors.ql +++ b/python/ql/test/library-tests/ControlFlow/successors/Successors.ql @@ -3,15 +3,15 @@ import semmle.python.TestUtils from ControlFlowNode p, ControlFlowNode s, string what where - s = p.getAFalseSuccessor() and what = "false" - or - s = p.getATrueSuccessor() and what = "true" - or - s = p.getAnExceptionalSuccessor() and what = "exceptional" - or - s = p.getANormalSuccessor() and what = "normal" - or - // Add fake edges for node that raise out of scope - p.isExceptionalExit(_) and s = p.getScope().getEntryNode() and what = "exit" + s = p.getAFalseSuccessor() and what = "false" + or + s = p.getATrueSuccessor() and what = "true" + or + s = p.getAnExceptionalSuccessor() and what = "exceptional" + or + s = p.getANormalSuccessor() and what = "normal" + or + // Add fake edges for node that raise out of scope + p.isExceptionalExit(_) and s = p.getScope().getEntryNode() and what = "exit" select compact_location(p.getNode()), p.getNode().toString(), compact_location(s.getNode()), - s.getNode().toString(), what + s.getNode().toString(), what diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql b/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql index 352b1d2890d..fa4b918559e 100644 --- a/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql +++ b/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql @@ -9,8 +9,8 @@ import python from ControlFlowNode p, ControlFlowNode s where - s = p.getAnExceptionalSuccessor() - or - // Add fake edges for node that raise out of scope - p.isExceptionalExit(_) and s = p.getScope().getEntryNode() + s = p.getAnExceptionalSuccessor() + or + // Add fake edges for node that raise out of scope + p.isExceptionalExit(_) and s = p.getScope().getEntryNode() select p.getLocation().getFile().getShortName(), p.getLocation().getStartLine(), p, s.toString() diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql b/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql index 1dedb90ea49..66e621a0d96 100644 --- a/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql +++ b/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql @@ -9,8 +9,8 @@ import python from ControlFlowNode p, ControlFlowNode s, string which where - s = p.getAFalseSuccessor() and which = "False" - or - s = p.getATrueSuccessor() and which = "True" + s = p.getAFalseSuccessor() and which = "False" + or + s = p.getATrueSuccessor() and which = "True" select p.getLocation().getFile().getShortName(), p.getLocation().getStartLine(), p, s.toString(), - which + which diff --git a/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql b/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql index 8ac7583b627..4d75a7728f2 100644 --- a/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql +++ b/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql @@ -3,4 +3,4 @@ import python from SsaVariable var, ControlFlowNode use where use = var.getAUse() select var.getLocation().getFile().getShortName(), var.toString(), var.getLocation().getStartLine(), - use.toString(), use.getLocation().getStartLine() + use.toString(), use.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/DuplicateCode/Duplicate.ql b/python/ql/test/library-tests/DuplicateCode/Duplicate.ql index c680d481398..a8f04593ea3 100644 --- a/python/ql/test/library-tests/DuplicateCode/Duplicate.ql +++ b/python/ql/test/library-tests/DuplicateCode/Duplicate.ql @@ -9,15 +9,15 @@ import python import external.CodeDuplication predicate lexically_sorted(DuplicateBlock dup1, DuplicateBlock dup2) { - dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() - or - dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and - dup1.sourceStartLine() < dup2.sourceStartLine() + dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() + or + dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and + dup1.sourceStartLine() < dup2.sourceStartLine() } from DuplicateBlock dup1, DuplicateBlock dup2 where - dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and - lexically_sorted(dup1, dup2) + dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and + lexically_sorted(dup1, dup2) select dup1.toString(), dup2.toString(), dup1.sourceFile().getShortName(), dup1.sourceStartLine(), - dup1.sourceEndLine() + dup1.sourceEndLine() diff --git a/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql b/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql index 17904ea65cd..ca297eb3718 100644 --- a/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql +++ b/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql @@ -9,18 +9,18 @@ import python import external.CodeDuplication predicate mostlyDuplicateFunction(Function f) { - exists(int covered, int total, Function other, int percent | - duplicateStatements(f, other, covered, total) and - covered != total and - total > 5 and - covered * 100 / total = percent and - percent > 80 and - not exists(Scope s | s = f.getScope*() | duplicateScopes(s, _, _, _)) - ) + exists(int covered, int total, Function other, int percent | + duplicateStatements(f, other, covered, total) and + covered != total and + total > 5 and + covered * 100 / total = percent and + percent > 80 and + not exists(Scope s | s = f.getScope*() | duplicateScopes(s, _, _, _)) + ) } from Stmt s where - mostlyDuplicateFunction(s.getScope()) and - not duplicateStatement(s.getScope(), _, s, _) + mostlyDuplicateFunction(s.getScope()) and + not duplicateStatement(s.getScope(), _, s, _) select s.toString(), s.getLocation().toString() diff --git a/python/ql/test/library-tests/DuplicateCode/Similar.ql b/python/ql/test/library-tests/DuplicateCode/Similar.ql index 528908336d8..3f9a99c8ecf 100644 --- a/python/ql/test/library-tests/DuplicateCode/Similar.ql +++ b/python/ql/test/library-tests/DuplicateCode/Similar.ql @@ -9,14 +9,14 @@ import python import external.CodeDuplication predicate lexically_sorted(SimilarBlock dup1, SimilarBlock dup2) { - dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() - or - dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and - dup1.sourceStartLine() < dup2.sourceStartLine() + dup1.sourceFile().getAbsolutePath() < dup2.sourceFile().getAbsolutePath() + or + dup1.sourceFile().getAbsolutePath() = dup2.sourceFile().getAbsolutePath() and + dup1.sourceStartLine() < dup2.sourceStartLine() } from SimilarBlock dup1, SimilarBlock dup2 where - dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and - lexically_sorted(dup1, dup2) + dup1.getEquivalenceClass() = dup2.getEquivalenceClass() and + lexically_sorted(dup1, dup2) select dup1, dup2, dup1.sourceFile().getShortName(), dup1.sourceStartLine(), dup1.sourceEndLine() diff --git a/python/ql/test/library-tests/PointsTo/api/ClassValue.ql b/python/ql/test/library-tests/PointsTo/api/ClassValue.ql index a71380b7603..230cc487ea4 100644 --- a/python/ql/test/library-tests/PointsTo/api/ClassValue.ql +++ b/python/ql/test/library-tests/PointsTo/api/ClassValue.ql @@ -2,13 +2,13 @@ import python from ClassValue cls, string description where - cls = ClassValue::bool() and description = "bool" - or - cls = ClassValue::int_() and description = "int" - or - cls = ClassValue::float_() and description = "float" - or - cls = ClassValue::classmethod() and description = "classmethod" - or - cls = ClassValue::bool().getMro().getItem(2) and description = "object" + cls = ClassValue::bool() and description = "bool" + or + cls = ClassValue::int_() and description = "int" + or + cls = ClassValue::float_() and description = "float" + or + cls = ClassValue::classmethod() and description = "classmethod" + or + cls = ClassValue::bool().getMro().getItem(2) and description = "object" select cls, description diff --git a/python/ql/test/library-tests/PointsTo/api/Constants.ql b/python/ql/test/library-tests/PointsTo/api/Constants.ql index 39763e6fc24..e44e52b4b9f 100644 --- a/python/ql/test/library-tests/PointsTo/api/Constants.ql +++ b/python/ql/test/library-tests/PointsTo/api/Constants.ql @@ -2,15 +2,15 @@ import python from string txt, Value val where - exists(string s | - txt = "u'" + s + "'" and val = Value::forUnicode(s) - or - txt = "b'" + s + "'" and val = Value::forBytes(s) - | - s = "a" or s = "b" or s = "c" or s = "d" - ) + exists(string s | + txt = "u'" + s + "'" and val = Value::forUnicode(s) or - exists(int i | txt = i.toString() and val = Value::forInt(i) | - i in [1 .. 10] or i in [1000 .. 1010] - ) + txt = "b'" + s + "'" and val = Value::forBytes(s) + | + s = "a" or s = "b" or s = "c" or s = "d" + ) + or + exists(int i | txt = i.toString() and val = Value::forInt(i) | + i in [1 .. 10] or i in [1000 .. 1010] + ) select txt, val diff --git a/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql b/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql index 226b2520521..daafad0492b 100644 --- a/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql +++ b/python/ql/test/library-tests/PointsTo/api/QualifedNames.ql @@ -2,14 +2,14 @@ import python from FunctionValue v, string name where - name = v.getQualifiedName() and - ( - v = Value::named("len") - or - v instanceof PythonFunctionValue - or - v = Value::named("sys.exit") - or - v = Value::named("list").(ClassValue).lookup("append") - ) + name = v.getQualifiedName() and + ( + v = Value::named("len") + or + v instanceof PythonFunctionValue + or + v = Value::named("sys.exit") + or + v = Value::named("list").(ClassValue).lookup("append") + ) select v, name diff --git a/python/ql/test/library-tests/PointsTo/api/Value.ql b/python/ql/test/library-tests/PointsTo/api/Value.ql index 23d78317764..5af0d1061e7 100644 --- a/python/ql/test/library-tests/PointsTo/api/Value.ql +++ b/python/ql/test/library-tests/PointsTo/api/Value.ql @@ -2,12 +2,12 @@ import python from Value val, string name where - val = Value::named(name) and - ( - name = "bool" or - name = "sys" or - name = "sys.argv" or - name = "ValueError" or - name = "slice" - ) + val = Value::named(name) and + ( + name = "bool" or + name = "sys" or + name = "sys.argv" or + name = "ValueError" or + name = "slice" + ) select val, name diff --git a/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql index de13f0504e8..7c76b9e0ce5 100644 --- a/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql +++ b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql @@ -2,4 +2,4 @@ import python from CallNode call, CallableValue callable, int i select call.getLocation().getStartLine(), call.toString(), callable.toString(), i, - callable.getArgumentForCall(call, i).toString() + callable.getArgumentForCall(call, i).toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql index c531a9ab57a..4cb3fbdf335 100644 --- a/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql +++ b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql @@ -2,4 +2,4 @@ import python from CallNode call, CallableValue callable, string name select call.getLocation().getStartLine(), call.toString(), callable.toString(), name, - callable.getNamedArgumentForCall(call, name).toString() + callable.getNamedArgumentForCall(call, name).toString() diff --git a/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql b/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql index 958306c53e6..00d2b448c78 100644 --- a/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/comparisons/PointsTo.ql @@ -3,7 +3,7 @@ import semmle.python.objects.ObjectAPI from int line, ControlFlowNode f, Value v where - any(ExprStmt s).getValue() = f.getNode() and - line = f.getLocation().getStartLine() and - f.pointsTo(v) + any(ExprStmt s).getValue() = f.getNode() and + line = f.getLocation().getStartLine() and + f.pointsTo(v) select line, v diff --git a/python/ql/test/library-tests/PointsTo/customise/test.ql b/python/ql/test/library-tests/PointsTo/customise/test.ql index 8aea8b05b18..c2fceb95225 100644 --- a/python/ql/test/library-tests/PointsTo/customise/test.ql +++ b/python/ql/test/library-tests/PointsTo/customise/test.ql @@ -7,29 +7,29 @@ import semmle.python.types.Extensions */ class HasTypeFact extends CustomPointsToOriginFact { - HasTypeFact() { - exists(FunctionObject func, string name | - func.getACall() = this and - name = func.getName() and - name.prefix("has_type_".length()) = "has_type_" - ) - } + HasTypeFact() { + exists(FunctionObject func, string name | + func.getACall() = this and + name = func.getName() and + name.prefix("has_type_".length()) = "has_type_" + ) + } - override predicate pointsTo(Object value, ClassObject cls) { - exists(FunctionObject func, string name | - func.getACall() = this and - name = func.getName() and - name.prefix("has_type_".length()) = "has_type_" - | - cls.getName() = name.suffix("has_type_".length()) - ) and - value = this - } + override predicate pointsTo(Object value, ClassObject cls) { + exists(FunctionObject func, string name | + func.getACall() = this and + name = func.getName() and + name.prefix("has_type_".length()) = "has_type_" + | + cls.getName() = name.suffix("has_type_".length()) + ) and + value = this + } } from int line, ControlFlowNode f, Object o, ClassObject c where - f.getLocation().getStartLine() = line and - exists(Comment ct | ct.getLocation().getStartLine() < line) and - f.refersTo(o, c, _) + f.getLocation().getStartLine() = line and + exists(Comment ct | ct.getLocation().getStartLine() < line) and + f.refersTo(o, c, _) select line, f.toString(), o.toString(), c.toString() diff --git a/python/ql/test/library-tests/PointsTo/decorators/Test.ql b/python/ql/test/library-tests/PointsTo/decorators/Test.ql index 3aff12a3a1f..b5175845070 100644 --- a/python/ql/test/library-tests/PointsTo/decorators/Test.ql +++ b/python/ql/test/library-tests/PointsTo/decorators/Test.ql @@ -4,7 +4,7 @@ import python // version to version, just the end result. from NameNode f, Object o, ControlFlowNode x, int line where - f.refersTo(o, x) and - f.getLocation().getFile().getBaseName() = "test.py" and - line = f.getLocation().getStartLine() + f.refersTo(o, x) and + f.getLocation().getFile().getBaseName() = "test.py" and + line = f.getLocation().getStartLine() select line, f.toString(), o.toString(), x.getLocation().toString() diff --git a/python/ql/test/library-tests/PointsTo/decorators/Values.ql b/python/ql/test/library-tests/PointsTo/decorators/Values.ql index 712cc025786..fc7a08db20b 100644 --- a/python/ql/test/library-tests/PointsTo/decorators/Values.ql +++ b/python/ql/test/library-tests/PointsTo/decorators/Values.ql @@ -4,6 +4,6 @@ import semmle.python.objects.ObjectInternal from NameNode f, Context ctx, ObjectInternal v where - f.getLocation().getFile().getBaseName() = "test.py" and - PointsTo::pointsTo(f, ctx, v, _) + f.getLocation().getFile().getBaseName() = "test.py" and + PointsTo::pointsTo(f, ctx, v, _) select f, ctx, v diff --git a/python/ql/test/library-tests/PointsTo/extensions/Extend.ql b/python/ql/test/library-tests/PointsTo/extensions/Extend.ql index 14082905ce4..078b925a1df 100644 --- a/python/ql/test/library-tests/PointsTo/extensions/Extend.ql +++ b/python/ql/test/library-tests/PointsTo/extensions/Extend.ql @@ -3,55 +3,55 @@ import semmle.python.pointsto.PointsTo private import semmle.python.types.Extensions class CfgExtension extends CustomPointsToOriginFact { - CfgExtension() { - this.(NameNode).getId() = "one" - or - this.(NameNode).getId() = "two" - } + CfgExtension() { + this.(NameNode).getId() = "one" + or + this.(NameNode).getId() = "two" + } - override predicate pointsTo(Object value, ClassObject cls) { - cls = theIntType() and - ( - this.(NameNode).getId() = "one" and value.(NumericObject).intValue() = 1 - or - this.(NameNode).getId() = "two" and value.(NumericObject).intValue() = 2 - ) - } + override predicate pointsTo(Object value, ClassObject cls) { + cls = theIntType() and + ( + this.(NameNode).getId() = "one" and value.(NumericObject).intValue() = 1 + or + this.(NameNode).getId() = "two" and value.(NumericObject).intValue() = 2 + ) + } } class AttributeExtension extends CustomPointsToAttribute { - AttributeExtension() { this = this } + AttributeExtension() { this = this } - override predicate attributePointsTo( - string name, Object value, ClassObject cls, ControlFlowNode origin - ) { - cls = theIntType() and - origin = any(Module m).getEntryNode() and - ( - name = "three" and value.(NumericObject).intValue() = 3 - or - name = "four" and value.(NumericObject).intValue() = 4 - ) - } + override predicate attributePointsTo( + string name, Object value, ClassObject cls, ControlFlowNode origin + ) { + cls = theIntType() and + origin = any(Module m).getEntryNode() and + ( + name = "three" and value.(NumericObject).intValue() = 3 + or + name = "four" and value.(NumericObject).intValue() = 4 + ) + } } class NoClassExtension extends CustomPointsToObjectFact { - NoClassExtension() { this = this } + NoClassExtension() { this = this } - override predicate pointsTo(Object value) { - this.(NameNode).getId() = "five" and value.(NumericObject).intValue() = 5 - or - this.(NameNode).getId() = "six" and value.(NumericObject).intValue() = 6 - } + override predicate pointsTo(Object value) { + this.(NameNode).getId() = "five" and value.(NumericObject).intValue() = 5 + or + this.(NameNode).getId() = "six" and value.(NumericObject).intValue() = 6 + } } /* Check that we can use old API without causing non-monotonic recursion */ class RecurseIntoOldPointsTo extends CustomPointsToOriginFact { - RecurseIntoOldPointsTo() { PointsTo::points_to(this, _, unknownValue(), _, _) } + RecurseIntoOldPointsTo() { PointsTo::points_to(this, _, unknownValue(), _, _) } - override predicate pointsTo(Object value, ClassObject cls) { - value = unknownValue() and cls = theUnknownType() - } + override predicate pointsTo(Object value, ClassObject cls) { + value = unknownValue() and cls = theUnknownType() + } } from ControlFlowNode f, Object o diff --git a/python/ql/test/library-tests/PointsTo/functions/Calls.ql b/python/ql/test/library-tests/PointsTo/functions/Calls.ql index 2833c2e60be..5bc29b5aaf3 100644 --- a/python/ql/test/library-tests/PointsTo/functions/Calls.ql +++ b/python/ql/test/library-tests/PointsTo/functions/Calls.ql @@ -2,10 +2,10 @@ import python from CallNode call, FunctionObject func, string kind where - ( - func.getAMethodCall() = call and kind = "method" - or - func.getAFunctionCall() = call and kind = "function" - ) and - call.getLocation().getFile().getShortName().matches("odasa%") + ( + func.getAMethodCall() = call and kind = "method" + or + func.getAFunctionCall() = call and kind = "function" + ) and + call.getLocation().getFile().getShortName().matches("odasa%") select call.getLocation().getStartLine(), call.toString(), func.toString(), kind diff --git a/python/ql/test/library-tests/PointsTo/functions/test.ql b/python/ql/test/library-tests/PointsTo/functions/test.ql index f520f6b2254..f85e95f5fe4 100644 --- a/python/ql/test/library-tests/PointsTo/functions/test.ql +++ b/python/ql/test/library-tests/PointsTo/functions/test.ql @@ -2,6 +2,6 @@ import python from Call c, FunctionObject f where - c.getFunc().(Attribute).getObject().(Name).getId() = "self" and - f.getACall().getNode() = c + c.getFunc().(Attribute).getObject().(Name).getId() = "self" and + f.getACall().getNode() = c select c.getLocation().getStartLine(), f.toString() diff --git a/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql b/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql index a7b9403a18d..8caa54ccc23 100644 --- a/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql @@ -3,7 +3,7 @@ import interesting from int line, ControlFlowNode f, Object o, ImportTimeScope n where - of_interest(f, line) and - f.refersTo(o) and - f.getScope() = n + of_interest(f, line) and + f.refersTo(o) and + f.getScope() = n select n.toString(), line, f.toString(), o.toString() diff --git a/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql b/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql index 342a329746e..aee2cb11bf4 100644 --- a/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql @@ -11,6 +11,6 @@ import Util from int line, ControlFlowNode f, Object o where - of_interest(f, line) and - f.refersTo(o) + of_interest(f, line) and + f.refersTo(o) select line, f.toString(), repr(o) diff --git a/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql b/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql index c80de106c3d..fe14e61e01b 100644 --- a/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql +++ b/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql @@ -4,6 +4,6 @@ import Util from int line, ControlFlowNode f, Object o, ClassObject cls where - of_interest(f, line) and - f.refersTo(o, cls, _) + of_interest(f, line) and + f.refersTo(o, cls, _) select line, f.toString(), repr(o), repr(cls) diff --git a/python/ql/test/library-tests/PointsTo/general/Util.qll b/python/ql/test/library-tests/PointsTo/general/Util.qll index f75ed24f4f0..1c26d7d830b 100644 --- a/python/ql/test/library-tests/PointsTo/general/Util.qll +++ b/python/ql/test/library-tests/PointsTo/general/Util.qll @@ -1,10 +1,10 @@ import python string repr(Object o) { - not o instanceof StringObject and not o = theBoundMethodType() and result = o.toString() - or - /* Work around differing names in 2/3 */ - result = "'" + o.(StringObject).getText() + "'" - or - o = theBoundMethodType() and result = "builtin-class method" + not o instanceof StringObject and not o = theBoundMethodType() and result = o.toString() + or + /* Work around differing names in 2/3 */ + result = "'" + o.(StringObject).getText() + "'" + or + o = theBoundMethodType() and result = "builtin-class method" } diff --git a/python/ql/test/library-tests/PointsTo/general/interesting.qll b/python/ql/test/library-tests/PointsTo/general/interesting.qll index f7c3e9d682d..728738d65bc 100644 --- a/python/ql/test/library-tests/PointsTo/general/interesting.qll +++ b/python/ql/test/library-tests/PointsTo/general/interesting.qll @@ -1,13 +1,13 @@ import python predicate of_interest(ControlFlowNode n, int line) { - exists(Location l, File f | l = n.getLocation() | - line = l.getStartLine() and - f = l.getFile() and - f.getName().matches("%test.py%") and - exists(Comment c | - c.getLocation().getStartLine() < line and - c.getLocation().getFile() = f - ) + exists(Location l, File f | l = n.getLocation() | + line = l.getStartLine() and + f = l.getFile() and + f.getName().matches("%test.py%") and + exists(Comment c | + c.getLocation().getStartLine() < line and + c.getLocation().getFile() = f ) + ) } diff --git a/python/ql/test/library-tests/PointsTo/global/Global.ql b/python/ql/test/library-tests/PointsTo/global/Global.ql index d9b8a246d11..d3ab7bc0269 100644 --- a/python/ql/test/library-tests/PointsTo/global/Global.ql +++ b/python/ql/test/library-tests/PointsTo/global/Global.ql @@ -6,6 +6,6 @@ import semmle.python.objects.ObjectInternal from ControlFlowNode f, PointsToContext ctx, Value obj, ControlFlowNode orig where - exists(ExprStmt s | s.getValue().getAFlowNode() = f) and - PointsTo::pointsTo(f, ctx, obj, orig) + exists(ExprStmt s | s.getValue().getAFlowNode() = f) and + PointsTo::pointsTo(f, ctx, obj, orig) select ctx, f, obj.toString(), orig diff --git a/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql b/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql index b545f6e6a18..db4710786ac 100644 --- a/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode f, Object o, ControlFlowNode x where - f.refersTo(o, x) and - exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) + f.refersTo(o, x) and + exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) select f.getLocation().getFile().getShortName(), f.getLocation().getStartLine(), f.toString(), - o.toString(), x.getLocation().getStartLine() + o.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql b/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql index 2bf6b6b62a9..1c294e64282 100644 --- a/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql +++ b/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where - f.refersTo(o, c, x) and - exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) + f.refersTo(o, c, x) and + exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) select f.getLocation().getFile().getShortName(), f.getLocation().getStartLine(), f.toString(), - o.toString(), c.toString(), x.getLocation().getStartLine() + o.toString(), c.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/imports/Runtime.ql b/python/ql/test/library-tests/PointsTo/imports/Runtime.ql index 4d917aa5af6..f694bc64cf0 100644 --- a/python/ql/test/library-tests/PointsTo/imports/Runtime.ql +++ b/python/ql/test/library-tests/PointsTo/imports/Runtime.ql @@ -2,8 +2,8 @@ import python from int line, ControlFlowNode f, Object o, ControlFlowNode orig where - not f.getLocation().getFile().inStdlib() and - f.refersTo(o, orig) and - line = f.getLocation().getStartLine() and - line != 0 + not f.getLocation().getFile().inStdlib() and + f.refersTo(o, orig) and + line = f.getLocation().getStartLine() and + line != 0 select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), orig.toString() diff --git a/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql b/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql index 7e915d04573..99a5f7b8163 100644 --- a/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql +++ b/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql @@ -2,9 +2,9 @@ import python from int line, ControlFlowNode f, Object o, ClassObject cls, ControlFlowNode orig where - not f.getLocation().getFile().inStdlib() and - f.refersTo(o, cls, orig) and - line = f.getLocation().getStartLine() and - line != 0 + not f.getLocation().getFile().inStdlib() and + f.refersTo(o, cls, orig) and + line = f.getLocation().getStartLine() and + line != 0 select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), cls.toString(), - orig.toString() + orig.toString() diff --git a/python/ql/test/library-tests/PointsTo/indexing/Test.ql b/python/ql/test/library-tests/PointsTo/indexing/Test.ql index 825cb1cf3be..694e4d8e98e 100644 --- a/python/ql/test/library-tests/PointsTo/indexing/Test.ql +++ b/python/ql/test/library-tests/PointsTo/indexing/Test.ql @@ -2,6 +2,6 @@ import python from ControlFlowNode f, Object o, ControlFlowNode x where - f.refersTo(o, x) and - f.getLocation().getFile().getBaseName() = "test.py" + f.refersTo(o, x) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation().getStartLine(), f.toString(), o.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql b/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql index e11999a75de..9f16abc2de0 100644 --- a/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql +++ b/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql @@ -2,7 +2,7 @@ import python from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where - f.refersTo(o, c, x) and - f.getLocation().getFile().getBaseName() = "test.py" + f.refersTo(o, c, x) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation().getStartLine(), f.toString(), o.toString(), c.toString(), - x.getLocation().getStartLine() + x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql b/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql index c4677b7df51..f1201eba0dc 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject base, int n where - not cls.isBuiltin() and - base = cls.getBaseType(n) + not cls.isBuiltin() and + base = cls.getBaseType(n) select cls.toString(), n, base.toString() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql b/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql index 3768116ff11..0ca90782119 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject meta where - not cls.isBuiltin() and - meta = cls.getMetaClass() + not cls.isBuiltin() and + meta = cls.getMetaClass() select cls.toString(), meta.toString() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql b/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql index 7fdd431c216..2627eac8e38 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql @@ -4,7 +4,7 @@ private import semmle.python.pointsto.PointsTo /** Make unknown type visible */ class UnknownType extends UnknownClassInternal { - override string toString() { result = "*UNKNOWN TYPE" } + override string toString() { result = "*UNKNOWN TYPE" } } from ClassObjectInternal c diff --git a/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql b/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql index 7810c607787..338ea118ac1 100644 --- a/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql +++ b/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, ClassObject sup where - not cls.isBuiltin() and - sup = cls.getASuperType() + not cls.isBuiltin() and + sup = cls.getASuperType() select cls.toString(), sup.toString() diff --git a/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql b/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql index 996b8597d5e..c81bd0ed3de 100644 --- a/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql +++ b/python/ql/test/library-tests/PointsTo/local/LocalPointsTo.ql @@ -4,6 +4,6 @@ import semmle.python.objects.ObjectInternal from ControlFlowNode f, ObjectInternal obj, ControlFlowNode orig where - exists(ExprStmt s | s.getValue().getAFlowNode() = f) and - PointsTo::pointsTo(f, _, obj, orig) + exists(ExprStmt s | s.getValue().getAFlowNode() = f) and + PointsTo::pointsTo(f, _, obj, orig) select f, obj.toString(), orig diff --git a/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql b/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql index 67aff9597c2..b984c682742 100644 --- a/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql +++ b/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql @@ -2,12 +2,12 @@ import python from string l, NameNode n where - n.getLocation().getFile().getShortName() = "test.py" and - ( - n.isGlobal() and l = "global" - or - n.isLocal() and l = "local" - or - n.isNonLocal() and l = "non-local" - ) + n.getLocation().getFile().getShortName() = "test.py" and + ( + n.isGlobal() and l = "global" + or + n.isLocal() and l = "local" + or + n.isNonLocal() and l = "non-local" + ) select n.getLocation().getStartLine(), n.getId(), l diff --git a/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql b/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql index d9cb2f019a6..a5d2afd4241 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/Failed.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, string reason where - cls.getPyClass().getEnclosingModule().getName() = "test" and - cls.failedInference(reason) + cls.getPyClass().getEnclosingModule().getName() = "test" and + cls.failedInference(reason) select cls, reason diff --git a/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql b/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql index 5a10701ef83..0f48b7a1034 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/Mro.ql @@ -4,7 +4,7 @@ private import semmle.python.pointsto.PointsTo /** Make unknown type visible */ class UnknownType extends UnknownClassInternal { - override string toString() { result = "*UNKNOWN TYPE" } + override string toString() { result = "*UNKNOWN TYPE" } } from PythonClassObjectInternal cls diff --git a/python/ql/test/library-tests/PointsTo/metaclass/Style.ql b/python/ql/test/library-tests/PointsTo/metaclass/Style.ql index 29feef64ec1..f29ba3a8b7c 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/Style.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/Style.ql @@ -2,10 +2,10 @@ import python from ClassObject cls, string style where - cls.getPyClass().getEnclosingModule().getName() = "test" and - ( - cls.isNewStyle() and style = "new" - or - cls.isOldStyle() and style = "old" - ) + cls.getPyClass().getEnclosingModule().getName() = "test" and + ( + cls.isNewStyle() and style = "new" + or + cls.isOldStyle() and style = "old" + ) select cls, style diff --git a/python/ql/test/library-tests/PointsTo/metaclass/test.ql b/python/ql/test/library-tests/PointsTo/metaclass/test.ql index 17b90483315..a0d47fa9717 100644 --- a/python/ql/test/library-tests/PointsTo/metaclass/test.ql +++ b/python/ql/test/library-tests/PointsTo/metaclass/test.ql @@ -3,7 +3,7 @@ private import semmle.python.objects.ObjectInternal /** Make unknown type visible */ class UnknownType extends UnknownClassInternal { - override string toString() { result = "*UNKNOWN TYPE" } + override string toString() { result = "*UNKNOWN TYPE" } } from ClassObject cls diff --git a/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql b/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql index 5ad6fabd380..5810cb22d8e 100644 --- a/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql +++ b/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql @@ -5,4 +5,4 @@ import Util from ClassMethodObject cm, CallNode call where call = cm.getACall() select locate(call.getLocation(), "lp"), cm.getFunction().toString(), - cm.(ControlFlowNode).getLocation().toString() + cm.(ControlFlowNode).getLocation().toString() diff --git a/python/ql/test/library-tests/PointsTo/new/Consistency.ql b/python/ql/test/library-tests/PointsTo/new/Consistency.ql index fe6cd9a861c..282b96fc541 100644 --- a/python/ql/test/library-tests/PointsTo/new/Consistency.ql +++ b/python/ql/test/library-tests/PointsTo/new/Consistency.ql @@ -3,142 +3,142 @@ import semmle.python.pointsto.PointsTo import semmle.python.objects.ObjectInternal predicate ssa_consistency(string clsname, string problem, string what) { + /* Exactly one definition of each SSA variable */ + exists(EssaVariable var | clsname = var.getAQlClass() | /* Exactly one definition of each SSA variable */ - exists(EssaVariable var | clsname = var.getAQlClass() | - /* Exactly one definition of each SSA variable */ - count(var.getDefinition()) != 1 and - problem = " has " + count(var.getDefinition()) + " definitions." and - what = "SSA variable " + var.getSourceVariable().getName() - or - /* Backing variable */ - not exists(var.getSourceVariable()) and - problem = "An SSA variable has no backing variable." and - what = "An SSA variable" - or - count(var.getSourceVariable()) != 1 and - problem = - var.getSourceVariable().getName() + " has " + count(var.getSourceVariable()) + - " backing variables." and - what = "SSA variable " + var.getSourceVariable().getName() - ) + count(var.getDefinition()) != 1 and + problem = " has " + count(var.getDefinition()) + " definitions." and + what = "SSA variable " + var.getSourceVariable().getName() or - /* Exactly one location */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - what = - "SSA Definition " + def.getSourceVariable().getName() + " in " + - def.getSourceVariable().(Variable).getScope().getName() and - count(def.getLocation()) != 1 and - problem = " has " + count(def.getLocation()) + " locations" - ) + /* Backing variable */ + not exists(var.getSourceVariable()) and + problem = "An SSA variable has no backing variable." and + what = "An SSA variable" or - /* Must have a source variable */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - not exists(def.getSourceVariable()) and - what = " at " + def.getLocation() and - problem = "has not source variable" - ) - or - /* Variables must have exactly one representation */ - exists(EssaVariable var | - clsname = var.getAQlClass() and - what = - "SSA variable " + var.getSourceVariable().getName() + " defined at " + - var.getDefinition().getLocation() and - count(var.getRepresentation()) != 1 and - problem = " has " + count(var.getRepresentation()) + " representations" - ) - or - /* Definitions must have exactly one representation */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - what = "SSA definition " + def.getSourceVariable().getName() + " at " + def.getLocation() and - count(def.getRepresentation()) != 1 and - problem = - " has " + count(def.getRepresentation()) + " representations: " + def.getRepresentation() - ) - or - /* Refinements must have exactly one input */ - exists(EssaNodeRefinement ref | - clsname = ref.getAQlClass() and - what = "Refinement " + ref.getSourceVariable().getName() + " at " + ref.getLocation() and - count(ref.getInput()) != 1 and - problem = " has " + count(ref.getInput()) + " inputs: " + ref.getInput().getRepresentation() - ) - or - /* - * Ideally filter nodes should have exactly one input, but it is not a big deal - * if we prune away the input, leaving it with none. - */ + count(var.getSourceVariable()) != 1 and + problem = + var.getSourceVariable().getName() + " has " + count(var.getSourceVariable()) + + " backing variables." and + what = "SSA variable " + var.getSourceVariable().getName() + ) + or + /* Exactly one location */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = + "SSA Definition " + def.getSourceVariable().getName() + " in " + + def.getSourceVariable().(Variable).getScope().getName() and + count(def.getLocation()) != 1 and + problem = " has " + count(def.getLocation()) + " locations" + ) + or + /* Must have a source variable */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + not exists(def.getSourceVariable()) and + what = " at " + def.getLocation() and + problem = "has not source variable" + ) + or + /* Variables must have exactly one representation */ + exists(EssaVariable var | + clsname = var.getAQlClass() and + what = + "SSA variable " + var.getSourceVariable().getName() + " defined at " + + var.getDefinition().getLocation() and + count(var.getRepresentation()) != 1 and + problem = " has " + count(var.getRepresentation()) + " representations" + ) + or + /* Definitions must have exactly one representation */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = "SSA definition " + def.getSourceVariable().getName() + " at " + def.getLocation() and + count(def.getRepresentation()) != 1 and + problem = + " has " + count(def.getRepresentation()) + " representations: " + def.getRepresentation() + ) + or + /* Refinements must have exactly one input */ + exists(EssaNodeRefinement ref | + clsname = ref.getAQlClass() and + what = "Refinement " + ref.getSourceVariable().getName() + " at " + ref.getLocation() and + count(ref.getInput()) != 1 and + problem = " has " + count(ref.getInput()) + " inputs: " + ref.getInput().getRepresentation() + ) + or + /* + * Ideally filter nodes should have exactly one input, but it is not a big deal + * if we prune away the input, leaving it with none. + */ - exists(EssaEdgeRefinement def | - clsname = def.getAQlClass() and - what = def.getSourceVariable().getName() + " at " + def.getLocation() - | - count(def.getInput()) > 1 and problem = " has " + count(def.getInput()) + " inputs." - ) + exists(EssaEdgeRefinement def | + clsname = def.getAQlClass() and + what = def.getSourceVariable().getName() + " at " + def.getLocation() + | + count(def.getInput()) > 1 and problem = " has " + count(def.getInput()) + " inputs." + ) + or + /* Each use has only one reaching SSA variable */ + exists(ControlFlowNode use, SsaSourceVariable v, int c | + c = strictcount(EssaVariable s | s.getAUse() = use and s.getSourceVariable() = v) and + clsname = use.getAQlClass() and + c != 1 and + what = use + " at " + use.getLocation() and + problem = " has " + c + " SSA variables reaching." + ) + or + /* Python-specific subclasses of EssaDefinitions should be disjoint and complete */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = def.getVariable().getName() + " at " + def.getLocation() and + problem = "has non-disjoint subclasses" + | + strictcount(def.getAQlClass()) > 2 or - /* Each use has only one reaching SSA variable */ - exists(ControlFlowNode use, SsaSourceVariable v, int c | - c = strictcount(EssaVariable s | s.getAUse() = use and s.getSourceVariable() = v) and - clsname = use.getAQlClass() and - c != 1 and - what = use + " at " + use.getLocation() and - problem = " has " + c + " SSA variables reaching." - ) + /* OK if method call and argument overlap: `x.foo(x)` */ + strictcount(def.getAQlClass()) > 1 and + not clsname = "ArgumentRefinement" and + not clsname = "SelfCallsiteRefinement" + ) + or + exists(EssaDefinition def | + clsname = def.getAQlClass() and + clsname.prefix(4) = "Essa" and + what = " at " + def.getLocation() and + problem = "not covered by Python-specific subclass." + ) + or + // All modules should have __name__ + exists(Module m | + what = " at " + m.getLocation() and + clsname = "Module" + | + not exists(m.getName()) and + problem = "does not have a name" or - /* Python-specific subclasses of EssaDefinitions should be disjoint and complete */ - exists(EssaDefinition def | - clsname = def.getAQlClass() and - what = def.getVariable().getName() + " at " + def.getLocation() and - problem = "has non-disjoint subclasses" - | - strictcount(def.getAQlClass()) > 2 - or - /* OK if method call and argument overlap: `x.foo(x)` */ - strictcount(def.getAQlClass()) > 1 and - not clsname = "ArgumentRefinement" and - not clsname = "SelfCallsiteRefinement" - ) + not m.isPackage() and + not exists(Variable v | v.getId() = "__name__" and v.getScope() = m) and + problem = "does not have a __name__ variable" or - exists(EssaDefinition def | - clsname = def.getAQlClass() and - clsname.prefix(4) = "Essa" and - what = " at " + def.getLocation() and - problem = "not covered by Python-specific subclass." - ) - or - // All modules should have __name__ - exists(Module m | - what = " at " + m.getLocation() and - clsname = "Module" - | - not exists(m.getName()) and - problem = "does not have a name" - or - not m.isPackage() and - not exists(Variable v | v.getId() = "__name__" and v.getScope() = m) and - problem = "does not have a __name__ variable" - or - not m.isPackage() and - not exists(EssaNodeDefinition def | - def.getDefiningNode().getScope() = m and - def.getVariable().getName() = "__name__" - ) and - problem = "does not have an ImplicitModuleNameDefinition" - ) + not m.isPackage() and + not exists(EssaNodeDefinition def | + def.getDefiningNode().getScope() = m and + def.getVariable().getName() = "__name__" + ) and + problem = "does not have an ImplicitModuleNameDefinition" + ) } predicate undefined_consistency(string clsname, string problem, string what) { - /* Variables may be undefined, but values cannot be */ - exists(ControlFlowNode f | - PointsToInternal::pointsTo(f, _, ObjectInternal::undefined(), _) and - clsname = f.getAQlClass() and - not clsname = "AnyNode" and - problem = " points-to an undefined variable" and - what = f.toString() - ) + /* Variables may be undefined, but values cannot be */ + exists(ControlFlowNode f | + PointsToInternal::pointsTo(f, _, ObjectInternal::undefined(), _) and + clsname = f.getAQlClass() and + not clsname = "AnyNode" and + problem = " points-to an undefined variable" and + what = f.toString() + ) } from string clsname, string problem, string what diff --git a/python/ql/test/library-tests/PointsTo/new/Dataflow.ql b/python/ql/test/library-tests/PointsTo/new/Dataflow.ql index 47a12acee53..d4dd0878942 100755 --- a/python/ql/test/library-tests/PointsTo/new/Dataflow.ql +++ b/python/ql/test/library-tests/PointsTo/new/Dataflow.ql @@ -4,4 +4,4 @@ import Util from EssaVariable v, EssaDefinition def where def = v.getDefinition() and not v.getSourceVariable() instanceof SpecialSsaSourceVariable select locate(def.getLocation(), "abdefghijknrs_"), - v.getRepresentation() + " = " + def.getRepresentation() + v.getRepresentation() + " = " + def.getRepresentation() diff --git a/python/ql/test/library-tests/PointsTo/new/Live.ql b/python/ql/test/library-tests/PointsTo/new/Live.ql index 4bcb7da27e6..daa641c6624 100644 --- a/python/ql/test/library-tests/PointsTo/new/Live.ql +++ b/python/ql/test/library-tests/PointsTo/new/Live.ql @@ -4,7 +4,7 @@ import Util from Variable var, BasicBlock b, ControlFlowNode loc, string end where - Liveness::liveAtEntry(var, b) and end = "entry" and loc = b.getNode(0) - or - Liveness::liveAtExit(var, b) and end = "exit" and loc = b.getLastNode() + Liveness::liveAtEntry(var, b) and end = "entry" and loc = b.getNode(0) + or + Liveness::liveAtExit(var, b) and end = "exit" and loc = b.getLastNode() select var, locate(loc.getLocation(), "b"), end diff --git a/python/ql/test/library-tests/PointsTo/new/NameSpace.ql b/python/ql/test/library-tests/PointsTo/new/NameSpace.ql index 18fd5e9e37c..5d099f78b61 100644 --- a/python/ql/test/library-tests/PointsTo/new/NameSpace.ql +++ b/python/ql/test/library-tests/PointsTo/new/NameSpace.ql @@ -3,16 +3,16 @@ import Util from Scope s, string name, Object val where - name != "__name__" and - ( - exists(ModuleObject m | - m.getModule() = s and - m.attributeRefersTo(name, val, _) - ) - or - exists(ClassObject cls | - cls.getPyClass() = s and - cls.declaredAttribute(name) = val - ) + name != "__name__" and + ( + exists(ModuleObject m | + m.getModule() = s and + m.attributeRefersTo(name, val, _) ) + or + exists(ClassObject cls | + cls.getPyClass() = s and + cls.declaredAttribute(name) = val + ) + ) select locate(s.getLocation(), "abcdghijklopqrs"), s.toString(), name, repr(val) diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql b/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql index 044d33c2887..cac31b95572 100644 --- a/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql +++ b/python/ql/test/library-tests/PointsTo/new/PointsToMissing.ql @@ -5,21 +5,21 @@ import semmle.python.objects.ObjectInternal /* This test should return _no_ results. */ predicate relevant_node(ControlFlowNode n) { - exists(CallNode c | - c.getFunction().(NameNode).getId() = "check" and - n = c.getAnArg() - ) - or - exists(Comment c, string filepath, int bl | - n.getNode().getScope().getLocation().hasLocationInfo(filepath, bl, _, _, _) and - c.getLocation().hasLocationInfo(filepath, bl, _, _, _) and - c.getText().matches("%check") and - not n.(NameNode).isStore() - ) + exists(CallNode c | + c.getFunction().(NameNode).getId() = "check" and + n = c.getAnArg() + ) + or + exists(Comment c, string filepath, int bl | + n.getNode().getScope().getLocation().hasLocationInfo(filepath, bl, _, _, _) and + c.getLocation().hasLocationInfo(filepath, bl, _, _, _) and + c.getText().matches("%check") and + not n.(NameNode).isStore() + ) } from ControlFlowNode f where - relevant_node(f) and - not PointsTo::pointsTo(f, _, _, _) + relevant_node(f) and + not PointsTo::pointsTo(f, _, _, _) select locate(f.getLocation(), "abchlr"), f.toString() diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql b/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql index 56a58642f1c..dbed1a9a7f6 100755 --- a/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql +++ b/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql @@ -6,4 +6,4 @@ import semmle.python.pointsto.PointsToContext from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x, PointsToContext ctx where PointsTo::points_to(f, ctx, o, c, x) select locate(f.getLocation(), "abeghijklmnpqrstu"), f.toString(), repr(o), repr(c), - x.getLocation().getStartLine(), ctx + x.getLocation().getStartLine(), ctx diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql b/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql index ed04a0b3dc3..5747ce18fd5 100644 --- a/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql +++ b/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql @@ -5,4 +5,4 @@ import semmle.python.pointsto.PointsTo from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x where PointsTo::points_to(f, _, o, c, x) select locate(f.getLocation(), "abdeghijkls"), f.toString(), repr(o), repr(c), - x.getLocation().getStartLine() + x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/new/Precedes.ql b/python/ql/test/library-tests/PointsTo/new/Precedes.ql index bda245eca6d..d14b206b5a8 100644 --- a/python/ql/test/library-tests/PointsTo/new/Precedes.ql +++ b/python/ql/test/library-tests/PointsTo/new/Precedes.ql @@ -4,4 +4,4 @@ import Util from Scope pre, Scope post where pre.precedes(post) select locate(pre.getLocation(), "q"), pre.toString(), locate(post.getLocation(), "q"), - post.toString() + post.toString() diff --git a/python/ql/test/library-tests/PointsTo/new/SSA.ql b/python/ql/test/library-tests/PointsTo/new/SSA.ql index 6c154f57e57..b8f2c4cf54b 100644 --- a/python/ql/test/library-tests/PointsTo/new/SSA.ql +++ b/python/ql/test/library-tests/PointsTo/new/SSA.ql @@ -5,8 +5,8 @@ import Util from EssaVariable v, EssaDefinition def, Object o, ClassObject cls where - def = v.getDefinition() and - not v.getSourceVariable() instanceof SpecialSsaSourceVariable and - PointsTo::ssa_variable_points_to(v, _, o, cls, _) + def = v.getDefinition() and + not v.getSourceVariable() instanceof SpecialSsaSourceVariable and + PointsTo::ssa_variable_points_to(v, _, o, cls, _) select locate(def.getLocation(), "abcdegjqmns_"), - v.getRepresentation() + " = " + def.getRepresentation(), repr(o), repr(cls) + v.getRepresentation() + " = " + def.getRepresentation(), repr(o), repr(cls) diff --git a/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql b/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql index a1547da65c6..77f6ab0923b 100644 --- a/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql +++ b/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql @@ -4,10 +4,10 @@ import Util from SsaSourceVariable var, ControlFlowNode defn, string kind where - not var instanceof SpecialSsaSourceVariable and - ( - var.hasDefiningNode(defn) and kind = "definition" - or - var.hasRefinement(_, defn) and kind = "refinement" - ) + not var instanceof SpecialSsaSourceVariable and + ( + var.hasDefiningNode(defn) and kind = "definition" + or + var.hasRefinement(_, defn) and kind = "refinement" + ) select locate(defn.getLocation(), "ab"), var.(Variable), defn.toString(), kind diff --git a/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql b/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql index dc71ac5df65..67d85c2e3bd 100644 --- a/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql +++ b/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql @@ -5,7 +5,7 @@ import Util from EssaVariable var, string name, ObjectInternal o, Context ctx where - AttributePointsTo::variableAttributePointsTo(var, ctx, name, o, _) and - not var.getSourceVariable() instanceof SpecialSsaSourceVariable + AttributePointsTo::variableAttributePointsTo(var, ctx, name, o, _) and + not var.getSourceVariable() instanceof SpecialSsaSourceVariable select locate(var.getDefinition().getLocation(), "abdfgikm"), var.getRepresentation(), name, - var.getDefinition().getRepresentation(), o, ctx + var.getDefinition().getRepresentation(), o, ctx diff --git a/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql b/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql index 2367df63b63..d473e4e804a 100644 --- a/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql +++ b/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql @@ -4,16 +4,16 @@ import semmle.python.pointsto.PointsToContext import Util from - ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx, - ControlFlowNode origin, string what + ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx, + ControlFlowNode origin, string what where - not use instanceof NameConstantNode and - not use.getNode() instanceof ImmutableLiteral and - eval = Conditionals::testEvaluates(test, use, ctx, val, origin) and - ( - what = val.getSource().(Object).toString() - or - not exists(val.getSource()) and what = origin.getNode().toString() - ) + not use instanceof NameConstantNode and + not use.getNode() instanceof ImmutableLiteral and + eval = Conditionals::testEvaluates(test, use, ctx, val, origin) and + ( + what = val.getSource().(Object).toString() + or + not exists(val.getSource()) and what = origin.getNode().toString() + ) select locate(test.getLocation(), "bc"), test.getNode().toString(), eval.toString(), - use.getNode().toString(), what + use.getNode().toString(), what diff --git a/python/ql/test/library-tests/PointsTo/new/Util.qll b/python/ql/test/library-tests/PointsTo/new/Util.qll index f59a1a8dae1..b83ad89d1c8 100644 --- a/python/ql/test/library-tests/PointsTo/new/Util.qll +++ b/python/ql/test/library-tests/PointsTo/new/Util.qll @@ -3,47 +3,47 @@ import semmle.python.objects.ObjectInternal bindingset[which] string locate(Location l, string which) { - exists(string file, int line | - file = l.getFile().getShortName() and - line = l.getStartLine() and - file.charAt(0) = which.charAt(_) and - file.charAt(1) = "_" and - result = file + ":" + line - ) + exists(string file, int line | + file = l.getFile().getShortName() and + line = l.getStartLine() and + file.charAt(0) = which.charAt(_) and + file.charAt(1) = "_" and + result = file + ":" + line + ) } string repr(Object o) { - /* - * Do not show `unknownValue()` to keep noise levels down. - * To show it add: - * `o = unknownValue() and result = "*UNKNOWN VALUE*"` - */ + /* + * Do not show `unknownValue()` to keep noise levels down. + * To show it add: + * `o = unknownValue() and result = "*UNKNOWN VALUE*"` + */ - not o instanceof StringObject and - not o = undefinedVariable() and - not o = theUnknownType() and - not o = theBoundMethodType() and - result = o.toString() - or - o = undefinedVariable() and result = "*UNDEFINED*" - or - o = theUnknownType() and result = "*UNKNOWN TYPE*" - or - /* Work around differing names in 2/3 */ - result = "'" + o.(StringObject).getText() + "'" - or - o = theBoundMethodType() and result = "builtin-class method" + not o instanceof StringObject and + not o = undefinedVariable() and + not o = theUnknownType() and + not o = theBoundMethodType() and + result = o.toString() + or + o = undefinedVariable() and result = "*UNDEFINED*" + or + o = theUnknownType() and result = "*UNKNOWN TYPE*" + or + /* Work around differing names in 2/3 */ + result = "'" + o.(StringObject).getText() + "'" + or + o = theBoundMethodType() and result = "builtin-class method" } predicate long_tuple(Value v) { v.(TupleObjectInternal).length() > 3 } string vrepr(Value v) { - /* Work around differing names in 2/3 */ - not v = ObjectInternal::boundMethod() and - not long_tuple(v) and - result = v.toString() - or - v = ObjectInternal::boundMethod() and result = "builtin-class method" - or - long_tuple(v) and result = "(..., ...)" + /* Work around differing names in 2/3 */ + not v = ObjectInternal::boundMethod() and + not long_tuple(v) and + result = v.toString() + or + v = ObjectInternal::boundMethod() and result = "builtin-class method" + or + long_tuple(v) and result = "(..., ...)" } diff --git a/python/ql/test/library-tests/PointsTo/new/Values.ql b/python/ql/test/library-tests/PointsTo/new/Values.ql index 754fcfede54..668e7a6b265 100644 --- a/python/ql/test/library-tests/PointsTo/new/Values.ql +++ b/python/ql/test/library-tests/PointsTo/new/Values.ql @@ -4,4 +4,4 @@ import Util from ControlFlowNode f, Context ctx, Value v, ControlFlowNode origin where f.pointsTo(ctx, v, origin) select locate(f.getLocation(), "abeghijklmnpqrstu"), f.toString(), ctx, vrepr(v), - vrepr(v.getClass()) + vrepr(v.getClass()) diff --git a/python/ql/test/library-tests/PointsTo/new/VarUses.ql b/python/ql/test/library-tests/PointsTo/new/VarUses.ql index 56c1ca637a1..58de54d7a3d 100644 --- a/python/ql/test/library-tests/PointsTo/new/VarUses.ql +++ b/python/ql/test/library-tests/PointsTo/new/VarUses.ql @@ -4,6 +4,6 @@ import Util from SsaSourceVariable var, ControlFlowNode use where - (use = var.getAUse() or var.hasRefinement(use, _)) and - not var instanceof SpecialSsaSourceVariable + (use = var.getAUse() or var.hasRefinement(use, _)) and + not var instanceof SpecialSsaSourceVariable select locate(use.getLocation(), "abd"), var.getName(), use.toString() diff --git a/python/ql/test/library-tests/PointsTo/properties/Values.ql b/python/ql/test/library-tests/PointsTo/properties/Values.ql index 597a54cb641..23416efc0eb 100644 --- a/python/ql/test/library-tests/PointsTo/properties/Values.ql +++ b/python/ql/test/library-tests/PointsTo/properties/Values.ql @@ -2,10 +2,10 @@ import python import semmle.python.objects.ObjectInternal string vrepr(Value v) { - /* Work around differing names in 2/3 */ - not v = ObjectInternal::boundMethod() and result = v.toString() - or - v = ObjectInternal::boundMethod() and result = "builtin-class method" + /* Work around differing names in 2/3 */ + not v = ObjectInternal::boundMethod() and result = v.toString() + or + v = ObjectInternal::boundMethod() and result = "builtin-class method" } from ControlFlowNode f, Context ctx, Value v, ControlFlowNode origin diff --git a/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql index c9e7d4caf3e..db02e9c4f08 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/missing/if-urlsplit-access/Test.ql @@ -2,9 +2,9 @@ import python from ControlFlowNode arg, CallNode call, string debug where - call.getAnArg() = arg and - call.getFunction().(NameNode).getId() = "check" and - if exists(arg.pointsTo()) - then debug = arg.pointsTo().toString() - else debug = "" + call.getAnArg() = arg and + call.getFunction().(NameNode).getId() = "check" and + if exists(arg.pointsTo()) + then debug = arg.pointsTo().toString() + else debug = "" select arg, debug diff --git a/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql index c9e7d4caf3e..db02e9c4f08 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/missing/re-compile/Test.ql @@ -2,9 +2,9 @@ import python from ControlFlowNode arg, CallNode call, string debug where - call.getAnArg() = arg and - call.getFunction().(NameNode).getId() = "check" and - if exists(arg.pointsTo()) - then debug = arg.pointsTo().toString() - else debug = "" + call.getAnArg() = arg and + call.getFunction().(NameNode).getId() = "check" and + if exists(arg.pointsTo()) + then debug = arg.pointsTo().toString() + else debug = "" select arg, debug diff --git a/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql index c9e7d4caf3e..db02e9c4f08 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/missing/uncalled-function/Test.ql @@ -2,9 +2,9 @@ import python from ControlFlowNode arg, CallNode call, string debug where - call.getAnArg() = arg and - call.getFunction().(NameNode).getId() = "check" and - if exists(arg.pointsTo()) - then debug = arg.pointsTo().toString() - else debug = "" + call.getAnArg() = arg and + call.getFunction().(NameNode).getId() = "check" and + if exists(arg.pointsTo()) + then debug = arg.pointsTo().toString() + else debug = "" select arg, debug diff --git a/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql b/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql index dd894ad5cea..700e0dd72a5 100644 --- a/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql +++ b/python/ql/test/library-tests/PointsTo/regressions/wrong/classmethod/Test.ql @@ -2,9 +2,9 @@ import python from NameNode name, CallNode call, string debug where - call.getAnArg() = name and - call.getFunction().(NameNode).getId() = "check" and - if exists(name.pointsTo()) - then debug = name.pointsTo().toString() - else debug = "" + call.getAnArg() = name and + call.getFunction().(NameNode).getId() = "check" and + if exists(name.pointsTo()) + then debug = name.pointsTo().toString() + else debug = "" select name, debug diff --git a/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql b/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql index 0f197edeb0a..b726c5885b3 100644 --- a/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql +++ b/python/ql/test/library-tests/PointsTo/subclass/TestEvaluate.ql @@ -4,9 +4,9 @@ import semmle.python.objects.ObjectInternal import semmle.python.pointsto.PointsToContext from - ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx + ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx where - PointsTo::pointsTo(use, ctx, val, _) and - eval = Conditionals::testEvaluates(test, use, ctx, val, _) + PointsTo::pointsTo(use, ctx, val, _) and + eval = Conditionals::testEvaluates(test, use, ctx, val, _) select test.getLocation().getStartLine(), test.getNode().toString(), eval.toString(), - use.getNode().toString(), val.toString() + use.getNode().toString(), val.toString() diff --git a/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql b/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql index 6245b56f711..86ad5bee155 100644 --- a/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql +++ b/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql @@ -5,7 +5,7 @@ import semmle.python.objects.ObjectInternal from CallNode call, SuperInstance sup, BoundMethodObjectInternal bm where - call.getFunction().inferredValue() = bm and - call.getFunction().(AttrNode).getObject().inferredValue() = sup + call.getFunction().inferredValue() = bm and + call.getFunction().(AttrNode).getObject().inferredValue() = sup select call.getLocation().getStartLine(), call.toString(), - bm.getFunction().getSource().(FunctionObject).getQualifiedName() + bm.getFunction().getSource().(FunctionObject).getQualifiedName() diff --git a/python/ql/test/library-tests/attributes/SelfAttribute.ql b/python/ql/test/library-tests/attributes/SelfAttribute.ql index 7ac995d2061..b99843fee79 100644 --- a/python/ql/test/library-tests/attributes/SelfAttribute.ql +++ b/python/ql/test/library-tests/attributes/SelfAttribute.ql @@ -3,7 +3,7 @@ import semmle.python.SelfAttribute from SelfAttributeRead sa, int line, string g, string l where - line = sa.getLocation().getStartLine() and - (if sa.guardedByHasattr() then g = "guarded" else g = "") and - if sa.locallyDefined() then l = "defined" else l = "" + line = sa.getLocation().getStartLine() and + (if sa.guardedByHasattr() then g = "guarded" else g = "") and + if sa.locallyDefined() then l = "defined" else l = "" select line, sa.getName(), g + l diff --git a/python/ql/test/library-tests/classes/abstract/Abstract.ql b/python/ql/test/library-tests/classes/abstract/Abstract.ql index 6773bb22785..bd2f98034cb 100644 --- a/python/ql/test/library-tests/classes/abstract/Abstract.ql +++ b/python/ql/test/library-tests/classes/abstract/Abstract.ql @@ -2,6 +2,6 @@ import python from ClassObject cls, string abstract where - not cls.isBuiltin() and - if cls.isAbstract() then abstract = "yes" else abstract = "no" + not cls.isBuiltin() and + if cls.isAbstract() then abstract = "yes" else abstract = "no" select cls.toString(), abstract diff --git a/python/ql/test/library-tests/classes/attr/class_attr.ql b/python/ql/test/library-tests/classes/attr/class_attr.ql index 3b7bf8b3ba0..197ab1a1e5e 100644 --- a/python/ql/test/library-tests/classes/attr/class_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/library-tests/classes/attr/class_defined_attr.ql b/python/ql/test/library-tests/classes/attr/class_defined_attr.ql index ec798dcf190..d7583e689c4 100644 --- a/python/ql/test/library-tests/classes/attr/class_defined_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_defined_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.declaredAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.declaredAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/library-tests/classes/attr/class_defines_attr.ql b/python/ql/test/library-tests/classes/attr/class_defines_attr.ql index 858d3e49e20..6b266a0d40f 100644 --- a/python/ql/test/library-tests/classes/attr/class_defines_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_defines_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.declaresAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.declaresAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/library-tests/classes/attr/class_has_attr.ql b/python/ql/test/library-tests/classes/attr/class_has_attr.ql index 2f16aa4ca97..be8272d1bd6 100644 --- a/python/ql/test/library-tests/classes/attr/class_has_attr.ql +++ b/python/ql/test/library-tests/classes/attr/class_has_attr.ql @@ -8,8 +8,8 @@ import python from ClassObject cls, int line, string name where - cls.hasLocationInfo(_, line, _, _, _) and - cls.hasAttribute(name) and - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") + cls.hasLocationInfo(_, line, _, _, _) and + cls.hasAttribute(name) and + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") select line, cls.toString(), name diff --git a/python/ql/test/library-tests/classes/attr/hash.ql b/python/ql/test/library-tests/classes/attr/hash.ql index a8ccf6c9d6b..19ac8933f69 100644 --- a/python/ql/test/library-tests/classes/attr/hash.ql +++ b/python/ql/test/library-tests/classes/attr/hash.ql @@ -8,9 +8,9 @@ import python from ClassObject cls, int line, Object obj where - cls.hasLocationInfo(_, line, _, _, _) and - obj = cls.lookupAttribute("__hash__") and - not cls.isC() and - not obj = theObjectType().lookupAttribute("__hash__") and - not obj = theTypeType().lookupAttribute("__hash__") + cls.hasLocationInfo(_, line, _, _, _) and + obj = cls.lookupAttribute("__hash__") and + not cls.isC() and + not obj = theObjectType().lookupAttribute("__hash__") and + not obj = theTypeType().lookupAttribute("__hash__") select line, cls.toString(), obj.toString() diff --git a/python/ql/test/library-tests/comments/length.ql b/python/ql/test/library-tests/comments/length.ql index 0a15328c35e..460b79c5531 100644 --- a/python/ql/test/library-tests/comments/length.ql +++ b/python/ql/test/library-tests/comments/length.ql @@ -3,6 +3,6 @@ import Lexical.CommentedOutCode from CommentBlock block, int line, boolean code where - block.hasLocationInfo(_, line, _, _, _) and - if block instanceof CommentedOutCodeBlock then code = true else code = false + block.hasLocationInfo(_, line, _, _, _) and + if block instanceof CommentedOutCodeBlock then code = true else code = false select line, block.length(), code diff --git a/python/ql/test/library-tests/comparisons/Compare2.ql b/python/ql/test/library-tests/comparisons/Compare2.ql index ade279c9efd..c8e05d39e22 100644 --- a/python/ql/test/library-tests/comparisons/Compare2.ql +++ b/python/ql/test/library-tests/comparisons/Compare2.ql @@ -3,10 +3,10 @@ import semmle.python.Comparisons from Comparison c, NameNode l, CompareOp op, NameNode r, float k, string add where - c.tests(l, op, r, k) and - ( - k < 0 and add = "" - or - k >= 0 and add = "+" - ) + c.tests(l, op, r, k) and + ( + k < 0 and add = "" + or + k >= 0 and add = "+" + ) select c.getLocation().getStartLine(), l.getId() + " " + op.repr() + " " + r.getId() + add + k diff --git a/python/ql/test/library-tests/comparisons/CompareControls.ql b/python/ql/test/library-tests/comparisons/CompareControls.ql index b803e40dfed..9da8b566d4e 100644 --- a/python/ql/test/library-tests/comparisons/CompareControls.ql +++ b/python/ql/test/library-tests/comparisons/CompareControls.ql @@ -4,4 +4,4 @@ import semmle.python.Comparisons from ComparisonControlBlock comp, SsaVariable v, CompareOp op, float k, BasicBlock b where comp.controls(v.getAUse(), op, k, b) select comp.getTest().getLocation().getStartLine(), v.getId() + " " + op.repr() + " " + k, - b.getNode(0).getLocation().getStartLine() + b.getNode(0).getLocation().getStartLine() diff --git a/python/ql/test/library-tests/dependencies/Dependencies.ql b/python/ql/test/library-tests/dependencies/Dependencies.ql index cab84c4417b..12378a567d2 100644 --- a/python/ql/test/library-tests/dependencies/Dependencies.ql +++ b/python/ql/test/library-tests/dependencies/Dependencies.ql @@ -4,4 +4,4 @@ import semmle.python.dependencies.Dependencies from DependencyKind dk, AstNode src, Object target where dk.isADependency(src, target) select dk.toString(), src.getLocation().getFile().getShortName(), src.getLocation().getStartLine(), - src.toString(), target.toString() + src.toString(), target.toString() diff --git a/python/ql/test/library-tests/descriptors/Descriptors.ql b/python/ql/test/library-tests/descriptors/Descriptors.ql index dd97b623f7f..e577d47b421 100644 --- a/python/ql/test/library-tests/descriptors/Descriptors.ql +++ b/python/ql/test/library-tests/descriptors/Descriptors.ql @@ -2,8 +2,8 @@ import python from ClassObject cls, string kind where - cls.isDescriptorType() and - /* Exclude bound-method as its name differs between 2 and 3 */ - not cls = theBoundMethodType() and - (if cls.isOverridingDescriptorType() then kind = "overriding" else kind = "non-overriding") + cls.isDescriptorType() and + /* Exclude bound-method as its name differs between 2 and 3 */ + not cls = theBoundMethodType() and + (if cls.isOverridingDescriptorType() then kind = "overriding" else kind = "non-overriding") select cls.toString(), kind diff --git a/python/ql/test/library-tests/descriptors/Methods.ql b/python/ql/test/library-tests/descriptors/Methods.ql index 4a2ec39d70c..b112bfd1d4b 100644 --- a/python/ql/test/library-tests/descriptors/Methods.ql +++ b/python/ql/test/library-tests/descriptors/Methods.ql @@ -5,7 +5,7 @@ int lineof(Object o) { result = o.getOrigin().getLocation().getStartLine() } from Object m, FunctionObject f where - m.(ClassMethodObject).getFunction() = f - or - m.(StaticMethodObject).getFunction() = f + m.(ClassMethodObject).getFunction() = f + or + m.(StaticMethodObject).getFunction() = f select lineof(m), m.toString(), lineof(f), f.toString() diff --git a/python/ql/test/library-tests/descriptors/Properties.ql b/python/ql/test/library-tests/descriptors/Properties.ql index ed36fb4e5bc..598c6d36c3b 100644 --- a/python/ql/test/library-tests/descriptors/Properties.ql +++ b/python/ql/test/library-tests/descriptors/Properties.ql @@ -3,9 +3,9 @@ import semmle.python.types.Descriptors from PropertyValue p, string method_name, FunctionValue method where - method_name = "getter" and method = p.getGetter() - or - method_name = "setter" and method = p.getSetter() - or - method_name = "deleter" and method = p.getDeleter() + method_name = "getter" and method = p.getGetter() + or + method_name = "setter" and method = p.getSetter() + or + method_name = "deleter" and method = p.getDeleter() select method, method_name, p diff --git a/python/ql/test/library-tests/encoding/CheckEncoding.ql b/python/ql/test/library-tests/encoding/CheckEncoding.ql index 60fc167e293..1e7fc8f5cfe 100644 --- a/python/ql/test/library-tests/encoding/CheckEncoding.ql +++ b/python/ql/test/library-tests/encoding/CheckEncoding.ql @@ -2,7 +2,7 @@ import python from File f, string encoding where - encoding = f.getSpecifiedEncoding() - or - not exists(f.getSpecifiedEncoding()) and encoding = "none" + encoding = f.getSpecifiedEncoding() + or + not exists(f.getSpecifiedEncoding()) and encoding = "none" select f.getAbsolutePath(), encoding diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll b/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll index 64cbacae2a6..343fc976503 100644 --- a/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll +++ b/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll @@ -3,38 +3,38 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class MySimpleSanitizer extends Sanitizer { - MySimpleSanitizer() { this = "MySimpleSanitizer" } + MySimpleSanitizer() { this = "MySimpleSanitizer" } - /** - * The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. - * - * Can't handle `if not is_safe(arg):` :\ that's why it's called MySimpleSanitizer - */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof ExternalStringKind and - exists(CallNode call | test.getTest() = call and test.getSense() = true | - call = Value::named("test.is_safe").getACall() and - test.getInput().getAUse() = call.getAnArg() - ) - } + /** + * The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. + * + * Can't handle `if not is_safe(arg):` :\ that's why it's called MySimpleSanitizer + */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof ExternalStringKind and + exists(CallNode call | test.getTest() = call and test.getSense() = true | + call = Value::named("test.is_safe").getACall() and + test.getInput().getAUse() = call.getAnArg() + ) + } } class MySanitizerHandlingNot extends Sanitizer { - MySanitizerHandlingNot() { this = "MySanitizerHandlingNot" } + MySanitizerHandlingNot() { this = "MySanitizerHandlingNot" } - /** The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof ExternalStringKind and - clears_taint_on_true(test.getTest(), test.getSense(), test) - } + /** The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof ExternalStringKind and + clears_taint_on_true(test.getTest(), test.getSense(), test) + } } /** @@ -47,30 +47,30 @@ class MySanitizerHandlingNot extends Sanitizer { * this predicate, since the tuple where `test = c` and `sense = true` would hold. */ private predicate clears_taint_on_true( - ControlFlowNode test, boolean sense, PyEdgeRefinement edge_refinement + ControlFlowNode test, boolean sense, PyEdgeRefinement edge_refinement ) { - edge_refinement.getTest().getNode().(Expr).getASubExpression*() = test.getNode() and - ( - test = Value::named("test.is_safe").getACall() and - edge_refinement.getInput().getAUse() = test.(CallNode).getAnArg() and - sense = true - or - test.(UnaryExprNode).getNode().getOp() instanceof Not and - exists(ControlFlowNode nested_test | - nested_test = test.(UnaryExprNode).getOperand() and - clears_taint_on_true(nested_test, sense.booleanNot(), edge_refinement) - ) + edge_refinement.getTest().getNode().(Expr).getASubExpression*() = test.getNode() and + ( + test = Value::named("test.is_safe").getACall() and + edge_refinement.getInput().getAUse() = test.(CallNode).getAnArg() and + sense = true + or + test.(UnaryExprNode).getNode().getOp() instanceof Not and + exists(ControlFlowNode nested_test | + nested_test = test.(UnaryExprNode).getOperand() and + clears_taint_on_true(nested_test, sense.booleanNot(), edge_refinement) ) + ) } class TestConfig extends TaintTracking::Configuration { - TestConfig() { this = "TestConfig" } + TestConfig() { this = "TestConfig" } - override predicate isSanitizer(Sanitizer sanitizer) { - sanitizer instanceof MySanitizerHandlingNot - } + override predicate isSanitizer(Sanitizer sanitizer) { + sanitizer instanceof MySanitizerHandlingNot + } - override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } + override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } - override predicate isSink(TaintTracking::Sink sink) { none() } + override predicate isSink(TaintTracking::Sink sink) { none() } } diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql b/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql index 571672cb312..431e96e4d5d 100644 --- a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql +++ b/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql @@ -3,29 +3,29 @@ import semmle.python.dataflow.TaintTracking import Taint from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string + Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, + string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false + call.getLocation().getFile().getShortName() = "test.py" and + ( + call.getFunc().(Name).getId() = "ensure_tainted" and + expected_taint = true + or + call.getFunc().(Name).getId() = "ensure_not_tainted" and + expected_taint = false + ) and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "" and + has_taint = false + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok" else test_res = "failure" + has_taint = true + ) and + if expected_taint = has_taint then test_res = "ok" else test_res = "failure" // if expected_taint = has_taint then test_res = "✓" else test_res = "✕" select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string, test_res + taint_string, test_res diff --git a/python/ql/test/library-tests/exceptions/Legal.ql b/python/ql/test/library-tests/exceptions/Legal.ql index eb27a82d614..bfe47717d23 100644 --- a/python/ql/test/library-tests/exceptions/Legal.ql +++ b/python/ql/test/library-tests/exceptions/Legal.ql @@ -2,9 +2,9 @@ import python from ClassObject cls, string legal where - not cls.isC() and cls.isLegalExceptionType() and legal = "yes" and not cls.failedInference() - or - not cls.isC() and not cls.isLegalExceptionType() and legal = "no" and not cls.failedInference() - or - not cls.isC() and cls.failedInference(legal) + not cls.isC() and cls.isLegalExceptionType() and legal = "yes" and not cls.failedInference() + or + not cls.isC() and not cls.isLegalExceptionType() and legal = "no" and not cls.failedInference() + or + not cls.isC() and cls.failedInference(legal) select cls.toString(), legal diff --git a/python/ql/test/library-tests/exprs/ast/AstParent.ql b/python/ql/test/library-tests/exprs/ast/AstParent.ql index f472a6f6e5b..fb783070e92 100644 --- a/python/ql/test/library-tests/exprs/ast/AstParent.ql +++ b/python/ql/test/library-tests/exprs/ast/AstParent.ql @@ -1,4 +1,4 @@ import python select count(AstNode c | not exists(c.getParentNode()) and not c instanceof Module) + - count(AstNode c | strictcount(c.getParentNode()) > 1) + count(AstNode c | strictcount(c.getParentNode()) > 1) diff --git a/python/ql/test/library-tests/filters/generated/Filter.ql b/python/ql/test/library-tests/filters/generated/Filter.ql index 389440ffd3a..fa6d342710b 100644 --- a/python/ql/test/library-tests/filters/generated/Filter.ql +++ b/python/ql/test/library-tests/filters/generated/Filter.ql @@ -3,7 +3,7 @@ import semmle.python.filters.GeneratedCode from GeneratedFile f, string tool where - tool = f.getTool() - or - not exists(f.getTool()) and tool = "none" + tool = f.getTool() + or + not exists(f.getTool()) and tool = "none" select f.toString(), tool diff --git a/python/ql/test/library-tests/formatting/FormatArguments.ql b/python/ql/test/library-tests/formatting/FormatArguments.ql index f2cc38f7e8c..0c3f007131c 100644 --- a/python/ql/test/library-tests/formatting/FormatArguments.ql +++ b/python/ql/test/library-tests/formatting/FormatArguments.ql @@ -3,7 +3,7 @@ import Expressions.Formatting.AdvancedFormatting from AdvancedFormatString a, string name, int start, int end where - name = "'" + a.getFieldName(start, end) + "'" - or - name = a.getFieldNumber(start, end).toString() + name = "'" + a.getFieldName(start, end) + "'" + or + name = a.getFieldNumber(start, end).toString() select a.getLocation().getStartLine(), a.getText(), start, end, name diff --git a/python/ql/test/library-tests/jump_to_defn/Consistency.ql b/python/ql/test/library-tests/jump_to_defn/Consistency.ql index ba274e0aa21..3e49e8b0e39 100644 --- a/python/ql/test/library-tests/jump_to_defn/Consistency.ql +++ b/python/ql/test/library-tests/jump_to_defn/Consistency.ql @@ -3,17 +3,17 @@ import analysis.DefinitionTracking import analysis.CrossProjectDefinitions predicate local_problem(Definition defn, string issue, string repr) { - not exists(defn.toString()) and issue = "no toString()" and repr = "a local definition" - or - not exists(defn.getAstNode()) and issue = "no getAstNode()" and repr = defn.toString() - or - not exists(defn.getLocation()) and issue = "no getLocation()" and repr = defn.toString() - or - count(defn.getLocation()) > 1 and issue = "more than one getLocation()" and repr = defn.toString() + not exists(defn.toString()) and issue = "no toString()" and repr = "a local definition" + or + not exists(defn.getAstNode()) and issue = "no getAstNode()" and repr = defn.toString() + or + not exists(defn.getLocation()) and issue = "no getLocation()" and repr = defn.toString() + or + count(defn.getLocation()) > 1 and issue = "more than one getLocation()" and repr = defn.toString() } predicate remote_problem(Symbol s, string issue, string repr) { - not exists(s.toString()) and issue = "no toString()" and repr = "a symbol" + not exists(s.toString()) and issue = "no toString()" and repr = "a symbol" } from string issue, string repr diff --git a/python/ql/test/library-tests/jump_to_defn/Remote.ql b/python/ql/test/library-tests/jump_to_defn/Remote.ql index 7602e5839d3..120fbab6f11 100644 --- a/python/ql/test/library-tests/jump_to_defn/Remote.ql +++ b/python/ql/test/library-tests/jump_to_defn/Remote.ql @@ -4,7 +4,7 @@ import analysis.CrossProjectDefinitions from Definition defn, Symbol s where - s.find() = defn.getAstNode() and - // Exclude dunder names as these vary from version to version. - not s.toString().regexpMatch(".+__") + s.find() = defn.getAstNode() and + // Exclude dunder names as these vary from version to version. + not s.toString().regexpMatch(".+__") select s.toString() diff --git a/python/ql/test/library-tests/jump_to_defn/test.ql b/python/ql/test/library-tests/jump_to_defn/test.ql index 0f952578997..ae29e7b3027 100644 --- a/python/ql/test/library-tests/jump_to_defn/test.ql +++ b/python/ql/test/library-tests/jump_to_defn/test.ql @@ -7,6 +7,6 @@ import analysis.DefinitionTracking from Expr use, Definition defn where - defn = getADefinition(use) and - use.getEnclosingModule().getName() = "test" + defn = getADefinition(use) and + use.getEnclosingModule().getName() = "test" select use.getLocation().toString(), use.toString(), defn.toString() diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql b/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql index aac64976f75..07e339bc0f1 100644 --- a/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql +++ b/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql @@ -1,9 +1,9 @@ import python class ImplicitConcat extends StrConst { - ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } + ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } } from StringPart s select s.getLocation().getStartLine(), s.getText(), s.getLocation().getStartColumn(), - s.getLocation().getEndColumn() + s.getLocation().getEndColumn() diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql b/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql index 49fe354b6ee..0637e72df6e 100644 --- a/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql +++ b/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql @@ -1,7 +1,7 @@ import python class ImplicitConcat extends StrConst { - ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } + ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } } from StrConst s, StringPart part, int n diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/test.ql b/python/ql/test/library-tests/locations/implicit_concatenation/test.ql index 09ba3dcd1c4..ca595f53833 100644 --- a/python/ql/test/library-tests/locations/implicit_concatenation/test.ql +++ b/python/ql/test/library-tests/locations/implicit_concatenation/test.ql @@ -1,13 +1,13 @@ import python class ImplicitConcat extends StrConst { - ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } + ImplicitConcat() { exists(this.getAnImplicitlyConcatenatedPart()) } } from StrConst s, boolean isConcat where - s instanceof ImplicitConcat and isConcat = true - or - not s instanceof ImplicitConcat and isConcat = false + s instanceof ImplicitConcat and isConcat = true + or + not s instanceof ImplicitConcat and isConcat = false select s.getLocation().getStartLine(), s.getText(), isConcat, s.getText().length(), - s.getLocation().getStartColumn(), s.getLocation().getEndColumn() + s.getLocation().getStartColumn(), s.getLocation().getEndColumn() diff --git a/python/ql/test/library-tests/locations/negative_numbers/negative.ql b/python/ql/test/library-tests/locations/negative_numbers/negative.ql index 0fe2cdcc2bc..f36bb716167 100644 --- a/python/ql/test/library-tests/locations/negative_numbers/negative.ql +++ b/python/ql/test/library-tests/locations/negative_numbers/negative.ql @@ -2,6 +2,6 @@ import python from Expr e, int bl, int bc, int el, int ec, string p where - e.getLocation().hasLocationInfo(_, bl, bc, el, ec) and - if e.isParenthesized() then p = "()" else p = "" + e.getLocation().hasLocationInfo(_, bl, bc, el, ec) and + if e.isParenthesized() then p = "()" else p = "" select e.toString(), bl, bc, el, ec, p diff --git a/python/ql/test/library-tests/modules/usage/ModuleUsage.ql b/python/ql/test/library-tests/modules/usage/ModuleUsage.ql index 3ff70adb69d..5f776e2374e 100644 --- a/python/ql/test/library-tests/modules/usage/ModuleUsage.ql +++ b/python/ql/test/library-tests/modules/usage/ModuleUsage.ql @@ -2,15 +2,15 @@ import python from ModuleValue mv, string usage where - // builtin module has different name in Python 2 and 3 - not mv = Module::builtinModule() and - ( - mv.isUsedAsModule() and usage = "isUsedAsModule" - or - mv.isUsedAsScript() and usage = "isUsedAsScript" - or - not mv.isUsedAsModule() and - not mv.isUsedAsScript() and - usage = "" - ) + // builtin module has different name in Python 2 and 3 + not mv = Module::builtinModule() and + ( + mv.isUsedAsModule() and usage = "isUsedAsModule" + or + mv.isUsedAsScript() and usage = "isUsedAsScript" + or + not mv.isUsedAsModule() and + not mv.isUsedAsScript() and + usage = "" + ) select mv, usage diff --git a/python/ql/test/library-tests/objects/Literals.ql b/python/ql/test/library-tests/objects/Literals.ql index ad6e1181cfd..a7f10b358ff 100644 --- a/python/ql/test/library-tests/objects/Literals.ql +++ b/python/ql/test/library-tests/objects/Literals.ql @@ -2,9 +2,9 @@ import python string repr(Expr e) { - result = e.(Num).getN() or - result = e.(Bytes).getS() or - result = e.(Unicode).getS() + result = e.(Num).getN() or + result = e.(Bytes).getS() or + result = e.(Unicode).getS() } from ImmutableLiteral l diff --git a/python/ql/test/library-tests/objects/Name.ql b/python/ql/test/library-tests/objects/Name.ql index c20358b9062..900af0cf2e3 100644 --- a/python/ql/test/library-tests/objects/Name.ql +++ b/python/ql/test/library-tests/objects/Name.ql @@ -2,20 +2,20 @@ import python from Object o, string name where - o.hasLongName(name) and - ( - name = "sys.modules" - or - name = "test.n" - or - name = "test.l" - or - name = "test.d" - or - name = "test.C.meth" - or - name = "test.C.cmeth" - or - name = "test.C.smeth" - ) + o.hasLongName(name) and + ( + name = "sys.modules" + or + name = "test.n" + or + name = "test.l" + or + name = "test.d" + or + name = "test.C.meth" + or + name = "test.C.cmeth" + or + name = "test.C.smeth" + ) select name, o.toString() diff --git a/python/ql/test/library-tests/overrides/FunctionOverrides.ql b/python/ql/test/library-tests/overrides/FunctionOverrides.ql index 1493223aa8e..c719006665d 100644 --- a/python/ql/test/library-tests/overrides/FunctionOverrides.ql +++ b/python/ql/test/library-tests/overrides/FunctionOverrides.ql @@ -2,6 +2,6 @@ import python from PythonFunctionValue f, string overriding, string overridden where - (if f.isOverridingMethod() then overriding = "overriding" else overriding = "not overriding") and - (if f.isOverriddenMethod() then overridden = "overridden" else overridden = "not overridden") + (if f.isOverridingMethod() then overriding = "overriding" else overriding = "not overriding") and + (if f.isOverriddenMethod() then overridden = "overridden" else overridden = "not overridden") select f, overriding, overridden diff --git a/python/ql/test/library-tests/parameters/Special.ql b/python/ql/test/library-tests/parameters/Special.ql index 4987599bc72..e26e0797ff6 100644 --- a/python/ql/test/library-tests/parameters/Special.ql +++ b/python/ql/test/library-tests/parameters/Special.ql @@ -2,9 +2,9 @@ import python from Parameter p, string type where - p.isKwargs() and type = "kwargs" - or - p.isVarargs() and type = "varargs" - or - not p.isKwargs() and not p.isVarargs() and type = "normal" + p.isKwargs() and type = "kwargs" + or + p.isVarargs() and type = "varargs" + or + not p.isKwargs() and not p.isVarargs() and type = "normal" select p.getName(), type diff --git a/python/ql/test/library-tests/regex/Alternation.ql b/python/ql/test/library-tests/regex/Alternation.ql index 79622fae32e..b369f822d4a 100644 --- a/python/ql/test/library-tests/regex/Alternation.ql +++ b/python/ql/test/library-tests/regex/Alternation.ql @@ -4,4 +4,4 @@ import semmle.python.regex from Regex r, int start, int end, int part_start, int part_end where r.alternationOption(start, end, part_start, part_end) select r.getText(), start, end, r.getText().substring(start, end), part_start, part_end, - r.getText().substring(part_start, part_end) + r.getText().substring(part_start, part_end) diff --git a/python/ql/test/library-tests/regex/FirstLast.ql b/python/ql/test/library-tests/regex/FirstLast.ql index 7a57eb51382..5bca6fdf542 100644 --- a/python/ql/test/library-tests/regex/FirstLast.ql +++ b/python/ql/test/library-tests/regex/FirstLast.ql @@ -2,9 +2,9 @@ import python import semmle.python.regex predicate part(Regex r, int start, int end, string kind) { - r.lastItem(start, end) and kind = "last" - or - r.firstItem(start, end) and kind = "first" + r.lastItem(start, end) and kind = "last" + or + r.firstItem(start, end) and kind = "first" } from Regex r, int start, int end, string kind diff --git a/python/ql/test/library-tests/regex/GroupContents.ql b/python/ql/test/library-tests/regex/GroupContents.ql index 28ad5749c0a..4fd7d9d229e 100644 --- a/python/ql/test/library-tests/regex/GroupContents.ql +++ b/python/ql/test/library-tests/regex/GroupContents.ql @@ -4,4 +4,4 @@ import semmle.python.regex from Regex r, int start, int end, int part_start, int part_end where r.groupContents(start, end, part_start, part_end) select r.getText(), start, end, r.getText().substring(start, end), part_start, part_end, - r.getText().substring(part_start, part_end) + r.getText().substring(part_start, part_end) diff --git a/python/ql/test/library-tests/regex/Regex.ql b/python/ql/test/library-tests/regex/Regex.ql index 708ad82804d..ab320bb744e 100644 --- a/python/ql/test/library-tests/regex/Regex.ql +++ b/python/ql/test/library-tests/regex/Regex.ql @@ -2,21 +2,21 @@ import python import semmle.python.regex predicate part(Regex r, int start, int end, string kind) { - r.alternation(start, end) and kind = "choice" - or - r.normalCharacter(start, end) and kind = "char" - or - r.specialCharacter(start, end, kind) - or - r.sequence(start, end) and kind = "sequence" - or - r.charSet(start, end) and kind = "char-set" - or - r.zeroWidthMatch(start, end) and kind = "empty group" - or - r.group(start, end) and not r.zeroWidthMatch(start, end) and kind = "non-empty group" - or - r.qualifiedItem(start, end, _) and kind = "qualified" + r.alternation(start, end) and kind = "choice" + or + r.normalCharacter(start, end) and kind = "char" + or + r.specialCharacter(start, end, kind) + or + r.sequence(start, end) and kind = "sequence" + or + r.charSet(start, end) and kind = "char-set" + or + r.zeroWidthMatch(start, end) and kind = "empty group" + or + r.group(start, end) and not r.zeroWidthMatch(start, end) and kind = "non-empty group" + or + r.qualifiedItem(start, end, _) and kind = "qualified" } from Regex r, int start, int end, string kind diff --git a/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll b/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll index 666b963b686..7d1a812be92 100644 --- a/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll +++ b/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll @@ -4,21 +4,21 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.injection.Command class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class FabricExecuteTestConfiguration extends TaintTracking::Configuration { - FabricExecuteTestConfiguration() { this = "FabricExecuteTestConfiguration" } + FabricExecuteTestConfiguration() { this = "FabricExecuteTestConfiguration" } - override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } + override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } - override predicate isExtension(TaintTracking::Extension extension) { - extension instanceof FabricExecuteExtension - } + override predicate isExtension(TaintTracking::Extension extension) { + extension instanceof FabricExecuteExtension + } } diff --git a/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql b/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql index bdc2f60cbe9..7b9c6025c9d 100644 --- a/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql +++ b/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql @@ -2,33 +2,32 @@ import python import semmle.python.security.TaintTracking import semmle.python.web.HttpRequest import semmle.python.security.strings.Untrusted - import Taint from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string + Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, + string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false + call.getLocation().getFile().getShortName() = "test.py" and + ( + call.getFunc().(Name).getId() = "ensure_tainted" and + expected_taint = true + or + call.getFunc().(Name).getId() = "ensure_not_tainted" and + expected_taint = false + ) and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "" and + has_taint = false + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok " else test_res = "fail" + has_taint = true + ) and + if expected_taint = has_taint then test_res = "ok " else test_res = "fail" // if expected_taint = has_taint then test_res = "✓" else test_res = "✕" select arg.getLocation().toString(), test_res, call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/state_tracking/Lib.qll b/python/ql/test/library-tests/state_tracking/Lib.qll index e784b7e198b..dc24e2bf0b7 100644 --- a/python/ql/test/library-tests/state_tracking/Lib.qll +++ b/python/ql/test/library-tests/state_tracking/Lib.qll @@ -4,15 +4,15 @@ import semmle.python.dataflow.StateTracking predicate callTo(CallNode call, string name) { call.getFunction().(NameNode).getId() = name } class Initialized extends TrackableState { - Initialized() { this = "initialized" } + Initialized() { this = "initialized" } - override predicate startsAt(ControlFlowNode f) { callTo(f, "initialize") } + override predicate startsAt(ControlFlowNode f) { callTo(f, "initialize") } } class Frobnicated extends TrackableState { - Frobnicated() { this = "frobnicated" } + Frobnicated() { this = "frobnicated" } - override predicate startsAt(ControlFlowNode f) { callTo(f, "frobnicate") } + override predicate startsAt(ControlFlowNode f) { callTo(f, "frobnicate") } - override predicate endsAt(ControlFlowNode f) { callTo(f, "defrobnicate") } + override predicate endsAt(ControlFlowNode f) { callTo(f, "defrobnicate") } } diff --git a/python/ql/test/library-tests/state_tracking/Test.ql b/python/ql/test/library-tests/state_tracking/Test.ql index cfdfa7c77aa..a0a12e8615d 100644 --- a/python/ql/test/library-tests/state_tracking/Test.ql +++ b/python/ql/test/library-tests/state_tracking/Test.ql @@ -3,10 +3,10 @@ import Lib from ControlFlowNode f, TrackableState state, Context ctx, boolean sense where - f.getLocation().getStartLine() >= 20 and - ( - state.appliesTo(f, ctx) and sense = true - or - state.mayNotApplyTo(f, ctx) and sense = false - ) + f.getLocation().getStartLine() >= 20 and + ( + state.appliesTo(f, ctx) and sense = true + or + state.mayNotApplyTo(f, ctx) and sense = false + ) select f.getLocation().toString(), f, ctx, state, sense diff --git a/python/ql/test/library-tests/state_tracking/Violations.ql b/python/ql/test/library-tests/state_tracking/Violations.ql index db70e7d3368..c4228141e29 100644 --- a/python/ql/test/library-tests/state_tracking/Violations.ql +++ b/python/ql/test/library-tests/state_tracking/Violations.ql @@ -3,10 +3,10 @@ import Lib from ControlFlowNode f, TrackableState state where - ( - callTo(f, "exacerbate") and state = "frobnicated" - or - callTo(f, "frobnicate") and state = "initialized" - ) and - state.mayNotApplyTo(f) + ( + callTo(f, "exacerbate") and state = "frobnicated" + or + callTo(f, "frobnicate") and state = "initialized" + ) and + state.mayNotApplyTo(f) select f.getLocation().toString(), f.toString(), state.toString() diff --git a/python/ql/test/library-tests/stmts/general/AstParent.ql b/python/ql/test/library-tests/stmts/general/AstParent.ql index 85e0f4947fa..a54c8c669e2 100644 --- a/python/ql/test/library-tests/stmts/general/AstParent.ql +++ b/python/ql/test/library-tests/stmts/general/AstParent.ql @@ -2,4 +2,4 @@ import python /* The result of this query should always be 0, *regardless* of the database. */ select count(AstNode c | not exists(c.getParentNode()) and not c instanceof Module) + - count(AstNode c | strictcount(c.getParentNode()) > 1) + count(AstNode c | strictcount(c.getParentNode()) > 1) diff --git a/python/ql/test/library-tests/stmts/general/SubExpressions.ql b/python/ql/test/library-tests/stmts/general/SubExpressions.ql index e3b5eed1ced..352feba7a0c 100644 --- a/python/ql/test/library-tests/stmts/general/SubExpressions.ql +++ b/python/ql/test/library-tests/stmts/general/SubExpressions.ql @@ -2,4 +2,4 @@ import python from Stmt s select s.toString(), s.getASubExpression().toString(), - s.getASubExpression().getASubExpression*().toString(), s.getLocation().getStartLine() + s.getASubExpression().getASubExpression*().toString(), s.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/stmts/raise_stmt/AST.ql b/python/ql/test/library-tests/stmts/raise_stmt/AST.ql index 62719f1179f..7dee8499025 100644 --- a/python/ql/test/library-tests/stmts/raise_stmt/AST.ql +++ b/python/ql/test/library-tests/stmts/raise_stmt/AST.ql @@ -3,4 +3,4 @@ import python from AstNode parent, AstNode child where child.getParentNode() = parent select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), - child.toString() + child.toString() diff --git a/python/ql/test/library-tests/stmts/try_stmt/AST.ql b/python/ql/test/library-tests/stmts/try_stmt/AST.ql index 62719f1179f..7dee8499025 100644 --- a/python/ql/test/library-tests/stmts/try_stmt/AST.ql +++ b/python/ql/test/library-tests/stmts/try_stmt/AST.ql @@ -3,4 +3,4 @@ import python from AstNode parent, AstNode child where child.getParentNode() = parent select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), - child.toString() + child.toString() diff --git a/python/ql/test/library-tests/stmts/with_stmt/AST.ql b/python/ql/test/library-tests/stmts/with_stmt/AST.ql index 62719f1179f..7dee8499025 100644 --- a/python/ql/test/library-tests/stmts/with_stmt/AST.ql +++ b/python/ql/test/library-tests/stmts/with_stmt/AST.ql @@ -3,4 +3,4 @@ import python from AstNode parent, AstNode child where child.getParentNode() = parent select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), - child.toString() + child.toString() diff --git a/python/ql/test/library-tests/taint/collections/Taint.qll b/python/ql/test/library-tests/taint/collections/Taint.qll index 21e16aabac5..010b9738c5c 100644 --- a/python/ql/test/library-tests/taint/collections/Taint.qll +++ b/python/ql/test/library-tests/taint/collections/Taint.qll @@ -3,25 +3,25 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } diff --git a/python/ql/test/library-tests/taint/collections/TestStep.ql b/python/ql/test/library-tests/taint/collections/TestStep.ql index 7e42b878e74..177edce3498 100644 --- a/python/ql/test/library-tests/taint/collections/TestStep.ql +++ b/python/ql/test/library-tests/taint/collections/TestStep.ql @@ -4,8 +4,8 @@ import Taint from TaintedNode n, TaintedNode s where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() + n.getLocation().getFile().getShortName() = "test.py" and + s.getLocation().getFile().getShortName() = "test.py" and + s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() + " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/collections/TestTaint.ql b/python/ql/test/library-tests/taint/collections/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/collections/TestTaint.ql +++ b/python/ql/test/library-tests/taint/collections/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/config/RockPaperScissors.ql b/python/ql/test/library-tests/taint/config/RockPaperScissors.ql index abcc862f418..8d6170351f1 100644 --- a/python/ql/test/library-tests/taint/config/RockPaperScissors.ql +++ b/python/ql/test/library-tests/taint/config/RockPaperScissors.ql @@ -10,4 +10,4 @@ import semmle.python.security.Paths from RockPaperScissorConfig config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ loses to $@.", src.getNode(), src.getTaintKind().toString(), - sink.getNode(), sink.getTaintKind().toString() + sink.getNode(), sink.getTaintKind().toString() diff --git a/python/ql/test/library-tests/taint/config/Simple.ql b/python/ql/test/library-tests/taint/config/Simple.ql index b3593354f5e..9a87a67c9f1 100644 --- a/python/ql/test/library-tests/taint/config/Simple.ql +++ b/python/ql/test/library-tests/taint/config/Simple.ql @@ -10,4 +10,4 @@ import semmle.python.security.Paths from SimpleConfig config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) select sink.getSink(), src, sink, "$@ flows to $@.", src.getNode(), src.getTaintKind().toString(), - sink.getNode(), sink.getTaintKind().toString() + sink.getNode(), sink.getTaintKind().toString() diff --git a/python/ql/test/library-tests/taint/config/TaintLib.qll b/python/ql/test/library-tests/taint/config/TaintLib.qll index 52e7c71858b..35eebe8ffa6 100644 --- a/python/ql/test/library-tests/taint/config/TaintLib.qll +++ b/python/ql/test/library-tests/taint/config/TaintLib.qll @@ -2,247 +2,247 @@ import python import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { - SimpleTest() { this = "simple.test" } + SimpleTest() { this = "simple.test" } } abstract class TestConfig extends TaintTracking::Configuration { - bindingset[this] - TestConfig() { any() } + bindingset[this] + TestConfig() { any() } } class SimpleConfig extends TestConfig { - SimpleConfig() { this = "Simple config" } + SimpleConfig() { this = "Simple config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(NameNode).getId() = "SOURCE" and - kind instanceof SimpleTest - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(NameNode).getId() = "SOURCE" and + kind instanceof SimpleTest + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - node.asCfgNode() = call.getAnArg() - ) and - kind instanceof SimpleTest - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + node.asCfgNode() = call.getAnArg() + ) and + kind instanceof SimpleTest + } - override predicate isBarrier(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and - kind instanceof SimpleTest - } + override predicate isBarrier(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and + kind instanceof SimpleTest + } } class BasicCustomTaint extends TaintKind { - BasicCustomTaint() { this = "basic.custom" } + BasicCustomTaint() { this = "basic.custom" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(CallNode).getAnArg() = fromnode and - tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and - result = this - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(CallNode).getAnArg() = fromnode and + tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and + result = this + } } class BasicCustomConfig extends TestConfig { - BasicCustomConfig() { this = "Basic custom config" } + BasicCustomConfig() { this = "Basic custom config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(NameNode).getId() = "CUSTOM_SOURCE" and - kind instanceof SimpleTest - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(NameNode).getId() = "CUSTOM_SOURCE" and + kind instanceof SimpleTest + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and - node.asCfgNode() = call.getAnArg() - ) and - kind instanceof SimpleTest - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and + node.asCfgNode() = call.getAnArg() + ) and + kind instanceof SimpleTest + } } class Rock extends TaintKind { - Rock() { this = "rock" } + Rock() { this = "rock" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Scissors - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Scissors + } } class Paper extends TaintKind { - Paper() { this = "paper" } + Paper() { this = "paper" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Rock - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Rock + } } class Scissors extends TaintKind { - Scissors() { this = "scissors" } + Scissors() { this = "scissors" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Paper - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Paper + } } class RockPaperScissorConfig extends TestConfig { - RockPaperScissorConfig() { this = "Rock-paper-scissors config" } + RockPaperScissorConfig() { this = "Rock-paper-scissors config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - exists(string name | - node.asCfgNode().(NameNode).getId() = name and - kind = name.toLowerCase() - | - name = "ROCK" or name = "PAPER" or name = "SCISSORS" - ) - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + exists(string name | + node.asCfgNode().(NameNode).getId() = name and + kind = name.toLowerCase() + | + name = "ROCK" or name = "PAPER" or name = "SCISSORS" + ) + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(string name | function_param(name, node) | - name = "paper" and kind = "rock" - or - name = "rock" and kind = "scissors" - or - name = "scissors" and kind = "paper" - ) - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(string name | function_param(name, node) | + name = "paper" and kind = "rock" + or + name = "rock" and kind = "scissors" + or + name = "scissors" and kind = "paper" + ) + } } private predicate function_param(string funcname, DataFlow::Node arg) { - exists(FunctionObject f | - f.getName() = funcname and - arg.asCfgNode() = f.getArgumentForCall(_, _) - ) + exists(FunctionObject f | + f.getName() = funcname and + arg.asCfgNode() = f.getArgumentForCall(_, _) + ) } class TaintCarrier extends TaintKind { - TaintCarrier() { this = "explicit.carrier" } + TaintCarrier() { this = "explicit.carrier" } - override TaintKind getTaintOfMethodResult(string name) { - name = "get_taint" and result instanceof SimpleTest - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get_taint" and result instanceof SimpleTest + } } class TaintCarrierConfig extends TestConfig { - TaintCarrierConfig() { this = "Taint carrier config" } + TaintCarrierConfig() { this = "Taint carrier config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(NameNode).getId() = "TAINT_CARRIER_SOURCE" and - kind instanceof TaintCarrier - } + override predicate isSource(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(NameNode).getId() = "TAINT_CARRIER_SOURCE" and + kind instanceof TaintCarrier + } - override predicate isSink(DataFlow::Node node, TaintKind kind) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - node.asCfgNode() = call.getAnArg() - ) and - kind instanceof SimpleTest - } + override predicate isSink(DataFlow::Node node, TaintKind kind) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + node.asCfgNode() = call.getAnArg() + ) and + kind instanceof SimpleTest + } - override predicate isBarrier(DataFlow::Node node, TaintKind kind) { - node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and - kind instanceof SimpleTest - } + override predicate isBarrier(DataFlow::Node node, TaintKind kind) { + node.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and + kind instanceof SimpleTest + } } /* Some more realistic examples */ abstract class UserInput extends TaintKind { - bindingset[this] - UserInput() { any() } + bindingset[this] + UserInput() { any() } } class UserInputSource extends TaintSource { - UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } + UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } - override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } + override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } - override string toString() { result = "user.input.source" } + override string toString() { result = "user.input.source" } } class SqlInjectionTaint extends UserInput { - SqlInjectionTaint() { this = "SQL injection" } + SqlInjectionTaint() { this = "SQL injection" } } class CommandInjectionTaint extends UserInput { - CommandInjectionTaint() { this = "Command injection" } + CommandInjectionTaint() { this = "Command injection" } } class SqlSanitizer extends Sanitizer { - SqlSanitizer() { this = "SQL sanitizer" } + SqlSanitizer() { this = "SQL sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f, CallNode call | - f.getName() = "isEscapedSql" and - test.getTest() = call and - call.getAnArg() = test.getSourceVariable().getAUse() and - f.getACall() = call and - test.getSense() = true - ) and - taint instanceof SqlInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f, CallNode call | + f.getName() = "isEscapedSql" and + test.getTest() = call and + call.getAnArg() = test.getSourceVariable().getAUse() and + f.getACall() = call and + test.getSense() = true + ) and + taint instanceof SqlInjectionTaint + } } class CommandSanitizer extends Sanitizer { - CommandSanitizer() { this = "Command sanitizer" } + CommandSanitizer() { this = "Command sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f | - f.getName() = "isValidCommand" and - f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and - test.getSense() = true - ) and - taint instanceof CommandInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f | + f.getName() = "isValidCommand" and + f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and + test.getSense() = true + ) and + taint instanceof CommandInjectionTaint + } } class SqlQuery extends TaintSink { - SqlQuery() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "sql_query" and - call.getAnArg() = this - ) - } + SqlQuery() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "sql_query" and + call.getAnArg() = this + ) + } - override string toString() { result = "SQL query" } + override string toString() { result = "SQL query" } - override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } } class OsCommand extends TaintSink { - OsCommand() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "os_command" and - call.getAnArg() = this - ) - } + OsCommand() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "os_command" and + call.getAnArg() = this + ) + } - override string toString() { result = "OS command" } + override string toString() { result = "OS command" } - override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } } class Falsey extends TaintKind { - Falsey() { this = "falsey" } + Falsey() { this = "falsey" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } class FalseySource extends TaintSource { - FalseySource() { this.(NameNode).getId() = "FALSEY" } + FalseySource() { this.(NameNode).getId() = "FALSEY" } - override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } + override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } - override string toString() { result = "falsey.source" } + override string toString() { result = "falsey.source" } } class TaintIterable extends TaintKind { - TaintIterable() { this = "iterable.simple" } + TaintIterable() { this = "iterable.simple" } - override TaintKind getTaintForIteration() { result instanceof SimpleTest } + override TaintKind getTaintForIteration() { result instanceof SimpleTest } } class TaintIterableSource extends TaintSource { - TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } + TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } + override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } } diff --git a/python/ql/test/library-tests/taint/config/TaintedArgument.ql b/python/ql/test/library-tests/taint/config/TaintedArgument.ql index 0663fce65e1..b8753b3fe00 100644 --- a/python/ql/test/library-tests/taint/config/TaintedArgument.ql +++ b/python/ql/test/library-tests/taint/config/TaintedArgument.ql @@ -4,9 +4,9 @@ import TaintLib import semmle.python.dataflow.Implementation from - TaintTrackingImplementation config, TaintTrackingNode src, CallNode call, - TaintTrackingContext caller, CallableValue pyfunc, int arg, AttributePath path, TaintKind kind + TaintTrackingImplementation config, TaintTrackingNode src, CallNode call, + TaintTrackingContext caller, CallableValue pyfunc, int arg, AttributePath path, TaintKind kind where - config instanceof TestConfig and - config.callWithTaintedArgument(src, call, caller, pyfunc, arg, path, kind) + config instanceof TestConfig and + config.callWithTaintedArgument(src, call, caller, pyfunc, arg, path, kind) select config, src, call, caller, pyfunc, arg, path, kind diff --git a/python/ql/test/library-tests/taint/config/TestNode.ql b/python/ql/test/library-tests/taint/config/TestNode.ql index 688002f3eb0..d45943ddaaa 100644 --- a/python/ql/test/library-tests/taint/config/TestNode.ql +++ b/python/ql/test/library-tests/taint/config/TestNode.ql @@ -6,4 +6,4 @@ import TaintLib from TaintTrackingNode n where n.getConfiguration() instanceof TestConfig select n.getLocation().toString(), n.getTaintKind(), n.getNode().toString(), n.getPath().toString(), - n.getContext().toString() + n.getContext().toString() diff --git a/python/ql/test/library-tests/taint/config/TestSource.ql b/python/ql/test/library-tests/taint/config/TestSource.ql index 45c5dd3ac57..6698d7cb8dc 100644 --- a/python/ql/test/library-tests/taint/config/TestSource.ql +++ b/python/ql/test/library-tests/taint/config/TestSource.ql @@ -5,4 +5,4 @@ import TaintLib from TestConfig config, DataFlow::Node source, TaintKind kind where config.isSource(source, kind) select config, source.getLocation().toString(), source.getLocation().getStartLine(), - source.toString(), kind + source.toString(), kind diff --git a/python/ql/test/library-tests/taint/config/TestStep.ql b/python/ql/test/library-tests/taint/config/TestStep.ql index 2773321d300..c03d5c1ba10 100644 --- a/python/ql/test/library-tests/taint/config/TestStep.ql +++ b/python/ql/test/library-tests/taint/config/TestStep.ql @@ -6,5 +6,5 @@ import semmle.python.dataflow.Implementation from TaintTrackingNode n, TaintTrackingNode s, TestConfig config where s = n.getASuccessor() and config = n.getConfiguration() select config + ":", n.getTaintKind(), n.getLocation().toString(), n.getNode().toString(), - n.getContext(), " --> ", s.getTaintKind(), s.getLocation().toString(), s.getNode().toString(), - s.getContext() + n.getContext(), " --> ", s.getTaintKind(), s.getLocation().toString(), s.getNode().toString(), + s.getContext() diff --git a/python/ql/test/library-tests/taint/dataflow/Config.qll b/python/ql/test/library-tests/taint/dataflow/Config.qll index 34a36dd9e1f..a02d2e0227c 100644 --- a/python/ql/test/library-tests/taint/dataflow/Config.qll +++ b/python/ql/test/library-tests/taint/dataflow/Config.qll @@ -2,14 +2,14 @@ import python import semmle.python.dataflow.DataFlow class TestConfiguration extends DataFlow::Configuration { - TestConfiguration() { this = "Test configuration" } + TestConfiguration() { this = "Test configuration" } - override predicate isSource(ControlFlowNode source) { source.(NameNode).getId() = "SOURCE" } + override predicate isSource(ControlFlowNode source) { source.(NameNode).getId() = "SOURCE" } - override predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - sink = call.getAnArg() - ) - } + override predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + sink = call.getAnArg() + ) + } } diff --git a/python/ql/test/library-tests/taint/dataflow/TestNode.ql b/python/ql/test/library-tests/taint/dataflow/TestNode.ql index 3498d5546da..93b25572a52 100644 --- a/python/ql/test/library-tests/taint/dataflow/TestNode.ql +++ b/python/ql/test/library-tests/taint/dataflow/TestNode.ql @@ -3,4 +3,4 @@ import Config from TaintedNode n select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().getNode().toString(), - n.getContext() + n.getContext() diff --git a/python/ql/test/library-tests/taint/example/DilbertConfig.qll b/python/ql/test/library-tests/taint/example/DilbertConfig.qll index c54ea8060a3..c27c3dfd103 100644 --- a/python/ql/test/library-tests/taint/example/DilbertConfig.qll +++ b/python/ql/test/library-tests/taint/example/DilbertConfig.qll @@ -10,41 +10,41 @@ import semmle.python.dataflow.Configuration /* First of all we set up some TaintKinds */ class Engineer extends TaintKind { - Engineer() { this = "Wally" or this = "Dilbert" } + Engineer() { this = "Wally" or this = "Dilbert" } } class Wally extends Engineer { - Wally() { this = "Wally" } + Wally() { this = "Wally" } } /** Then the configuration */ class DilbertConfig extends TaintTracking::Configuration { - DilbertConfig() { this = "Dilbert config" } + DilbertConfig() { this = "Dilbert config" } - override predicate isSource(DataFlow::Node node, TaintKind kind) { - node.asAstNode().(Name).getId() = "ENGINEER" and kind instanceof Engineer - } + 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 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, 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) - } + 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() - ) + exists(Call call | + call.getFunc().(Name).getId() = funcname and + arg.asAstNode() = call.getAnArg() + ) } diff --git a/python/ql/test/library-tests/taint/example/Edges.ql b/python/ql/test/library-tests/taint/example/Edges.ql index 063f4883316..022ae760a55 100644 --- a/python/ql/test/library-tests/taint/example/Edges.ql +++ b/python/ql/test/library-tests/taint/example/Edges.ql @@ -4,34 +4,34 @@ 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() + ")" + 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()) + 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) - ) + exists(string label | + s = "" and label = "[dataflow]" + or + s != "" and label = s + | + result = ljust(label, len) + ) } from TaintTrackingNode p, TaintTrackingNode s, string label diff --git a/python/ql/test/library-tests/taint/example/ExampleConfig.ql b/python/ql/test/library-tests/taint/example/ExampleConfig.ql index e3809c7a024..e406fc73e21 100644 --- a/python/ql/test/library-tests/taint/example/ExampleConfig.ql +++ b/python/ql/test/library-tests/taint/example/ExampleConfig.ql @@ -12,4 +12,4 @@ 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" + sink.getNode(), "meeting" diff --git a/python/ql/test/library-tests/taint/example/Nodes.ql b/python/ql/test/library-tests/taint/example/Nodes.ql index c7544767bba..4cdeb13c303 100644 --- a/python/ql/test/library-tests/taint/example/Nodes.ql +++ b/python/ql/test/library-tests/taint/example/Nodes.ql @@ -6,4 +6,4 @@ 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() + n.getContext().toString(), n.getTaintKind() diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql b/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql index d66d80dae40..d892afe9999 100644 --- a/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql +++ b/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql @@ -4,6 +4,6 @@ import semmle.python.web.HttpResponse from TaintSource src, TaintKind kind where - src.isSourceOf(kind) and - not src.getLocation().getFile().inStdlib() + src.isSourceOf(kind) and + not src.getLocation().getFile().inStdlib() select src.getLocation().toString(), src.(ControlFlowNode).getNode().toString(), kind diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql b/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql index 6d10a7c5ed3..b1ab12d0f25 100644 --- a/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql +++ b/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql @@ -4,9 +4,9 @@ import semmle.python.web.HttpResponse from TaintedNode n, TaintedNode s where - s = n.getASuccessor() and - not n.getLocation().getFile().inStdlib() and - not s.getLocation().getFile().inStdlib() + s = n.getASuccessor() and + not n.getLocation().getFile().inStdlib() and + not s.getLocation().getFile().inStdlib() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().toString(), - n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), - s.getNode().toString(), s.getContext() + n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), + s.getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll index 19e369412ac..08ba0ce6e40 100644 --- a/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll +++ b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll @@ -2,72 +2,72 @@ import python import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { - SimpleTest() { this = "simple.test" } + SimpleTest() { this = "simple.test" } } class SimpleSink extends TaintSink { - override string toString() { result = "Simple sink" } + override string toString() { result = "Simple sink" } - SimpleSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - this = call.getAnArg() - ) - } + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } + override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } } class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "SOURCE" } + SimpleSource() { this.(NameNode).getId() = "SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } + override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } - override string toString() { result = "simple.source" } + override string toString() { result = "simple.source" } } predicate visit_call(CallNode call, FunctionObject func) { - exists(AttrNode attr, ClassObject cls, string name | - name.prefix(6) = "visit_" and - func = cls.lookupAttribute(name) and - attr.getObject("visit").refersTo(_, cls, _) and - attr = call.getFunction() - ) + exists(AttrNode attr, ClassObject cls, string name | + name.prefix(6) = "visit_" and + func = cls.lookupAttribute(name) and + attr.getObject("visit").refersTo(_, cls, _) and + attr = call.getFunction() + ) } /* Test call extensions by tracking taint through visitor methods */ class TestCallReturnExtension extends DataFlowExtension::DataFlowNode { - TestCallReturnExtension() { - exists(PyFunctionObject func | - visit_call(_, func) and - this = func.getAReturnedNode() - ) - } + TestCallReturnExtension() { + exists(PyFunctionObject func | + visit_call(_, func) and + this = func.getAReturnedNode() + ) + } - override ControlFlowNode getAReturnSuccessorNode(CallNode call) { - exists(PyFunctionObject func | - visit_call(call, func) and - this = func.getAReturnedNode() and - result = call - ) - } + override ControlFlowNode getAReturnSuccessorNode(CallNode call) { + exists(PyFunctionObject func | + visit_call(call, func) and + this = func.getAReturnedNode() and + result = call + ) + } } class TestCallParameterExtension extends DataFlowExtension::DataFlowNode { - TestCallParameterExtension() { - exists(PyFunctionObject func, CallNode call | - visit_call(call, func) and - this = call.getAnArg() - ) - } + TestCallParameterExtension() { + exists(PyFunctionObject func, CallNode call | + visit_call(call, func) and + this = call.getAnArg() + ) + } - override ControlFlowNode getACalleeSuccessorNode(CallNode call) { - exists(PyFunctionObject func | - visit_call(call, func) and - exists(int n | - this = call.getArg(n) and - result.getNode() = func.getFunction().getArg(n + 1) - ) - ) - } + override ControlFlowNode getACalleeSuccessorNode(CallNode call) { + exists(PyFunctionObject func | + visit_call(call, func) and + exists(int n | + this = call.getArg(n) and + result.getNode() = func.getFunction().getArg(n + 1) + ) + ) + } } diff --git a/python/ql/test/library-tests/taint/extensions/TestNode.ql b/python/ql/test/library-tests/taint/extensions/TestNode.ql index 2fa17776be3..b884345b69a 100644 --- a/python/ql/test/library-tests/taint/extensions/TestNode.ql +++ b/python/ql/test/library-tests/taint/extensions/TestNode.ql @@ -3,4 +3,4 @@ import ExtensionsLib from TaintedNode n select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().getNode().toString(), - n.getContext() + n.getContext() diff --git a/python/ql/test/library-tests/taint/extensions/TestStep.ql b/python/ql/test/library-tests/taint/extensions/TestStep.ql index 9005aba858e..9defeb85b41 100644 --- a/python/ql/test/library-tests/taint/extensions/TestStep.ql +++ b/python/ql/test/library-tests/taint/extensions/TestStep.ql @@ -4,5 +4,5 @@ import ExtensionsLib from TaintedNode n, TaintedNode s where s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().getNode().toString(), - n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), - s.getNode().getNode().toString(), s.getContext() + n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), + s.getNode().getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/flowpath_regression/Config.qll b/python/ql/test/library-tests/taint/flowpath_regression/Config.qll index 446365b2d12..ae9d0e3c332 100644 --- a/python/ql/test/library-tests/taint/flowpath_regression/Config.qll +++ b/python/ql/test/library-tests/taint/flowpath_regression/Config.qll @@ -3,43 +3,43 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class FooSource extends TaintSource { - FooSource() { this.(CallNode).getFunction().(NameNode).getId() = "foo_source" } + FooSource() { this.(CallNode).getFunction().(NameNode).getId() = "foo_source" } - override predicate isSourceOf(TaintKind kind) { kind instanceof UntrustedStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof UntrustedStringKind } - override string toString() { result = "FooSource" } + override string toString() { result = "FooSource" } } class FooSink extends TaintSink { - FooSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "foo_sink" and - call.getAnArg() = this - ) - } + FooSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "foo_sink" and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } + override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } - override string toString() { result = "FooSink" } + override string toString() { result = "FooSink" } } class FooConfig extends TaintTracking::Configuration { - FooConfig() { this = "FooConfig" } + FooConfig() { this = "FooConfig" } - override predicate isSource(TaintTracking::Source source) { source instanceof FooSource } + override predicate isSource(TaintTracking::Source source) { source instanceof FooSource } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof FooSink } + override predicate isSink(TaintTracking::Sink sink) { sink instanceof FooSink } } class BarSink extends TaintSink { - BarSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "bar_sink" and - call.getAnArg() = this - ) - } + BarSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "bar_sink" and + call.getAnArg() = this + ) + } - override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } + override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } - override string toString() { result = "BarSink" } + override string toString() { result = "BarSink" } } diff --git a/python/ql/test/library-tests/taint/general/Contexts.ql b/python/ql/test/library-tests/taint/general/Contexts.ql index 6eee5f449b2..509c8c938f0 100644 --- a/python/ql/test/library-tests/taint/general/Contexts.ql +++ b/python/ql/test/library-tests/taint/general/Contexts.ql @@ -4,6 +4,6 @@ import TaintLib from CallContext context, Scope s where - exists(CallContext caller | caller.getCallee(_) = context) and - context.appliesToScope(s) + exists(CallContext caller | caller.getCallee(_) = context) and + context.appliesToScope(s) select s.getLocation().toString(), context, s.toString() diff --git a/python/ql/test/library-tests/taint/general/ParamSource.ql b/python/ql/test/library-tests/taint/general/ParamSource.ql index 192de466882..7ac2b7699ef 100644 --- a/python/ql/test/library-tests/taint/general/ParamSource.ql +++ b/python/ql/test/library-tests/taint/general/ParamSource.ql @@ -4,36 +4,36 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.injection.Command class TestKind extends TaintKind { - TestKind() { this = "test" } + TestKind() { this = "test" } } class CustomSource extends TaintSource { - CustomSource() { - exists(Parameter p | - p.asName().getId() = "arg" and - this.(ControlFlowNode).getNode() = p - ) - } + CustomSource() { + exists(Parameter p | + p.asName().getId() = "arg" and + this.(ControlFlowNode).getNode() = p + ) + } - override predicate isSourceOf(TaintKind kind) { kind instanceof TestKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof TestKind } - override string toString() { result = "Source of untrusted input" } + override string toString() { result = "Source of untrusted input" } } class SimpleSink extends TaintSink { - override string toString() { result = "Simple sink" } + override string toString() { result = "Simple sink" } - SimpleSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - this = call.getAnArg() - ) - } + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof TestKind } + override predicate sinks(TaintKind taint) { taint instanceof TestKind } } from TaintSource src, TaintSink sink, TaintKind srckind, TaintKind sinkkind where src.flowsToSink(srckind, sink) and sink.sinks(sinkkind) select srckind, src.getLocation().toString(), sink.getLocation().getStartLine(), - sink.(ControlFlowNode).getNode().toString(), sinkkind + sink.(ControlFlowNode).getNode().toString(), sinkkind diff --git a/python/ql/test/library-tests/taint/general/TaintConsistency.ql b/python/ql/test/library-tests/taint/general/TaintConsistency.ql index d07828e5947..8cef7c378a4 100644 --- a/python/ql/test/library-tests/taint/general/TaintConsistency.ql +++ b/python/ql/test/library-tests/taint/general/TaintConsistency.ql @@ -4,26 +4,26 @@ import semmle.python.dataflow.Implementation import TaintLib from - TaintKind taint, TaintTrackingContext c, DataFlow::Node n, string what, - TaintTrackingImplementation impl + TaintKind taint, TaintTrackingContext c, DataFlow::Node n, string what, + TaintTrackingImplementation impl where - not exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c) and - ( - impl.flowStep(_, n, c, _, taint, _) and what = "missing node at end of step" - or - impl.flowSource(n, c, _, taint) and what = "missing node for source" - ) + not exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c) and + ( + impl.flowStep(_, n, c, _, taint, _) and what = "missing node at end of step" or - exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c | - not impl.flowStep(_, n, c, _, taint, _) and - not impl.flowSource(n, c, _, taint) and - what = "TaintedNode with no reason" - or - impl.flowStep(t, n, c, _, taint, _) and what = "step ends where it starts" - or - impl.flowStep(t, _, _, _, _, _) and - not impl.flowStep(_, n, c, _, taint, _) and - not impl.flowSource(n, c, _, taint) and - what = "No predecessor and not a source" - ) + impl.flowSource(n, c, _, taint) and what = "missing node for source" + ) + or + exists(TaintedNode t | t.getTaintKind() = taint and t.getNode() = n and t.getContext() = c | + not impl.flowStep(_, n, c, _, taint, _) and + not impl.flowSource(n, c, _, taint) and + what = "TaintedNode with no reason" + or + impl.flowStep(t, n, c, _, taint, _) and what = "step ends where it starts" + or + impl.flowStep(t, _, _, _, _, _) and + not impl.flowStep(_, n, c, _, taint, _) and + not impl.flowSource(n, c, _, taint) and + what = "No predecessor and not a source" + ) select n.getLocation(), taint, c, n.toString(), what diff --git a/python/ql/test/library-tests/taint/general/TaintLib.qll b/python/ql/test/library-tests/taint/general/TaintLib.qll index d0e8b9902ec..af3799c3b95 100644 --- a/python/ql/test/library-tests/taint/general/TaintLib.qll +++ b/python/ql/test/library-tests/taint/general/TaintLib.qll @@ -2,279 +2,279 @@ import python import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { - SimpleTest() { this = "simple.test" } + SimpleTest() { this = "simple.test" } } class SimpleSink extends TaintSink { - override string toString() { result = "Simple sink" } + override string toString() { result = "Simple sink" } - SimpleSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "SINK" and - this = call.getAnArg() - ) - } + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } + override predicate sinks(TaintKind taint) { taint instanceof SimpleTest } } class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "SOURCE" } + SimpleSource() { this.(NameNode).getId() = "SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } + override predicate isSourceOf(TaintKind kind) { kind instanceof SimpleTest } - override string toString() { result = "simple.source" } + override string toString() { result = "simple.source" } } class SimpleSanitizer extends Sanitizer { - SimpleSanitizer() { this = "Simple sanitizer" } + SimpleSanitizer() { this = "Simple sanitizer" } - override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { - node.(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and - taint instanceof SimpleTest - } + override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { + node.(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and + taint instanceof SimpleTest + } - override predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { - exists(CallNode call | - def.(ArgumentRefinement).getInput().getAUse() = call.getAnArg() and - call.getFunction().(NameNode).getId() = "SANITIZE" - ) and - taint instanceof SimpleTest - } + override predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { + exists(CallNode call | + def.(ArgumentRefinement).getInput().getAUse() = call.getAnArg() and + call.getFunction().(NameNode).getId() = "SANITIZE" + ) and + taint instanceof SimpleTest + } } class BasicCustomTaint extends TaintKind { - BasicCustomTaint() { this = "basic.custom" } + BasicCustomTaint() { this = "basic.custom" } - override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { - tonode.(CallNode).getAnArg() = fromnode and - tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and - result = this - } + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(CallNode).getAnArg() = fromnode and + tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and + result = this + } } class BasicCustomSink extends TaintSink { - override string toString() { result = "Basic custom sink" } + override string toString() { result = "Basic custom sink" } - BasicCustomSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and - this = call.getAnArg() - ) - } + BasicCustomSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and + this = call.getAnArg() + ) + } - override predicate sinks(TaintKind taint) { taint instanceof BasicCustomTaint } + override predicate sinks(TaintKind taint) { taint instanceof BasicCustomTaint } } class BasicCustomSource extends TaintSource { - BasicCustomSource() { this.(NameNode).getId() = "CUSTOM_SOURCE" } + BasicCustomSource() { this.(NameNode).getId() = "CUSTOM_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof BasicCustomTaint } + override predicate isSourceOf(TaintKind kind) { kind instanceof BasicCustomTaint } - override string toString() { result = "basic.custom.source" } + override string toString() { result = "basic.custom.source" } } class Rock extends TaintKind { - Rock() { this = "rock" } + Rock() { this = "rock" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Scissors - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Scissors + } - predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getArg(0) = sink and - call.getFunction().(NameNode).getId() = "paper" - ) - } + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "paper" + ) + } } class Paper extends TaintKind { - Paper() { this = "paper" } + Paper() { this = "paper" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Rock - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Rock + } - predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getArg(0) = sink and - call.getFunction().(NameNode).getId() = "scissors" - ) - } + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "scissors" + ) + } } class Scissors extends TaintKind { - Scissors() { this = "scissors" } + Scissors() { this = "scissors" } - override TaintKind getTaintOfMethodResult(string name) { - name = "prev" and result instanceof Paper - } + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Paper + } - predicate isSink(ControlFlowNode sink) { - exists(CallNode call | - call.getArg(0) = sink and - call.getFunction().(NameNode).getId() = "rock" - ) - } + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "rock" + ) + } } class RockPaperScissorSource extends TaintSource { - RockPaperScissorSource() { - exists(string name | this.(NameNode).getId() = name | - name = "ROCK" or name = "PAPER" or name = "SCISSORS" - ) - } + RockPaperScissorSource() { + exists(string name | this.(NameNode).getId() = name | + name = "ROCK" or name = "PAPER" or name = "SCISSORS" + ) + } - override predicate isSourceOf(TaintKind kind) { kind = this.(NameNode).getId().toLowerCase() } + override predicate isSourceOf(TaintKind kind) { kind = this.(NameNode).getId().toLowerCase() } - override string toString() { result = "rock.paper.scissors.source" } + override string toString() { result = "rock.paper.scissors.source" } } private predicate function_param(string funcname, ControlFlowNode arg) { - exists(FunctionObject f | - f.getName() = funcname and - arg = f.getArgumentForCall(_, _) - ) + exists(FunctionObject f | + f.getName() = funcname and + arg = f.getArgumentForCall(_, _) + ) } class RockPaperScissorSink extends TaintSink { - RockPaperScissorSink() { - exists(string name | function_param(name, this) | - name = "rock" or name = "paper" or name = "scissors" - ) - } + RockPaperScissorSink() { + exists(string name | function_param(name, this) | + name = "rock" or name = "paper" or name = "scissors" + ) + } - override predicate sinks(TaintKind taint) { - exists(string name | function_param(name, this) | - name = "paper" and taint = "rock" - or - name = "rock" and taint = "scissors" - or - name = "scissors" and taint = "paper" - ) - } + override predicate sinks(TaintKind taint) { + exists(string name | function_param(name, this) | + name = "paper" and taint = "rock" + or + name = "rock" and taint = "scissors" + or + name = "scissors" and taint = "paper" + ) + } - override string toString() { result = "rock.paper.scissors.sink" } + override string toString() { result = "rock.paper.scissors.sink" } } class TaintCarrier extends TaintKind { - TaintCarrier() { this = "explicit.carrier" } + TaintCarrier() { this = "explicit.carrier" } - override TaintKind getTaintOfMethodResult(string name) { - name = "get_taint" and result instanceof SimpleTest - } + override TaintKind getTaintOfMethodResult(string name) { + name = "get_taint" and result instanceof SimpleTest + } } /* There is no sink for `TaintCarrier`. It is not "dangerous" in itself; it merely holds a `SimpleTest`. */ class TaintCarrierSource extends TaintSource { - TaintCarrierSource() { this.(NameNode).getId() = "TAINT_CARRIER_SOURCE" } + TaintCarrierSource() { this.(NameNode).getId() = "TAINT_CARRIER_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TaintCarrier } + override predicate isSourceOf(TaintKind kind) { kind instanceof TaintCarrier } - override string toString() { result = "taint.carrier.source" } + override string toString() { result = "taint.carrier.source" } } /* Some more realistic examples */ abstract class UserInput extends TaintKind { - bindingset[this] - UserInput() { any() } + bindingset[this] + UserInput() { any() } } class UserInputSource extends TaintSource { - UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } + UserInputSource() { this.(CallNode).getFunction().(NameNode).getId() = "user_input" } - override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } + override predicate isSourceOf(TaintKind kind) { kind instanceof UserInput } - override string toString() { result = "user.input.source" } + override string toString() { result = "user.input.source" } } class SqlInjectionTaint extends UserInput { - SqlInjectionTaint() { this = "SQL injection" } + SqlInjectionTaint() { this = "SQL injection" } } class CommandInjectionTaint extends UserInput { - CommandInjectionTaint() { this = "Command injection" } + CommandInjectionTaint() { this = "Command injection" } } class SqlSanitizer extends Sanitizer { - SqlSanitizer() { this = "SQL sanitizer" } + SqlSanitizer() { this = "SQL sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f, CallNode call | - f.getName() = "isEscapedSql" and - test.getTest() = call and - call.getAnArg() = test.getSourceVariable().getAUse() and - f.getACall() = call and - test.getSense() = true - ) and - taint instanceof SqlInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f, CallNode call | + f.getName() = "isEscapedSql" and + test.getTest() = call and + call.getAnArg() = test.getSourceVariable().getAUse() and + f.getACall() = call and + test.getSense() = true + ) and + taint instanceof SqlInjectionTaint + } } class CommandSanitizer extends Sanitizer { - CommandSanitizer() { this = "Command sanitizer" } + CommandSanitizer() { this = "Command sanitizer" } - /** Holds if `test` shows value to be untainted with `taint` */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - exists(FunctionObject f | - f.getName() = "isValidCommand" and - f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and - test.getSense() = true - ) and - taint instanceof CommandInjectionTaint - } + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f | + f.getName() = "isValidCommand" and + f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and + test.getSense() = true + ) and + taint instanceof CommandInjectionTaint + } } class SqlQuery extends TaintSink { - SqlQuery() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "sql_query" and - call.getAnArg() = this - ) - } + SqlQuery() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "sql_query" and + call.getAnArg() = this + ) + } - override string toString() { result = "SQL query" } + override string toString() { result = "SQL query" } - override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof SqlInjectionTaint } } class OsCommand extends TaintSink { - OsCommand() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "os_command" and - call.getAnArg() = this - ) - } + OsCommand() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "os_command" and + call.getAnArg() = this + ) + } - override string toString() { result = "OS command" } + override string toString() { result = "OS command" } - override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } + override predicate sinks(TaintKind taint) { taint instanceof CommandInjectionTaint } } class Falsey extends TaintKind { - Falsey() { this = "falsey" } + Falsey() { this = "falsey" } - override boolean booleanValue() { result = false } + override boolean booleanValue() { result = false } } class FalseySource extends TaintSource { - FalseySource() { this.(NameNode).getId() = "FALSEY" } + FalseySource() { this.(NameNode).getId() = "FALSEY" } - override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } + override predicate isSourceOf(TaintKind kind) { kind instanceof Falsey } - override string toString() { result = "falsey.source" } + override string toString() { result = "falsey.source" } } class TaintIterable extends TaintKind { - TaintIterable() { this = "iterable.simple" } + TaintIterable() { this = "iterable.simple" } - override TaintKind getTaintForIteration() { result instanceof SimpleTest } + override TaintKind getTaintForIteration() { result instanceof SimpleTest } } class TaintIterableSource extends TaintSource { - TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } + TaintIterableSource() { this.(NameNode).getId() = "ITERABLE_SOURCE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } + override predicate isSourceOf(TaintKind kind) { kind instanceof TaintIterable } } diff --git a/python/ql/test/library-tests/taint/general/TestDefn.ql b/python/ql/test/library-tests/taint/general/TestDefn.ql index e2791bf2e72..242ab7018a8 100644 --- a/python/ql/test/library-tests/taint/general/TestDefn.ql +++ b/python/ql/test/library-tests/taint/general/TestDefn.ql @@ -4,4 +4,4 @@ import TaintLib from EssaNodeDefinition defn, TaintedNode n where n.getNode().asVariable() = defn.getVariable() select defn.getLocation().toString(), defn.getRepresentation(), n.getLocation().toString(), - "Taint " + n.toString(), defn.getDefiningNode().getNode().toString() + "Taint " + n.toString(), defn.getDefiningNode().getNode().toString() diff --git a/python/ql/test/library-tests/taint/general/TestSink.ql b/python/ql/test/library-tests/taint/general/TestSink.ql index 2405ee3af06..0ad7df6a560 100644 --- a/python/ql/test/library-tests/taint/general/TestSink.ql +++ b/python/ql/test/library-tests/taint/general/TestSink.ql @@ -5,4 +5,4 @@ import TaintLib from TaintSource src, TaintSink sink, TaintKind srckind, TaintKind sinkkind where src.flowsToSink(srckind, sink) and sink.sinks(sinkkind) select srckind, src.getLocation().toString(), sink.getLocation().getStartLine(), - sink.(ControlFlowNode).getNode().toString(), sinkkind + sink.(ControlFlowNode).getNode().toString(), sinkkind diff --git a/python/ql/test/library-tests/taint/general/TestStep.ql b/python/ql/test/library-tests/taint/general/TestStep.ql index 5274cd0af44..82ca707013e 100644 --- a/python/ql/test/library-tests/taint/general/TestStep.ql +++ b/python/ql/test/library-tests/taint/general/TestStep.ql @@ -5,4 +5,4 @@ import TaintLib from TaintedNode n, TaintedNode s where s = n.getASuccessor() select n.toString(), n.getLocation().toString(), n.getNode().toString(), n.getContext(), "-->", - s.toString(), s.getLocation().toString(), s.getNode().toString(), s.getContext() + s.toString(), s.getLocation().toString(), s.getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/general/TestTaint.ql b/python/ql/test/library-tests/taint/general/TestTaint.ql index 7c513d7b52c..79c350d9aa2 100644 --- a/python/ql/test/library-tests/taint/general/TestTaint.ql +++ b/python/ql/test/library-tests/taint/general/TestTaint.ql @@ -4,16 +4,16 @@ import TaintLib from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "assignment.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "assignment.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/general/TestVar.ql b/python/ql/test/library-tests/taint/general/TestVar.ql index 991d3cdbfa4..28fd4fa74ac 100644 --- a/python/ql/test/library-tests/taint/general/TestVar.ql +++ b/python/ql/test/library-tests/taint/general/TestVar.ql @@ -4,4 +4,4 @@ import TaintLib from EssaVariable var, TaintedNode n where n.getNode().asVariable() = var select var.getDefinition().getLocation().toString(), var.getRepresentation(), - n.getLocation().toString(), "Taint " + n.toString() + n.getLocation().toString(), "Taint " + n.toString() diff --git a/python/ql/test/library-tests/taint/namedtuple/Taint.qll b/python/ql/test/library-tests/taint/namedtuple/Taint.qll index bb40491c202..0dc3c71ec84 100644 --- a/python/ql/test/library-tests/taint/namedtuple/Taint.qll +++ b/python/ql/test/library-tests/taint/namedtuple/Taint.qll @@ -3,43 +3,43 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } class TestConfig extends TaintTracking::Configuration { - TestConfig() { this = "TestConfig" } + TestConfig() { this = "TestConfig" } - override predicate isSanitizer(Sanitizer sanitizer) { - sanitizer instanceof UrlsplitUrlparseTempSanitizer - } + override predicate isSanitizer(Sanitizer sanitizer) { + sanitizer instanceof UrlsplitUrlparseTempSanitizer + } - override predicate isSource(TaintTracking::Source source) { - source instanceof SimpleSource - or - source instanceof ListSource - or - source instanceof DictSource - } + override predicate isSource(TaintTracking::Source source) { + source instanceof SimpleSource + or + source instanceof ListSource + or + source instanceof DictSource + } - override predicate isSink(TaintTracking::Sink sink) { none() } + override predicate isSink(TaintTracking::Sink sink) { none() } } diff --git a/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql b/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql +++ b/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/strings/Taint.qll b/python/ql/test/library-tests/taint/strings/Taint.qll index 3840df662ef..3368a1c4f70 100644 --- a/python/ql/test/library-tests/taint/strings/Taint.qll +++ b/python/ql/test/library-tests/taint/strings/Taint.qll @@ -4,41 +4,41 @@ import semmle.python.security.strings.Untrusted import semmle.python.security.Exceptions class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } class ExceptionInfoSource extends TaintSource { - ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" } + ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } - override string toString() { result = "Exception info source" } + override string toString() { result = "Exception info source" } } class ExternalFileObjectSource extends TaintSource { - ExternalFileObjectSource() { this.(NameNode).getId() = "TAINTED_FILE" } + ExternalFileObjectSource() { this.(NameNode).getId() = "TAINTED_FILE" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalFileObject } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalFileObject } - override string toString() { result = "Tainted file source" } + override string toString() { result = "Tainted file source" } } diff --git a/python/ql/test/library-tests/taint/strings/TestStep.ql b/python/ql/test/library-tests/taint/strings/TestStep.ql index 7e42b878e74..177edce3498 100644 --- a/python/ql/test/library-tests/taint/strings/TestStep.ql +++ b/python/ql/test/library-tests/taint/strings/TestStep.ql @@ -4,8 +4,8 @@ import Taint from TaintedNode n, TaintedNode s where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() + n.getLocation().getFile().getShortName() = "test.py" and + s.getLocation().getFile().getShortName() = "test.py" and + s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() + " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/strings/TestTaint.ql b/python/ql/test/library-tests/taint/strings/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/strings/TestTaint.ql +++ b/python/ql/test/library-tests/taint/strings/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/taint/unpacking/Taint.qll b/python/ql/test/library-tests/taint/unpacking/Taint.qll index 21e16aabac5..010b9738c5c 100644 --- a/python/ql/test/library-tests/taint/unpacking/Taint.qll +++ b/python/ql/test/library-tests/taint/unpacking/Taint.qll @@ -3,25 +3,25 @@ import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } + SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - override string toString() { result = "taint source" } + override string toString() { result = "taint source" } } class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - override string toString() { result = "list taint source" } + override string toString() { result = "list taint source" } } class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } + override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - override string toString() { result = "dict taint source" } + override string toString() { result = "dict taint source" } } diff --git a/python/ql/test/library-tests/taint/unpacking/TestStep.ql b/python/ql/test/library-tests/taint/unpacking/TestStep.ql index 7e42b878e74..177edce3498 100644 --- a/python/ql/test/library-tests/taint/unpacking/TestStep.ql +++ b/python/ql/test/library-tests/taint/unpacking/TestStep.ql @@ -4,8 +4,8 @@ import Taint from TaintedNode n, TaintedNode s where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() + n.getLocation().getFile().getShortName() = "test.py" and + s.getLocation().getFile().getShortName() = "test.py" and + s = n.getASuccessor() select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() + " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/unpacking/TestTaint.ql b/python/ql/test/library-tests/taint/unpacking/TestTaint.ql index fb1d102aa7a..47883578516 100644 --- a/python/ql/test/library-tests/taint/unpacking/TestTaint.ql +++ b/python/ql/test/library-tests/taint/unpacking/TestTaint.ql @@ -4,16 +4,16 @@ import Taint from Call call, Expr arg, string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) + call.getLocation().getFile().getShortName() = "test.py" and + call.getFunc().(Name).getId() = "test" and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "NO TAINT" + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) + ) select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/thrift/Function.ql b/python/ql/test/library-tests/thrift/Function.ql index 2161fd8ec8a..59411777280 100644 --- a/python/ql/test/library-tests/thrift/Function.ql +++ b/python/ql/test/library-tests/thrift/Function.ql @@ -2,9 +2,9 @@ import external.Thrift from ThriftFunction t, string n, ThriftElement x where - exists(int i | x = t.getArgument(i) and n = i.toString()) - or - x = t.getAThrows() and n = "throws" - or - x = t.getReturnType() and n = "returns" + exists(int i | x = t.getArgument(i) and n = i.toString()) + or + x = t.getAThrows() and n = "throws" + or + x = t.getReturnType() and n = "returns" select t, n, x diff --git a/python/ql/test/library-tests/types/attributes/Test.ql b/python/ql/test/library-tests/types/attributes/Test.ql index a012b0d3a15..9e066a6414b 100644 --- a/python/ql/test/library-tests/types/attributes/Test.ql +++ b/python/ql/test/library-tests/types/attributes/Test.ql @@ -3,4 +3,4 @@ import python from ClassObject cls, ClassObject start, string name, Object val where not name.substring(0, 2) = "__" and val = cls.lookupMro(start, name) select cls.getOrigin().getLocation().getStartLine(), cls.toString(), start.toString(), name, - val.toString(), val.getOrigin().getLocation().getStartLine() + val.toString(), val.getOrigin().getLocation().getStartLine() diff --git a/python/ql/test/library-tests/types/classattr/ClassAttribute.ql b/python/ql/test/library-tests/types/classattr/ClassAttribute.ql index 6e023dcada5..d0633a36e3a 100644 --- a/python/ql/test/library-tests/types/classattr/ClassAttribute.ql +++ b/python/ql/test/library-tests/types/classattr/ClassAttribute.ql @@ -8,11 +8,11 @@ import python from ClassObject cls, string name, string kind where - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") and - ( - cls.hasAttribute(name) and kind = "has" - or - cls.declaresAttribute(name) and kind = "declares" - ) + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") and + ( + cls.hasAttribute(name) and kind = "has" + or + cls.declaresAttribute(name) and kind = "declares" + ) select cls.toString(), kind, name diff --git a/python/ql/test/library-tests/types/classattr/ClassMember.ql b/python/ql/test/library-tests/types/classattr/ClassMember.ql index 1357deb0da9..d1e136a5108 100644 --- a/python/ql/test/library-tests/types/classattr/ClassMember.ql +++ b/python/ql/test/library-tests/types/classattr/ClassMember.ql @@ -8,11 +8,11 @@ import python from ClassObject cls, string name, string kind, Object o where - not cls.isC() and - not name.matches("\\_\\_%\\_\\_") and - ( - o = cls.lookupAttribute(name) and kind = "has" - or - o = cls.declaredAttribute(name) and kind = "declares" - ) + not cls.isC() and + not name.matches("\\_\\_%\\_\\_") and + ( + o = cls.lookupAttribute(name) and kind = "has" + or + o = cls.declaredAttribute(name) and kind = "declares" + ) select cls.toString(), kind, name, o.toString() diff --git a/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql b/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql index 26e9ad08c26..dc21b250c1e 100644 --- a/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql +++ b/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql @@ -2,12 +2,12 @@ import python from ClassObject cls, string name, string kind, Object o where - not cls.isC() and - name.matches("\\_\\_%\\_\\_") and - not o = theObjectType().lookupAttribute(name) and - ( - o = cls.lookupAttribute(name) and kind = "has" - or - o = cls.declaredAttribute(name) and kind = "declares" - ) + not cls.isC() and + name.matches("\\_\\_%\\_\\_") and + not o = theObjectType().lookupAttribute(name) and + ( + o = cls.lookupAttribute(name) and kind = "has" + or + o = cls.declaredAttribute(name) and kind = "declares" + ) select cls.toString(), kind, name, o.toString() diff --git a/python/ql/test/library-tests/types/exceptions/Impossible.ql b/python/ql/test/library-tests/types/exceptions/Impossible.ql index 69c658edba6..787ba8898f9 100644 --- a/python/ql/test/library-tests/types/exceptions/Impossible.ql +++ b/python/ql/test/library-tests/types/exceptions/Impossible.ql @@ -2,18 +2,18 @@ import python from RaisingNode r, ControlFlowNode n, string kind where - r.unlikelySuccessor(n) and - ( - r.getATrueSuccessor() = n and kind = "true" - or - r.getAFalseSuccessor() = n and kind = "false" - or - r.getAnExceptionalSuccessor() = n and kind = "exceptional" - or - not r.getATrueSuccessor() = n and - not r.getAFalseSuccessor() = n and - not r.getAnExceptionalSuccessor() = n and - kind = "normal" - ) + r.unlikelySuccessor(n) and + ( + r.getATrueSuccessor() = n and kind = "true" + or + r.getAFalseSuccessor() = n and kind = "false" + or + r.getAnExceptionalSuccessor() = n and kind = "exceptional" + or + not r.getATrueSuccessor() = n and + not r.getAFalseSuccessor() = n and + not r.getAnExceptionalSuccessor() = n and + kind = "normal" + ) select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), - n.getNode().toString(), kind + n.getNode().toString(), kind diff --git a/python/ql/test/library-tests/types/exceptions/LineRaises.ql b/python/ql/test/library-tests/types/exceptions/LineRaises.ql index 933eb8d59bb..de8b834e520 100644 --- a/python/ql/test/library-tests/types/exceptions/LineRaises.ql +++ b/python/ql/test/library-tests/types/exceptions/LineRaises.ql @@ -2,11 +2,11 @@ import python from RaisingNode r, string type where - type = r.getARaisedType().toString() - or - type = "Unknown" and r.raisesUnknownType() - or - not exists(r.getARaisedType()) and - not r.raisesUnknownType() and - type = "None" + type = r.getARaisedType().toString() + or + type = "Unknown" and r.raisesUnknownType() + or + not exists(r.getARaisedType()) and + not r.raisesUnknownType() and + type = "None" select r.getNode().getLocation().getStartLine(), type diff --git a/python/ql/test/library-tests/types/exceptions/Raises.ql b/python/ql/test/library-tests/types/exceptions/Raises.ql index aa477f718a2..2415c707967 100644 --- a/python/ql/test/library-tests/types/exceptions/Raises.ql +++ b/python/ql/test/library-tests/types/exceptions/Raises.ql @@ -2,11 +2,11 @@ import python from PyFunctionObject f, string type where - type = f.getARaisedType().toString() - or - type = "Unknown" and f.raisesUnknownType() - or - not exists(f.getARaisedType()) and - not f.raisesUnknownType() and - type = "None" + type = f.getARaisedType().toString() + or + type = "Unknown" and f.raisesUnknownType() + or + not exists(f.getARaisedType()) and + not f.raisesUnknownType() and + type = "None" select f.toString(), type diff --git a/python/ql/test/library-tests/types/exceptions/Viable.ql b/python/ql/test/library-tests/types/exceptions/Viable.ql index ed388e2faf2..bf00a0675d6 100644 --- a/python/ql/test/library-tests/types/exceptions/Viable.ql +++ b/python/ql/test/library-tests/types/exceptions/Viable.ql @@ -3,4 +3,4 @@ import python from RaisingNode r, ControlFlowNode n, ClassObject ex where r.viableExceptionEdge_objectapi(n, ex) select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), - n.getNode().toString(), ex.toString() + n.getNode().toString(), ex.toString() diff --git a/python/ql/test/library-tests/variables/scopes/free.ql b/python/ql/test/library-tests/variables/scopes/free.ql index 65789e76a52..cd5e0325de0 100644 --- a/python/ql/test/library-tests/variables/scopes/free.ql +++ b/python/ql/test/library-tests/variables/scopes/free.ql @@ -2,7 +2,7 @@ import python from LocalVariable v, Scope inner where - v.escapes() and - inner = v.getAnAccess().getScope() and - inner != v.getScope() + v.escapes() and + inner = v.getAnAccess().getScope() and + inner != v.getScope() select v.toString(), v.getScope().toString(), inner.toString() diff --git a/python/ql/test/library-tests/variables/scopes/locals.ql b/python/ql/test/library-tests/variables/scopes/locals.ql index 62814925fe9..35f29176653 100644 --- a/python/ql/test/library-tests/variables/scopes/locals.ql +++ b/python/ql/test/library-tests/variables/scopes/locals.ql @@ -2,7 +2,7 @@ import python from LocalVariable l, string kind where - l instanceof FastLocalVariable and kind = "fast" - or - l instanceof NameLocalVariable and kind = "name" + l instanceof FastLocalVariable and kind = "fast" + or + l instanceof NameLocalVariable and kind = "name" select l, l.getScope(), kind diff --git a/python/ql/test/library-tests/variables/scopes/lookup.ql b/python/ql/test/library-tests/variables/scopes/lookup.ql index 248cd62b911..84dfaac48b5 100644 --- a/python/ql/test/library-tests/variables/scopes/lookup.ql +++ b/python/ql/test/library-tests/variables/scopes/lookup.ql @@ -2,17 +2,17 @@ import python from NameNode n, string l where - n.isLoad() and - ( - n.isGlobal() and l = "global" - or - n.isLocal() and l = "local" - or - n.isNonLocal() and l = "non-local" - or - not n.isGlobal() and - not n.isLocal() and - not n.isNonLocal() and - l = "none" - ) + n.isLoad() and + ( + n.isGlobal() and l = "global" + or + n.isLocal() and l = "local" + or + n.isNonLocal() and l = "non-local" + or + not n.isGlobal() and + not n.isLocal() and + not n.isNonLocal() and + l = "none" + ) select n.getLocation().getStartLine(), n.toString(), l diff --git a/python/ql/test/library-tests/web/stdlib/HttpSources.ql b/python/ql/test/library-tests/web/stdlib/HttpSources.ql index 3b91a26d14f..f4e9a1b48d3 100644 --- a/python/ql/test/library-tests/web/stdlib/HttpSources.ql +++ b/python/ql/test/library-tests/web/stdlib/HttpSources.ql @@ -4,6 +4,6 @@ import semmle.python.security.strings.Untrusted from HttpRequestTaintSource source, TaintKind kind where - source.isSourceOf(kind) and - source.getLocation().getFile().getShortName() != "cgi.py" + source.isSourceOf(kind) and + source.getLocation().getFile().getShortName() != "cgi.py" select source.(ControlFlowNode).getNode(), kind diff --git a/python/ql/test/library-tests/web/stdlib/TestTaint.ql b/python/ql/test/library-tests/web/stdlib/TestTaint.ql index 1ac84c3d290..cd2b08ef235 100644 --- a/python/ql/test/library-tests/web/stdlib/TestTaint.ql +++ b/python/ql/test/library-tests/web/stdlib/TestTaint.ql @@ -4,29 +4,29 @@ import semmle.python.web.HttpRequest import semmle.python.security.strings.Untrusted from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string + Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, + string taint_string where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false + call.getLocation().getFile().getShortName() = "test.py" and + ( + call.getFunc().(Name).getId() = "ensure_tainted" and + expected_taint = true + or + call.getFunc().(Name).getId() = "ensure_not_tainted" and + expected_taint = false + ) and + arg = call.getAnArg() and + ( + not exists(TaintedNode tainted | tainted.getAstNode() = arg) and + taint_string = "" and + has_taint = false + or + exists(TaintedNode tainted | tainted.getAstNode() = arg | + taint_string = tainted.getTaintKind().toString() ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok " else test_res = "fail" + has_taint = true + ) and + if expected_taint = has_taint then test_res = "ok " else test_res = "fail" // if expected_taint = has_taint then test_res = "✓" else test_res = "✕" select arg.getLocation().toString(), test_res, call.getScope().(Function).getName(), arg.toString(), - taint_string + taint_string diff --git a/python/ql/test/library-tests/web/zope/Test.ql b/python/ql/test/library-tests/web/zope/Test.ql index e694883237b..ca705ac292e 100644 --- a/python/ql/test/library-tests/web/zope/Test.ql +++ b/python/ql/test/library-tests/web/zope/Test.ql @@ -3,8 +3,8 @@ import semmle.python.TestUtils from ControlFlowNode f, Value v, ControlFlowNode x where - exists(ExprStmt s | s.getValue().getAFlowNode() = f) and - f.pointsTo(v, x) and - f.getLocation().getFile().getBaseName() = "test.py" + exists(ExprStmt s | s.getValue().getAFlowNode() = f) and + f.pointsTo(v, x) and + f.getLocation().getFile().getBaseName() = "test.py" select f.getLocation().getStartLine(), f.toString(), v.toString(), - remove_library_prefix(x.getLocation()) + remove_library_prefix(x.getLocation()) diff --git a/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql b/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql index 8525edcb8b8..745568f2405 100644 --- a/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql +++ b/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql @@ -3,4 +3,4 @@ import python from Module m, ModuleMetrics mm where mm = m.getMetrics() and mm.getNumberOfLines() > 0 select m, 100.0 * (mm.getNumberOfLinesOfCode().(float) / mm.getNumberOfLines().(float)) as ratio - order by ratio desc + order by ratio desc diff --git a/python/ql/test/query-tests/Resources/Dataflow.ql b/python/ql/test/query-tests/Resources/Dataflow.ql index 4e2cf15b50d..fad31d80ec1 100644 --- a/python/ql/test/query-tests/Resources/Dataflow.ql +++ b/python/ql/test/query-tests/Resources/Dataflow.ql @@ -3,12 +3,12 @@ import Resources.FileOpen from EssaVariable v, EssaDefinition def, string open, string exit where - def = v.getDefinition() and - v.getSourceVariable().getName().charAt(0) = "f" and - ( - var_is_open(v, _) and open = "open" - or - not var_is_open(v, _) and open = "closed" - ) and - if BaseFlow::reaches_exit(v) then exit = "exit" else exit = "" + def = v.getDefinition() and + v.getSourceVariable().getName().charAt(0) = "f" and + ( + var_is_open(v, _) and open = "open" + or + not var_is_open(v, _) and open = "closed" + ) and + if BaseFlow::reaches_exit(v) then exit = "exit" else exit = "" select v.getRepresentation() + " = " + v.getDefinition().getRepresentation(), open, exit diff --git a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql index 1c5aa49030e..e6d27a1fda8 100644 --- a/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql +++ b/python/upgrades/f635b392038a494915307f913657cd3058f9b476/py_exprs.ql @@ -1,82 +1,82 @@ class Location extends @location { - /** Gets the start line of this location */ - int getStartLine() { - locations_default(this, _, result, _, _, _) or - locations_ast(this, _, result, _, _, _) - } + /** Gets the start line of this location */ + int getStartLine() { + locations_default(this, _, result, _, _, _) or + locations_ast(this, _, result, _, _, _) + } - /** Gets the start column of this location */ - int getStartColumn() { - locations_default(this, _, _, result, _, _) or - locations_ast(this, _, _, result, _, _) - } + /** Gets the start column of this location */ + int getStartColumn() { + locations_default(this, _, _, result, _, _) or + locations_ast(this, _, _, result, _, _) + } - string toString() { result = "" + ":" + this.getStartLine().toString() } + string toString() { result = "" + ":" + this.getStartLine().toString() } } class Expr_ extends @py_expr { - string toString() { result = "Expr" } + string toString() { result = "Expr" } - Location getLocation() { py_locations(result, this) } + Location getLocation() { py_locations(result, this) } } class ExprParent_ extends @py_expr_parent { - string toString() { result = "ExprParent" } + string toString() { result = "ExprParent" } } class ExprList_ extends @py_expr_list { - /** Gets the nth item of this expression list */ - Expr_ getItem(int index) { py_exprs(result, _, this, index) } + /** Gets the nth item of this expression list */ + Expr_ getItem(int index) { py_exprs(result, _, this, index) } - string toString() { result = "ExprList" } + string toString() { result = "ExprList" } } class Parameter_ extends @py_parameter { - string toString() { result = "Parameter" } + string toString() { result = "Parameter" } - Location getLocation() { result = this.(Expr_).getLocation() } + Location getLocation() { result = this.(Expr_).getLocation() } } class ParameterList extends @py_parameter_list { - /** Gets the nth parameter */ - Parameter_ getItem(int index) { - /* Item can be a Name or a Tuple, both of which are expressions */ - py_exprs(result, _, this, index) - } + /** Gets the nth parameter */ + Parameter_ getItem(int index) { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, index) + } - string toString() { result = "ParameterList" } + string toString() { result = "ParameterList" } } class Arguments_ extends @py_arguments { - /** Gets the keyword-only default values of this parameters definition. */ - ExprList_ getKwDefaults() { py_expr_lists(result, this, 0) } + /** Gets the keyword-only default values of this parameters definition. */ + ExprList_ getKwDefaults() { py_expr_lists(result, this, 0) } - /** Gets the nth keyword-only default value of this parameters definition. */ - Expr_ getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } + /** Gets the nth keyword-only default value of this parameters definition. */ + Expr_ getKwDefault(int index) { result = this.getKwDefaults().getItem(index) } - /** Gets the default values of this parameters definition. */ - ExprList_ getDefaults() { py_expr_lists(result, this, 1) } + /** Gets the default values of this parameters definition. */ + ExprList_ getDefaults() { py_expr_lists(result, this, 1) } - /** Gets the nth default value of this parameters definition. */ - Expr_ getDefault(int index) { result = this.getDefaults().getItem(index) } + /** Gets the nth default value of this parameters definition. */ + Expr_ getDefault(int index) { result = this.getDefaults().getItem(index) } - string toString() { result = "Arguments" } + string toString() { result = "Arguments" } } class Function_ extends @py_Function { - /** Gets the positional parameter list of this function. */ - ParameterList getArgs() { py_parameter_lists(result, this) } + /** Gets the positional parameter list of this function. */ + ParameterList getArgs() { py_parameter_lists(result, this) } - /** Gets the nth positional parameter of this function. */ - Parameter_ getArg(int index) { result = this.getArgs().getItem(index) } + /** Gets the nth positional parameter of this function. */ + Parameter_ getArg(int index) { result = this.getArgs().getItem(index) } - /** Gets the keyword-only parameter list of this function. */ - ExprList_ getKwonlyargs() { py_expr_lists(result, this, 3) } + /** Gets the keyword-only parameter list of this function. */ + ExprList_ getKwonlyargs() { py_expr_lists(result, this, 3) } - /** Gets the nth keyword-only parameter of this function. */ - Expr_ getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } + /** Gets the nth keyword-only parameter of this function. */ + Expr_ getKwonlyarg(int index) { result = this.getKwonlyargs().getItem(index) } - string toString() { result = "Function" } + string toString() { result = "Function" } } /** @@ -85,77 +85,77 @@ class Function_ extends @py_Function { * hierarchy slightly different (that's why it's called Adjusted) */ abstract class CallableExprAdjusted extends Expr_ { - /** - * Gets The default values and annotations (type-hints) for the arguments of this callable. - * - * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. - */ - abstract Arguments_ getArgs(); + /** + * Gets The default values and annotations (type-hints) for the arguments of this callable. + * + * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. + */ + abstract Arguments_ getArgs(); - /** Gets the function scope of this code expression. */ - abstract Function_ getInnerScope(); + /** Gets the function scope of this code expression. */ + abstract Function_ getInnerScope(); } class Lambda_ extends @py_Lambda, CallableExprAdjusted, Expr_ { - /** Gets the arguments of this lambda expression. */ - override Arguments_ getArgs() { py_arguments(result, this) } + /** Gets the arguments of this lambda expression. */ + override Arguments_ getArgs() { py_arguments(result, this) } - /** Gets the function scope of this lambda expression. */ - override Function_ getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this lambda expression. */ + override Function_ getInnerScope() { py_Functions(result, this) } - override string toString() { result = "Lambda" } + override string toString() { result = "Lambda" } } class FunctionExpr_ extends @py_FunctionExpr, CallableExprAdjusted, Expr_ { - /** Gets the parameters of this function definition. */ - override Arguments_ getArgs() { py_arguments(result, this) } + /** Gets the parameters of this function definition. */ + override Arguments_ getArgs() { py_arguments(result, this) } - /** Gets the function scope of this function definition. */ - override Function_ getInnerScope() { py_Functions(result, this) } + /** Gets the function scope of this function definition. */ + override Function_ getInnerScope() { py_Functions(result, this) } - override string toString() { result = "FunctionExpr" } + override string toString() { result = "FunctionExpr" } } - /* * This upgrade changes the *layout* of the default values for parameters, by * making `Argument.getKwDefault(i)` return the default value for keyword-only parameter `i` * (instead of the i'th default for a keyword-only parameter). `Argument.getDefault` is * changed in the same manner to keep consistency. */ + from Expr_ expr, int kind, ExprParent_ parent, int oldidx, int newidx where - py_exprs(expr, kind, parent, oldidx) and - ( - // expr is not a parameter default - not exists(Arguments_ args | args.getDefault(oldidx) = expr) and - not exists(Arguments_ args | args.getKwDefault(oldidx) = expr) and - newidx = oldidx - or - // expr is a default for a normal parameter - exists(Arguments_ args, CallableExprAdjusted callable | - callable.getArgs() = args and - args.getDefault(oldidx) = expr and - newidx = oldidx + count(callable.getInnerScope().getArg(_)) - count(args.getDefault(_)) - ) - or - // expr is a default for a keyword-only parameter. - // before this upgrade, we would not always attach the default value to the correct keyword-only parameter, - // to fix this, we calculate the new index based on the source location of the default value (a default value - // must belong to the parameter that was defined immediately before the default value). - exists(Arguments_ args, CallableExprAdjusted callable | - callable.getArgs() = args and - args.getKwDefault(oldidx) = expr and - newidx = - // the last parameter to be defined before this default value - max(int i | - exists(Parameter_ param | param = callable.getInnerScope().getKwonlyarg(i) | - param.getLocation().getStartLine() < expr.getLocation().getStartLine() - or - param.getLocation().getStartLine() = expr.getLocation().getStartLine() and - param.getLocation().getStartColumn() < expr.getLocation().getStartColumn() - ) - ) + py_exprs(expr, kind, parent, oldidx) and + ( + // expr is not a parameter default + not exists(Arguments_ args | args.getDefault(oldidx) = expr) and + not exists(Arguments_ args | args.getKwDefault(oldidx) = expr) and + newidx = oldidx + or + // expr is a default for a normal parameter + exists(Arguments_ args, CallableExprAdjusted callable | + callable.getArgs() = args and + args.getDefault(oldidx) = expr and + newidx = oldidx + count(callable.getInnerScope().getArg(_)) - count(args.getDefault(_)) + ) + or + // expr is a default for a keyword-only parameter. + // before this upgrade, we would not always attach the default value to the correct keyword-only parameter, + // to fix this, we calculate the new index based on the source location of the default value (a default value + // must belong to the parameter that was defined immediately before the default value). + exists(Arguments_ args, CallableExprAdjusted callable | + callable.getArgs() = args and + args.getKwDefault(oldidx) = expr and + newidx = + // the last parameter to be defined before this default value + max(int i | + exists(Parameter_ param | param = callable.getInnerScope().getKwonlyarg(i) | + param.getLocation().getStartLine() < expr.getLocation().getStartLine() + or + param.getLocation().getStartLine() = expr.getLocation().getStartLine() and + param.getLocation().getStartColumn() < expr.getLocation().getStartColumn() + ) ) ) + ) select expr, kind, parent, newidx