From bdcf4198e60b9957f0da04a204d13fa028e362e1 Mon Sep 17 00:00:00 2001 From: "lcartey@github.com" Date: Tue, 30 Jun 2020 11:01:17 +0100 Subject: [PATCH 001/185] Add additional Hibernate SQL sinks --- java/ql/src/semmle/code/java/frameworks/Hibernate.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/ql/src/semmle/code/java/frameworks/Hibernate.qll b/java/ql/src/semmle/code/java/frameworks/Hibernate.qll index fdc237aac8a..1366b3cac45 100644 --- a/java/ql/src/semmle/code/java/frameworks/Hibernate.qll +++ b/java/ql/src/semmle/code/java/frameworks/Hibernate.qll @@ -10,11 +10,11 @@ class HibernateSession extends RefType { } /** - * Holds if `m` is a method on `HibernateSession` taking an SQL string as its - * first argument. + * Holds if `m` is a method on `HibernateSession`, or a subclass, taking an SQL + * string as its first argument. */ predicate hibernateSqlMethod(Method m) { - m.getDeclaringType() instanceof HibernateSession and + m.getDeclaringType().getASourceSupertype*() instanceof HibernateSession and m.getParameterType(0) instanceof TypeString and ( m.hasName("createQuery") or From 26639a113e3991e755464ce4089e67a6e34076dd Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 2 Sep 2020 19:41:22 +0200 Subject: [PATCH 002/185] C#: Rename `Layout.Condition` to `FilePattern` and move to separate file --- .../Semmle.Extraction/FilePattern.cs | 34 +++++++++++++++ csharp/extractor/Semmle.Extraction/Layout.cs | 42 ++++--------------- 2 files changed, 42 insertions(+), 34 deletions(-) create mode 100644 csharp/extractor/Semmle.Extraction/FilePattern.cs diff --git a/csharp/extractor/Semmle.Extraction/FilePattern.cs b/csharp/extractor/Semmle.Extraction/FilePattern.cs new file mode 100644 index 00000000000..b3aab69b181 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction/FilePattern.cs @@ -0,0 +1,34 @@ +using System.IO; + +namespace Semmle.Extraction +{ + /// + /// An file pattern, as used in either an extractor layout file or + /// a path transformer file. + /// + class FilePattern + { + private readonly bool include; + private readonly string prefix; + + public bool Include => include; + + public string Prefix => prefix; + + public FilePattern(string line) + { + include = false; + if (line.StartsWith("-")) + line = line.Substring(1); + else + include = true; + prefix = Normalize(line.Trim()); + } + + static public string Normalize(string path) + { + path = Path.GetFullPath(path); + return path.Replace('\\', '/'); + } + } +} \ No newline at end of file diff --git a/csharp/extractor/Semmle.Extraction/Layout.cs b/csharp/extractor/Semmle.Extraction/Layout.cs index 9ab7ed5738a..1f772635df5 100644 --- a/csharp/extractor/Semmle.Extraction/Layout.cs +++ b/csharp/extractor/Semmle.Extraction/Layout.cs @@ -61,7 +61,7 @@ namespace Semmle.Extraction /// /// The source file. /// A newly created TrapWriter. - public TrapWriter CreateTrapWriter(ILogger logger, string srcFile, bool discardDuplicates, TrapWriter.CompressionMode trapCompression) => + public TrapWriter CreateTrapWriter(ILogger logger, string srcFile, bool discardDuplicates, TrapWriter.CompressionMode trapCompression) => new TrapWriter(logger, srcFile, TRAP_FOLDER, SOURCE_ARCHIVE, discardDuplicates, trapCompression); } @@ -167,33 +167,7 @@ namespace Semmle.Extraction sealed class LayoutBlock { - struct Condition - { - private readonly bool include; - private readonly string prefix; - - public bool Include => include; - - public string Prefix => prefix; - - public Condition(string line) - { - include = false; - if (line.StartsWith("-")) - line = line.Substring(1); - else - include = true; - prefix = Normalise(line.Trim()); - } - - static public string Normalise(string path) - { - path = Path.GetFullPath(path); - return path.Replace('\\', '/'); - } - } - - private readonly List conditions = new List(); + private readonly List filePatterns = new List(); public readonly Layout.SubProject Directories; @@ -219,20 +193,20 @@ namespace Semmle.Extraction ReadVariable("ODASA_BUILD_ERROR_DIR", lines[i++]); while (i < lines.Length && !lines[i].StartsWith("#")) { - conditions.Add(new Condition(lines[i++])); + filePatterns.Add(new FilePattern(lines[i++])); } } public bool Matches(string path) { bool matches = false; - path = Condition.Normalise(path); - foreach (Condition condition in conditions) + path = FilePattern.Normalize(path); + foreach (var filePattern in filePatterns) { - if (condition.Include) - matches |= path.StartsWith(condition.Prefix); + if (filePattern.Include) + matches |= path.StartsWith(filePattern.Prefix); else - matches &= !path.StartsWith(condition.Prefix); + matches &= !path.StartsWith(filePattern.Prefix); } return matches; } From 14567f531408c47f9e7b58ef9b0a34bb2c5e4c65 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 7 Sep 2020 08:57:12 +0200 Subject: [PATCH 003/185] C#: Support wild-cards in file patterns Implements the specification at https://wiki.semmle.com/display/SDmaster/project-layout+format by compiling file path specifications to regular expressions. --- .../Semmle.Extraction.Tests/FilePattern.cs | 48 +++++++ .../Semmle.Extraction/FilePattern.cs | 125 +++++++++++++++--- csharp/extractor/Semmle.Extraction/Layout.cs | 14 +- 3 files changed, 157 insertions(+), 30 deletions(-) create mode 100644 csharp/extractor/Semmle.Extraction.Tests/FilePattern.cs diff --git a/csharp/extractor/Semmle.Extraction.Tests/FilePattern.cs b/csharp/extractor/Semmle.Extraction.Tests/FilePattern.cs new file mode 100644 index 00000000000..dfff75ea18b --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.Tests/FilePattern.cs @@ -0,0 +1,48 @@ +using Xunit; + +namespace Semmle.Extraction.Tests +{ + public class FilePatternTests + { + [Fact] + public void TestRegexCompilation() + { + var fp = new FilePattern("/hadoop*"); + Assert.Equal("^hadoop[^/]*.*", fp.RegexPattern); + fp = new FilePattern("**/org/apache/hadoop"); + Assert.Equal("^.*/org/apache/hadoop.*", fp.RegexPattern); + fp = new FilePattern("hadoop-common/**/test// "); + Assert.Equal("^hadoop-common/.*/test(?/).*", fp.RegexPattern); + fp = new FilePattern(@"-C:\agent\root\asdf//"); + Assert.Equal("^C:/agent/root/asdf(?/).*", fp.RegexPattern); + fp = new FilePattern(@"-C:\agent+\[root]\asdf//"); + Assert.Equal(@"^C:/agent\+/\[root]/asdf(?/).*", fp.RegexPattern); + } + + [Fact] + public void TestMatching() + { + var fp1 = new FilePattern(@"C:\agent\root\abc//"); + var fp2 = new FilePattern(@"C:\agent\root\def//ghi"); + var patterns = new[] { fp1, fp2 }; + + var success = FilePattern.Matches(patterns, @"C:\agent\root\abc\file.cs", out var s); + Assert.True(success); + Assert.Equal("/file.cs", s); + + success = FilePattern.Matches(patterns, @"C:\agent\root\def\ghi\file.cs", out s); + Assert.True(success); + Assert.Equal("/ghi/file.cs", s); + + success = FilePattern.Matches(patterns, @"C:\agent\root\def\file.cs", out s); + Assert.False(success); + } + + [Fact] + public void TestInvalidPatterns() + { + Assert.Throws(() => new FilePattern("/abc//def//ghi")); + Assert.Throws(() => new FilePattern("/abc**def")); + } + } +} diff --git a/csharp/extractor/Semmle.Extraction/FilePattern.cs b/csharp/extractor/Semmle.Extraction/FilePattern.cs index b3aab69b181..c648faadbe1 100644 --- a/csharp/extractor/Semmle.Extraction/FilePattern.cs +++ b/csharp/extractor/Semmle.Extraction/FilePattern.cs @@ -1,34 +1,125 @@ -using System.IO; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using System.Diagnostics.CodeAnalysis; +using Semmle.Util; namespace Semmle.Extraction { + public sealed class InvalidFilePatternException : Exception + { + public InvalidFilePatternException(string pattern, string message) : + base($"Invalid file pattern '{pattern}': {message}") + { } + } + /// /// An file pattern, as used in either an extractor layout file or /// a path transformer file. /// - class FilePattern + public sealed class FilePattern { - private readonly bool include; - private readonly string prefix; + /// + /// Whether this is an inclusion pattern. + /// + public bool Include { get; } - public bool Include => include; - - public string Prefix => prefix; - - public FilePattern(string line) + public FilePattern(string pattern) { - include = false; - if (line.StartsWith("-")) - line = line.Substring(1); + Include = false; + if (pattern.StartsWith("-")) + pattern = pattern.Substring(1); else - include = true; - prefix = Normalize(line.Trim()); + Include = true; + pattern = FileUtils.ConvertToUnix(pattern.Trim()).TrimStart('/'); + RegexPattern = BuildRegex(pattern).ToString(); } - static public string Normalize(string path) + /// + /// Constructs a regex string from a file pattern. Throws + /// `InvalidFilePatternException` for invalid patterns. + /// + static StringBuilder BuildRegex(string pattern) { - path = Path.GetFullPath(path); - return path.Replace('\\', '/'); + bool HasCharAt(int i, Predicate p) => + i >= 0 && i < pattern.Length && p(pattern[i]); + var sb = new StringBuilder(); + var i = 0; + var seenDoubleSlash = false; + sb.Append('^'); + while (i < pattern.Length) + { + if (pattern[i] == '/') + { + if (HasCharAt(i + 1, c => c == '/')) + { + if (seenDoubleSlash) + throw new InvalidFilePatternException(pattern, "'//' is allowed at most once."); + sb.Append("(?/)"); + i += 2; + seenDoubleSlash = true; + } + else + { + sb.Append('/'); + i++; + } + } + else if (pattern[i] == '*') + { + if (HasCharAt(i + 1, c => c == '*')) + { + if (HasCharAt(i - 1, c => c != '/')) + throw new InvalidFilePatternException(pattern, "'**' preceeded by non-`/` character."); + if (HasCharAt(i + 2, c => c != '/')) + throw new InvalidFilePatternException(pattern, "'**' succeeded by non-`/` character"); + sb.Append(".*"); + i += 2; + } + else + { + sb.Append("[^/]*"); + i++; + } + } + else + sb.Append(Regex.Escape(pattern[i++].ToString())); + } + return sb.Append(".*"); + } + + + /// + /// The regex pattern compiled from this file pattern. + /// + public string RegexPattern { get; } + + /// + /// Returns `true` if the set of file patterns `patterns` match the path `path`. + /// If so, `transformerSuffix` will contain the part of `path` that needs to be + /// suffixed when using path transformers. + /// + public static bool Matches(IEnumerable patterns, string path, [NotNullWhen(true)] out string? transformerSuffix) + { + path = FileUtils.ConvertToUnix(path).TrimStart('/'); + Match? lastMatch = null; + foreach (var pattern in patterns) + { + var m = new Regex(pattern.RegexPattern).Match(path); + if (m.Success) + lastMatch = pattern.Include ? m : null; + } + if (lastMatch is Match) + { + transformerSuffix = lastMatch.Groups.TryGetValue("doubleslash", out var group) + ? path.Substring(group.Index) + : path; + return true; + } + + transformerSuffix = null; + return false; } } } \ No newline at end of file diff --git a/csharp/extractor/Semmle.Extraction/Layout.cs b/csharp/extractor/Semmle.Extraction/Layout.cs index 1f772635df5..1e44c3142e4 100644 --- a/csharp/extractor/Semmle.Extraction/Layout.cs +++ b/csharp/extractor/Semmle.Extraction/Layout.cs @@ -197,18 +197,6 @@ namespace Semmle.Extraction } } - public bool Matches(string path) - { - bool matches = false; - path = FilePattern.Normalize(path); - foreach (var filePattern in filePatterns) - { - if (filePattern.Include) - matches |= path.StartsWith(filePattern.Prefix); - else - matches &= !path.StartsWith(filePattern.Prefix); - } - return matches; - } + public bool Matches(string path) => FilePattern.Matches(filePatterns, path, out var _); } } From 37f1ce312256e36553c3951fba3c5c0c8f40ad9c Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 7 Sep 2020 08:57:49 +0200 Subject: [PATCH 004/185] C#: Implement support for path transformers --- .../Semmle.Extraction.CIL/Context.cs | 2 +- .../Entities/Assembly.cs | 10 +- .../Semmle.Extraction.CIL/Entities/File.cs | 27 +-- .../Semmle.Extraction.CIL/Entities/Folder.cs | 20 +- .../Semmle.Extraction.CIL/Factories.cs | 4 +- .../Semmle.Extraction.CSharp/Analyser.cs | 26 ++- .../Entities/Compilation.cs | 13 +- .../Semmle.Extraction.CSharp/Extractor.cs | 13 +- .../Semmle.Extraction.Tests/Layout.cs | 84 +++++---- .../PathTransformer.cs | 45 +++++ .../Semmle.Extraction.Tests/TrapWriter.cs | 27 +-- .../Semmle.Extraction/Entities/File.cs | 89 +++------ .../Semmle.Extraction/Entities/Folder.cs | 43 ++--- .../extractor/Semmle.Extraction/Extractor.cs | 12 +- csharp/extractor/Semmle.Extraction/Layout.cs | 13 +- .../Semmle.Extraction/PathTransformer.cs | 178 ++++++++++++++++++ .../extractor/Semmle.Extraction/TrapWriter.cs | 44 ++--- .../Semmle.Util/CanonicalPathCache.cs | 23 +++ 18 files changed, 442 insertions(+), 231 deletions(-) create mode 100644 csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs create mode 100644 csharp/extractor/Semmle.Extraction/PathTransformer.cs diff --git a/csharp/extractor/Semmle.Extraction.CIL/Context.cs b/csharp/extractor/Semmle.Extraction.CIL/Context.cs index 79fb8b7a8d4..6b8b01b109a 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Context.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Context.cs @@ -37,7 +37,7 @@ namespace Semmle.Extraction.CIL namespaceFactory = new CachedFunction(n => CreateNamespace(mdReader.GetString(n))); namespaceDefinitionFactory = new CachedFunction(CreateNamespace); sourceFiles = new CachedFunction(path => new Entities.PdbSourceFile(this, path)); - folders = new CachedFunction(path => new Entities.Folder(this, path)); + folders = new CachedFunction(path => new Entities.Folder(this, path)); sourceLocations = new CachedFunction(location => new Entities.PdbSourceLocation(this, location)); defaultGenericContext = new EmptyContext(this); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs index 255d37699f2..2504d7e7954 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs @@ -5,6 +5,7 @@ using Semmle.Util.Logging; using System; using Semmle.Extraction.Entities; using System.IO; +using Semmle.Util; namespace Semmle.Extraction.CIL.Entities { @@ -134,9 +135,12 @@ namespace Semmle.Extraction.CIL.Entities extracted = false; try { - var extractor = new Extractor(false, assemblyPath, logger); - var project = layout.LookupProjectOrDefault(assemblyPath); - using (var trapWriter = project.CreateTrapWriter(logger, assemblyPath + ".cil", true, trapCompression)) + var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); + var pathTransformer = new PathTransformer(canonicalPathCache); + var extractor = new Extractor(false, assemblyPath, logger, pathTransformer); + var transformedAssemblyPath = pathTransformer.Transform(assemblyPath); + var project = layout.LookupProjectOrDefault(transformedAssemblyPath); + using (var trapWriter = project.CreateTrapWriter(logger, transformedAssemblyPath.WithSuffix(".cil"), true, trapCompression)) { trapFile = trapWriter.TrapFile; if (nocache || !System.IO.File.Exists(trapFile)) diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs index bc8c4c8c76d..b164fad1acd 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs @@ -13,33 +13,38 @@ namespace Semmle.Extraction.CIL.Entities public class File : LabelledEntity, IFile { - protected readonly string path; + protected readonly string OriginalPath; + protected readonly PathTransformer.ITransformedPath TransformedPath; public File(Context cx, string path) : base(cx) { - this.path = Semmle.Extraction.Entities.File.PathAsDatabaseString(path); + this.OriginalPath = path; + TransformedPath = cx.cx.Extractor.PathTransformer.Transform(OriginalPath); } public override void WriteId(TextWriter trapFile) { - trapFile.Write(Semmle.Extraction.Entities.File.PathAsDatabaseId(path)); + trapFile.Write(TransformedPath.DatabaseId); } public override bool Equals(object obj) { - return GetType() == obj.GetType() && path == ((File)obj).path; + return GetType() == obj.GetType() && OriginalPath == ((File)obj).OriginalPath; } - public override int GetHashCode() => 11 * path.GetHashCode(); + public override int GetHashCode() => 11 * OriginalPath.GetHashCode(); public override IEnumerable Contents { get { - var parent = cx.CreateFolder(System.IO.Path.GetDirectoryName(path)); - yield return parent; - yield return Tuples.containerparent(parent, this); - yield return Tuples.files(this, path, System.IO.Path.GetFileNameWithoutExtension(path), System.IO.Path.GetExtension(path).Substring(1)); + if (TransformedPath.ParentDirectory is PathTransformer.ITransformedPath dir) + { + var parent = cx.CreateFolder(dir); + yield return parent; + yield return Tuples.containerparent(parent, this); + } + yield return Tuples.files(this, TransformedPath.Value, TransformedPath.NameWithoutExtension, TransformedPath.Extension); } } @@ -65,9 +70,9 @@ namespace Semmle.Extraction.CIL.Entities var text = file.Contents; if (text == null) - cx.cx.Extractor.Logger.Log(Util.Logging.Severity.Warning, string.Format("PDB source file {0} could not be found", path)); + cx.cx.Extractor.Logger.Log(Util.Logging.Severity.Warning, string.Format("PDB source file {0} could not be found", OriginalPath)); else - cx.cx.TrapWriter.Archive(path, text); + cx.cx.TrapWriter.Archive(TransformedPath, text); yield return Tuples.file_extraction_mode(this, 2); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs index 48ebe6a19d1..f95492bb0e4 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs @@ -9,16 +9,16 @@ namespace Semmle.Extraction.CIL.Entities public sealed class Folder : LabelledEntity, IFolder { - readonly string path; + readonly PathTransformer.ITransformedPath TransformedPath; - public Folder(Context cx, string path) : base(cx) + public Folder(Context cx, PathTransformer.ITransformedPath path) : base(cx) { - this.path = path; + this.TransformedPath = path; } public override void WriteId(TextWriter trapFile) { - trapFile.Write(Semmle.Extraction.Entities.File.PathAsDatabaseId(path)); + trapFile.Write(TransformedPath.DatabaseId); } public override string IdSuffix => ";folder"; @@ -27,25 +27,21 @@ namespace Semmle.Extraction.CIL.Entities { get { - // On Posix, we could get a Windows directory of the form "C:" - bool windowsDriveLetter = path.Length == 2 && char.IsLetter(path[0]) && path[1] == ':'; - - var parent = Path.GetDirectoryName(path); - if (parent != null && !windowsDriveLetter) + if (TransformedPath.ParentDirectory is PathTransformer.ITransformedPath parent) { var parentFolder = cx.CreateFolder(parent); yield return parentFolder; yield return Tuples.containerparent(parentFolder, this); } - yield return Tuples.folders(this, Semmle.Extraction.Entities.File.PathAsDatabaseString(path), Path.GetFileName(path)); + yield return Tuples.folders(this, TransformedPath.Value, TransformedPath.NameWithoutExtension); } } public override bool Equals(object obj) { - return obj is Folder folder && path == folder.path; + return obj is Folder folder && TransformedPath == folder.TransformedPath; } - public override int GetHashCode() => path.GetHashCode(); + public override int GetHashCode() => TransformedPath.GetHashCode(); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Factories.cs b/csharp/extractor/Semmle.Extraction.CIL/Factories.cs index f522521a845..5c37228a41e 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Factories.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Factories.cs @@ -201,7 +201,7 @@ namespace Semmle.Extraction.CIL #region Locations readonly CachedFunction sourceFiles; - readonly CachedFunction folders; + readonly CachedFunction folders; readonly CachedFunction sourceLocations; /// @@ -216,7 +216,7 @@ namespace Semmle.Extraction.CIL /// /// The path of the folder. /// A folder entity. - public Folder CreateFolder(string path) => folders[path]; + public Folder CreateFolder(PathTransformer.ITransformedPath path) => folders[path]; /// /// Creates a source location. diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs b/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs index 544a1819117..a2db3e4a2db 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs @@ -25,12 +25,15 @@ namespace Semmle.Extraction.CSharp public readonly ILogger Logger; - public Analyser(IProgressMonitor pm, ILogger logger) + public readonly PathTransformer PathTransformer; + + public Analyser(IProgressMonitor pm, ILogger logger, PathTransformer pathTransformer) { Logger = logger; Logger.Log(Severity.Info, "EXTRACTION STARTING at {0}", DateTime.Now); stopWatch.Start(); progressMonitor = pm; + PathTransformer = pathTransformer; } CSharpCompilation compilation; @@ -64,7 +67,7 @@ namespace Semmle.Extraction.CSharp layout = new Layout(); this.options = options; this.compilation = compilation; - extractor = new Extraction.Extractor(false, GetOutputName(compilation, commandLineArguments), Logger); + extractor = new Extraction.Extractor(false, GetOutputName(compilation, commandLineArguments), Logger, PathTransformer); LogDiagnostics(); SetReferencePaths(); @@ -114,7 +117,7 @@ namespace Semmle.Extraction.CSharp { compilation = compilationIn; layout = new Layout(); - extractor = new Extraction.Extractor(true, null, Logger); + extractor = new Extraction.Extractor(true, null, Logger, PathTransformer); this.options = options; LogExtractorInfo(Extraction.Extractor.Version); SetReferencePaths(); @@ -227,9 +230,10 @@ namespace Semmle.Extraction.CSharp try { var assemblyPath = extractor.OutputPath; + var transformedAssemblyPath = PathTransformer.Transform(assemblyPath); var assembly = compilation.Assembly; - var projectLayout = layout.LookupProjectOrDefault(assemblyPath); - var trapWriter = projectLayout.CreateTrapWriter(Logger, assemblyPath, true, options.TrapCompression); + var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath); + var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, true, options.TrapCompression); compilationTrapFile = trapWriter; // Dispose later var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath, true)); @@ -257,8 +261,9 @@ namespace Semmle.Extraction.CSharp stopwatch.Start(); var assemblyPath = r.FilePath; - var projectLayout = layout.LookupProjectOrDefault(assemblyPath); - using (var trapWriter = projectLayout.CreateTrapWriter(Logger, assemblyPath, true, options.TrapCompression)) + var transformedAssemblyPath = PathTransformer.Transform(assemblyPath); + var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath); + using (var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, true, options.TrapCompression)) { var skipExtraction = options.Cache && File.Exists(trapWriter.TrapFile); @@ -357,16 +362,17 @@ namespace Semmle.Extraction.CSharp var stopwatch = new Stopwatch(); stopwatch.Start(); var sourcePath = tree.FilePath; + var transformedSourcePath = PathTransformer.Transform(sourcePath); - var projectLayout = layout.LookupProjectOrNull(sourcePath); + var projectLayout = layout.LookupProjectOrNull(transformedSourcePath); bool excluded = projectLayout == null; - string trapPath = excluded ? "" : projectLayout.GetTrapPath(Logger, sourcePath, options.TrapCompression); + string trapPath = excluded ? "" : projectLayout.GetTrapPath(Logger, transformedSourcePath, options.TrapCompression); bool upToDate = false; if (!excluded) { // compilation.Clone() is used to allow symbols to be garbage collected. - using (var trapWriter = projectLayout.CreateTrapWriter(Logger, sourcePath, false, options.TrapCompression)) + using (var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedSourcePath, false, options.TrapCompression)) { upToDate = options.Fast && FileIsUpToDate(sourcePath, trapWriter.TrapFile); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs index b6ff91f5988..9f96b03f9f3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Semmle.Util; namespace Semmle.Extraction.CSharp.Entities { @@ -22,32 +23,32 @@ namespace Semmle.Extraction.CSharp.Entities { Extraction.Entities.Assembly.CreateOutputAssembly(cx); - trapFile.compilations(this, Extraction.Entities.File.PathAsDatabaseString(cwd)); + trapFile.compilations(this, FileUtils.ConvertToUnix(cwd)); // Arguments int index = 0; - foreach(var arg in args) + foreach (var arg in args) { trapFile.compilation_args(this, index++, arg); } // Files index = 0; - foreach(var file in cx.Compilation.SyntaxTrees.Select(tree => Extraction.Entities.File.Create(cx, tree.FilePath))) + foreach (var file in cx.Compilation.SyntaxTrees.Select(tree => Extraction.Entities.File.Create(cx, tree.FilePath))) { trapFile.compilation_compiling_files(this, index++, file); } // References index = 0; - foreach(var file in cx.Compilation.References.OfType().Select(r => Extraction.Entities.File.Create(cx, r.FilePath))) + foreach (var file in cx.Compilation.References.OfType().Select(r => Extraction.Entities.File.Create(cx, r.FilePath))) { trapFile.compilation_referencing_files(this, index++, file); } // Diagnostics index = 0; - foreach(var diag in cx.Compilation.GetDiagnostics().Select(d => new Diagnostic(cx, d))) + foreach (var diag in cx.Compilation.GetDiagnostics().Select(d => new Diagnostic(cx, d))) { trapFile.diagnostic_for(diag, this, 0, index++); } @@ -57,7 +58,7 @@ namespace Semmle.Extraction.CSharp.Entities { var trapFile = cx.TrapWriter.Writer; int index = 0; - foreach(float metric in p.Metrics) + foreach (float metric in p.Metrics) { trapFile.compilation_time(this, -1, index++, metric); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs index 7ca12e00f26..e775e422e8c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs @@ -76,16 +76,16 @@ namespace Semmle.Extraction.CSharp return ExitCode.Ok; } - using (var analyser = new Analyser(new LogProgressMonitor(logger), logger)) + var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); + var pathTransformer = new PathTransformer(canonicalPathCache); + + using (var analyser = new Analyser(new LogProgressMonitor(logger), logger, pathTransformer)) using (var references = new BlockingCollection()) { try { var compilerVersion = new CompilerVersion(commandLineArguments); - bool preserveSymlinks = Environment.GetEnvironmentVariable("SEMMLE_PRESERVE_SYMLINKS") == "true"; - var canonicalPathCache = CanonicalPathCache.Create(logger, 1000, preserveSymlinks ? CanonicalPathCache.Symlinks.Preserve : CanonicalPathCache.Symlinks.Follow); - if (compilerVersion.SkipExtraction) { logger.Log(Severity.Warning, " Unrecognized compiler '{0}' because {1}", compilerVersion.SpecifiedCompiler, compilerVersion.SkipReason); @@ -317,7 +317,10 @@ namespace Semmle.Extraction.CSharp ILogger logger, CommonOptions options) { - using (var analyser = new Analyser(pm, logger)) + var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); + var pathTransformer = new PathTransformer(canonicalPathCache); + + using (var analyser = new Analyser(pm, logger, pathTransformer)) using (var references = new BlockingCollection()) { try diff --git a/csharp/extractor/Semmle.Extraction.Tests/Layout.cs b/csharp/extractor/Semmle.Extraction.Tests/Layout.cs index 49da0fd45c5..301b2d8a881 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/Layout.cs +++ b/csharp/extractor/Semmle.Extraction.Tests/Layout.cs @@ -5,6 +5,26 @@ using System.Runtime.InteropServices; namespace Semmle.Extraction.Tests { + struct TransformedPathStub : PathTransformer.ITransformedPath + { + readonly string value; + public TransformedPathStub(string value) => this.value = value; + public string Value => value; + + public string Extension => throw new System.NotImplementedException(); + + public string NameWithoutExtension => throw new System.NotImplementedException(); + + public PathTransformer.ITransformedPath ParentDirectory => throw new System.NotImplementedException(); + + public string DatabaseId => throw new System.NotImplementedException(); + + public PathTransformer.ITransformedPath WithSuffix(string suffix) + { + throw new System.NotImplementedException(); + } + } + public class Layout { readonly ILogger Logger = new LoggerMock(); @@ -13,10 +33,10 @@ namespace Semmle.Extraction.Tests public void TestDefaultLayout() { var layout = new Semmle.Extraction.Layout(null, null, null); - var project = layout.LookupProjectOrNull("foo.cs"); + var project = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs")); // All files are mapped when there's no layout file. - Assert.True(layout.FileInLayout("foo.cs")); + Assert.True(layout.FileInLayout(new TransformedPathStub("foo.cs"))); // Test trap filename var tmpDir = Path.GetTempPath(); @@ -28,13 +48,13 @@ namespace Semmle.Extraction.Tests Assert.NotEqual(Directory.GetCurrentDirectory(), tmpDir); return; } - var f1 = project.GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip); - var g1 = TrapWriter.NestPaths(Logger, tmpDir, "foo.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE); + var f1 = project.GetTrapPath(Logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip); + var g1 = TrapWriter.NestPaths(Logger, tmpDir, "foo.cs.trap.gz"); Assert.Equal(f1, g1); // Test trap file generation - var trapwriterFilename = project.GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip); - using (var trapwriter = project.CreateTrapWriter(Logger, "foo.cs", false, TrapWriter.CompressionMode.Gzip)) + var trapwriterFilename = project.GetTrapPath(Logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip); + using (var trapwriter = project.CreateTrapWriter(Logger, new TransformedPathStub("foo.cs"), false, TrapWriter.CompressionMode.Gzip)) { trapwriter.Emit("1=*"); Assert.False(File.Exists(trapwriterFilename)); @@ -63,23 +83,23 @@ namespace Semmle.Extraction.Tests var layout = new Semmle.Extraction.Layout(null, null, "layout.txt"); // Test general pattern matching - Assert.True(layout.FileInLayout("bar.cs")); - Assert.False(layout.FileInLayout("foo.cs")); - Assert.False(layout.FileInLayout("goo.cs")); - Assert.False(layout.FileInLayout("excluded/bar.cs")); - Assert.True(layout.FileInLayout("excluded/foo.cs")); - Assert.True(layout.FileInLayout("included/foo.cs")); + Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs"))); + Assert.False(layout.FileInLayout(new TransformedPathStub("foo.cs"))); + Assert.False(layout.FileInLayout(new TransformedPathStub("goo.cs"))); + Assert.False(layout.FileInLayout(new TransformedPathStub("excluded/bar.cs"))); + Assert.True(layout.FileInLayout(new TransformedPathStub("excluded/foo.cs"))); + Assert.True(layout.FileInLayout(new TransformedPathStub("included/foo.cs"))); // Test the trap file - var project = layout.LookupProjectOrNull("bar.cs"); - var trapwriterFilename = project.GetTrapPath(Logger, "bar.cs", TrapWriter.CompressionMode.Gzip); - Assert.Equal(TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap"), "bar.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE), + var project = layout.LookupProjectOrNull(new TransformedPathStub("bar.cs")); + var trapwriterFilename = project.GetTrapPath(Logger, new TransformedPathStub("bar.cs"), TrapWriter.CompressionMode.Gzip); + Assert.Equal(TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap"), "bar.cs.trap.gz"), trapwriterFilename); // Test the source archive - var trapWriter = project.CreateTrapWriter(Logger, "bar.cs", false, TrapWriter.CompressionMode.Gzip); - trapWriter.Archive("layout.txt", System.Text.Encoding.ASCII); - var writtenFile = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\archive"), "layout.txt", TrapWriter.InnerPathComputation.ABSOLUTE); + var trapWriter = project.CreateTrapWriter(Logger, new TransformedPathStub("bar.cs"), false, TrapWriter.CompressionMode.Gzip); + trapWriter.Archive("layout.txt", new TransformedPathStub("layout.txt"), System.Text.Encoding.ASCII); + var writtenFile = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\archive"), "layout.txt"); Assert.True(File.Exists(writtenFile)); File.Delete("layout.txt"); } @@ -89,9 +109,9 @@ namespace Semmle.Extraction.Tests { // When you specify both a trap file and a layout, use the trap file. var layout = new Semmle.Extraction.Layout(Path.GetFullPath("snapshot\\trap"), null, "something.txt"); - Assert.True(layout.FileInLayout("bar.cs")); - var f1 = layout.LookupProjectOrNull("foo.cs").GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip); - var g1 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap"), "foo.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE); + Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs"))); + var f1 = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs")).GetTrapPath(Logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip); + var g1 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap"), "foo.cs.trap.gz"); Assert.Equal(f1, g1); } @@ -117,26 +137,26 @@ namespace Semmle.Extraction.Tests var layout = new Semmle.Extraction.Layout(null, null, "layout.txt"); // Use Section 2 - Assert.True(layout.FileInLayout("bar.cs")); - var f1 = layout.LookupProjectOrNull("bar.cs").GetTrapPath(Logger, "bar.cs", TrapWriter.CompressionMode.Gzip); - var g1 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap2"), "bar.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE); + Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs"))); + var f1 = layout.LookupProjectOrNull(new TransformedPathStub("bar.cs")).GetTrapPath(Logger, new TransformedPathStub("bar.cs"), TrapWriter.CompressionMode.Gzip); + var g1 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap2"), "bar.cs.trap.gz"); Assert.Equal(f1, g1); // Use Section 1 - Assert.True(layout.FileInLayout("foo.cs")); - var f2 = layout.LookupProjectOrNull("foo.cs").GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip); - var g2 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap1"), "foo.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE); + Assert.True(layout.FileInLayout(new TransformedPathStub("foo.cs"))); + var f2 = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs")).GetTrapPath(Logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip); + var g2 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap1"), "foo.cs.trap.gz"); Assert.Equal(f2, g2); // boo.dll is not in the layout, so use layout from first section. - Assert.False(layout.FileInLayout("boo.dll")); - var f3 = layout.LookupProjectOrDefault("boo.dll").GetTrapPath(Logger, "boo.dll", TrapWriter.CompressionMode.Gzip); - var g3 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap1"), "boo.dll.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE); + Assert.False(layout.FileInLayout(new TransformedPathStub("boo.dll"))); + var f3 = layout.LookupProjectOrDefault(new TransformedPathStub("boo.dll")).GetTrapPath(Logger, new TransformedPathStub("boo.dll"), TrapWriter.CompressionMode.Gzip); + var g3 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap1"), "boo.dll.trap.gz"); Assert.Equal(f3, g3); // boo.cs is not in the layout, so return null - Assert.False(layout.FileInLayout("boo.cs")); - Assert.Null(layout.LookupProjectOrNull("boo.cs")); + Assert.False(layout.FileInLayout(new TransformedPathStub("boo.cs"))); + Assert.Null(layout.LookupProjectOrNull(new TransformedPathStub("boo.cs"))); } [Fact] diff --git a/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs b/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs new file mode 100644 index 00000000000..04865ceb8c1 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs @@ -0,0 +1,45 @@ +using Semmle.Util; +using Xunit; + +namespace Semmle.Extraction.Tests +{ + class PathCacheStub : IPathCache + { + public string GetCanonicalPath(string path) => path; + } + + public class PathTransformerTests + { + [Fact] + public void TestTransformerFile() + { + var spec = new string[] + { + @"#D:\src", + @"C:\agent*\src//", + @"-C:\agent*\src\external", + @"", + @"#empty", + @"", + @"#src2", + @"/agent*//src", + @"", + @"#optsrc", + @"opt/src//" + }; + + var pathTransformer = new PathTransformer(new PathCacheStub(), spec); + + // Windows-style matching + Assert.Equal(@"C:\bar.cs", pathTransformer.Transform(@"C:\bar.cs").Value); + Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent42\src\file.cs").Value); + Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent43\src\file.cs").Value); + Assert.Equal(@"C:\agent43\src\external\file.cs", pathTransformer.Transform(@"C:\agent43\src\external\file.cs").Value); + + // Linux-style matching + Assert.Equal(@"src2/src/file.cs", pathTransformer.Transform(@"/agent/src/file.cs").Value); + Assert.Equal(@"src2/src/file.cs", pathTransformer.Transform(@"/agent42/src/file.cs").Value); + Assert.Equal(@"optsrc/file.cs", pathTransformer.Transform(@"/opt/src/file.cs").Value); + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs b/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs index fd7f77f427b..54da865689b 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs +++ b/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs @@ -14,7 +14,7 @@ namespace Semmle.Extraction.Tests string tempDir = System.IO.Path.GetTempPath(); string root1, root2, root3; - if(Win32.IsWindows()) + if (Win32.IsWindows()) { root1 = "E:"; root2 = "e:"; @@ -27,32 +27,21 @@ namespace Semmle.Extraction.Tests root3 = "/"; } - string formattedTempDir = tempDir.Replace('/', '\\').Replace(':', '_').Trim('\\'); - var logger = new LoggerMock(); - System.IO.Directory.SetCurrentDirectory(tempDir); - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - // `Directory.SetCurrentDirectory()` doesn't seem to work on macOS, - // so disable this test on macOS, for now - Assert.NotEqual(Directory.GetCurrentDirectory(), tempDir); - return; - } + Assert.Equal($@"C:\Temp\source_archive\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs").Replace('/', '\\')); - Assert.Equal($@"C:\Temp\source_archive\{formattedTempDir}\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/','\\')); + Assert.Equal(@"C:\Temp\source_archive\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs").Replace('/', '\\')); - Assert.Equal(@"C:\Temp\source_archive\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs", TrapWriter.InnerPathComputation.RELATIVE).Replace('/', '\\')); + Assert.Equal(@"C:\Temp\source_archive\E_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root1}\source\def.cs").Replace('/', '\\')); - Assert.Equal(@"C:\Temp\source_archive\E_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root1}\source\def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/', '\\')); + Assert.Equal(@"C:\Temp\source_archive\e_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root2}\source\def.cs").Replace('/', '\\')); - Assert.Equal(@"C:\Temp\source_archive\e_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root2}\source\def.cs", TrapWriter.InnerPathComputation.RELATIVE).Replace('/', '\\')); + Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs").Replace('/', '\\')); - Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/', '\\')); + Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs").Replace('/', '\\')); - Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs", TrapWriter.InnerPathComputation.RELATIVE).Replace('/', '\\')); - - Assert.Equal(@"C:\Temp\source_archive\diskstation\share\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}{root3}diskstation\share\source\def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/', '\\')); + Assert.Equal(@"C:\Temp\source_archive\diskstation\share\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}{root3}diskstation\share\source\def.cs").Replace('/', '\\')); } class LoggerMock : ILogger diff --git a/csharp/extractor/Semmle.Extraction/Entities/File.cs b/csharp/extractor/Semmle.Extraction/Entities/File.cs index cbdf1535fbb..a0e9ea6d725 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/File.cs @@ -10,93 +10,54 @@ namespace Semmle.Extraction.Entities File(Context cx, string path) : base(cx, path) { - Path = path; + OriginalPath = path; + TransformedPath = Context.Extractor.PathTransformer.Transform(OriginalPath); } - public string Path - { - get; - private set; - } + readonly string OriginalPath; + readonly PathTransformer.ITransformedPath TransformedPath; - public string DatabasePath => PathAsDatabaseId(Path); - - public override bool NeedsPopulation => Context.DefinesFile(Path) || Path == Context.Extractor.OutputPath; + public override bool NeedsPopulation => Context.DefinesFile(OriginalPath) || OriginalPath == Context.Extractor.OutputPath; public override void Populate(TextWriter trapFile) { - if (Path == null) + trapFile.files(this, TransformedPath.Value, TransformedPath.NameWithoutExtension, TransformedPath.Extension); + + if (TransformedPath.ParentDirectory is PathTransformer.ITransformedPath dir) + trapFile.containerparent(Folder.Create(Context, dir), this); + + var fromSource = TransformedPath.Extension.ToLowerInvariant().Equals("cs"); + if (fromSource) { - trapFile.files(this, "", "", ""); - } - else - { - var fi = new FileInfo(Path); - - string extension = fi.Extension ?? ""; - string name = fi.Name; - name = name.Substring(0, name.Length - extension.Length); - int fromSource = extension.ToLowerInvariant().Equals(".cs") ? 1 : 2; - - // remove the dot from the extension - if (extension.Length > 0) - extension = extension.Substring(1); - trapFile.files(this, PathAsDatabaseString(Path), name, extension); - - trapFile.containerparent(Folder.Create(Context, fi.Directory), this); - if (fromSource == 1) + foreach (var text in Context.Compilation.SyntaxTrees. + Where(t => t.FilePath == OriginalPath). + Select(tree => tree.GetText())) { - foreach (var text in Context.Compilation.SyntaxTrees. - Where(t => t.FilePath == Path). - Select(tree => tree.GetText())) - { - var rawText = text.ToString() ?? ""; - var lineCounts = LineCounter.ComputeLineCounts(rawText); - if (rawText.Length > 0 && rawText[rawText.Length - 1] != '\n') lineCounts.Total++; + var rawText = text.ToString() ?? ""; + var lineCounts = LineCounter.ComputeLineCounts(rawText); + if (rawText.Length > 0 && rawText[rawText.Length - 1] != '\n') lineCounts.Total++; - trapFile.numlines(this, lineCounts); - Context.TrapWriter.Archive(fi.FullName, text.Encoding ?? System.Text.Encoding.Default); - } + trapFile.numlines(this, lineCounts); + Context.TrapWriter.Archive(OriginalPath, TransformedPath, text.Encoding ?? System.Text.Encoding.Default); } - - trapFile.file_extraction_mode(this, Context.Extractor.Standalone ? 1 : 0); } + + trapFile.file_extraction_mode(this, Context.Extractor.Standalone ? 1 : 0); } public override void WriteId(System.IO.TextWriter trapFile) { - if (Path is null) - trapFile.Write("GENERATED;sourcefile"); - else - { - trapFile.Write(DatabasePath); - trapFile.Write(";sourcefile"); - } + trapFile.Write(TransformedPath.DatabaseId); + trapFile.Write(";sourcefile"); } - /// - /// Converts a path string into a string to use as an ID - /// in the QL database. - /// - /// An absolute path. - /// The database ID. - public static string PathAsDatabaseId(string path) - { - if (path.Length >= 2 && path[1] == ':' && Char.IsLower(path[0])) - path = Char.ToUpper(path[0]) + "_" + path.Substring(2); - return path.Replace('\\', '/').Replace(":", "_"); - } - - public static string PathAsDatabaseString(string path) => path.Replace('\\', '/'); - public static File Create(Context cx, string path) => FileFactory.Instance.CreateEntity(cx, path); public static File CreateGenerated(Context cx) => GeneratedFile.Create(cx); class GeneratedFile : File { - GeneratedFile(Context cx) - : base(cx, "") { } + GeneratedFile(Context cx) : base(cx, "") { } public override bool NeedsPopulation => true; diff --git a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs index 3c29ee38bd9..0e275e37d8d 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs @@ -2,65 +2,44 @@ using System.IO; namespace Semmle.Extraction.Entities { - sealed class Folder : CachedEntity + sealed class Folder : CachedEntity { - Folder(Context cx, DirectoryInfo init) - : base(cx, init) - { - Path = init.FullName; - } - - public string Path - { - get; - private set; - } - - public string DatabasePath => File.PathAsDatabaseId(Path); + Folder(Context cx, PathTransformer.ITransformedPath init) : base(cx, init) { } public override void Populate(TextWriter trapFile) { - // Ensure that the name of the root directory is consistent - // with the XmlTrapWriter. - // Linux/Windows: java.io.File.getName() returns "" - // On Linux: System.IO.DirectoryInfo.Name returns "/" - // On Windows: System.IO.DirectoryInfo.Name returns "L:\" - string shortName = symbol.Parent == null ? "" : symbol.Name; - - trapFile.folders(this, File.PathAsDatabaseString(Path), shortName); - if (symbol.Parent != null) - { - trapFile.containerparent(Create(Context, symbol.Parent), this); - } + trapFile.folders(this, symbol.Value, symbol.NameWithoutExtension); + if (symbol.ParentDirectory is PathTransformer.ITransformedPath parent) + trapFile.containerparent(Create(Context, parent), this); } public override bool NeedsPopulation => true; public override void WriteId(System.IO.TextWriter trapFile) { - trapFile.Write(DatabasePath); + trapFile.Write(symbol.DatabaseId); trapFile.Write(";folder"); } - public static Folder Create(Context cx, DirectoryInfo folder) => + public static Folder Create(Context cx, PathTransformer.ITransformedPath folder) => FolderFactory.Instance.CreateEntity2(cx, folder); public override Microsoft.CodeAnalysis.Location? ReportingLocation => null; - class FolderFactory : ICachedEntityFactory + class FolderFactory : ICachedEntityFactory { public static readonly FolderFactory Instance = new FolderFactory(); - public Folder Create(Context cx, DirectoryInfo init) => new Folder(cx, init); + public Folder Create(Context cx, PathTransformer.ITransformedPath init) => new Folder(cx, init); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel; - public override int GetHashCode() => Path.GetHashCode(); + public override int GetHashCode() => symbol.GetHashCode(); public override bool Equals(object? obj) { - return obj is Folder folder && folder.Path == Path; + return obj is Folder folder && Equals(folder.symbol, symbol); } } } diff --git a/csharp/extractor/Semmle.Extraction/Extractor.cs b/csharp/extractor/Semmle.Extraction/Extractor.cs index 13750c1aa5c..7b176f20c9f 100644 --- a/csharp/extractor/Semmle.Extraction/Extractor.cs +++ b/csharp/extractor/Semmle.Extraction/Extractor.cs @@ -81,6 +81,11 @@ namespace Semmle.Extraction /// ILogger Logger { get; } + /// + /// The path transformer to apply. + /// + PathTransformer PathTransformer { get; } + /// /// Creates a new context. /// @@ -111,11 +116,14 @@ namespace Semmle.Extraction /// /// If the extraction is standalone. /// The name of the output DLL/EXE, or null if not specified (standalone extraction). - public Extractor(bool standalone, string outputPath, ILogger logger) + /// The object used for logging. + /// The object used for path transformations. + public Extractor(bool standalone, string outputPath, ILogger logger, PathTransformer pathTransformer) { Standalone = standalone; OutputPath = outputPath; Logger = logger; + PathTransformer = pathTransformer; } // Limit the number of error messages in the log file @@ -205,5 +213,7 @@ namespace Semmle.Extraction public ILogger Logger { get; private set; } public static string Version => $"{ThisAssembly.Git.BaseTag} ({ThisAssembly.Git.Sha})"; + + public PathTransformer PathTransformer { get; } } } diff --git a/csharp/extractor/Semmle.Extraction/Layout.cs b/csharp/extractor/Semmle.Extraction/Layout.cs index 1e44c3142e4..d740d2c05b9 100644 --- a/csharp/extractor/Semmle.Extraction/Layout.cs +++ b/csharp/extractor/Semmle.Extraction/Layout.cs @@ -54,14 +54,15 @@ namespace Semmle.Extraction /// /// The source file. /// The full filepath of the trap file. - public string GetTrapPath(ILogger logger, string srcFile, TrapWriter.CompressionMode trapCompression) => TrapWriter.TrapPath(logger, TRAP_FOLDER, srcFile, trapCompression); + public string GetTrapPath(ILogger logger, PathTransformer.ITransformedPath srcFile, TrapWriter.CompressionMode trapCompression) => + TrapWriter.TrapPath(logger, TRAP_FOLDER, srcFile, trapCompression); /// /// Creates a trap writer for a given source/assembly file. /// /// The source file. /// A newly created TrapWriter. - public TrapWriter CreateTrapWriter(ILogger logger, string srcFile, bool discardDuplicates, TrapWriter.CompressionMode trapCompression) => + public TrapWriter CreateTrapWriter(ILogger logger, PathTransformer.ITransformedPath srcFile, bool discardDuplicates, TrapWriter.CompressionMode trapCompression) => new TrapWriter(logger, srcFile, TRAP_FOLDER, SOURCE_ARCHIVE, discardDuplicates, trapCompression); } @@ -73,7 +74,7 @@ namespace Semmle.Extraction /// /// The file to look up. /// The relevant subproject, or null if not found. - public SubProject? LookupProjectOrNull(string sourceFile) + public SubProject? LookupProjectOrNull(PathTransformer.ITransformedPath sourceFile) { if (!useLayoutFile) return DefaultProject; @@ -89,7 +90,7 @@ namespace Semmle.Extraction /// /// The file to look up. /// The relevant subproject, or DefaultProject if not found. - public SubProject LookupProjectOrDefault(string sourceFile) + public SubProject LookupProjectOrDefault(PathTransformer.ITransformedPath sourceFile) { return LookupProjectOrNull(sourceFile) ?? DefaultProject; } @@ -134,7 +135,7 @@ namespace Semmle.Extraction /// /// The absolute path of the file to query. /// True iff there is no layout file or the layout file specifies the file. - public bool FileInLayout(string path) => LookupProjectOrNull(path) != null; + public bool FileInLayout(PathTransformer.ITransformedPath path) => LookupProjectOrNull(path) != null; void ReadLayoutFile(string layout) { @@ -197,6 +198,6 @@ namespace Semmle.Extraction } } - public bool Matches(string path) => FilePattern.Matches(filePatterns, path, out var _); + public bool Matches(PathTransformer.ITransformedPath path) => FilePattern.Matches(filePatterns, path.Value, out var _); } } diff --git a/csharp/extractor/Semmle.Extraction/PathTransformer.cs b/csharp/extractor/Semmle.Extraction/PathTransformer.cs new file mode 100644 index 00000000000..3c972f48114 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction/PathTransformer.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Diagnostics.CodeAnalysis; +using Semmle.Util; + +namespace Semmle.Extraction +{ + /// + /// A class for interpreting path transformers specified using the environment + /// variable `CODEQL_PATH_TRANSFORMER`. + /// + public sealed class PathTransformer + { + public class InvalidPathTransformerException : Exception + { + public InvalidPathTransformerException(string message) : + base($"Invalid path transformer specification: {message}") + { } + } + + /// + /// A transformed path. + /// + public interface ITransformedPath + { + string Value { get; } + + string Extension { get; } + + string NameWithoutExtension { get; } + + ITransformedPath? ParentDirectory { get; } + + ITransformedPath WithSuffix(string suffix); + + string DatabaseId { get; } + } + + struct TransformedPath : ITransformedPath + { + public TransformedPath(string value) { this.value = value; } + readonly string value; + + public string Value => value; + + public string Extension => Path.GetExtension(value)?.Substring(1) ?? ""; + + public string NameWithoutExtension => Path.GetFileNameWithoutExtension(value); + + public ITransformedPath? ParentDirectory + { + get + { + var dir = Path.GetDirectoryName(value); + if (dir is null) + return null; + var isWindowsDriveLetter = dir.Length == 2 && char.IsLetter(dir[0]) && dir[1] == ':'; + if (isWindowsDriveLetter) + return null; + return new TransformedPath(FileUtils.ConvertToUnix(dir)); + } + } + + public ITransformedPath WithSuffix(string suffix) => new TransformedPath(value + suffix); + + public string DatabaseId + { + get + { + var ret = value; + if (ret.Length >= 2 && ret[1] == ':' && Char.IsLower(ret[0])) + ret = Char.ToUpper(ret[0]) + "_" + ret.Substring(2); + return ret.Replace('\\', '/').Replace(":", "_"); + } + } + + public override int GetHashCode() => 11 * value.GetHashCode(); + + public override bool Equals(object? obj) => obj is TransformedPath tp && tp.value == value; + + public override string ToString() => value; + } + + readonly Func transform; + + /// + /// Returns the path obtained by transforming `path`. + /// + public ITransformedPath Transform(string path) => new TransformedPath(transform(path)); + + /// + /// Default constructor reads parameters from the environment. + /// + public PathTransformer(IPathCache pathCache) : + this(pathCache, Environment.GetEnvironmentVariable("CODEQL_PATH_TRANSFORMER") is string file ? File.ReadAllLines(file) : null) + { + } + + /// + /// Creates a path transformer based on the specification in `lines`. + /// Throws `InvalidPathTransformerException` for invalid specifications. + /// + public PathTransformer(IPathCache pathCache, string[]? lines) + { + if (lines is null) + { + transform = path => FileUtils.ConvertToUnix(pathCache.GetCanonicalPath(path)); + return; + } + + var sections = ParsePathTransformerSpec(lines); + transform = path => + { + path = FileUtils.ConvertToUnix(pathCache.GetCanonicalPath(path)); + foreach (var section in sections) + { + if (section.Matches(path, out var transformed)) + return transformed; + } + return path; + }; + } + + static IEnumerable ParsePathTransformerSpec(string[] lines) + { + var sections = new List(); + try + { + int i = 0; + while (i < lines.Length && !lines[i].StartsWith("#")) + i++; + while (i < lines.Length) + { + var section = new TransformerSection(lines, ref i); + sections.Add(section); + } + + if (sections.Count == 0) + throw new InvalidPathTransformerException("contains no sections."); + } + catch (InvalidFilePatternException ex) + { + throw new InvalidPathTransformerException(ex.Message); + } + return sections; + } + } + + sealed class TransformerSection + { + readonly string name; + readonly List filePatterns = new List(); + + public TransformerSection(string[] lines, ref int i) + { + name = lines[i++].Substring(1); + while (i < lines.Length && !lines[i].StartsWith("#")) + { + if (string.IsNullOrEmpty(lines[i])) + i++; + else + filePatterns.Add(new FilePattern(lines[i++])); + } + } + + public bool Matches(string path, [NotNullWhen(true)] out string? transformed) + { + if (FilePattern.Matches(filePatterns, path, out var suffix)) + { + transformed = FileUtils.ConvertToUnix(name) + suffix; + return true; + } + transformed = null; + return false; + } + } +} diff --git a/csharp/extractor/Semmle.Extraction/TrapWriter.cs b/csharp/extractor/Semmle.Extraction/TrapWriter.cs index 7ea08eafc1c..8082567c825 100644 --- a/csharp/extractor/Semmle.Extraction/TrapWriter.cs +++ b/csharp/extractor/Semmle.Extraction/TrapWriter.cs @@ -14,12 +14,6 @@ namespace Semmle.Extraction public sealed class TrapWriter : IDisposable { - public enum InnerPathComputation - { - ABSOLUTE, - RELATIVE - } - public enum CompressionMode { None, @@ -45,7 +39,7 @@ namespace Semmle.Extraction readonly CompressionMode TrapCompression; - public TrapWriter(ILogger logger, string outputfile, string? trap, string? archive, bool discardDuplicates, CompressionMode trapCompression) + public TrapWriter(ILogger logger, PathTransformer.ITransformedPath outputfile, string? trap, string? archive, bool discardDuplicates, CompressionMode trapCompression) { Logger = logger; TrapCompression = trapCompression; @@ -107,16 +101,17 @@ namespace Semmle.Extraction /// Adds the specified input file to the source archive. It may end up in either the normal or long path area /// of the source archive, depending on the length of its full path. /// - /// The path to the input file. + /// The path to the input file. + /// The transformed path to the input file. /// The encoding used by the input file. - public void Archive(string inputPath, Encoding inputEncoding) + public void Archive(string originalPath, PathTransformer.ITransformedPath transformedPath, Encoding inputEncoding) { if (string.IsNullOrEmpty(archive)) return; // Calling GetFullPath makes this use the canonical capitalisation, if the file exists. - string fullInputPath = Path.GetFullPath(inputPath); + string fullInputPath = Path.GetFullPath(originalPath); - ArchivePath(fullInputPath, inputEncoding); + ArchivePath(fullInputPath, transformedPath, inputEncoding); } /// @@ -124,14 +119,11 @@ namespace Semmle.Extraction /// /// The path of the file. /// The contents of the file. - public void Archive(string inputPath, string contents) + public void Archive(PathTransformer.ITransformedPath inputPath, string contents) { if (string.IsNullOrEmpty(archive)) return; - // Calling GetFullPath makes this use the canonical capitalisation, if the file exists. - string fullInputPath = Path.GetFullPath(inputPath); - - ArchiveContents(fullInputPath, contents); + ArchiveContents(inputPath, contents); } /// @@ -210,18 +202,19 @@ namespace Semmle.Extraction /// source archive less than the system path limit of 260 characters. /// /// The full path to the input file. + /// The transformed path to the input file. /// The encoding used by the input file. /// If the output path in the source archive would /// exceed the system path limit of 260 characters. - private void ArchivePath(string fullInputPath, Encoding inputEncoding) + private void ArchivePath(string fullInputPath, PathTransformer.ITransformedPath transformedPath, Encoding inputEncoding) { string contents = File.ReadAllText(fullInputPath, inputEncoding); - ArchiveContents(fullInputPath, contents); + ArchiveContents(transformedPath, contents); } - private void ArchiveContents(string fullInputPath, string contents) + private void ArchiveContents(PathTransformer.ITransformedPath transformedPath, string contents) { - string dest = NestPaths(Logger, archive, fullInputPath, InnerPathComputation.ABSOLUTE); + string dest = NestPaths(Logger, archive, transformedPath.Value); string tmpSrcFile = Path.GetTempFileName(); File.WriteAllText(tmpSrcFile, contents, UTF8); try @@ -236,14 +229,11 @@ namespace Semmle.Extraction } } - public static string NestPaths(ILogger logger, string? outerpath, string innerpath, InnerPathComputation innerPathComputation) + public static string NestPaths(ILogger logger, string? outerpath, string innerpath) { string nested = innerpath; if (!string.IsNullOrEmpty(outerpath)) { - if (!Path.IsPathRooted(innerpath) && innerPathComputation == InnerPathComputation.ABSOLUTE) - innerpath = Path.GetFullPath(innerpath); - // Remove all leading path separators / or \ // For example, UNC paths have two leading \\ innerpath = innerpath.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); @@ -276,13 +266,13 @@ namespace Semmle.Extraction } } - public static string TrapPath(ILogger logger, string? folder, string filename, TrapWriter.CompressionMode trapCompression) + public static string TrapPath(ILogger logger, string? folder, PathTransformer.ITransformedPath path, TrapWriter.CompressionMode trapCompression) { - filename = $"{Path.GetFullPath(filename)}.trap{TrapExtension(trapCompression)}"; + var filename = $"{path.Value}.trap{TrapExtension(trapCompression)}"; if (string.IsNullOrEmpty(folder)) folder = Directory.GetCurrentDirectory(); - return NestPaths(logger, folder, filename, InnerPathComputation.ABSOLUTE); ; + return NestPaths(logger, folder, filename); } } } diff --git a/csharp/extractor/Semmle.Util/CanonicalPathCache.cs b/csharp/extractor/Semmle.Util/CanonicalPathCache.cs index bbc8ab995b4..339641ecb35 100644 --- a/csharp/extractor/Semmle.Util/CanonicalPathCache.cs +++ b/csharp/extractor/Semmle.Util/CanonicalPathCache.cs @@ -222,6 +222,29 @@ namespace Semmle.Util this.pathStrategy = pathStrategy; } + + /// + /// Create a CanonicalPathCache. + /// + /// + /// + /// Creates the appropriate PathStrategy object which encapsulates + /// the correct algorithm. Falls back to different implementations + /// depending on platform. + /// + /// + /// Size of the cache. + /// Policy for following symlinks. + /// A new CanonicalPathCache. + public static CanonicalPathCache Create(ILogger logger, int maxCapacity) + { + var preserveSymlinks = + Environment.GetEnvironmentVariable("CODEQL_PRESERVE_SYMLINKS") == "true" || + Environment.GetEnvironmentVariable("SEMMLE_PRESERVE_SYMLINKS") == "true"; + return Create(logger, maxCapacity, preserveSymlinks ? CanonicalPathCache.Symlinks.Preserve : CanonicalPathCache.Symlinks.Follow); + + } + /// /// Create a CanonicalPathCache. /// From 4d0a1ee8578a061f3286b6e4b7a626e4d9211921 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 8 Sep 2020 09:29:35 +0200 Subject: [PATCH 005/185] Address review comments --- .../PathTransformer.cs | 4 +-- .../Semmle.Extraction/FilePattern.cs | 34 +++++++++++-------- .../Semmle.Extraction/PathTransformer.cs | 11 +++--- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs b/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs index 04865ceb8c1..b0f0ba8c51f 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs +++ b/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs @@ -31,10 +31,10 @@ namespace Semmle.Extraction.Tests var pathTransformer = new PathTransformer(new PathCacheStub(), spec); // Windows-style matching - Assert.Equal(@"C:\bar.cs", pathTransformer.Transform(@"C:\bar.cs").Value); + Assert.Equal(@"C:/bar.cs", pathTransformer.Transform(@"C:\bar.cs").Value); Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent42\src\file.cs").Value); Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent43\src\file.cs").Value); - Assert.Equal(@"C:\agent43\src\external\file.cs", pathTransformer.Transform(@"C:\agent43\src\external\file.cs").Value); + Assert.Equal(@"C:/agent43/src/external/file.cs", pathTransformer.Transform(@"C:\agent43\src\external\file.cs").Value); // Linux-style matching Assert.Equal(@"src2/src/file.cs", pathTransformer.Transform(@"/agent/src/file.cs").Value); diff --git a/csharp/extractor/Semmle.Extraction/FilePattern.cs b/csharp/extractor/Semmle.Extraction/FilePattern.cs index c648faadbe1..2d61badd2f2 100644 --- a/csharp/extractor/Semmle.Extraction/FilePattern.cs +++ b/csharp/extractor/Semmle.Extraction/FilePattern.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Diagnostics.CodeAnalysis; @@ -15,7 +16,7 @@ namespace Semmle.Extraction } /// - /// An file pattern, as used in either an extractor layout file or + /// A file pattern, as used in either an extractor layout file or /// a path transformer file. /// public sealed class FilePattern @@ -27,11 +28,12 @@ namespace Semmle.Extraction public FilePattern(string pattern) { - Include = false; + Include = true; if (pattern.StartsWith("-")) + { pattern = pattern.Substring(1); - else - Include = true; + Include = false; + } pattern = FileUtils.ConvertToUnix(pattern.Trim()).TrimStart('/'); RegexPattern = BuildRegex(pattern).ToString(); } @@ -103,19 +105,23 @@ namespace Semmle.Extraction public static bool Matches(IEnumerable patterns, string path, [NotNullWhen(true)] out string? transformerSuffix) { path = FileUtils.ConvertToUnix(path).TrimStart('/'); - Match? lastMatch = null; - foreach (var pattern in patterns) + + foreach (var pattern in patterns.Reverse()) { var m = new Regex(pattern.RegexPattern).Match(path); if (m.Success) - lastMatch = pattern.Include ? m : null; - } - if (lastMatch is Match) - { - transformerSuffix = lastMatch.Groups.TryGetValue("doubleslash", out var group) - ? path.Substring(group.Index) - : path; - return true; + { + if (pattern.Include) + { + transformerSuffix = m.Groups.TryGetValue("doubleslash", out var group) + ? path.Substring(group.Index) + : path; + return true; + } + + transformerSuffix = null; + return false; + } } transformerSuffix = null; diff --git a/csharp/extractor/Semmle.Extraction/PathTransformer.cs b/csharp/extractor/Semmle.Extraction/PathTransformer.cs index 3c972f48114..2c9770e790e 100644 --- a/csharp/extractor/Semmle.Extraction/PathTransformer.cs +++ b/csharp/extractor/Semmle.Extraction/PathTransformer.cs @@ -154,13 +154,12 @@ namespace Semmle.Extraction public TransformerSection(string[] lines, ref int i) { - name = lines[i++].Substring(1); - while (i < lines.Length && !lines[i].StartsWith("#")) + name = lines[i++].Substring(1); // skip the '#' + for (; i < lines.Length && !lines[i].StartsWith("#"); i++) { - if (string.IsNullOrEmpty(lines[i])) - i++; - else - filePatterns.Add(new FilePattern(lines[i++])); + var line = lines[i]; + if (!string.IsNullOrWhiteSpace(line)) + filePatterns.Add(new FilePattern(line)); } } From b1e6e3a6f23c9f6c03c4f8b752aa5f6a38112da1 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 8 Sep 2020 14:18:20 +0200 Subject: [PATCH 006/185] Java: Add 1.25 change notes. --- change-notes/1.25/analysis-java.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/change-notes/1.25/analysis-java.md b/change-notes/1.25/analysis-java.md index 7cdd9e491a2..5adb02e4d47 100644 --- a/change-notes/1.25/analysis-java.md +++ b/change-notes/1.25/analysis-java.md @@ -4,6 +4,8 @@ The following changes in version 1.25 affect Java analysis in all applications. ## General improvements +The Java autobuilder has been improved to detect more Gradle Java versions. + ## New queries | **Query** | **Tags** | **Purpose** | @@ -14,10 +16,20 @@ The following changes in version 1.25 affect Java analysis in all applications. | **Query** | **Expected impact** | **Change** | |------------------------------|------------------------|-----------------------------------| - +| Hard-coded credential in API call (`java/hardcoded-credential-api-call`) | More results | The query now recognizes the `BasicAWSCredentials` class of the Amazon client SDK library with hardcoded access key/secret key. | +| Deserialization of user-controlled data (`java/unsafe-deserialization`) | Fewer false positive results | The query no longer reports results using `org.apache.commons.io.serialization.ValidatingObjectInputStream`. | +| Use of a broken or risky cryptographic algorithm (`java/weak-cryptographic-algorithm`) | More results | The query now recognizes the `MessageDigest.getInstance` method. | +| Use of a potentially broken or risky cryptographic algorithm (`java/potentially-weak-cryptographic-algorithm`) | More results | The query now recognizes the `MessageDigest.getInstance` method. | +| Reading from a world writable file (`java/world-writable-file-read`) | More results | The query now recognizes more JDK file operations. | ## Changes to libraries +* The data-flow library has been improved with more taint flow modeling for the + Collections framework and other classes of the JDK. This affects all security + queries using data flow and can yield additional results. +* The data-flow library has been improved with more taint flow modeling for the + Spring framework. This affects all security queries using data flow and can + yield additional results on project that rely on the Spring framework. * The data-flow library has been improved, which affects most security queries by potentially adding more results. Flow through methods now takes nested field reads/writes into account. For example, the library is able to track flow from `"taint"` to `sink()` via the method @@ -39,3 +51,5 @@ The following changes in version 1.25 affect Java analysis in all applications. } } ``` +* The library has been extended with more support for Java 14 features + (`switch` expressions and pattern-matching for `instanceof`). From 2979f9813ee0ba08779623a72106f00ab35e30d8 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 8 Sep 2020 14:27:12 +0200 Subject: [PATCH 007/185] Python: Add missing change notes I looked through PRs between rc/1.24 and rc/1.25 and added missing change notes for: - https://github.com/github/codeql/pull/3314 - https://github.com/github/codeql/pull/3302 - https://github.com/github/codeql/pull/3212 - https://github.com/github/codeql/pull/3453 - https://github.com/github/codeql/pull/3407 - https://github.com/github/codeql/pull/3563 ``` git log --grep="Merge pull request" --format=oneline rc/1.24..rc/1.25 -- python/ ``` --- change-notes/1.25/analysis-python.md | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/change-notes/1.25/analysis-python.md b/change-notes/1.25/analysis-python.md index 5d0fc69ec80..345cce5a227 100644 --- a/change-notes/1.25/analysis-python.md +++ b/change-notes/1.25/analysis-python.md @@ -1,22 +1,9 @@ # Improvements to Python analysis -The following changes in version 1.25 affect Python analysis in all applications. - -## General improvements - - -## New queries - -| **Query** | **Tags** | **Purpose** | -|-----------------------------|-----------|--------------------------------------------------------------------| - - -## Changes to existing queries - -| **Query** | **Expected impact** | **Change** | -|----------------------------|------------------------|------------------------------------------------------------------| - - -## Changes to libraries - * Importing `semmle.python.web.HttpRequest` will no longer import `UntrustedStringKind` transitively. `UntrustedStringKind` is the most commonly used non-abstract subclass of `ExternalStringKind`. If not imported (by one mean or another), taint-tracking queries that concern `ExternalStringKind` will not produce any results. Please ensure such queries contain an explicit import (`import semmle.python.security.strings.Untrusted`). +* Added model of taint sources for HTTP servers using `http.server`. +* Added taint modeling of routed parameters in flask. +* Improved modeling of builtin methods on strings for taint tracking. +* Improved classification of test files. +* New class `BoundMethodValue` exposing information about a bound method. +* The query `py/command-line-injection` now recognizes command execution with the `fabric` and `invoke` Python libraries. From 02da80aa25fc7690bef9a4c77c057571b2c27406 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 8 Sep 2020 14:40:33 +0200 Subject: [PATCH 008/185] Java: Remove "New Queries" section. --- change-notes/1.25/analysis-java.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/change-notes/1.25/analysis-java.md b/change-notes/1.25/analysis-java.md index 5adb02e4d47..ab11e5aaaf1 100644 --- a/change-notes/1.25/analysis-java.md +++ b/change-notes/1.25/analysis-java.md @@ -6,12 +6,6 @@ The following changes in version 1.25 affect Java analysis in all applications. The Java autobuilder has been improved to detect more Gradle Java versions. -## New queries - -| **Query** | **Tags** | **Purpose** | -|-----------------------------|-----------|--------------------------------------------------------------------| - - ## Changes to existing queries | **Query** | **Expected impact** | **Change** | From 1f4028f4a08a7faa99b614a6af6ecc36ef2eeb66 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 8 Sep 2020 16:24:57 +0200 Subject: [PATCH 009/185] Java: Add new SQL sinks for Hibernate versions 4 and 6 --- .../semmle/code/java/frameworks/Hibernate.qll | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/java/ql/src/semmle/code/java/frameworks/Hibernate.qll b/java/ql/src/semmle/code/java/frameworks/Hibernate.qll index 1366b3cac45..5ad0448249f 100644 --- a/java/ql/src/semmle/code/java/frameworks/Hibernate.qll +++ b/java/ql/src/semmle/code/java/frameworks/Hibernate.qll @@ -4,20 +4,36 @@ import java +/** The interface `org.hibernate.query.QueryProducer`. */ +class HibernateQueryProducer extends RefType { + HibernateQueryProducer() { this.hasQualifiedName("org.hibernate.query", "QueryProducer") } +} + +/** The interface `org.hibernate.SharedSessionContract`. */ +class HibernateSharedSessionContract extends RefType { + HibernateSharedSessionContract() { + this.hasQualifiedName("org.hibernate", "SharedSessionContract") + } +} + /** The interface `org.hibernate.Session`. */ class HibernateSession extends RefType { HibernateSession() { this.hasQualifiedName("org.hibernate", "Session") } } /** - * Holds if `m` is a method on `HibernateSession`, or a subclass, taking an SQL - * string as its first argument. + * Holds if `m` is a method on `HibernateQueryProducer`, or `HibernateSharedSessionContract` + * or `HibernateSession`, or a subclass, taking an SQL string as its first argument. */ predicate hibernateSqlMethod(Method m) { - m.getDeclaringType().getASourceSupertype*() instanceof HibernateSession and + exists(RefType t | + t = m.getDeclaringType().getASourceSupertype*() and + ( + t instanceof HibernateQueryProducer or + t instanceof HibernateSharedSessionContract or + t instanceof HibernateSession + ) + ) and m.getParameterType(0) instanceof TypeString and - ( - m.hasName("createQuery") or - m.hasName("createSQLQuery") - ) + m.hasName(["createQuery", "createNativeQuery", "createSQLQuery"]) } From 983f54f11a74736eed61075c24229032131d09a2 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 25 Aug 2020 15:21:16 -0700 Subject: [PATCH 010/185] C++: simple tests for vector output iterators --- .../dataflow/taint-tests/localTaint.expected | 82 +++++++++++++++++++ .../dataflow/taint-tests/vector.cpp | 27 ++++++ 2 files changed, 109 insertions(+) diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 9a9fca0cd59..bca6b6299ad 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -3173,3 +3173,85 @@ | vector.cpp:324:7:324:8 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | | | vector.cpp:325:7:325:8 | ref arg v3 | vector.cpp:327:1:327:1 | v3 | | | vector.cpp:326:7:326:8 | ref arg v4 | vector.cpp:327:1:327:1 | v4 | | +| vector.cpp:329:62:329:65 | iter | vector.cpp:330:3:330:6 | iter | | +| vector.cpp:330:2:330:17 | ... = ... | vector.cpp:330:2:330:2 | call to operator* [post update] | | +| vector.cpp:330:3:330:6 | iter | vector.cpp:330:2:330:2 | call to operator* | TAINT | +| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:17 | ... = ... | | +| vector.cpp:334:22:334:24 | call to vector | vector.cpp:336:34:336:35 | v1 | | +| vector.cpp:334:22:334:24 | call to vector | vector.cpp:338:7:338:8 | v1 | | +| vector.cpp:334:22:334:24 | call to vector | vector.cpp:354:1:354:1 | v1 | | +| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:38:340:39 | v2 | | +| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:56:340:57 | v2 | | +| vector.cpp:334:30:334:32 | call to vector | vector.cpp:343:7:343:8 | v2 | | +| vector.cpp:334:30:334:32 | call to vector | vector.cpp:354:1:354:1 | v2 | | +| vector.cpp:334:38:334:40 | call to vector | vector.cpp:345:15:345:16 | v3 | | +| vector.cpp:334:38:334:40 | call to vector | vector.cpp:348:7:348:8 | v3 | | +| vector.cpp:334:38:334:40 | call to vector | vector.cpp:354:1:354:1 | v3 | | +| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:38:350:39 | v4 | | +| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:56:350:57 | v4 | | +| vector.cpp:334:46:334:48 | call to vector | vector.cpp:353:7:353:8 | v4 | | +| vector.cpp:334:46:334:48 | call to vector | vector.cpp:354:1:354:1 | v4 | | +| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:338:7:338:8 | v1 | | +| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:354:1:354:1 | v1 | | +| vector.cpp:336:34:336:35 | v1 | vector.cpp:336:37:336:41 | call to begin | TAINT | +| vector.cpp:336:37:336:41 | call to begin | vector.cpp:337:3:337:4 | i1 | | +| vector.cpp:337:2:337:15 | ... = ... | vector.cpp:337:2:337:2 | call to operator* [post update] | | +| vector.cpp:337:3:337:4 | i1 | vector.cpp:337:2:337:2 | call to operator* | TAINT | +| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:15 | ... = ... | | +| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:354:1:354:1 | v1 | | +| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | | +| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | | +| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | | +| vector.cpp:340:38:340:39 | v2 | vector.cpp:340:41:340:45 | call to begin | TAINT | +| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:50:340:51 | it | | +| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:68:340:69 | it | | +| vector.cpp:340:41:340:45 | call to begin | vector.cpp:341:4:341:5 | it | | +| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | | +| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | | +| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | | +| vector.cpp:340:56:340:57 | v2 | vector.cpp:340:59:340:61 | call to end | TAINT | +| vector.cpp:340:68:340:69 | it | vector.cpp:340:66:340:66 | call to operator++ | TAINT | +| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:50:340:51 | it | | +| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:68:340:69 | it | | +| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:341:4:341:5 | it | | +| vector.cpp:341:3:341:16 | ... = ... | vector.cpp:341:3:341:3 | call to operator* [post update] | | +| vector.cpp:341:4:341:5 | it | vector.cpp:341:3:341:3 | call to operator* | TAINT | +| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:16 | ... = ... | | +| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | | +| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator* | TAINT | +| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator++ | TAINT | +| vector.cpp:345:15:345:15 | (__end) | vector.cpp:345:15:345:15 | call to iterator | | +| vector.cpp:345:15:345:15 | (__range) | vector.cpp:345:15:345:15 | call to begin | TAINT | +| vector.cpp:345:15:345:15 | (__range) | vector.cpp:345:15:345:15 | call to end | TAINT | +| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | | +| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | | +| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | | +| vector.cpp:345:15:345:15 | call to end | vector.cpp:345:15:345:15 | (__end) | | +| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | | +| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | | +| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | | +| vector.cpp:345:15:345:15 | ref arg (__range) | vector.cpp:345:15:345:15 | (__range) | | +| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | | +| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | | +| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | call to operator* | TAINT | +| vector.cpp:346:7:346:12 | call to source | vector.cpp:346:3:346:14 | ... = ... | | +| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:354:1:354:1 | v3 | | +| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | | +| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | | +| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | | +| vector.cpp:350:38:350:39 | v4 | vector.cpp:350:41:350:45 | call to begin | TAINT | +| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:50:350:51 | it | | +| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:68:350:69 | it | | +| vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:4:351:5 | it | | +| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | | +| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | | +| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | | +| vector.cpp:350:56:350:57 | v4 | vector.cpp:350:59:350:61 | call to end | TAINT | +| vector.cpp:350:68:350:69 | it | vector.cpp:350:66:350:66 | call to operator++ | TAINT | +| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:50:350:51 | it | | +| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | | +| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:4:351:5 | it | | +| vector.cpp:351:3:351:16 | ... = ... | vector.cpp:351:3:351:3 | call to operator* [post update] | | +| vector.cpp:351:4:351:5 | it | vector.cpp:351:3:351:3 | call to operator* | TAINT | +| vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:16 | ... = ... | | +| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp index e2d12bed6c1..6360438663a 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp @@ -325,3 +325,30 @@ void test_constructors_more() { sink(v3); sink(v4); // tainted } + +void taint_vector_output_iterator(std::vector::iterator iter) { + *iter = source(); +} + +void test_vector_output_iterator() { + std::vector v1(10), v2(10), v3(10), v4(10); + + std::vector::iterator i1 = v1.begin(); + *i1 = source(); + sink(v1); // tainted [NOT DETECTED] + + for(std::vector::iterator it = v2.begin(); it != v2.end(); ++it) { + *it = source(); // tainted [NOT DETECTED] + } + sink(v2); + + for(int& x : v3) { + x = source(); + } + sink(v3); // tainted [NOT DETECTED] + + for(std::vector::iterator it = v4.begin(); it != v4.end(); ++it) { + *it = source(); + } + sink(v4); // tainted [NOT DETECTED] +} From 703db0b9a6ffdb53af4cef150cf1ae6f10ca4517 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 25 Aug 2020 15:26:18 -0700 Subject: [PATCH 011/185] C++: noisy output iterators in AST taint tracking --- .../dataflow/internal/TaintTrackingUtil.qll | 31 +++++++++++++++++++ .../cpp/models/implementations/Iterator.qll | 12 +++++++ .../dataflow/taint-tests/localTaint.expected | 11 +++++++ .../dataflow/taint-tests/taint.expected | 3 ++ .../dataflow/taint-tests/test_diff.expected | 3 ++ .../dataflow/taint-tests/vector.cpp | 10 +++--- 6 files changed, 65 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll index a3f0c435e16..c15f39c0c15 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -10,6 +10,7 @@ private import semmle.code.cpp.models.interfaces.DataFlow private import semmle.code.cpp.models.interfaces.Taint +private import semmle.code.cpp.models.implementations.Iterator private module DataFlow { import semmle.code.cpp.dataflow.internal.DataFlowUtil @@ -186,6 +187,12 @@ private predicate exprToExprStep(Expr exprIn, Expr exprOut) { exprIn = call.getQualifier() ) ) + or + exists(Variable iterator, Variable collection | + assignmentViaIterator(iterator, exprIn) and + isIteratorForCollection(iterator, collection) and + collection.getAnAccess() = exprOut + ) } private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) { @@ -249,3 +256,27 @@ private predicate exprToPartialDefinitionStep(Expr exprIn, Expr exprOut) { ) ) } + +private predicate isIteratorForCollection(Variable iterator, Variable collection) { + exists(Call beginOrEnd | + beginOrEnd.getTarget() instanceof BeginOrEndFunction and + beginOrEnd.getQualifier() = collection.getAnAccess() and + iterator.getAnAssignedValue() = beginOrEnd + ) +} + +private predicate assignmentViaIterator(Variable iterator, Expr rvalue) { + exists(Assignment a, Call c | + c.getTarget() instanceof IteratorArrayMemberOperator and + c.getQualifier() = iterator.getAnAccess() + or + c.getTarget() instanceof IteratorPointerDereferenceMemberOperator and + c.getQualifier() = iterator.getAnAccess() + or + c.getTarget() instanceof IteratorPointerDereferenceOperator and + c.getArgument(0) = iterator.getAnAccess() + | + c = a.getLValue() and + rvalue = a.getRValue() + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll index 2ad464d1634..758d093bedc 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll @@ -271,3 +271,15 @@ class IteratorArrayMemberOperator extends MemberFunction, TaintFunction { output.isReturnValue() } } + +class BeginOrEndFunction extends MemberFunction, TaintFunction { + BeginOrEndFunction() { + this.hasName(["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend"]) and + this.getType().getUnspecifiedType() instanceof Iterator + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and + output.isReturnValue() + } +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index bca6b6299ad..0c0362206e1 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -3197,7 +3197,10 @@ | vector.cpp:336:37:336:41 | call to begin | vector.cpp:337:3:337:4 | i1 | | | vector.cpp:337:2:337:15 | ... = ... | vector.cpp:337:2:337:2 | call to operator* [post update] | | | vector.cpp:337:3:337:4 | i1 | vector.cpp:337:2:337:2 | call to operator* | TAINT | +| vector.cpp:337:8:337:13 | call to source | vector.cpp:336:34:336:35 | v1 | TAINT | | vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:15 | ... = ... | | +| vector.cpp:337:8:337:13 | call to source | vector.cpp:338:7:338:8 | v1 | TAINT | +| vector.cpp:337:8:337:13 | call to source | vector.cpp:354:1:354:1 | v1 | TAINT | | vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:354:1:354:1 | v1 | | | vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | | | vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | | @@ -3216,7 +3219,11 @@ | vector.cpp:340:68:340:69 | ref arg it | vector.cpp:341:4:341:5 | it | | | vector.cpp:341:3:341:16 | ... = ... | vector.cpp:341:3:341:3 | call to operator* [post update] | | | vector.cpp:341:4:341:5 | it | vector.cpp:341:3:341:3 | call to operator* | TAINT | +| vector.cpp:341:9:341:14 | call to source | vector.cpp:340:38:340:39 | v2 | TAINT | +| vector.cpp:341:9:341:14 | call to source | vector.cpp:340:56:340:57 | v2 | TAINT | | vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:16 | ... = ... | | +| vector.cpp:341:9:341:14 | call to source | vector.cpp:343:7:343:8 | v2 | TAINT | +| vector.cpp:341:9:341:14 | call to source | vector.cpp:354:1:354:1 | v2 | TAINT | | vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | | | vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator* | TAINT | | vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator++ | TAINT | @@ -3253,5 +3260,9 @@ | vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:4:351:5 | it | | | vector.cpp:351:3:351:16 | ... = ... | vector.cpp:351:3:351:3 | call to operator* [post update] | | | vector.cpp:351:4:351:5 | it | vector.cpp:351:3:351:3 | call to operator* | TAINT | +| vector.cpp:351:9:351:14 | call to source | vector.cpp:350:38:350:39 | v4 | TAINT | +| vector.cpp:351:9:351:14 | call to source | vector.cpp:350:56:350:57 | v4 | TAINT | | vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:16 | ... = ... | | +| vector.cpp:351:9:351:14 | call to source | vector.cpp:353:7:353:8 | v4 | TAINT | +| vector.cpp:351:9:351:14 | call to source | vector.cpp:354:1:354:1 | v4 | TAINT | | vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index 15744c35716..6697e57e4a3 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -326,3 +326,6 @@ | vector.cpp:312:7:312:7 | d | vector.cpp:303:14:303:19 | call to source | | vector.cpp:324:7:324:8 | v2 | vector.cpp:318:15:318:20 | call to source | | vector.cpp:326:7:326:8 | v4 | vector.cpp:318:15:318:20 | call to source | +| vector.cpp:338:7:338:8 | v1 | vector.cpp:337:8:337:13 | call to source | +| vector.cpp:343:7:343:8 | v2 | vector.cpp:341:9:341:14 | call to source | +| vector.cpp:353:7:353:8 | v4 | vector.cpp:351:9:351:14 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index d4383d4e900..7533d079093 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -275,3 +275,6 @@ | vector.cpp:312:7:312:7 | vector.cpp:303:14:303:19 | AST only | | vector.cpp:324:7:324:8 | vector.cpp:318:15:318:20 | AST only | | vector.cpp:326:7:326:8 | vector.cpp:318:15:318:20 | AST only | +| vector.cpp:338:7:338:8 | vector.cpp:337:8:337:13 | AST only | +| vector.cpp:343:7:343:8 | vector.cpp:341:9:341:14 | AST only | +| vector.cpp:353:7:353:8 | vector.cpp:351:9:351:14 | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp index 6360438663a..f1cf4e91457 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp @@ -21,7 +21,7 @@ void test_range_based_for_loop_vector(int source1) { } for(std::vector::iterator it = v.begin(); it != v.end(); ++it) { - sink(*it); // tainted + sink(*it); // tainted [NOT DETECTED by IR] } for(int& x : v) { @@ -335,12 +335,12 @@ void test_vector_output_iterator() { std::vector::iterator i1 = v1.begin(); *i1 = source(); - sink(v1); // tainted [NOT DETECTED] + sink(v1); // tainted [NOT DETECTED by IR] for(std::vector::iterator it = v2.begin(); it != v2.end(); ++it) { - *it = source(); // tainted [NOT DETECTED] + *it = source(); } - sink(v2); + sink(v2); // tainted [NOT DETECTED by IR] for(int& x : v3) { x = source(); @@ -350,5 +350,5 @@ void test_vector_output_iterator() { for(std::vector::iterator it = v4.begin(); it != v4.end(); ++it) { *it = source(); } - sink(v4); // tainted [NOT DETECTED] + sink(v4); // tainted [NOT DETECTED by IR] } From c8cdf68bf9b8881faae02f89ca3cb5091ec1c926 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 25 Aug 2020 16:34:36 -0700 Subject: [PATCH 012/185] C++: Remove StdStringBeginEnd --- .../cpp/models/implementations/Iterator.qll | 4 ++++ .../cpp/models/implementations/StdString.qll | 17 ----------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll index 758d093bedc..93a04d5ef90 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll @@ -272,6 +272,10 @@ class IteratorArrayMemberOperator extends MemberFunction, TaintFunction { } } +/** + * A `begin` or `end` member function, or a related member function, that + * returns an iterator. + */ class BeginOrEndFunction extends MemberFunction, TaintFunction { BeginOrEndFunction() { this.hasName(["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend"]) and diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll index 8e7cb24be73..3a2c914eb4c 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll @@ -212,23 +212,6 @@ class StdStringAssign extends TaintFunction { } } -/** - * The standard functions `std::string.begin` and `std::string.end` and their - * variants. - */ -class StdStringBeginEnd extends TaintFunction { - StdStringBeginEnd() { - this - .hasQualifiedName("std", "basic_string", - ["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend"]) - } - - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - input.isQualifierObject() and - output.isReturnValue() - } -} - /** * The standard function `std::string.copy`. */ From 13c45b6664a6b4fcad59a60a096236cb702d21b0 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 27 Aug 2020 15:09:01 -0700 Subject: [PATCH 013/185] C++: remove unnecessary parameter in FlowVar.qll --- .../code/cpp/dataflow/internal/FlowVar.qll | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index 6dc5da2f20b..83941140fa6 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -240,7 +240,7 @@ module FlowVar_internal { ( initializer(v, sbb.getANode()) or - assignmentLikeOperation(sbb, v, _, _) + assignmentLikeOperation(sbb, v, _) or exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, sbb)) or @@ -359,7 +359,7 @@ module FlowVar_internal { } override predicate definedByExpr(Expr e, ControlFlowNode node) { - assignmentLikeOperation(node, v, _, e) and + assignmentLikeOperation(node, v, e) and node = sbb or // We pick the defining `ControlFlowNode` of an `Initializer` to be its @@ -449,7 +449,7 @@ module FlowVar_internal { pragma[noinline] private Variable getAVariableAssignedInLoop() { exists(BasicBlock bbAssign | - assignmentLikeOperation(bbAssign.getANode(), result, _, _) and + assignmentLikeOperation(bbAssign.getANode(), result, _) and this.bbInLoop(bbAssign) ) } @@ -487,7 +487,7 @@ module FlowVar_internal { pragma[noinline] private predicate assignsToVar(BasicBlock bb, Variable v) { - assignmentLikeOperation(bb.getANode(), v, _, _) and + assignmentLikeOperation(bb.getANode(), v, _) and exists(AlwaysTrueUponEntryLoop loop | v = loop.getARelevantVariable()) } @@ -524,7 +524,7 @@ module FlowVar_internal { result = mid.getASuccessor() and variableLiveInSBB(result, v) and forall(AlwaysTrueUponEntryLoop loop | skipLoop(mid, result, v, loop) | loop.sbbInLoop(sbbDef)) and - not assignmentLikeOperation(result, v, _, _) + not assignmentLikeOperation(result, v, _) ) } @@ -566,7 +566,7 @@ module FlowVar_internal { * Holds if liveness of `v` should stop propagating backwards from `sbb`. */ private predicate variableNotLiveBefore(SubBasicBlock sbb, Variable v) { - assignmentLikeOperation(sbb, v, _, _) + assignmentLikeOperation(sbb, v, _) or // Liveness of `v` is killed when going backwards from a block that declares it exists(DeclStmt ds | ds.getADeclaration().(LocalVariable) = v and sbb.contains(ds)) @@ -687,20 +687,18 @@ module FlowVar_internal { * predicate. */ predicate assignmentLikeOperation( - ControlFlowNode node, Variable v, VariableAccess va, Expr assignedExpr + ControlFlowNode node, Variable v, Expr assignedExpr ) { // Together, the two following cases cover `Assignment` node = any(AssignExpr ae | - va = ae.getLValue() and - v = va.getTarget() and + v.getAnAccess() = ae.getLValue() and assignedExpr = ae.getRValue() ) or node = any(AssignOperation ao | - va = ao.getLValue() and - v = va.getTarget() and + v.getAnAccess() = ao.getLValue() and // Here and in the `PrefixCrementOperation` case, we say that the assigned // expression is the operation itself. For example, we say that `x += 1` // assigns `x += 1` to `x`. The justification is that after this operation, @@ -712,8 +710,7 @@ module FlowVar_internal { // `PrefixCrementOperation` is itself a source node = any(CrementOperation op | - va = op.getOperand() and - v = va.getTarget() and + v.getAnAccess() = op.getOperand() and assignedExpr = op ) } @@ -749,7 +746,7 @@ module FlowVar_internal { class DataFlowSubBasicBlockCutNode extends SubBasicBlockCutNode { DataFlowSubBasicBlockCutNode() { exists(Variable v | not fullySupportedSsaVariable(v) | - assignmentLikeOperation(this, v, _, _) + assignmentLikeOperation(this, v, _) or exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, this)) // It is not necessary to cut the basic blocks at `Initializer` nodes From eab1557e27cfefef7b826960e3840e5ba79279e1 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 8 Sep 2020 14:09:57 -0700 Subject: [PATCH 014/185] C++: output iterator flow via FlowVar --- .../cpp/dataflow/internal/DataFlowUtil.qll | 20 ++- .../code/cpp/dataflow/internal/FlowVar.qll | 98 +++++++++-- .../dataflow/internal/TaintTrackingUtil.qll | 34 +--- .../dataflow/taint-tests/localTaint.expected | 160 +++++++++++++++--- .../dataflow/taint-tests/taint.expected | 8 + .../dataflow/taint-tests/test_diff.expected | 8 + .../dataflow/taint-tests/vector.cpp | 32 +++- 7 files changed, 287 insertions(+), 73 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 6647dda4072..9705ca492ce 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -283,12 +283,12 @@ abstract class PostUpdateNode extends Node { override Location getLocation() { result = getPreUpdateNode().getLocation() } } -private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode { +private abstract class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode { PartialDefinition pd; - PartialDefinitionNode() { this = TPartialDefinitionNode(pd) } - - override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) } + PartialDefinitionNode() { + this = TPartialDefinitionNode(pd) + } override Location getLocation() { result = pd.getActualLocation() } @@ -297,6 +297,18 @@ private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNo override string toString() { result = getPreUpdateNode().toString() + " [post update]" } } +private class VariablePartialDefinitionNode extends PartialDefinitionNode { + override VariablePartialDefinition pd; + + override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) } +} + +private class IteratorPartialDefinitionNode extends PartialDefinitionNode { + override IteratorPartialDefinition pd; + + override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) } +} + /** * A post-update node on the `e->f` in `f(&e->f)` (and other forms). */ diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index 83941140fa6..cb2cdd68f08 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -6,6 +6,7 @@ import cpp private import semmle.code.cpp.controlflow.SSA private import semmle.code.cpp.dataflow.internal.SubBasicBlocks private import semmle.code.cpp.dataflow.internal.AddressFlow +private import semmle.code.cpp.models.implementations.Iterator /** * A conceptual variable that is assigned only once, like an SSA variable. This @@ -109,20 +110,16 @@ class FlowVar extends TFlowVar { */ private module PartialDefinitions { class PartialDefinition extends Expr { - Expr innerDefinedExpr; ControlFlowNode node; PartialDefinition() { - exists(Expr convertedInner | - valueToUpdate(convertedInner, this.getFullyConverted(), node) and - innerDefinedExpr = convertedInner.getUnconverted() and - not this instanceof Conversion - ) + valueToUpdate(_, this.getFullyConverted(), node) and + not this instanceof Conversion } - deprecated predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() } + deprecated predicate partiallyDefines(Variable v) { none() } - deprecated predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e } + deprecated predicate partiallyDefinesThis(ThisExpr e) { none() } /** * Gets the subBasicBlock where this `PartialDefinition` is defined. @@ -133,10 +130,9 @@ private module PartialDefinitions { * Holds if this `PartialDefinition` defines variable `v` at control-flow * node `cfn`. */ - pragma[noinline] + pragma[noinline] // does this work with a dispred? predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { - innerDefinedExpr = v.getAnAccess() and - cfn = node + none() } /** @@ -147,10 +143,7 @@ private module PartialDefinitions { * - `inner` = `... .x`, `outer` = `&...` * - `inner` = `a`, `outer` = `*` */ - predicate definesExpressions(Expr inner, Expr outer) { - inner = innerDefinedExpr and - outer = this - } + predicate definesExpressions(Expr inner, Expr outer) { none() } /** * Gets the location of this element, adjusted to avoid unknown locations @@ -166,6 +159,68 @@ private module PartialDefinitions { } } + class IteratorPartialDefinition extends PartialDefinition { + Variable collection; + Call innerDefinedExpr; + + IteratorPartialDefinition() { + exists(Expr convertedInner | + valueToUpdate(convertedInner, this.getFullyConverted(), node) and + innerDefinedExpr = convertedInner.getUnconverted() and + innerDefinedExpr.getQualifier() = getAnIteratorAccess(collection) and + innerDefinedExpr.getTarget() instanceof IteratorPointerDereferenceMemberOperator + ) + } + + deprecated override predicate partiallyDefines(Variable v) { v = collection } + + deprecated override predicate partiallyDefinesThis(ThisExpr e) { none() } + + override predicate definesExpressions(Expr inner, Expr outer) { + inner = innerDefinedExpr and + outer = this + } + + override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { + v = collection and + cfn = node + } + } + + class VariablePartialDefinition extends PartialDefinition { + Expr innerDefinedExpr; + + VariablePartialDefinition() { + exists(Expr convertedInner | + valueToUpdate(convertedInner, this.getFullyConverted(), node) and + innerDefinedExpr = convertedInner.getUnconverted() and + not this instanceof Conversion + ) + } + + deprecated override predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() } + + deprecated override predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e } + + /** + * Holds if this partial definition may modify `inner` (or what it points + * to) through `outer`. These expressions will never be `Conversion`s. + * + * For example, in `f(& (*a).x)`, there are two results: + * - `inner` = `... .x`, `outer` = `&...` + * - `inner` = `a`, `outer` = `*` + */ + override predicate definesExpressions(Expr inner, Expr outer) { + inner = innerDefinedExpr and + outer = this + } + + override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { + innerDefinedExpr = v.getAnAccess() and + cfn = node + } + } + /** * A partial definition that's a definition by reference. */ @@ -686,9 +741,7 @@ module FlowVar_internal { * `node instanceof Initializer` is covered by `initializer` instead of this * predicate. */ - predicate assignmentLikeOperation( - ControlFlowNode node, Variable v, Expr assignedExpr - ) { + predicate assignmentLikeOperation(ControlFlowNode node, Variable v, Expr assignedExpr) { // Together, the two following cases cover `Assignment` node = any(AssignExpr ae | @@ -715,6 +768,15 @@ module FlowVar_internal { ) } + Expr getAnIteratorAccess(Variable collection) { + exists(Call c, SsaDefinition def, Variable iterator | + c.getQualifier() = collection.getAnAccess() and + c.getTarget() instanceof BeginOrEndFunction and + def.getAnUltimateDefiningValue(iterator) = c and + result = def.getAUse(iterator) + ) + } + /** * Holds if `v` is initialized to have value `assignedExpr`. */ diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll index c15f39c0c15..c8d963866a1 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -187,12 +187,6 @@ private predicate exprToExprStep(Expr exprIn, Expr exprOut) { exprIn = call.getQualifier() ) ) - or - exists(Variable iterator, Variable collection | - assignmentViaIterator(iterator, exprIn) and - isIteratorForCollection(iterator, collection) and - collection.getAnAccess() = exprOut - ) } private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) { @@ -255,28 +249,18 @@ private predicate exprToPartialDefinitionStep(Expr exprIn, Expr exprOut) { exprIn = call.getArgument(argInIndex) ) ) -} - -private predicate isIteratorForCollection(Variable iterator, Variable collection) { - exists(Call beginOrEnd | - beginOrEnd.getTarget() instanceof BeginOrEndFunction and - beginOrEnd.getQualifier() = collection.getAnAccess() and - iterator.getAnAssignedValue() = beginOrEnd + or + exists(Assignment a | + iteratorDereference(exprOut) and + a.getLValue() = exprOut and + a.getRValue() = exprIn ) } -private predicate assignmentViaIterator(Variable iterator, Expr rvalue) { - exists(Assignment a, Call c | - c.getTarget() instanceof IteratorArrayMemberOperator and - c.getQualifier() = iterator.getAnAccess() +private predicate iteratorDereference(Call c) { + c.getTarget() instanceof IteratorArrayMemberOperator or - c.getTarget() instanceof IteratorPointerDereferenceMemberOperator and - c.getQualifier() = iterator.getAnAccess() + c.getTarget() instanceof IteratorPointerDereferenceMemberOperator or - c.getTarget() instanceof IteratorPointerDereferenceOperator and - c.getArgument(0) = iterator.getAnAccess() - | - c = a.getLValue() and - rvalue = a.getRValue() - ) + c.getTarget() instanceof IteratorPointerDereferenceOperator } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 0c0362206e1..c0f34638554 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -3176,55 +3176,75 @@ | vector.cpp:329:62:329:65 | iter | vector.cpp:330:3:330:6 | iter | | | vector.cpp:330:2:330:17 | ... = ... | vector.cpp:330:2:330:2 | call to operator* [post update] | | | vector.cpp:330:3:330:6 | iter | vector.cpp:330:2:330:2 | call to operator* | TAINT | +| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:2 | call to operator* [post update] | TAINT | | vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:17 | ... = ... | | +| vector.cpp:333:38:333:38 | b | vector.cpp:368:5:368:5 | b | | | vector.cpp:334:22:334:24 | call to vector | vector.cpp:336:34:336:35 | v1 | | | vector.cpp:334:22:334:24 | call to vector | vector.cpp:338:7:338:8 | v1 | | -| vector.cpp:334:22:334:24 | call to vector | vector.cpp:354:1:354:1 | v1 | | +| vector.cpp:334:22:334:24 | call to vector | vector.cpp:382:1:382:1 | v1 | | | vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:38:340:39 | v2 | | | vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:56:340:57 | v2 | | | vector.cpp:334:30:334:32 | call to vector | vector.cpp:343:7:343:8 | v2 | | -| vector.cpp:334:30:334:32 | call to vector | vector.cpp:354:1:354:1 | v2 | | +| vector.cpp:334:30:334:32 | call to vector | vector.cpp:382:1:382:1 | v2 | | | vector.cpp:334:38:334:40 | call to vector | vector.cpp:345:15:345:16 | v3 | | | vector.cpp:334:38:334:40 | call to vector | vector.cpp:348:7:348:8 | v3 | | -| vector.cpp:334:38:334:40 | call to vector | vector.cpp:354:1:354:1 | v3 | | +| vector.cpp:334:38:334:40 | call to vector | vector.cpp:382:1:382:1 | v3 | | | vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:38:350:39 | v4 | | | vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:56:350:57 | v4 | | | vector.cpp:334:46:334:48 | call to vector | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:334:46:334:48 | call to vector | vector.cpp:354:1:354:1 | v4 | | +| vector.cpp:334:46:334:48 | call to vector | vector.cpp:382:1:382:1 | v4 | | +| vector.cpp:334:54:334:56 | call to vector | vector.cpp:355:34:355:35 | v5 | | +| vector.cpp:334:54:334:56 | call to vector | vector.cpp:357:7:357:8 | v5 | | +| vector.cpp:334:54:334:56 | call to vector | vector.cpp:359:7:359:8 | v5 | | +| vector.cpp:334:54:334:56 | call to vector | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:334:62:334:64 | call to vector | vector.cpp:361:34:361:35 | v6 | | +| vector.cpp:334:62:334:64 | call to vector | vector.cpp:363:7:363:8 | v6 | | +| vector.cpp:334:62:334:64 | call to vector | vector.cpp:364:2:364:3 | v6 | | +| vector.cpp:334:62:334:64 | call to vector | vector.cpp:365:7:365:8 | v6 | | +| vector.cpp:334:62:334:64 | call to vector | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:334:70:334:72 | call to vector | vector.cpp:367:34:367:35 | v7 | | +| vector.cpp:334:70:334:72 | call to vector | vector.cpp:370:8:370:9 | v7 | | +| vector.cpp:334:70:334:72 | call to vector | vector.cpp:373:8:373:9 | v7 | | +| vector.cpp:334:70:334:72 | call to vector | vector.cpp:375:7:375:8 | v7 | | +| vector.cpp:334:70:334:72 | call to vector | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:334:78:334:80 | call to vector | vector.cpp:377:34:377:35 | v8 | | +| vector.cpp:334:78:334:80 | call to vector | vector.cpp:379:7:379:8 | v8 | | +| vector.cpp:334:78:334:80 | call to vector | vector.cpp:381:7:381:8 | v8 | | +| vector.cpp:334:78:334:80 | call to vector | vector.cpp:382:1:382:1 | v8 | | | vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:338:7:338:8 | v1 | | -| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:354:1:354:1 | v1 | | +| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:382:1:382:1 | v1 | | | vector.cpp:336:34:336:35 | v1 | vector.cpp:336:37:336:41 | call to begin | TAINT | | vector.cpp:336:37:336:41 | call to begin | vector.cpp:337:3:337:4 | i1 | | +| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:338:7:338:8 | v1 | | +| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v1 | | | vector.cpp:337:2:337:15 | ... = ... | vector.cpp:337:2:337:2 | call to operator* [post update] | | | vector.cpp:337:3:337:4 | i1 | vector.cpp:337:2:337:2 | call to operator* | TAINT | -| vector.cpp:337:8:337:13 | call to source | vector.cpp:336:34:336:35 | v1 | TAINT | +| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:2 | call to operator* [post update] | TAINT | | vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:15 | ... = ... | | -| vector.cpp:337:8:337:13 | call to source | vector.cpp:338:7:338:8 | v1 | TAINT | -| vector.cpp:337:8:337:13 | call to source | vector.cpp:354:1:354:1 | v1 | TAINT | -| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:354:1:354:1 | v1 | | +| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:382:1:382:1 | v1 | | | vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | | | vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | | -| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | | +| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | | | vector.cpp:340:38:340:39 | v2 | vector.cpp:340:41:340:45 | call to begin | TAINT | | vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:50:340:51 | it | | | vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:68:340:69 | it | | | vector.cpp:340:41:340:45 | call to begin | vector.cpp:341:4:341:5 | it | | | vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | | | vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | | -| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | | +| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | | | vector.cpp:340:56:340:57 | v2 | vector.cpp:340:59:340:61 | call to end | TAINT | | vector.cpp:340:68:340:69 | it | vector.cpp:340:66:340:66 | call to operator++ | TAINT | | vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:50:340:51 | it | | | vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:68:340:69 | it | | | vector.cpp:340:68:340:69 | ref arg it | vector.cpp:341:4:341:5 | it | | +| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:340:56:340:57 | v2 | | +| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:343:7:343:8 | v2 | | +| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v2 | | | vector.cpp:341:3:341:16 | ... = ... | vector.cpp:341:3:341:3 | call to operator* [post update] | | | vector.cpp:341:4:341:5 | it | vector.cpp:341:3:341:3 | call to operator* | TAINT | -| vector.cpp:341:9:341:14 | call to source | vector.cpp:340:38:340:39 | v2 | TAINT | -| vector.cpp:341:9:341:14 | call to source | vector.cpp:340:56:340:57 | v2 | TAINT | +| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:3 | call to operator* [post update] | TAINT | | vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:16 | ... = ... | | -| vector.cpp:341:9:341:14 | call to source | vector.cpp:343:7:343:8 | v2 | TAINT | -| vector.cpp:341:9:341:14 | call to source | vector.cpp:354:1:354:1 | v2 | TAINT | -| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | | +| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | | | vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator* | TAINT | | vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator++ | TAINT | | vector.cpp:345:15:345:15 | (__end) | vector.cpp:345:15:345:15 | call to iterator | | @@ -3242,27 +3262,119 @@ | vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | | | vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | call to operator* | TAINT | | vector.cpp:346:7:346:12 | call to source | vector.cpp:346:3:346:14 | ... = ... | | -| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:354:1:354:1 | v3 | | +| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:382:1:382:1 | v3 | | | vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | | | vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | | +| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | | | vector.cpp:350:38:350:39 | v4 | vector.cpp:350:41:350:45 | call to begin | TAINT | | vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:50:350:51 | it | | | vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:68:350:69 | it | | | vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:4:351:5 | it | | | vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | | | vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | | +| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | | | vector.cpp:350:56:350:57 | v4 | vector.cpp:350:59:350:61 | call to end | TAINT | | vector.cpp:350:68:350:69 | it | vector.cpp:350:66:350:66 | call to operator++ | TAINT | | vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:50:350:51 | it | | | vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | | | vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:4:351:5 | it | | +| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:350:56:350:57 | v4 | | +| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:353:7:353:8 | v4 | | +| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v4 | | | vector.cpp:351:3:351:16 | ... = ... | vector.cpp:351:3:351:3 | call to operator* [post update] | | | vector.cpp:351:4:351:5 | it | vector.cpp:351:3:351:3 | call to operator* | TAINT | -| vector.cpp:351:9:351:14 | call to source | vector.cpp:350:38:350:39 | v4 | TAINT | -| vector.cpp:351:9:351:14 | call to source | vector.cpp:350:56:350:57 | v4 | TAINT | +| vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:3 | call to operator* [post update] | TAINT | | vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:16 | ... = ... | | -| vector.cpp:351:9:351:14 | call to source | vector.cpp:353:7:353:8 | v4 | TAINT | -| vector.cpp:351:9:351:14 | call to source | vector.cpp:354:1:354:1 | v4 | TAINT | -| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | | +| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | | +| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:357:7:357:8 | v5 | | +| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | | +| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:355:34:355:35 | v5 | vector.cpp:355:37:355:41 | call to begin | TAINT | +| vector.cpp:355:37:355:41 | call to begin | vector.cpp:356:3:356:4 | i5 | | +| vector.cpp:355:37:355:41 | call to begin | vector.cpp:358:3:358:4 | i5 | | +| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:357:7:357:8 | v5 | | +| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | | +| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:356:2:356:15 | ... = ... | vector.cpp:356:2:356:2 | call to operator* [post update] | | +| vector.cpp:356:3:356:4 | i5 | vector.cpp:356:2:356:2 | call to operator* | TAINT | +| vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:2 | call to operator* [post update] | TAINT | +| vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:15 | ... = ... | | +| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | | +| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | | +| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:358:2:358:8 | ... = ... | vector.cpp:358:2:358:2 | call to operator* [post update] | | +| vector.cpp:358:3:358:4 | i5 | vector.cpp:358:2:358:2 | call to operator* | TAINT | +| vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:2 | call to operator* [post update] | TAINT | +| vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:8 | ... = ... | | +| vector.cpp:359:7:359:8 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:363:7:363:8 | v6 | | +| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | | +| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | | +| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:361:34:361:35 | v6 | vector.cpp:361:37:361:41 | call to begin | TAINT | +| vector.cpp:361:37:361:41 | call to begin | vector.cpp:362:3:362:4 | i6 | | +| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v6 | | +| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:364:2:364:3 | v6 | | +| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:365:7:365:8 | v6 | | +| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:362:2:362:15 | ... = ... | vector.cpp:362:2:362:2 | call to operator* [post update] | | +| vector.cpp:362:3:362:4 | i6 | vector.cpp:362:2:362:2 | call to operator* | TAINT | +| vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:2 | call to operator* [post update] | TAINT | +| vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:15 | ... = ... | | +| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | | +| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | | +| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | | +| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:2:364:3 | ref arg v6 | TAINT | +| vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:5:364:5 | call to operator= | TAINT | +| vector.cpp:365:7:365:8 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:370:8:370:9 | v7 | | +| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:373:8:373:9 | v7 | | +| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | | +| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:367:34:367:35 | v7 | vector.cpp:367:37:367:41 | call to begin | TAINT | +| vector.cpp:367:37:367:41 | call to begin | vector.cpp:369:4:369:5 | i7 | | +| vector.cpp:367:37:367:41 | call to begin | vector.cpp:372:4:372:5 | i7 | | +| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:370:8:370:9 | v7 | | +| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | | +| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:369:3:369:16 | ... = ... | vector.cpp:369:3:369:3 | call to operator* [post update] | | +| vector.cpp:369:4:369:5 | i7 | vector.cpp:369:3:369:3 | call to operator* | TAINT | +| vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:3 | call to operator* [post update] | TAINT | +| vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:16 | ... = ... | | +| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | | +| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:373:8:373:9 | v7 | | +| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | | +| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:372:3:372:9 | ... = ... | vector.cpp:372:3:372:3 | call to operator* [post update] | | +| vector.cpp:372:4:372:5 | i7 | vector.cpp:372:3:372:3 | call to operator* | TAINT | +| vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:3 | call to operator* [post update] | TAINT | +| vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:9 | ... = ... | | +| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | | +| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:375:7:375:8 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:379:7:379:8 | v8 | | +| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | | +| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | | +| vector.cpp:377:34:377:35 | v8 | vector.cpp:377:37:377:41 | call to begin | TAINT | +| vector.cpp:377:37:377:41 | call to begin | vector.cpp:378:3:378:4 | i8 | | +| vector.cpp:377:37:377:41 | call to begin | vector.cpp:380:3:380:4 | i8 | | +| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:379:7:379:8 | v8 | | +| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | | +| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v8 | | +| vector.cpp:378:2:378:15 | ... = ... | vector.cpp:378:2:378:2 | call to operator* [post update] | | +| vector.cpp:378:3:378:4 | i8 | vector.cpp:378:2:378:2 | call to operator* | TAINT | +| vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:2 | call to operator* [post update] | TAINT | +| vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:15 | ... = ... | | +| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | | +| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | | +| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | | +| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v8 | | +| vector.cpp:380:2:380:8 | ... = ... | vector.cpp:380:2:380:2 | call to operator* [post update] | | +| vector.cpp:380:3:380:4 | i8 | vector.cpp:380:2:380:2 | call to operator* | TAINT | +| vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:2 | call to operator* [post update] | TAINT | +| vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:8 | ... = ... | | +| vector.cpp:381:7:381:8 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index 6697e57e4a3..c8ed520e639 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -329,3 +329,11 @@ | vector.cpp:338:7:338:8 | v1 | vector.cpp:337:8:337:13 | call to source | | vector.cpp:343:7:343:8 | v2 | vector.cpp:341:9:341:14 | call to source | | vector.cpp:353:7:353:8 | v4 | vector.cpp:351:9:351:14 | call to source | +| vector.cpp:357:7:357:8 | v5 | vector.cpp:356:8:356:13 | call to source | +| vector.cpp:359:7:359:8 | v5 | vector.cpp:356:8:356:13 | call to source | +| vector.cpp:363:7:363:8 | v6 | vector.cpp:362:8:362:13 | call to source | +| vector.cpp:365:7:365:8 | v6 | vector.cpp:362:8:362:13 | call to source | +| vector.cpp:370:8:370:9 | v7 | vector.cpp:369:9:369:14 | call to source | +| vector.cpp:375:7:375:8 | v7 | vector.cpp:369:9:369:14 | call to source | +| vector.cpp:379:7:379:8 | v8 | vector.cpp:378:8:378:13 | call to source | +| vector.cpp:381:7:381:8 | v8 | vector.cpp:378:8:378:13 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 7533d079093..94f4cf9d2fe 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -278,3 +278,11 @@ | vector.cpp:338:7:338:8 | vector.cpp:337:8:337:13 | AST only | | vector.cpp:343:7:343:8 | vector.cpp:341:9:341:14 | AST only | | vector.cpp:353:7:353:8 | vector.cpp:351:9:351:14 | AST only | +| vector.cpp:357:7:357:8 | vector.cpp:356:8:356:13 | AST only | +| vector.cpp:359:7:359:8 | vector.cpp:356:8:356:13 | AST only | +| vector.cpp:363:7:363:8 | vector.cpp:362:8:362:13 | AST only | +| vector.cpp:365:7:365:8 | vector.cpp:362:8:362:13 | AST only | +| vector.cpp:370:8:370:9 | vector.cpp:369:9:369:14 | AST only | +| vector.cpp:375:7:375:8 | vector.cpp:369:9:369:14 | AST only | +| vector.cpp:379:7:379:8 | vector.cpp:378:8:378:13 | AST only | +| vector.cpp:381:7:381:8 | vector.cpp:378:8:378:13 | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp index f1cf4e91457..35145c3d815 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp @@ -330,8 +330,8 @@ void taint_vector_output_iterator(std::vector::iterator iter) { *iter = source(); } -void test_vector_output_iterator() { - std::vector v1(10), v2(10), v3(10), v4(10); +void test_vector_output_iterator(int b) { + std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10); std::vector::iterator i1 = v1.begin(); *i1 = source(); @@ -351,4 +351,32 @@ void test_vector_output_iterator() { *it = source(); } sink(v4); // tainted [NOT DETECTED by IR] + + std::vector::iterator i5 = v5.begin(); + *i5 = source(); + sink(v5); // tainted [NOT DETECTED by IR] + *i5 = 1; + sink(v5); // tainted [NOT DETECTED by IR] + + std::vector::iterator i6 = v6.begin(); + *i6 = source(); + sink(v6); // tainted [NOT DETECTED by IR] + v6 = std::vector(10); + sink(v6); // [FALSE POSITIVE in AST] + + std::vector::iterator i7 = v7.begin(); + if(b) { + *i7 = source(); + sink(v7); // tainted [NOT DETECTED by IR] + } else { + *i7 = 1; + sink(v7); + } + sink(v7); // tainted [NOT DETECTED by IR] + + std::vector::iterator i8 = v8.begin(); + *i8 = source(); + sink(v8); // tainted [NOT DETECTED by IR] + *i8 = 1; + sink(v8); } From 038688a55c5519a40c15950a323fdf0ea45d13ae Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 9 Sep 2020 10:34:40 +0200 Subject: [PATCH 015/185] Python: Minor updates to 1.25 change notes backporting fixes from `@sj` --- change-notes/1.25/analysis-python.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/change-notes/1.25/analysis-python.md b/change-notes/1.25/analysis-python.md index 345cce5a227..12586e8aec0 100644 --- a/change-notes/1.25/analysis-python.md +++ b/change-notes/1.25/analysis-python.md @@ -2,8 +2,8 @@ * Importing `semmle.python.web.HttpRequest` will no longer import `UntrustedStringKind` transitively. `UntrustedStringKind` is the most commonly used non-abstract subclass of `ExternalStringKind`. If not imported (by one mean or another), taint-tracking queries that concern `ExternalStringKind` will not produce any results. Please ensure such queries contain an explicit import (`import semmle.python.security.strings.Untrusted`). * Added model of taint sources for HTTP servers using `http.server`. -* Added taint modeling of routed parameters in flask. -* Improved modeling of builtin methods on strings for taint tracking. +* Added taint modeling of routed parameters in Flask. +* Improved modeling of built-in methods on strings for taint tracking. * Improved classification of test files. * New class `BoundMethodValue` exposing information about a bound method. * The query `py/command-line-injection` now recognizes command execution with the `fabric` and `invoke` Python libraries. From 2e187a51ae6db786322ed979e6fa449e494352a7 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 9 Sep 2020 12:45:06 -0700 Subject: [PATCH 016/185] C++: test for interprocedurl iterator flow --- .../dataflow/taint-tests/localTaint.expected | 12 +++--------- .../dataflow/taint-tests/taint.expected | 1 - .../dataflow/taint-tests/test_diff.expected | 1 - .../library-tests/dataflow/taint-tests/vector.cpp | 2 +- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index c0f34638554..13d38363b35 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -3269,7 +3269,7 @@ | vector.cpp:350:38:350:39 | v4 | vector.cpp:350:41:350:45 | call to begin | TAINT | | vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:50:350:51 | it | | | vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:68:350:69 | it | | -| vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:4:351:5 | it | | +| vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:32:351:33 | it | | | vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | | | vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | | | vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | | @@ -3277,14 +3277,8 @@ | vector.cpp:350:68:350:69 | it | vector.cpp:350:66:350:66 | call to operator++ | TAINT | | vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:50:350:51 | it | | | vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | | -| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:4:351:5 | it | | -| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:350:56:350:57 | v4 | | -| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v4 | | -| vector.cpp:351:3:351:16 | ... = ... | vector.cpp:351:3:351:3 | call to operator* [post update] | | -| vector.cpp:351:4:351:5 | it | vector.cpp:351:3:351:3 | call to operator* | TAINT | -| vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:3 | call to operator* [post update] | TAINT | -| vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:16 | ... = ... | | +| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:32:351:33 | it | | +| vector.cpp:351:32:351:33 | it | vector.cpp:351:32:351:33 | call to iterator | | | vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | | | vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:357:7:357:8 | v5 | | | vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index c8ed520e639..6759dd90d60 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -328,7 +328,6 @@ | vector.cpp:326:7:326:8 | v4 | vector.cpp:318:15:318:20 | call to source | | vector.cpp:338:7:338:8 | v1 | vector.cpp:337:8:337:13 | call to source | | vector.cpp:343:7:343:8 | v2 | vector.cpp:341:9:341:14 | call to source | -| vector.cpp:353:7:353:8 | v4 | vector.cpp:351:9:351:14 | call to source | | vector.cpp:357:7:357:8 | v5 | vector.cpp:356:8:356:13 | call to source | | vector.cpp:359:7:359:8 | v5 | vector.cpp:356:8:356:13 | call to source | | vector.cpp:363:7:363:8 | v6 | vector.cpp:362:8:362:13 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 94f4cf9d2fe..8fbeb0c246c 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -277,7 +277,6 @@ | vector.cpp:326:7:326:8 | vector.cpp:318:15:318:20 | AST only | | vector.cpp:338:7:338:8 | vector.cpp:337:8:337:13 | AST only | | vector.cpp:343:7:343:8 | vector.cpp:341:9:341:14 | AST only | -| vector.cpp:353:7:353:8 | vector.cpp:351:9:351:14 | AST only | | vector.cpp:357:7:357:8 | vector.cpp:356:8:356:13 | AST only | | vector.cpp:359:7:359:8 | vector.cpp:356:8:356:13 | AST only | | vector.cpp:363:7:363:8 | vector.cpp:362:8:362:13 | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp index 35145c3d815..153385a202d 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp @@ -348,7 +348,7 @@ void test_vector_output_iterator(int b) { sink(v3); // tainted [NOT DETECTED] for(std::vector::iterator it = v4.begin(); it != v4.end(); ++it) { - *it = source(); + taint_vector_output_iterator(it); } sink(v4); // tainted [NOT DETECTED by IR] From 10633019a6081ee9b169d3a022d2132789e057af Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 9 Sep 2020 12:45:17 -0700 Subject: [PATCH 017/185] C++: autoformat --- .../semmle/code/cpp/dataflow/internal/FlowVar.qll | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index cb2cdd68f08..c6929358543 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -130,10 +130,9 @@ private module PartialDefinitions { * Holds if this `PartialDefinition` defines variable `v` at control-flow * node `cfn`. */ - pragma[noinline] // does this work with a dispred? - predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { - none() - } + // does this work with a dispred? + pragma[noinline] + predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { none() } /** * Holds if this partial definition may modify `inner` (or what it points @@ -180,7 +179,7 @@ private module PartialDefinitions { inner = innerDefinedExpr and outer = this } - + override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { v = collection and cfn = node @@ -198,7 +197,9 @@ private module PartialDefinitions { ) } - deprecated override predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() } + deprecated override predicate partiallyDefines(Variable v) { + innerDefinedExpr = v.getAnAccess() + } deprecated override predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e } @@ -214,7 +215,7 @@ private module PartialDefinitions { inner = innerDefinedExpr and outer = this } - + override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { innerDefinedExpr = v.getAnAccess() and cfn = node From 3414063f2eedf3444c65cf3232193e4270476e2c Mon Sep 17 00:00:00 2001 From: Calum Grant <42069085+calumgrant@users.noreply.github.com> Date: Fri, 11 Sep 2020 13:16:26 +0100 Subject: [PATCH 018/185] Update change-notes/1.25/analysis-python.md Co-authored-by: Rasmus Wriedt Larsen --- change-notes/1.25/analysis-python.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change-notes/1.25/analysis-python.md b/change-notes/1.25/analysis-python.md index 12586e8aec0..ed3496bc734 100644 --- a/change-notes/1.25/analysis-python.md +++ b/change-notes/1.25/analysis-python.md @@ -5,5 +5,5 @@ * Added taint modeling of routed parameters in Flask. * Improved modeling of built-in methods on strings for taint tracking. * Improved classification of test files. -* New class `BoundMethodValue` exposing information about a bound method. +* New class `BoundMethodValue` represents a bound method during runtime. * The query `py/command-line-injection` now recognizes command execution with the `fabric` and `invoke` Python libraries. From eb5782d90822bcf6f3551aa599c4e0a0dde0fd93 Mon Sep 17 00:00:00 2001 From: "lcartey@github.com" Date: Fri, 11 Sep 2020 17:12:10 +0100 Subject: [PATCH 019/185] C++: Support customizable ranges for RangeSsaDefinitions. --- .../interfaces/SimpleRangeAnalysisDef.qll | 34 +++++++++++++++++++ .../cpp/rangeanalysis/SimpleRangeAnalysis.qll | 10 ++++++ 2 files changed, 44 insertions(+) create mode 100644 cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDef.qll diff --git a/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDef.qll b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDef.qll new file mode 100644 index 00000000000..c2647e2bebc --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDef.qll @@ -0,0 +1,34 @@ +/** + * EXPERIMENTAL: The API of this module may change without notice. + * + * Provides a class for modeling `RangeSsaDefinition`s with a restricted range. + */ + +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * EXPERIMENTAL: The API of this class may change without notice. + * + * An SSA definition for which a range can be deduced. Extend this class to add + * functionality to the range analysis library. + */ +abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition { + /** + * Gets the lower bound of the defomotopn. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their children. + */ + abstract float getLowerBounds(); + + /** + * Gets the upper bound of the definition. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their children. + */ + abstract float getUpperBounds(); +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll index c8a6e70ce1f..4c61c06c829 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll @@ -45,6 +45,7 @@ import cpp private import RangeAnalysisUtils private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr +private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDef import RangeSSA import SimpleRangeAnalysisCached private import NanAnalysis @@ -492,6 +493,9 @@ private predicate analyzableDef(RangeSsaDefinition def, StackVariable v) { v = def.getAVariable() or phiDependsOnDef(def, v, _, _) + or + // A modeled def for range analysis + def.(SimpleRangeAnalysisDefinition).getAVariable() =v } /** @@ -1211,6 +1215,9 @@ private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) { // Phi nodes. result = getPhiLowerBounds(v, def) or + // A modeled def for range analysis + result = def.(SimpleRangeAnalysisDefinition).getLowerBounds() + or // Unanalyzable definitions. unanalyzableDefBounds(def, v, result, _) } @@ -1244,6 +1251,9 @@ private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) { // Phi nodes. result = getPhiUpperBounds(v, def) or + // A modeled def for range analysis + result = def.(SimpleRangeAnalysisDefinition).getUpperBounds() + or // Unanalyzable definitions. unanalyzableDefBounds(def, v, _, result) } From 826fc0a630cb4742875221c31ee3d9ab269b76ca Mon Sep 17 00:00:00 2001 From: Faten Healy <5361987+fatenhealy@users.noreply.github.com> Date: Sun, 13 Sep 2020 21:04:07 +1000 Subject: [PATCH 020/185] Update BrokenCryptoAlgorithm - Blowfish to AES --- python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp index 1b4031b1cc5..ba5ab4d10c1 100644 --- a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp +++ b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp @@ -33,7 +33,7 @@ pycrypto you must specify the encryption algorithm to use. The first example uses DES, which is an older algorithm that is now considered weak. The second - example uses Blowfish, which is a stronger more modern algorithm. + example uses AES, which is a stronger modern algorithm.

From 6f20516f8433bdab89610c805a360d5b0b9eb3ca Mon Sep 17 00:00:00 2001 From: Faten Healy <5361987+fatenhealy@users.noreply.github.com> Date: Sun, 13 Sep 2020 21:07:28 +1000 Subject: [PATCH 021/185] Update broken_crypto.py to AES instead of Blowfish --- python/ql/src/Security/CWE-327/examples/broken_crypto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/Security/CWE-327/examples/broken_crypto.py b/python/ql/src/Security/CWE-327/examples/broken_crypto.py index ef9fc75e889..690ed0096b1 100644 --- a/python/ql/src/Security/CWE-327/examples/broken_crypto.py +++ b/python/ql/src/Security/CWE-327/examples/broken_crypto.py @@ -6,7 +6,7 @@ def send_encrypted(channel, message): channel.send(cipher.encrypt(message)) # BAD: weak encryption -cipher = Blowfish.new(SECRET_KEY) +cipher = AES.new(SECRET_KEY) def send_encrypted(channel, message): channel.send(cipher.encrypt(message)) # GOOD: strong encryption From e54937756129301eebb6428f97a3bd03eb0929b3 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 14 Sep 2020 11:00:59 +0200 Subject: [PATCH 022/185] C#: Construct `File::TransformedPathLazy` lazily This avoids calling the path transformer for `GeneratedFile`s. --- csharp/extractor/Semmle.Extraction/Entities/File.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction/Entities/File.cs b/csharp/extractor/Semmle.Extraction/Entities/File.cs index a0e9ea6d725..1a5da1cbe1b 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/File.cs @@ -11,11 +11,12 @@ namespace Semmle.Extraction.Entities : base(cx, path) { OriginalPath = path; - TransformedPath = Context.Extractor.PathTransformer.Transform(OriginalPath); + TransformedPathLazy = new Lazy(() => Context.Extractor.PathTransformer.Transform(OriginalPath)); } readonly string OriginalPath; - readonly PathTransformer.ITransformedPath TransformedPath; + readonly Lazy TransformedPathLazy; + PathTransformer.ITransformedPath TransformedPath => TransformedPathLazy.Value; public override bool NeedsPopulation => Context.DefinesFile(OriginalPath) || OriginalPath == Context.Extractor.OutputPath; From 5f2cafc4f5571f06928b43cbb37d97b5a7044640 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 14 Sep 2020 14:36:19 -0700 Subject: [PATCH 023/185] C++: Interprocedural iterator flow --- .../code/cpp/dataflow/internal/FlowVar.qll | 43 ++++++++++++++++--- .../dataflow/taint-tests/localTaint.expected | 17 ++++++++ .../dataflow/taint-tests/taint.expected | 1 + .../dataflow/taint-tests/test_diff.expected | 1 + 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index c6929358543..0163d1e0ce7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -109,11 +109,10 @@ class FlowVar extends TFlowVar { * ``` */ private module PartialDefinitions { - class PartialDefinition extends Expr { + abstract class PartialDefinition extends Expr { ControlFlowNode node; PartialDefinition() { - valueToUpdate(_, this.getFullyConverted(), node) and not this instanceof Conversion } @@ -160,14 +159,39 @@ private module PartialDefinitions { class IteratorPartialDefinition extends PartialDefinition { Variable collection; - Call innerDefinedExpr; + Expr innerDefinedExpr; IteratorPartialDefinition() { exists(Expr convertedInner | valueToUpdate(convertedInner, this.getFullyConverted(), node) and innerDefinedExpr = convertedInner.getUnconverted() and - innerDefinedExpr.getQualifier() = getAnIteratorAccess(collection) and - innerDefinedExpr.getTarget() instanceof IteratorPointerDereferenceMemberOperator + ( + innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection) + or + innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and + collection instanceof IteratorParameter + ) and + innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator + ) + or + // iterators passed by value without a copy constructor + exists(Call call | + call = node and + call.getAnArgument() = innerDefinedExpr and + innerDefinedExpr = this and + this = getAnIteratorAccess(collection) and + not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator + ) + or + // iterators passed by value with a copy constructor + exists(Call call, ConstructorCall copy | + copy.getTarget() instanceof CopyConstructor and + call = node and + call.getAnArgument() = copy and + copy.getArgument(0) = getAnIteratorAccess(collection) and + innerDefinedExpr = this and + this = copy and + not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator ) } @@ -267,7 +291,8 @@ module FlowVar_internal { // The SSA library has a theoretically accurate treatment of reference types, // treating them as immutable, but for data flow it gives better results in // practice to make the variable synonymous with its contents. - not v.getUnspecifiedType() instanceof ReferenceType + not v.getUnspecifiedType() instanceof ReferenceType and + not v instanceof IteratorParameter } /** @@ -616,6 +641,8 @@ module FlowVar_internal { refType = p.getUnderlyingType() and not refType.getBaseType().isConst() ) + or + p instanceof IteratorParameter } /** @@ -778,6 +805,10 @@ module FlowVar_internal { ) } + class IteratorParameter extends Parameter { + IteratorParameter() { this.getUnspecifiedType() instanceof Iterator } + } + /** * Holds if `v` is initialized to have value `assignedExpr`. */ diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 13d38363b35..396fecf212f 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -439,23 +439,29 @@ | movableclass.cpp:65:13:65:18 | call to source | movableclass.cpp:65:13:65:20 | call to MyMovableClass | TAINT | | movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | ref arg s3 | TAINT | | movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:11:65:11 | call to operator= | TAINT | +| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:39:45:39:51 | source1 | | | standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:40:11:40:17 | source1 | | | standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:41:12:41:18 | source1 | | | standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:42:14:42:20 | source1 | | | standalone_iterators.cpp:40:11:40:17 | source1 | standalone_iterators.cpp:40:10:40:10 | call to operator* | TAINT | +| standalone_iterators.cpp:41:12:41:18 | ref arg source1 | standalone_iterators.cpp:39:45:39:51 | source1 | | | standalone_iterators.cpp:41:12:41:18 | ref arg source1 | standalone_iterators.cpp:42:14:42:20 | source1 | | | standalone_iterators.cpp:41:12:41:18 | source1 | standalone_iterators.cpp:41:19:41:19 | call to operator++ | TAINT | | standalone_iterators.cpp:41:19:41:19 | call to operator++ | standalone_iterators.cpp:41:10:41:10 | call to operator* | TAINT | | standalone_iterators.cpp:42:12:42:12 | call to operator++ | standalone_iterators.cpp:42:10:42:10 | call to operator* | TAINT | +| standalone_iterators.cpp:42:14:42:20 | ref arg source1 | standalone_iterators.cpp:39:45:39:51 | source1 | | | standalone_iterators.cpp:42:14:42:20 | source1 | standalone_iterators.cpp:42:12:42:12 | call to operator++ | TAINT | +| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:45:39:45:45 | source1 | | | standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:46:11:46:17 | source1 | | | standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:47:12:47:18 | source1 | | | standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:48:14:48:20 | source1 | | | standalone_iterators.cpp:46:11:46:17 | source1 | standalone_iterators.cpp:46:10:46:10 | call to operator* | TAINT | +| standalone_iterators.cpp:47:12:47:18 | ref arg source1 | standalone_iterators.cpp:45:39:45:45 | source1 | | | standalone_iterators.cpp:47:12:47:18 | ref arg source1 | standalone_iterators.cpp:48:14:48:20 | source1 | | | standalone_iterators.cpp:47:12:47:18 | source1 | standalone_iterators.cpp:47:19:47:19 | call to operator++ | TAINT | | standalone_iterators.cpp:47:19:47:19 | call to operator++ | standalone_iterators.cpp:47:10:47:10 | call to operator* | TAINT | | standalone_iterators.cpp:48:12:48:12 | call to operator++ | standalone_iterators.cpp:48:10:48:10 | call to operator* | TAINT | +| standalone_iterators.cpp:48:14:48:20 | ref arg source1 | standalone_iterators.cpp:45:39:45:45 | source1 | | | standalone_iterators.cpp:48:14:48:20 | source1 | standalone_iterators.cpp:48:12:48:12 | call to operator++ | TAINT | | standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:52:11:52:17 | source1 | | | standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:53:12:53:18 | source1 | | @@ -2967,10 +2973,13 @@ | vector.cpp:255:3:255:4 | ref arg v6 | vector.cpp:262:2:262:2 | v6 | | | vector.cpp:255:13:255:14 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT | | vector.cpp:255:13:255:14 | i1 | vector.cpp:255:13:255:14 | call to iterator | | +| vector.cpp:255:13:255:14 | ref arg call to iterator | vector.cpp:277:1:277:1 | v3 | | +| vector.cpp:255:13:255:14 | ref arg i1 | vector.cpp:277:1:277:1 | v3 | | | vector.cpp:255:17:255:18 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT | | vector.cpp:255:17:255:18 | i2 | vector.cpp:255:17:255:18 | call to iterator | | | vector.cpp:257:8:257:9 | ref arg v4 | vector.cpp:262:2:262:2 | v4 | | | vector.cpp:258:8:258:9 | ref arg v5 | vector.cpp:262:2:262:2 | v5 | | +| vector.cpp:259:8:259:9 | ref arg i1 | vector.cpp:277:1:277:1 | v3 | | | vector.cpp:261:8:261:9 | ref arg v6 | vector.cpp:262:2:262:2 | v6 | | | vector.cpp:265:22:265:23 | call to vector | vector.cpp:269:3:269:4 | v7 | | | vector.cpp:265:22:265:23 | call to vector | vector.cpp:273:8:273:9 | v7 | | @@ -3173,7 +3182,9 @@ | vector.cpp:324:7:324:8 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | | | vector.cpp:325:7:325:8 | ref arg v3 | vector.cpp:327:1:327:1 | v3 | | | vector.cpp:326:7:326:8 | ref arg v4 | vector.cpp:327:1:327:1 | v4 | | +| vector.cpp:329:62:329:65 | iter | vector.cpp:329:62:329:65 | iter | | | vector.cpp:329:62:329:65 | iter | vector.cpp:330:3:330:6 | iter | | +| vector.cpp:330:2:330:2 | call to operator* [post update] | vector.cpp:329:62:329:65 | iter | | | vector.cpp:330:2:330:17 | ... = ... | vector.cpp:330:2:330:2 | call to operator* [post update] | | | vector.cpp:330:3:330:6 | iter | vector.cpp:330:2:330:2 | call to operator* | TAINT | | vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:2 | call to operator* [post update] | TAINT | @@ -3279,6 +3290,12 @@ | vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | | | vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:32:351:33 | it | | | vector.cpp:351:32:351:33 | it | vector.cpp:351:32:351:33 | call to iterator | | +| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:350:56:350:57 | v4 | | +| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:353:7:353:8 | v4 | | +| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:382:1:382:1 | v4 | | +| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:350:56:350:57 | v4 | | +| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:353:7:353:8 | v4 | | +| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:382:1:382:1 | v4 | | | vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | | | vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:357:7:357:8 | v5 | | | vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index 6759dd90d60..889e1da17e6 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -328,6 +328,7 @@ | vector.cpp:326:7:326:8 | v4 | vector.cpp:318:15:318:20 | call to source | | vector.cpp:338:7:338:8 | v1 | vector.cpp:337:8:337:13 | call to source | | vector.cpp:343:7:343:8 | v2 | vector.cpp:341:9:341:14 | call to source | +| vector.cpp:353:7:353:8 | v4 | vector.cpp:330:10:330:15 | call to source | | vector.cpp:357:7:357:8 | v5 | vector.cpp:356:8:356:13 | call to source | | vector.cpp:359:7:359:8 | v5 | vector.cpp:356:8:356:13 | call to source | | vector.cpp:363:7:363:8 | v6 | vector.cpp:362:8:362:13 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 8fbeb0c246c..0f6b7a574a5 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -277,6 +277,7 @@ | vector.cpp:326:7:326:8 | vector.cpp:318:15:318:20 | AST only | | vector.cpp:338:7:338:8 | vector.cpp:337:8:337:13 | AST only | | vector.cpp:343:7:343:8 | vector.cpp:341:9:341:14 | AST only | +| vector.cpp:353:7:353:8 | vector.cpp:330:10:330:15 | AST only | | vector.cpp:357:7:357:8 | vector.cpp:356:8:356:13 | AST only | | vector.cpp:359:7:359:8 | vector.cpp:356:8:356:13 | AST only | | vector.cpp:363:7:363:8 | vector.cpp:362:8:362:13 | AST only | From d728c3948c85b2bf782b38693071e44933f1bf82 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 15 Sep 2020 09:17:42 +0100 Subject: [PATCH 024/185] JS: Log the amount of memory passed to TypeScript process --- .../extractor/src/com/semmle/js/parser/TypeScriptParser.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java index 2dea7826879..c091e39af55 100644 --- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java @@ -299,6 +299,8 @@ public class TypeScriptParser { : getMegabyteCountFromPrefixedEnv(TYPESCRIPT_RAM_SUFFIX, 2000); int reserveMemoryMb = getMegabyteCountFromPrefixedEnv(TYPESCRIPT_RAM_RESERVE_SUFFIX, 400); + System.out.println("Memory for TypeScript process: " + mainMemoryMb + " MB, and " + reserveMemoryMb + " MB reserve"); + File parserWrapper = getParserWrapper(); String debugFlagString = Env.systemEnv().getNonEmpty(TYPESCRIPT_NODE_FLAGS); From fa0e27b2de6c9fa8d9fbe500b4ba7accda8b0cbe Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 16 Sep 2020 12:34:52 -0700 Subject: [PATCH 025/185] C++: move interprocedural iterator flow to taint --- .../code/cpp/dataflow/internal/DataFlowUtil.qll | 6 +++--- .../semmle/code/cpp/dataflow/internal/FlowVar.qll | 12 ++++++++---- .../cpp/dataflow/internal/TaintTrackingUtil.qll | 15 ++++++++++----- .../library-tests/dataflow/taint-tests/vector.cpp | 9 ++++++++- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 9705ca492ce..1103e50ce16 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -303,7 +303,7 @@ private class VariablePartialDefinitionNode extends PartialDefinitionNode { override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) } } -private class IteratorPartialDefinitionNode extends PartialDefinitionNode { +class IteratorPartialDefinitionNode extends PartialDefinitionNode { override IteratorPartialDefinition pd; override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) } @@ -546,10 +546,10 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { or // In `f(&x->a)`, this step provides the flow from post-`&` to post-`x->a`, // from which there is field flow to `x` via reverse read. - exists(PartialDefinition def, Expr inner, Expr outer | + exists(VariablePartialDefinition def, Expr inner, Expr outer | def.definesExpressions(inner, outer) and inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and - outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr() + outer = nodeFrom.(VariablePartialDefinitionNode).getPreUpdateNode().asExpr() ) or // Reverse flow: data that flows from the post-update node of a reference diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index 0163d1e0ce7..d4c264184d7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -109,7 +109,7 @@ class FlowVar extends TFlowVar { * ``` */ private module PartialDefinitions { - abstract class PartialDefinition extends Expr { + class PartialDefinition extends Expr { ControlFlowNode node; PartialDefinition() { @@ -216,8 +216,7 @@ private module PartialDefinitions { VariablePartialDefinition() { exists(Expr convertedInner | valueToUpdate(convertedInner, this.getFullyConverted(), node) and - innerDefinedExpr = convertedInner.getUnconverted() and - not this instanceof Conversion + innerDefinedExpr = convertedInner.getUnconverted() ) } @@ -249,11 +248,16 @@ private module PartialDefinitions { /** * A partial definition that's a definition by reference. */ - class DefinitionByReference extends PartialDefinition { + class DefinitionByReference extends VariablePartialDefinition { DefinitionByReference() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) } } } +predicate quickTest(PartialDefinition pd) { + pd instanceof DefinitionByReference and + pd instanceof IteratorPartialDefinition +} + import PartialDefinitions private import FlowVar_internal diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll index c8d963866a1..f52c3cddf7e 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -31,6 +31,11 @@ predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) { */ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { localAdditionalTaintStep(src, sink) + or + exists(FunctionCall call, int i | + sink.(DataFlow::IteratorPartialDefinitionNode).getPartialDefinition().definesExpressions(_, call.getArgument(i)) and + src.(DataFlow::RefParameterFinalValueNode).getParameter() = call.getTarget().getParameter(i) + ) } /** @@ -258,9 +263,9 @@ private predicate exprToPartialDefinitionStep(Expr exprIn, Expr exprOut) { } private predicate iteratorDereference(Call c) { - c.getTarget() instanceof IteratorArrayMemberOperator - or - c.getTarget() instanceof IteratorPointerDereferenceMemberOperator - or - c.getTarget() instanceof IteratorPointerDereferenceOperator + c.getTarget() instanceof IteratorArrayMemberOperator + or + c.getTarget() instanceof IteratorPointerDereferenceMemberOperator + or + c.getTarget() instanceof IteratorPointerDereferenceOperator } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp index 153385a202d..4fc63acbad2 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp @@ -331,7 +331,7 @@ void taint_vector_output_iterator(std::vector::iterator iter) { } void test_vector_output_iterator(int b) { - std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10); + std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10), v9(10); std::vector::iterator i1 = v1.begin(); *i1 = source(); @@ -379,4 +379,11 @@ void test_vector_output_iterator(int b) { sink(v8); // tainted [NOT DETECTED by IR] *i8 = 1; sink(v8); + + std::vector::iterator i9 = v9.begin(); + + *i9 = source(); + taint_vector_output_iterator(i9); + + sink(v9); } From 086d074a26e55d45ff6e7c70a91124eee0c3fac7 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 16 Sep 2020 12:48:38 -0700 Subject: [PATCH 026/185] C++: make PartialDefinition abstract --- .../src/semmle/code/cpp/dataflow/internal/FlowVar.qll | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index d4c264184d7..45a537f8049 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -109,16 +109,16 @@ class FlowVar extends TFlowVar { * ``` */ private module PartialDefinitions { - class PartialDefinition extends Expr { + abstract class PartialDefinition extends Expr { ControlFlowNode node; PartialDefinition() { not this instanceof Conversion } - deprecated predicate partiallyDefines(Variable v) { none() } + deprecated abstract predicate partiallyDefines(Variable v); - deprecated predicate partiallyDefinesThis(ThisExpr e) { none() } + deprecated abstract predicate partiallyDefinesThis(ThisExpr e); /** * Gets the subBasicBlock where this `PartialDefinition` is defined. @@ -131,7 +131,7 @@ private module PartialDefinitions { */ // does this work with a dispred? pragma[noinline] - predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { none() } + abstract predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn); /** * Holds if this partial definition may modify `inner` (or what it points @@ -141,7 +141,7 @@ private module PartialDefinitions { * - `inner` = `... .x`, `outer` = `&...` * - `inner` = `a`, `outer` = `*` */ - predicate definesExpressions(Expr inner, Expr outer) { none() } + abstract predicate definesExpressions(Expr inner, Expr outer); /** * Gets the location of this element, adjusted to avoid unknown locations From 44c5233459a67643a405200398fb5dc70c60fea2 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 16 Sep 2020 12:49:15 -0700 Subject: [PATCH 027/185] C++: accept test output --- .../dataflow/taint-tests/localTaint.expected | 116 ++++++++++-------- .../dataflow/taint-tests/taint.expected | 2 + .../dataflow/taint-tests/test_diff.expected | 2 + 3 files changed, 72 insertions(+), 48 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 396fecf212f..e5e4905e395 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -2972,9 +2972,9 @@ | vector.cpp:255:3:255:4 | ref arg v6 | vector.cpp:261:8:261:9 | v6 | | | vector.cpp:255:3:255:4 | ref arg v6 | vector.cpp:262:2:262:2 | v6 | | | vector.cpp:255:13:255:14 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT | +| vector.cpp:255:13:255:14 | call to iterator [post update] | vector.cpp:277:1:277:1 | v3 | | | vector.cpp:255:13:255:14 | i1 | vector.cpp:255:13:255:14 | call to iterator | | -| vector.cpp:255:13:255:14 | ref arg call to iterator | vector.cpp:277:1:277:1 | v3 | | -| vector.cpp:255:13:255:14 | ref arg i1 | vector.cpp:277:1:277:1 | v3 | | +| vector.cpp:255:13:255:14 | i1 [post update] | vector.cpp:277:1:277:1 | v3 | | | vector.cpp:255:17:255:18 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT | | vector.cpp:255:17:255:18 | i2 | vector.cpp:255:17:255:18 | call to iterator | | | vector.cpp:257:8:257:9 | ref arg v4 | vector.cpp:262:2:262:2 | v4 | | @@ -3192,57 +3192,60 @@ | vector.cpp:333:38:333:38 | b | vector.cpp:368:5:368:5 | b | | | vector.cpp:334:22:334:24 | call to vector | vector.cpp:336:34:336:35 | v1 | | | vector.cpp:334:22:334:24 | call to vector | vector.cpp:338:7:338:8 | v1 | | -| vector.cpp:334:22:334:24 | call to vector | vector.cpp:382:1:382:1 | v1 | | +| vector.cpp:334:22:334:24 | call to vector | vector.cpp:389:1:389:1 | v1 | | | vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:38:340:39 | v2 | | | vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:56:340:57 | v2 | | | vector.cpp:334:30:334:32 | call to vector | vector.cpp:343:7:343:8 | v2 | | -| vector.cpp:334:30:334:32 | call to vector | vector.cpp:382:1:382:1 | v2 | | +| vector.cpp:334:30:334:32 | call to vector | vector.cpp:389:1:389:1 | v2 | | | vector.cpp:334:38:334:40 | call to vector | vector.cpp:345:15:345:16 | v3 | | | vector.cpp:334:38:334:40 | call to vector | vector.cpp:348:7:348:8 | v3 | | -| vector.cpp:334:38:334:40 | call to vector | vector.cpp:382:1:382:1 | v3 | | +| vector.cpp:334:38:334:40 | call to vector | vector.cpp:389:1:389:1 | v3 | | | vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:38:350:39 | v4 | | | vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:56:350:57 | v4 | | | vector.cpp:334:46:334:48 | call to vector | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:334:46:334:48 | call to vector | vector.cpp:382:1:382:1 | v4 | | +| vector.cpp:334:46:334:48 | call to vector | vector.cpp:389:1:389:1 | v4 | | | vector.cpp:334:54:334:56 | call to vector | vector.cpp:355:34:355:35 | v5 | | | vector.cpp:334:54:334:56 | call to vector | vector.cpp:357:7:357:8 | v5 | | | vector.cpp:334:54:334:56 | call to vector | vector.cpp:359:7:359:8 | v5 | | -| vector.cpp:334:54:334:56 | call to vector | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:334:54:334:56 | call to vector | vector.cpp:389:1:389:1 | v5 | | | vector.cpp:334:62:334:64 | call to vector | vector.cpp:361:34:361:35 | v6 | | | vector.cpp:334:62:334:64 | call to vector | vector.cpp:363:7:363:8 | v6 | | | vector.cpp:334:62:334:64 | call to vector | vector.cpp:364:2:364:3 | v6 | | | vector.cpp:334:62:334:64 | call to vector | vector.cpp:365:7:365:8 | v6 | | -| vector.cpp:334:62:334:64 | call to vector | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:334:62:334:64 | call to vector | vector.cpp:389:1:389:1 | v6 | | | vector.cpp:334:70:334:72 | call to vector | vector.cpp:367:34:367:35 | v7 | | | vector.cpp:334:70:334:72 | call to vector | vector.cpp:370:8:370:9 | v7 | | | vector.cpp:334:70:334:72 | call to vector | vector.cpp:373:8:373:9 | v7 | | | vector.cpp:334:70:334:72 | call to vector | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:334:70:334:72 | call to vector | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:334:70:334:72 | call to vector | vector.cpp:389:1:389:1 | v7 | | | vector.cpp:334:78:334:80 | call to vector | vector.cpp:377:34:377:35 | v8 | | | vector.cpp:334:78:334:80 | call to vector | vector.cpp:379:7:379:8 | v8 | | | vector.cpp:334:78:334:80 | call to vector | vector.cpp:381:7:381:8 | v8 | | -| vector.cpp:334:78:334:80 | call to vector | vector.cpp:382:1:382:1 | v8 | | +| vector.cpp:334:78:334:80 | call to vector | vector.cpp:389:1:389:1 | v8 | | +| vector.cpp:334:86:334:88 | call to vector | vector.cpp:383:34:383:35 | v9 | | +| vector.cpp:334:86:334:88 | call to vector | vector.cpp:388:7:388:8 | v9 | | +| vector.cpp:334:86:334:88 | call to vector | vector.cpp:389:1:389:1 | v9 | | | vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:338:7:338:8 | v1 | | -| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:382:1:382:1 | v1 | | +| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:389:1:389:1 | v1 | | | vector.cpp:336:34:336:35 | v1 | vector.cpp:336:37:336:41 | call to begin | TAINT | | vector.cpp:336:37:336:41 | call to begin | vector.cpp:337:3:337:4 | i1 | | | vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:338:7:338:8 | v1 | | -| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v1 | | +| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v1 | | | vector.cpp:337:2:337:15 | ... = ... | vector.cpp:337:2:337:2 | call to operator* [post update] | | | vector.cpp:337:3:337:4 | i1 | vector.cpp:337:2:337:2 | call to operator* | TAINT | | vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:2 | call to operator* [post update] | TAINT | | vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:15 | ... = ... | | -| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:382:1:382:1 | v1 | | +| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:389:1:389:1 | v1 | | | vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | | | vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | | -| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | | +| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | | | vector.cpp:340:38:340:39 | v2 | vector.cpp:340:41:340:45 | call to begin | TAINT | | vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:50:340:51 | it | | | vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:68:340:69 | it | | | vector.cpp:340:41:340:45 | call to begin | vector.cpp:341:4:341:5 | it | | | vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | | | vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | | -| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | | +| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | | | vector.cpp:340:56:340:57 | v2 | vector.cpp:340:59:340:61 | call to end | TAINT | | vector.cpp:340:68:340:69 | it | vector.cpp:340:66:340:66 | call to operator++ | TAINT | | vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:50:340:51 | it | | @@ -3250,12 +3253,12 @@ | vector.cpp:340:68:340:69 | ref arg it | vector.cpp:341:4:341:5 | it | | | vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:340:56:340:57 | v2 | | | vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:343:7:343:8 | v2 | | -| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v2 | | +| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v2 | | | vector.cpp:341:3:341:16 | ... = ... | vector.cpp:341:3:341:3 | call to operator* [post update] | | | vector.cpp:341:4:341:5 | it | vector.cpp:341:3:341:3 | call to operator* | TAINT | | vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:3 | call to operator* [post update] | TAINT | | vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:16 | ... = ... | | -| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | | +| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | | | vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator* | TAINT | | vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator++ | TAINT | | vector.cpp:345:15:345:15 | (__end) | vector.cpp:345:15:345:15 | call to iterator | | @@ -3273,119 +3276,136 @@ | vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | | | vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | call to operator* | TAINT | | vector.cpp:346:7:346:12 | call to source | vector.cpp:346:3:346:14 | ... = ... | | -| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:382:1:382:1 | v3 | | +| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:389:1:389:1 | v3 | | | vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | | | vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | | +| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | | | vector.cpp:350:38:350:39 | v4 | vector.cpp:350:41:350:45 | call to begin | TAINT | | vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:50:350:51 | it | | | vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:68:350:69 | it | | | vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:32:351:33 | it | | | vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | | | vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | | +| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | | | vector.cpp:350:56:350:57 | v4 | vector.cpp:350:59:350:61 | call to end | TAINT | | vector.cpp:350:68:350:69 | it | vector.cpp:350:66:350:66 | call to operator++ | TAINT | | vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:50:350:51 | it | | | vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | | | vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:32:351:33 | it | | +| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:350:56:350:57 | v4 | | +| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:353:7:353:8 | v4 | | +| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:389:1:389:1 | v4 | | | vector.cpp:351:32:351:33 | it | vector.cpp:351:32:351:33 | call to iterator | | -| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:350:56:350:57 | v4 | | -| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:382:1:382:1 | v4 | | -| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:350:56:350:57 | v4 | | -| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:382:1:382:1 | v4 | | -| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | | +| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:350:56:350:57 | v4 | | +| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:353:7:353:8 | v4 | | +| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:389:1:389:1 | v4 | | +| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | | | vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:357:7:357:8 | v5 | | | vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | | -| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | | | vector.cpp:355:34:355:35 | v5 | vector.cpp:355:37:355:41 | call to begin | TAINT | | vector.cpp:355:37:355:41 | call to begin | vector.cpp:356:3:356:4 | i5 | | | vector.cpp:355:37:355:41 | call to begin | vector.cpp:358:3:358:4 | i5 | | | vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:357:7:357:8 | v5 | | | vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | | -| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v5 | | | vector.cpp:356:2:356:15 | ... = ... | vector.cpp:356:2:356:2 | call to operator* [post update] | | | vector.cpp:356:3:356:4 | i5 | vector.cpp:356:2:356:2 | call to operator* | TAINT | | vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:2 | call to operator* [post update] | TAINT | | vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:15 | ... = ... | | | vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | | -| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | | | vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | | -| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v5 | | | vector.cpp:358:2:358:8 | ... = ... | vector.cpp:358:2:358:2 | call to operator* [post update] | | | vector.cpp:358:3:358:4 | i5 | vector.cpp:358:2:358:2 | call to operator* | TAINT | | vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:2 | call to operator* [post update] | TAINT | | vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:8 | ... = ... | | -| vector.cpp:359:7:359:8 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | | +| vector.cpp:359:7:359:8 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | | | vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:363:7:363:8 | v6 | | | vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | | | vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | | -| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | | | vector.cpp:361:34:361:35 | v6 | vector.cpp:361:37:361:41 | call to begin | TAINT | | vector.cpp:361:37:361:41 | call to begin | vector.cpp:362:3:362:4 | i6 | | | vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v6 | | | vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:364:2:364:3 | v6 | | | vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:365:7:365:8 | v6 | | -| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v6 | | | vector.cpp:362:2:362:15 | ... = ... | vector.cpp:362:2:362:2 | call to operator* [post update] | | | vector.cpp:362:3:362:4 | i6 | vector.cpp:362:2:362:2 | call to operator* | TAINT | | vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:2 | call to operator* [post update] | TAINT | | vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:15 | ... = ... | | | vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | | | vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | | -| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | | | vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | | -| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | | | vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:2:364:3 | ref arg v6 | TAINT | | vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:5:364:5 | call to operator= | TAINT | -| vector.cpp:365:7:365:8 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | | +| vector.cpp:365:7:365:8 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | | | vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:370:8:370:9 | v7 | | | vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:373:8:373:9 | v7 | | | vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | | | vector.cpp:367:34:367:35 | v7 | vector.cpp:367:37:367:41 | call to begin | TAINT | | vector.cpp:367:37:367:41 | call to begin | vector.cpp:369:4:369:5 | i7 | | | vector.cpp:367:37:367:41 | call to begin | vector.cpp:372:4:372:5 | i7 | | | vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:370:8:370:9 | v7 | | | vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v7 | | | vector.cpp:369:3:369:16 | ... = ... | vector.cpp:369:3:369:3 | call to operator* [post update] | | | vector.cpp:369:4:369:5 | i7 | vector.cpp:369:3:369:3 | call to operator* | TAINT | | vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:3 | call to operator* [post update] | TAINT | | vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:16 | ... = ... | | | vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | | | vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:373:8:373:9 | v7 | | | vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v7 | | | vector.cpp:372:3:372:9 | ... = ... | vector.cpp:372:3:372:3 | call to operator* [post update] | | | vector.cpp:372:4:372:5 | i7 | vector.cpp:372:3:372:3 | call to operator* | TAINT | | vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:3 | call to operator* [post update] | TAINT | | vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:9 | ... = ... | | | vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | | -| vector.cpp:375:7:375:8 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | | +| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | | +| vector.cpp:375:7:375:8 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | | | vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:379:7:379:8 | v8 | | | vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | | -| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | | +| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | | | vector.cpp:377:34:377:35 | v8 | vector.cpp:377:37:377:41 | call to begin | TAINT | | vector.cpp:377:37:377:41 | call to begin | vector.cpp:378:3:378:4 | i8 | | | vector.cpp:377:37:377:41 | call to begin | vector.cpp:380:3:380:4 | i8 | | | vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:379:7:379:8 | v8 | | | vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | | -| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v8 | | +| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v8 | | | vector.cpp:378:2:378:15 | ... = ... | vector.cpp:378:2:378:2 | call to operator* [post update] | | | vector.cpp:378:3:378:4 | i8 | vector.cpp:378:2:378:2 | call to operator* | TAINT | | vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:2 | call to operator* [post update] | TAINT | | vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:15 | ... = ... | | | vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | | -| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | | +| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | | | vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | | -| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v8 | | +| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v8 | | | vector.cpp:380:2:380:8 | ... = ... | vector.cpp:380:2:380:2 | call to operator* [post update] | | | vector.cpp:380:3:380:4 | i8 | vector.cpp:380:2:380:2 | call to operator* | TAINT | | vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:2 | call to operator* [post update] | TAINT | | vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:8 | ... = ... | | -| vector.cpp:381:7:381:8 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | | +| vector.cpp:381:7:381:8 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | | +| vector.cpp:383:34:383:35 | ref arg v9 | vector.cpp:388:7:388:8 | v9 | | +| vector.cpp:383:34:383:35 | ref arg v9 | vector.cpp:389:1:389:1 | v9 | | +| vector.cpp:383:34:383:35 | v9 | vector.cpp:383:37:383:41 | call to begin | TAINT | +| vector.cpp:383:37:383:41 | call to begin | vector.cpp:385:3:385:4 | i9 | | +| vector.cpp:383:37:383:41 | call to begin | vector.cpp:386:31:386:32 | i9 | | +| vector.cpp:385:2:385:2 | call to operator* [post update] | vector.cpp:388:7:388:8 | v9 | | +| vector.cpp:385:2:385:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v9 | | +| vector.cpp:385:2:385:15 | ... = ... | vector.cpp:385:2:385:2 | call to operator* [post update] | | +| vector.cpp:385:3:385:4 | i9 | vector.cpp:385:2:385:2 | call to operator* | TAINT | +| vector.cpp:385:8:385:13 | call to source | vector.cpp:385:2:385:2 | call to operator* [post update] | TAINT | +| vector.cpp:385:8:385:13 | call to source | vector.cpp:385:2:385:15 | ... = ... | | +| vector.cpp:386:31:386:32 | call to iterator [post update] | vector.cpp:388:7:388:8 | v9 | | +| vector.cpp:386:31:386:32 | call to iterator [post update] | vector.cpp:389:1:389:1 | v9 | | +| vector.cpp:386:31:386:32 | i9 | vector.cpp:386:31:386:32 | call to iterator | | +| vector.cpp:386:31:386:32 | i9 [post update] | vector.cpp:388:7:388:8 | v9 | | +| vector.cpp:386:31:386:32 | i9 [post update] | vector.cpp:389:1:389:1 | v9 | | +| vector.cpp:388:7:388:8 | ref arg v9 | vector.cpp:389:1:389:1 | v9 | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index 889e1da17e6..01ef2e1a494 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -337,3 +337,5 @@ | vector.cpp:375:7:375:8 | v7 | vector.cpp:369:9:369:14 | call to source | | vector.cpp:379:7:379:8 | v8 | vector.cpp:378:8:378:13 | call to source | | vector.cpp:381:7:381:8 | v8 | vector.cpp:378:8:378:13 | call to source | +| vector.cpp:388:7:388:8 | v9 | vector.cpp:330:10:330:15 | call to source | +| vector.cpp:388:7:388:8 | v9 | vector.cpp:385:8:385:13 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 0f6b7a574a5..173bb28f831 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -286,3 +286,5 @@ | vector.cpp:375:7:375:8 | vector.cpp:369:9:369:14 | AST only | | vector.cpp:379:7:379:8 | vector.cpp:378:8:378:13 | AST only | | vector.cpp:381:7:381:8 | vector.cpp:378:8:378:13 | AST only | +| vector.cpp:388:7:388:8 | vector.cpp:330:10:330:15 | AST only | +| vector.cpp:388:7:388:8 | vector.cpp:385:8:385:13 | AST only | From 107e9770da5ebde3e06ba7fefd5952a0154ed105 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 18 Sep 2020 14:12:33 -0700 Subject: [PATCH 028/185] C++: remove accidentally committed test code --- cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index 45a537f8049..b9e5eadfcf5 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -253,11 +253,6 @@ private module PartialDefinitions { } } -predicate quickTest(PartialDefinition pd) { - pd instanceof DefinitionByReference and - pd instanceof IteratorPartialDefinition -} - import PartialDefinitions private import FlowVar_internal From b84bf5e9bb5a3a4562592a558290d476b68f83d3 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 18 Sep 2020 14:18:38 -0700 Subject: [PATCH 029/185] C++: QLDoc for IteratorPartialDefinitionNode --- .../src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index faa242aec64..d71b3a4ad62 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -304,6 +304,12 @@ private class VariablePartialDefinitionNode extends PartialDefinitionNode { override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) } } +/** + * INTERNAL: do not use. + * + * A synthetic data flow node used for flow into a collection when an iterator + * write occurs in a callee. + */ class IteratorPartialDefinitionNode extends PartialDefinitionNode { override IteratorPartialDefinition pd; From bd7f5a41d1016b001fa1f8dbf3988fac6d93cfb1 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 18 Sep 2020 14:19:29 -0700 Subject: [PATCH 030/185] C++: autoformat --- .../semmle/code/cpp/dataflow/internal/DataFlowUtil.qll | 8 +++----- cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index d71b3a4ad62..963f1a17826 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -284,12 +284,10 @@ abstract class PostUpdateNode extends Node { override Location getLocation() { result = getPreUpdateNode().getLocation() } } -private abstract class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode { +abstract private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode { PartialDefinition pd; - PartialDefinitionNode() { - this = TPartialDefinitionNode(pd) - } + PartialDefinitionNode() { this = TPartialDefinitionNode(pd) } override Location getLocation() { result = pd.getActualLocation() } @@ -306,7 +304,7 @@ private class VariablePartialDefinitionNode extends PartialDefinitionNode { /** * INTERNAL: do not use. - * + * * A synthetic data flow node used for flow into a collection when an iterator * write occurs in a callee. */ diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index b9e5eadfcf5..13f11f8b275 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -112,13 +112,11 @@ private module PartialDefinitions { abstract class PartialDefinition extends Expr { ControlFlowNode node; - PartialDefinition() { - not this instanceof Conversion - } + PartialDefinition() { not this instanceof Conversion } - deprecated abstract predicate partiallyDefines(Variable v); + abstract deprecated predicate partiallyDefines(Variable v); - deprecated abstract predicate partiallyDefinesThis(ThisExpr e); + abstract deprecated predicate partiallyDefinesThis(ThisExpr e); /** * Gets the subBasicBlock where this `PartialDefinition` is defined. From a2d006fe479ed4ca35457fa9c98da17cb2572da8 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 16 Sep 2020 12:55:19 +0200 Subject: [PATCH 031/185] Python: Tests for field flow --- .../dataflow/fieldflow/dataflow.expected | 3 + .../dataflow/fieldflow/dataflow.ql | 10 ++++ .../dataflow/fieldflow/examples.py | 50 +++++++++++++++++ .../experimental/dataflow/fieldflow/test.py | 56 +++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 python/ql/test/experimental/dataflow/fieldflow/dataflow.expected create mode 100644 python/ql/test/experimental/dataflow/fieldflow/dataflow.ql create mode 100644 python/ql/test/experimental/dataflow/fieldflow/examples.py create mode 100644 python/ql/test/experimental/dataflow/fieldflow/test.py diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected new file mode 100644 index 00000000000..58e3dda0964 --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected @@ -0,0 +1,3 @@ +edges +nodes +#select diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.ql b/python/ql/test/experimental/dataflow/fieldflow/dataflow.ql new file mode 100644 index 00000000000..1347c440496 --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.ql @@ -0,0 +1,10 @@ +/** + * @kind path-problem + */ + +import experimental.dataflow.testConfig +import DataFlow::PathGraph + +from TestConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "" diff --git a/python/ql/test/experimental/dataflow/fieldflow/examples.py b/python/ql/test/experimental/dataflow/fieldflow/examples.py new file mode 100644 index 00000000000..ed961438822 --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/examples.py @@ -0,0 +1,50 @@ +from python.ql.test.experimental.dataflow.testDefinitions import * + +# Preamble + +class MyObj(object): + + def __init__(self, foo): + self.foo = foo + + +class NestedObj(object): + + def __init__(self): + self.obj = MyObj("OK") + + def getObj(self): + return self.obj + + +# Example 1 +def setFoo(obj, x): + SINK_F(obj.foo) + obj.foo = x + +myobj = MyObj("OK") + +setFoo(myobj, SOURCE) +SINK(myobj.foo) + +# Example 2 +x = SOURCE + +a = NestedObj() + +a.obj.foo = x +a.getObj().foo = x + +SINK(a.obj.foo) + +# Example 3 +obj = MyObj(SOURCE) +SINK(obj.foo) + +# Local flow +def fields_with_local_flow(x): + obj = MyObj(x) + a = obj.foo + return a + +SINK(fields_with_local_flow(SOURCE)) \ No newline at end of file diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py new file mode 100644 index 00000000000..a747db14e24 --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/test.py @@ -0,0 +1,56 @@ +from python.ql.test.experimental.dataflow.testDefinitions import * + +# Preamble + + +class MyObj(object): + + def __init__(self, foo): + self.foo = foo + + +class NestedObj(object): + + def __init__(self): + self.obj = MyObj("OK") + + def getObj(self): + return self.obj + + +def setFoo(obj, x): + SINK_F(obj.foo) + obj.foo = x + + +def test_example1(): + myobj = MyObj("OK") + + setFoo(myobj, SOURCE) + SINK(myobj.foo) + + +def test_example2(): + x = SOURCE + + a = NestedObj() + + a.obj.foo = x + a.getObj().foo = x + + SINK(a.obj.foo) + + +def test_example3(): + obj = MyObj(SOURCE) + SINK(obj.foo) + + +def fields_with_local_flow(x): + obj = MyObj(x) + a = obj.foo + return a + + +def test_fields(): + SINK(fields_with_local_flow(SOURCE)) From 27b25565ca78a99c0565e65b767c8425d4ff28ee Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 16 Sep 2020 13:00:09 +0200 Subject: [PATCH 032/185] Python: Implement field-stores, -reads, and -content --- .../dataflow/internal/DataFlowPrivate.qll | 46 +++- .../dataflow/internal/DataFlowPublic.qll | 24 +- .../dataflow/fieldflow/allLocalFlow.expected | 40 ++++ .../dataflow/fieldflow/allLocalFlow.ql | 8 + .../dataflow/fieldflow/dataflow.expected | 22 ++ .../dataflow/fieldflow/globalStep.expected | 221 ++++++++++++++++++ .../dataflow/fieldflow/globalStep.ql | 9 + .../dataflow/fieldflow/localFlow.expected | 12 + .../dataflow/fieldflow/localFlow.ql | 8 + .../dataflow/fieldflow/postupdates.expected | 42 ++++ .../dataflow/fieldflow/postupdates.ql | 4 + 11 files changed, 427 insertions(+), 9 deletions(-) create mode 100644 python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected create mode 100644 python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql create mode 100644 python/ql/test/experimental/dataflow/fieldflow/globalStep.expected create mode 100644 python/ql/test/experimental/dataflow/fieldflow/globalStep.ql create mode 100644 python/ql/test/experimental/dataflow/fieldflow/localFlow.expected create mode 100644 python/ql/test/experimental/dataflow/fieldflow/localFlow.ql create mode 100644 python/ql/test/experimental/dataflow/fieldflow/postupdates.expected create mode 100644 python/ql/test/experimental/dataflow/fieldflow/postupdates.ql diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index a0edce84f51..b999ebb5982 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -361,7 +361,7 @@ class DataFlowType extends TDataFlowType { } /** A node that performs a type cast. */ -class CastNode extends Node { +class CastNode extends CfgNode { CastNode() { none() } } @@ -423,6 +423,8 @@ predicate storeStep(Node nodeFrom, Content c, Node nodeTo) { dictStoreStep(nodeFrom, c, nodeTo) or comprehensionStoreStep(nodeFrom, c, nodeTo) + or + attributeStoreStep(nodeFrom, c, nodeTo) } /** Data flows from an element of a list to the list. */ @@ -497,6 +499,30 @@ predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) { c instanceof ListElementContent } +/** + * In + * ```python + * obj.foo = x + * ``` + * data flows from `x` to (the post-update node for) `obj` via assignment to `foo`. + */ +predicate attributeStoreStep(CfgNode nodeFrom, Content c, PostUpdateNode nodeTo) { + exists(AssignStmt a, Attribute attr | + a.getValue().getAFlowNode() = nodeFrom.getNode() and + a.getATarget().(Attribute) = attr and + attr.getName() = c.(AttributeContent).getAttribute() and + attr.getObject().getAFlowNode() = nodeTo.getPreUpdateNode().(CfgNode).getNode() and + attr.getCtx() instanceof Store + ) + or + exists(AssignExpr ae | + ae.getValue().getAFlowNode() = nodeFrom.getNode() and + ae.getTarget().(Attribute).getName() = c.(AttributeContent).getAttribute() and + ae.getTarget().(Attribute).getObject().getAFlowNode() = + nodeTo.getPreUpdateNode().(CfgNode).getNode() + ) +} + /** * Holds if data can flow from `nodeFrom` to `nodeTo` via a read of content `c`. */ @@ -506,6 +532,8 @@ predicate readStep(Node nodeFrom, Content c, Node nodeTo) { popReadStep(nodeFrom, c, nodeTo) or comprehensionReadStep(nodeFrom, c, nodeTo) + or + attributeReadStep(nodeFrom, c, nodeTo) } /** Data flows from a sequence to a subscript of the sequence. */ @@ -592,6 +620,22 @@ predicate comprehensionReadStep(CfgNode nodeFrom, Content c, EssaNode nodeTo) { ) } +/** + * In + * ```python + * obj.foo + * ``` + * data flows from `obj` to `obj.foo` via a read from `foo`. + */ +predicate attributeReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) { + exists(Attribute attr | + nodeTo.asCfgNode().(AttrNode).getNode() = attr and + nodeFrom.asCfgNode() = attr.getObject().getAFlowNode() and + attr.getName() = c.(AttributeContent).getAttribute() and + attr.getCtx() instanceof Load + ) +} + /** * Holds if values stored inside content `c` are cleared at node `n`. For example, * any value stored inside `f` is cleared at the pre-update node associated with `x` diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 97626b8951e..e9e143ca7ad 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -202,7 +202,9 @@ newtype TContent = key = any(Keyword kw).getArg() } or /** An element of a dictionary at any key. */ - TDictionaryElementAnyContent() + TDictionaryElementAnyContent() or + /** An object attribute. */ + TAttributeContent(string attr) { attr = any(Attribute a).getName() } class Content extends TContent { /** Gets a textual representation of this element. */ @@ -210,12 +212,10 @@ class Content extends TContent { } class ListElementContent extends TListElementContent, Content { - /** Gets a textual representation of this element. */ override string toString() { result = "List element" } } class SetElementContent extends TSetElementContent, Content { - /** Gets a textual representation of this element. */ override string toString() { result = "Set element" } } @@ -224,10 +224,9 @@ class TupleElementContent extends TTupleElementContent, Content { TupleElementContent() { this = TTupleElementContent(index) } - /** Gets the index for this tuple element */ + /** Gets the index for this tuple element. */ int getIndex() { result = index } - /** Gets a textual representation of this element. */ override string toString() { result = "Tuple element at index " + index.toString() } } @@ -236,14 +235,23 @@ class DictionaryElementContent extends TDictionaryElementContent, Content { DictionaryElementContent() { this = TDictionaryElementContent(key) } - /** Gets the index for this tuple element */ + /** Gets the key for this dictionary element. */ string getKey() { result = key } - /** Gets a textual representation of this element. */ override string toString() { result = "Dictionary element at key " + key } } class DictionaryElementAnyContent extends TDictionaryElementAnyContent, Content { - /** Gets a textual representation of this element. */ override string toString() { result = "Any dictionary element" } } + +class AttributeContent extends TAttributeContent, Content { + private string attr; + + AttributeContent() { this = TAttributeContent(attr) } + + /** Gets the name of the attribute under which this content is stored. */ + string getAttribute() { result = attr } + + override string toString() { result = "Attribute " + attr } +} diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected new file mode 100644 index 00000000000..ba3032cd991 --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected @@ -0,0 +1,40 @@ +| test.py:0:0:0:0 | SSA variable $ | test.py:1:1:1:66 | SSA variable $ | +| test.py:0:0:0:0 | SSA variable * | test.py:1:1:1:66 | SSA variable * | +| test.py:6:13:6:18 | ControlFlowNode for object | test.py:12:17:12:22 | ControlFlowNode for object | +| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ | +| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | +| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | +| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | +| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ | +| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | +| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | +| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj | +| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self | +| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | +| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | +| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x | +| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x | +| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | +| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | +| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | +| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | +| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql new file mode 100644 index 00000000000..0da43687495 --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql @@ -0,0 +1,8 @@ +import python +import experimental.dataflow.DataFlow + +from DataFlow::Node nodeFrom, DataFlow::Node nodeTo +where + DataFlow::localFlowStep(nodeFrom, nodeTo) and + nodeFrom.getLocation().getFile().getBaseName() = "test.py" +select nodeFrom, nodeTo diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected index 58e3dda0964..fa1e4fc055d 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected @@ -1,3 +1,25 @@ edges +| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | +| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | +| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | ControlFlowNode for x | +| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | +| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | +| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | nodes +| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post] ControlFlowNode for myobj [Attribute foo] | +| test.py:29:19:29:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] | +| test.py:30:10:30:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:34:9:34:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post] ControlFlowNode for Attribute [Attribute foo] | +| test.py:38:17:38:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | +| test.py:41:10:41:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | #select +| test.py:30:10:30:18 | ControlFlowNode for Attribute | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:30:10:30:18 | ControlFlowNode for Attribute | | +| test.py:41:10:41:18 | ControlFlowNode for Attribute | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:41:10:41:18 | ControlFlowNode for Attribute | | diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected new file mode 100644 index 00000000000..204b08b85ce --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected @@ -0,0 +1,221 @@ +| test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj | +| test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj | +| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ | +| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ | +| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | +| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | +| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | +| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | +| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | +| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | +| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | +| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | +| test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj | +| test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj | +| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ | +| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ | +| test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj | +| test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj | +| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | +| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | +| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | +| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | +| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | +| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | +| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj | +| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj | +| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self | +| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self | +| test.py:21:1:21:19 | ControlFlowNode for FunctionExpr | test.py:21:5:21:10 | GSSA Variable setFoo | +| test.py:21:1:21:19 | ControlFlowNode for FunctionExpr | test.py:21:5:21:10 | GSSA Variable setFoo | +| test.py:21:1:21:19 | GSSA Variable SINK_F | test.py:22:5:22:10 | ControlFlowNode for SINK_F | +| test.py:21:1:21:19 | GSSA Variable SINK_F | test.py:22:5:22:10 | ControlFlowNode for SINK_F | +| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | +| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | +| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | +| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | +| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | +| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | +| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj | +| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj | +| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj | +| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj | +| test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | +| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] | +| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] | +| test.py:26:1:26:20 | ControlFlowNode for FunctionExpr | test.py:26:5:26:17 | GSSA Variable test_example1 | +| test.py:26:1:26:20 | ControlFlowNode for FunctionExpr | test.py:26:5:26:17 | GSSA Variable test_example1 | +| test.py:26:1:26:20 | GSSA Variable MyObj | test.py:27:13:27:17 | ControlFlowNode for MyObj | +| test.py:26:1:26:20 | GSSA Variable MyObj | test.py:27:13:27:17 | ControlFlowNode for MyObj | +| test.py:26:1:26:20 | GSSA Variable SINK | test.py:30:5:30:8 | ControlFlowNode for SINK | +| test.py:26:1:26:20 | GSSA Variable SINK | test.py:30:5:30:8 | ControlFlowNode for SINK | +| test.py:26:1:26:20 | GSSA Variable SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE | +| test.py:26:1:26:20 | GSSA Variable SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE | +| test.py:26:1:26:20 | GSSA Variable setFoo | test.py:29:5:29:10 | ControlFlowNode for setFoo | +| test.py:26:1:26:20 | GSSA Variable setFoo | test.py:29:5:29:10 | ControlFlowNode for setFoo | +| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | +| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | +| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj | +| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj | +| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x | +| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x | +| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | +| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | +| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | +| test.py:33:1:33:20 | ControlFlowNode for FunctionExpr | test.py:33:5:33:17 | GSSA Variable test_example2 | +| test.py:33:1:33:20 | ControlFlowNode for FunctionExpr | test.py:33:5:33:17 | GSSA Variable test_example2 | +| test.py:33:1:33:20 | GSSA Variable NestedObj | test.py:36:9:36:17 | ControlFlowNode for NestedObj | +| test.py:33:1:33:20 | GSSA Variable NestedObj | test.py:36:9:36:17 | ControlFlowNode for NestedObj | +| test.py:33:1:33:20 | GSSA Variable SINK | test.py:41:5:41:8 | ControlFlowNode for SINK | +| test.py:33:1:33:20 | GSSA Variable SINK | test.py:41:5:41:8 | ControlFlowNode for SINK | +| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:34:9:34:14 | ControlFlowNode for SOURCE | +| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:34:9:34:14 | ControlFlowNode for SOURCE | +| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x | +| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x | +| test.py:34:5:34:5 | SSA variable x | test.py:39:22:39:22 | ControlFlowNode for x | +| test.py:34:5:34:5 | SSA variable x | test.py:39:22:39:22 | ControlFlowNode for x | +| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x | +| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x | +| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | ControlFlowNode for x | +| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | ControlFlowNode for x | +| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:39:22:39:22 | ControlFlowNode for x | +| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:39:22:39:22 | ControlFlowNode for x | +| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | +| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | +| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | +| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | +| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] | +| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | +| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | +| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | +| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | +| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | +| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:14 | ControlFlowNode for Attribute | +| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:14 | ControlFlowNode for Attribute | +| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | +| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | +| test.py:44:1:44:20 | ControlFlowNode for FunctionExpr | test.py:44:5:44:17 | GSSA Variable test_example3 | +| test.py:44:1:44:20 | ControlFlowNode for FunctionExpr | test.py:44:5:44:17 | GSSA Variable test_example3 | +| test.py:44:1:44:20 | GSSA Variable MyObj | test.py:45:11:45:15 | ControlFlowNode for MyObj | +| test.py:44:1:44:20 | GSSA Variable MyObj | test.py:45:11:45:15 | ControlFlowNode for MyObj | +| test.py:44:1:44:20 | GSSA Variable SINK | test.py:46:5:46:8 | ControlFlowNode for SINK | +| test.py:44:1:44:20 | GSSA Variable SINK | test.py:46:5:46:8 | ControlFlowNode for SINK | +| test.py:44:1:44:20 | GSSA Variable SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | +| test.py:44:1:44:20 | GSSA Variable SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | +| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | +| test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow | +| test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow | +| test.py:49:1:49:30 | GSSA Variable MyObj | test.py:50:11:50:15 | ControlFlowNode for MyObj | +| test.py:49:1:49:30 | GSSA Variable MyObj | test.py:50:11:50:15 | ControlFlowNode for MyObj | +| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | +| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | +| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | +| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | +| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | +| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | +| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | +| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | +| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | +| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE | +| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE | +| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | +| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | +| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | +| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | +| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a | +| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a | +| test.py:52:12:52:12 | ControlFlowNode for a | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:52:12:52:12 | ControlFlowNode for a | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:55:1:55:18 | ControlFlowNode for FunctionExpr | test.py:55:5:55:15 | GSSA Variable test_fields | +| test.py:55:1:55:18 | ControlFlowNode for FunctionExpr | test.py:55:5:55:15 | GSSA Variable test_fields | +| test.py:55:1:55:18 | GSSA Variable SINK | test.py:56:5:56:8 | ControlFlowNode for SINK | +| test.py:55:1:55:18 | GSSA Variable SINK | test.py:56:5:56:8 | ControlFlowNode for SINK | +| test.py:55:1:55:18 | GSSA Variable SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE | +| test.py:55:1:55:18 | GSSA Variable SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE | +| test.py:55:1:55:18 | GSSA Variable fields_with_local_flow | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow | +| test.py:55:1:55:18 | GSSA Variable fields_with_local_flow | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow | +| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x | +| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x | diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.ql b/python/ql/test/experimental/dataflow/fieldflow/globalStep.ql new file mode 100644 index 00000000000..141e0587156 --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.ql @@ -0,0 +1,9 @@ +import experimental.dataflow.basic.allFlowsConfig + +from DataFlow::PathNode fromNode, DataFlow::PathNode toNode +where + toNode = fromNode.getASuccessor() and + fromNode.getNode().getLocation().getFile().getBaseName() = "test.py" and + fromNode.getNode().getLocation().getStartLine() > 1 and + not toNode.getNode().(DataFlow::CfgNode).getNode().isNormalExit() +select fromNode, toNode diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected new file mode 100644 index 00000000000..93a4d590d01 --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected @@ -0,0 +1,12 @@ +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | +| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | +| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | +| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | +| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | +| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | +| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | +| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.ql b/python/ql/test/experimental/dataflow/fieldflow/localFlow.ql new file mode 100644 index 00000000000..1b413bedeca --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.ql @@ -0,0 +1,8 @@ +import python +import experimental.dataflow.DataFlow + +from DataFlow::Node nodeFrom, DataFlow::Node nodeTo +where + DataFlow::localFlowStep(nodeFrom, nodeTo) and + nodeFrom.getEnclosingCallable().getName().matches("%\\_with\\_local\\_flow") +select nodeFrom, nodeTo diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected new file mode 100644 index 00000000000..750ef851ade --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected @@ -0,0 +1,42 @@ +| examples.py:8:9:8:12 | [post] ControlFlowNode for self | examples.py:8:9:8:12 | ControlFlowNode for self | +| examples.py:14:9:14:12 | [post] ControlFlowNode for self | examples.py:14:9:14:12 | ControlFlowNode for self | +| examples.py:14:26:14:29 | [post] ControlFlowNode for Str | examples.py:14:26:14:29 | ControlFlowNode for Str | +| examples.py:17:16:17:19 | [post] ControlFlowNode for self | examples.py:17:16:17:19 | ControlFlowNode for self | +| examples.py:22:12:22:14 | [post] ControlFlowNode for obj | examples.py:22:12:22:14 | ControlFlowNode for obj | +| examples.py:23:5:23:7 | [post] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:25:15:25:18 | [post] ControlFlowNode for Str | examples.py:25:15:25:18 | ControlFlowNode for Str | +| examples.py:27:8:27:12 | [post] ControlFlowNode for myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj | +| examples.py:27:15:27:20 | [post] ControlFlowNode for SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | +| examples.py:28:6:28:10 | [post] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:35:1:35:1 | [post] ControlFlowNode for a | examples.py:35:1:35:1 | ControlFlowNode for a | +| examples.py:35:1:35:5 | [post] ControlFlowNode for Attribute | examples.py:35:1:35:5 | ControlFlowNode for Attribute | +| examples.py:36:1:36:1 | [post] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:36:1:36:10 | [post] ControlFlowNode for Attribute() | examples.py:36:1:36:10 | ControlFlowNode for Attribute() | +| examples.py:38:6:38:6 | [post] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:38:6:38:10 | [post] ControlFlowNode for Attribute | examples.py:38:6:38:10 | ControlFlowNode for Attribute | +| examples.py:41:13:41:18 | [post] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:42:6:42:8 | [post] ControlFlowNode for obj | examples.py:42:6:42:8 | ControlFlowNode for obj | +| examples.py:46:15:46:15 | [post] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x | +| examples.py:47:7:47:9 | [post] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj | +| examples.py:50:29:50:34 | [post] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| test.py:9:9:9:12 | [post] ControlFlowNode for self | test.py:9:9:9:12 | ControlFlowNode for self | +| test.py:15:9:15:12 | [post] ControlFlowNode for self | test.py:15:9:15:12 | ControlFlowNode for self | +| test.py:15:26:15:29 | [post] ControlFlowNode for Str | test.py:15:26:15:29 | ControlFlowNode for Str | +| test.py:18:16:18:19 | [post] ControlFlowNode for self | test.py:18:16:18:19 | ControlFlowNode for self | +| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:22:12:22:14 | ControlFlowNode for obj | +| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:27:19:27:22 | [post] ControlFlowNode for Str | test.py:27:19:27:22 | ControlFlowNode for Str | +| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:29:19:29:24 | [post] ControlFlowNode for SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE | +| test.py:30:10:30:14 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute | test.py:38:5:38:9 | ControlFlowNode for Attribute | +| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:39:5:39:14 | [post] ControlFlowNode for Attribute() | test.py:39:5:39:14 | ControlFlowNode for Attribute() | +| test.py:41:10:41:10 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:41:10:41:14 | [post] ControlFlowNode for Attribute | test.py:41:10:41:14 | ControlFlowNode for Attribute | +| test.py:45:17:45:22 | [post] ControlFlowNode for SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | +| test.py:46:10:46:12 | [post] ControlFlowNode for obj | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:50:17:50:17 | ControlFlowNode for x | +| test.py:51:9:51:11 | [post] ControlFlowNode for obj | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE | diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.ql b/python/ql/test/experimental/dataflow/fieldflow/postupdates.ql new file mode 100644 index 00000000000..b034ad9c9be --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.ql @@ -0,0 +1,4 @@ +import experimental.dataflow.testConfig + +from DataFlow::PostUpdateNode pun +select pun, pun.getPreUpdateNode() From aa281671774d13f669c3e9d025997b0640d78ec6 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 17 Sep 2020 11:39:17 +0200 Subject: [PATCH 033/185] Python: Add malloc nodes --- .../dataflow/internal/DataFlowPrivate.qll | 52 +++++- .../dataflow/fieldflow/allLocalFlow.expected | 20 ++- .../dataflow/fieldflow/dataflow.expected | 16 +- .../dataflow/fieldflow/globalStep.expected | 152 +++++++++++------- .../dataflow/fieldflow/localFlow.expected | 6 +- .../dataflow/fieldflow/postupdates.expected | 94 ++++++----- 6 files changed, 218 insertions(+), 122 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index b999ebb5982..c2766a3ef95 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -17,10 +17,14 @@ class DataFlowCfgNode extends ControlFlowNode { } /** A data flow node which should have an associated post-update node. */ -abstract class PreUpdateNode extends Node { } +abstract class PreUpdateNode extends Node { + abstract string label(); +} /** An argument might have its value changed as a result of a call. */ -class ArgumentPreUpdateNode extends PreUpdateNode, ArgumentNode { } +class ArgumentPreUpdateNode extends PreUpdateNode, ExplicitArgumentNode { + override string label() { result = "arg" } +} /** An object might have its value changed after a store. */ class StorePreUpdateNode extends PreUpdateNode, CfgNode { @@ -30,6 +34,8 @@ class StorePreUpdateNode extends PreUpdateNode, CfgNode { a.getCtx() instanceof Store ) } + + override string label() { result = "store" } } /** A node marking the state change of an object after a read. */ @@ -40,6 +46,17 @@ class ReadPreUpdateNode extends PreUpdateNode, CfgNode { a.getCtx() instanceof Load ) } + + override string label() { result = "read" } +} + +class MallocNode extends PreUpdateNode, ImplicitSelfArgumentNode { + // ObjectCreationNode() { exists(ClassValue c | this.asCfgNode() = c.getACall()) } + override string toString() { + result = "malloc " + this.asCfgNode().(CallNode).getNode().(Call).toString() + } + + override string label() { result = "malloc" } } /** @@ -61,7 +78,7 @@ class PostUpdateNode extends Node, TPostUpdateNode { /** Gets the node before the state update. */ Node getPreUpdateNode() { result = pre } - override string toString() { result = "[post] " + pre.toString() } + override string toString() { result = "[post " + pre.label() + "] " + pre.toString() } override Scope getScope() { result = pre.getScope() } @@ -297,14 +314,33 @@ class SpecialCall extends DataFlowCall, TSpecialCall { } /** A data flow node that represents a call argument. */ -class ArgumentNode extends CfgNode { - ArgumentNode() { exists(DataFlowCall call, int pos | node = call.getArg(pos)) } - +abstract class ArgumentNode extends CfgNode { /** Holds if this argument occurs at the given position in the given call. */ - predicate argumentOf(DataFlowCall call, int pos) { node = call.getArg(pos) } + abstract predicate argumentOf(DataFlowCall call, int pos); /** Gets the call in which this node is an argument. */ - final DataFlowCall getCall() { this.argumentOf(result, _) } + abstract DataFlowCall getCall(); +} + +/** A data flow node that represents a call argument. */ +class ExplicitArgumentNode extends ArgumentNode { + ExplicitArgumentNode() { exists(DataFlowCall call, int pos | node = call.getArg(pos)) } + + /** Holds if this argument occurs at the given position in the given call. */ + override predicate argumentOf(DataFlowCall call, int pos) { node = call.getArg(pos) } + + /** Gets the call in which this node is an argument. */ + final override DataFlowCall getCall() { this.argumentOf(result, _) } +} + +class ImplicitSelfArgumentNode extends ArgumentNode { + ImplicitSelfArgumentNode() { exists(ClassValue cv | node = cv.getACall()) } + + /** Holds if this argument occurs at the given position in the given call. */ + override predicate argumentOf(DataFlowCall call, int pos) { call = TCallNode(node) and pos = -1 } + + /** Gets the call in which this node is an argument. */ + final override DataFlowCall getCall() { result = TCallNode(node) } } /** Gets a viable run-time target for the call `call`. */ diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected index ba3032cd991..1e36d80a5ee 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected @@ -14,27 +14,31 @@ | test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | | test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | | test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | | test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj | | test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x | | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x | | test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a | | test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | | test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | | test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | | test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected index fa1e4fc055d..47ab357dc2c 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected @@ -1,21 +1,21 @@ edges -| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | +| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | ControlFlowNode for x | -| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | nodes -| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post] ControlFlowNode for myobj [Attribute foo] | +| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] | | test.py:29:19:29:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] | | test.py:30:10:30:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | test.py:34:9:34:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post] ControlFlowNode for Attribute [Attribute foo] | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute [Attribute foo] | | test.py:38:17:38:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected index 204b08b85ce..0fe90c99f8f 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected @@ -4,6 +4,10 @@ | test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ | | test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | | test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | +| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | +| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | +| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | +| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | | test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | | test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | | test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | @@ -18,8 +22,14 @@ | test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj | | test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | | test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | +| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | +| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | | test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | | test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | +| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | +| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | +| test.py:15:20:15:30 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:15:20:15:30 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | | test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | | test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | | test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj | @@ -50,15 +60,15 @@ | test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | | test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | | test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj | -| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj | -| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj | -| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj | -| test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | -| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] | -| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] | +| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | +| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | +| test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | +| test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | +| test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | +| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | | test.py:26:1:26:20 | ControlFlowNode for FunctionExpr | test.py:26:5:26:17 | GSSA Variable test_example1 | | test.py:26:1:26:20 | ControlFlowNode for FunctionExpr | test.py:26:5:26:17 | GSSA Variable test_example1 | | test.py:26:1:26:20 | GSSA Variable MyObj | test.py:27:13:27:17 | ControlFlowNode for MyObj | @@ -75,26 +85,36 @@ | test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | | test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:27:13:27:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:13:27:23 | malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:13:27:23 | malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | | test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x | | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x | -| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | +| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | | test.py:33:1:33:20 | ControlFlowNode for FunctionExpr | test.py:33:5:33:17 | GSSA Variable test_example2 | @@ -123,39 +143,51 @@ | test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | | test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:14 | SSA variable a | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:14 | SSA variable a | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:14:18:14:21 | SSA variable self | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:14:18:14:21 | SSA variable self | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:14 | SSA variable a | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:14 | SSA variable a | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:9:36:19 | malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | -| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | -| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] | -| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | +| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj] | +| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | | test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | | test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | | test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | -| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:14 | ControlFlowNode for Attribute | | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:14 | ControlFlowNode for Attribute | @@ -171,10 +203,16 @@ | test.py:44:1:44:20 | GSSA Variable SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | | test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | | test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:11:45:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:45:11:45:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:11:45:23 | malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | | test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow | @@ -191,16 +229,22 @@ | test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | | test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:50:11:50:18 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | -| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE | -| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE | +| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | +| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected index 93a4d590d01..6651727c229 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected @@ -1,12 +1,14 @@ | examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | | examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | | examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | +| examples.py:46:9:46:16 | [post malloc] malloc MyObj() | examples.py:46:3:46:5 | SSA variable obj | +| examples.py:46:9:46:16 | malloc MyObj() | examples.py:46:3:46:5 | SSA variable obj | | examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | | examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | | test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | | test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected index 750ef851ade..4bbb7f3542b 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected @@ -1,42 +1,52 @@ -| examples.py:8:9:8:12 | [post] ControlFlowNode for self | examples.py:8:9:8:12 | ControlFlowNode for self | -| examples.py:14:9:14:12 | [post] ControlFlowNode for self | examples.py:14:9:14:12 | ControlFlowNode for self | -| examples.py:14:26:14:29 | [post] ControlFlowNode for Str | examples.py:14:26:14:29 | ControlFlowNode for Str | -| examples.py:17:16:17:19 | [post] ControlFlowNode for self | examples.py:17:16:17:19 | ControlFlowNode for self | -| examples.py:22:12:22:14 | [post] ControlFlowNode for obj | examples.py:22:12:22:14 | ControlFlowNode for obj | -| examples.py:23:5:23:7 | [post] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:25:15:25:18 | [post] ControlFlowNode for Str | examples.py:25:15:25:18 | ControlFlowNode for Str | -| examples.py:27:8:27:12 | [post] ControlFlowNode for myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj | -| examples.py:27:15:27:20 | [post] ControlFlowNode for SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | -| examples.py:28:6:28:10 | [post] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | -| examples.py:35:1:35:1 | [post] ControlFlowNode for a | examples.py:35:1:35:1 | ControlFlowNode for a | -| examples.py:35:1:35:5 | [post] ControlFlowNode for Attribute | examples.py:35:1:35:5 | ControlFlowNode for Attribute | -| examples.py:36:1:36:1 | [post] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:36:1:36:10 | [post] ControlFlowNode for Attribute() | examples.py:36:1:36:10 | ControlFlowNode for Attribute() | -| examples.py:38:6:38:6 | [post] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:38:6:38:10 | [post] ControlFlowNode for Attribute | examples.py:38:6:38:10 | ControlFlowNode for Attribute | -| examples.py:41:13:41:18 | [post] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:42:6:42:8 | [post] ControlFlowNode for obj | examples.py:42:6:42:8 | ControlFlowNode for obj | -| examples.py:46:15:46:15 | [post] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x | -| examples.py:47:7:47:9 | [post] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj | -| examples.py:50:29:50:34 | [post] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| test.py:9:9:9:12 | [post] ControlFlowNode for self | test.py:9:9:9:12 | ControlFlowNode for self | -| test.py:15:9:15:12 | [post] ControlFlowNode for self | test.py:15:9:15:12 | ControlFlowNode for self | -| test.py:15:26:15:29 | [post] ControlFlowNode for Str | test.py:15:26:15:29 | ControlFlowNode for Str | -| test.py:18:16:18:19 | [post] ControlFlowNode for self | test.py:18:16:18:19 | ControlFlowNode for self | -| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:22:12:22:14 | ControlFlowNode for obj | -| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:27:19:27:22 | [post] ControlFlowNode for Str | test.py:27:19:27:22 | ControlFlowNode for Str | -| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:29:19:29:24 | [post] ControlFlowNode for SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE | -| test.py:30:10:30:14 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute | test.py:38:5:38:9 | ControlFlowNode for Attribute | -| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:39:5:39:14 | [post] ControlFlowNode for Attribute() | test.py:39:5:39:14 | ControlFlowNode for Attribute() | -| test.py:41:10:41:10 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:41:10:41:14 | [post] ControlFlowNode for Attribute | test.py:41:10:41:14 | ControlFlowNode for Attribute | -| test.py:45:17:45:22 | [post] ControlFlowNode for SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | -| test.py:46:10:46:12 | [post] ControlFlowNode for obj | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:50:17:50:17 | ControlFlowNode for x | -| test.py:51:9:51:11 | [post] ControlFlowNode for obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:8:9:8:12 | ControlFlowNode for self | +| examples.py:14:9:14:12 | [post store] ControlFlowNode for self | examples.py:14:9:14:12 | ControlFlowNode for self | +| examples.py:14:20:14:30 | [post malloc] malloc MyObj() | examples.py:14:20:14:30 | malloc MyObj() | +| examples.py:14:26:14:29 | [post arg] ControlFlowNode for Str | examples.py:14:26:14:29 | ControlFlowNode for Str | +| examples.py:17:16:17:19 | [post read] ControlFlowNode for self | examples.py:17:16:17:19 | ControlFlowNode for self | +| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:22:12:22:14 | ControlFlowNode for obj | +| examples.py:23:5:23:7 | [post store] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:25:9:25:19 | [post malloc] malloc MyObj() | examples.py:25:9:25:19 | malloc MyObj() | +| examples.py:25:15:25:18 | [post arg] ControlFlowNode for Str | examples.py:25:15:25:18 | ControlFlowNode for Str | +| examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | +| examples.py:28:6:28:10 | [post read] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:33:5:33:15 | [post malloc] malloc NestedObj() | examples.py:33:5:33:15 | malloc NestedObj() | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:35:1:35:1 | ControlFlowNode for a | +| examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute | examples.py:35:1:35:5 | ControlFlowNode for Attribute | +| examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:36:1:36:10 | [post store] ControlFlowNode for Attribute() | examples.py:36:1:36:10 | ControlFlowNode for Attribute() | +| examples.py:38:6:38:6 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:38:6:38:10 | [post read] ControlFlowNode for Attribute | examples.py:38:6:38:10 | ControlFlowNode for Attribute | +| examples.py:41:7:41:19 | [post malloc] malloc MyObj() | examples.py:41:7:41:19 | malloc MyObj() | +| examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:42:6:42:8 | [post read] ControlFlowNode for obj | examples.py:42:6:42:8 | ControlFlowNode for obj | +| examples.py:46:9:46:16 | [post malloc] malloc MyObj() | examples.py:46:9:46:16 | malloc MyObj() | +| examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x | +| examples.py:47:7:47:9 | [post read] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj | +| examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:9:9:9:12 | ControlFlowNode for self | +| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:15:9:15:12 | ControlFlowNode for self | +| test.py:15:20:15:30 | [post malloc] malloc MyObj() | test.py:15:20:15:30 | malloc MyObj() | +| test.py:15:26:15:29 | [post arg] ControlFlowNode for Str | test.py:15:26:15:29 | ControlFlowNode for Str | +| test.py:18:16:18:19 | [post read] ControlFlowNode for self | test.py:18:16:18:19 | ControlFlowNode for self | +| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:22:12:22:14 | ControlFlowNode for obj | +| test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:13:27:23 | malloc MyObj() | +| test.py:27:19:27:22 | [post arg] ControlFlowNode for Str | test.py:27:19:27:22 | ControlFlowNode for Str | +| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:29:19:29:24 | [post arg] ControlFlowNode for SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE | +| test.py:30:10:30:14 | [post read] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:9:36:19 | malloc NestedObj() | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute | test.py:38:5:38:9 | ControlFlowNode for Attribute | +| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:39:5:39:14 | [post store] ControlFlowNode for Attribute() | test.py:39:5:39:14 | ControlFlowNode for Attribute() | +| test.py:41:10:41:10 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:41:10:41:14 | [post read] ControlFlowNode for Attribute | test.py:41:10:41:14 | ControlFlowNode for Attribute | +| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:11:45:23 | malloc MyObj() | +| test.py:45:17:45:22 | [post arg] ControlFlowNode for SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | +| test.py:46:10:46:12 | [post read] ControlFlowNode for obj | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:11:50:18 | malloc MyObj() | +| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:50:17:50:17 | ControlFlowNode for x | +| test.py:51:9:51:11 | [post read] ControlFlowNode for obj | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE | From e50b66554dfeba72c91b15cd58247ce133902022 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 17 Sep 2020 16:30:05 +0200 Subject: [PATCH 034/185] Python: Add explorative test --- .../dataflow/fieldflow/dataflowExplore.ql | 13 +++++++++++++ python/ql/test/experimental/dataflow/testConfig.qll | 2 ++ 2 files changed, 15 insertions(+) create mode 100644 python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql new file mode 100644 index 00000000000..393e14a0b3c --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql @@ -0,0 +1,13 @@ +/** + * @kind path-problem + */ + +import experimental.dataflow.testConfig +import DataFlow::PartialPathGraph + +from TestConfiguration config, DataFlow::PartialPathNode source, DataFlow::PartialPathNode sink +where + config.hasPartialFlow(source, sink, _) and + source.hasLocationInfo(_, 45, _, _, _) and + config.isSink(sink.getNode()) +select sink.getNode(), source, sink, "" diff --git a/python/ql/test/experimental/dataflow/testConfig.qll b/python/ql/test/experimental/dataflow/testConfig.qll index f2e35ab9d1c..518ac2b59fc 100644 --- a/python/ql/test/experimental/dataflow/testConfig.qll +++ b/python/ql/test/experimental/dataflow/testConfig.qll @@ -43,4 +43,6 @@ class TestConfiguration extends DataFlow::Configuration { node.(DataFlow::CfgNode).getNode() = call.getAnArg() ) } + + override int explorationLimit() { result = 4 } } From e1323617363b902f3647e9457a4d04209e12338e Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 17 Sep 2020 17:18:01 +0200 Subject: [PATCH 035/185] Python: Add missing .expected file --- .../fieldflow/dataflowExplore.expected | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected new file mode 100644 index 00000000000..d68142f928f --- /dev/null +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected @@ -0,0 +1,43 @@ +edges +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | +| test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | +| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | +| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | +| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x | +| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | +| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x | +| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x | +| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, ... (2)] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, ... (2)] | +| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, ... (2)] | +| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | +| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | +| test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, ... (2)] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, ... (2)] | +| test.py:39:22:39:22 | ControlFlowNode for x | test.py:39:5:39:14 | [post store] ControlFlowNode for Attribute() [Attribute foo] | +| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, ... (2)] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | +| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | +| test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:45:11:45:23 | [post malloc] malloc MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post malloc] malloc MyObj() [Attribute foo] | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:17:45:22 | [post arg] ControlFlowNode for SOURCE [Attribute foo] | +| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | +| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | +| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | +| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:50:11:50:18 | [post malloc] malloc MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post malloc] malloc MyObj() [Attribute foo] | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:17:50:17 | [post arg] ControlFlowNode for x [Attribute foo] | +| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | +| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | +| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | +| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x | +| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE [Attribute foo] | +#select +| test.py:46:10:46:16 | ControlFlowNode for Attribute | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:46:10:46:16 | ControlFlowNode for Attribute | | From b2f1c435a864bf9ae8a3975b89d7fb215a890d6d Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 18 Sep 2020 09:50:00 +0200 Subject: [PATCH 036/185] Python: update test expectations --- .../dataflow/basic/local.expected | 2 +- .../dataflow/basic/sinks.expected | 2 +- .../dataflow/basic/sources.expected | 2 +- .../coverage/classesCallGraph.expected | 169 +++++++++++++++++- 4 files changed, 168 insertions(+), 7 deletions(-) diff --git a/python/ql/test/experimental/dataflow/basic/local.expected b/python/ql/test/experimental/dataflow/basic/local.expected index 4bb6b8d6f5c..de8f6b6075e 100644 --- a/python/ql/test/experimental/dataflow/basic/local.expected +++ b/python/ql/test/experimental/dataflow/basic/local.expected @@ -38,4 +38,4 @@ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:7:5:7:20 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a | | test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:19:7:19 | ControlFlowNode for a | -| test.py:7:19:7:19 | [post] ControlFlowNode for a | test.py:7:19:7:19 | [post] ControlFlowNode for a | +| test.py:7:19:7:19 | [post arg] ControlFlowNode for a | test.py:7:19:7:19 | [post arg] ControlFlowNode for a | diff --git a/python/ql/test/experimental/dataflow/basic/sinks.expected b/python/ql/test/experimental/dataflow/basic/sinks.expected index 5b58a0f844f..bfebc2ef31e 100644 --- a/python/ql/test/experimental/dataflow/basic/sinks.expected +++ b/python/ql/test/experimental/dataflow/basic/sinks.expected @@ -23,4 +23,4 @@ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:7:5:7:20 | GSSA Variable a | | test.py:7:19:7:19 | ControlFlowNode for a | -| test.py:7:19:7:19 | [post] ControlFlowNode for a | +| test.py:7:19:7:19 | [post arg] ControlFlowNode for a | diff --git a/python/ql/test/experimental/dataflow/basic/sources.expected b/python/ql/test/experimental/dataflow/basic/sources.expected index 5b58a0f844f..bfebc2ef31e 100644 --- a/python/ql/test/experimental/dataflow/basic/sources.expected +++ b/python/ql/test/experimental/dataflow/basic/sources.expected @@ -23,4 +23,4 @@ | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | | test.py:7:5:7:20 | GSSA Variable a | | test.py:7:19:7:19 | ControlFlowNode for a | -| test.py:7:19:7:19 | [post] ControlFlowNode for a | +| test.py:7:19:7:19 | [post arg] ControlFlowNode for a | diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected index 1276083cecc..650f271bd27 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected @@ -1,39 +1,200 @@ +| classes.py:41:16:41:22 | malloc super() | classes.py:41:16:41:22 | malloc super() | | classes.py:41:16:41:35 | ControlFlowNode for Attribute() | classes.py:41:16:41:35 | ControlFlowNode for Attribute() | -| classes.py:264:9:264:24 | ControlFlowNode for set() | classes.py:264:9:264:24 | ControlFlowNode for set() | -| classes.py:269:9:269:30 | ControlFlowNode for frozenset() | classes.py:269:9:269:30 | ControlFlowNode for frozenset() | -| classes.py:274:9:274:28 | ControlFlowNode for dict() | classes.py:274:9:274:28 | ControlFlowNode for dict() | -| classes.py:454:29:454:52 | ControlFlowNode for dict() | classes.py:454:29:454:52 | ControlFlowNode for dict() | +| classes.py:45:16:45:25 | malloc With_new() | classes.py:45:16:45:25 | malloc With_new() | +| classes.py:58:17:58:27 | malloc With_init() | classes.py:52:18:52:21 | SSA variable self | +| classes.py:58:17:58:27 | malloc With_init() | classes.py:58:17:58:27 | malloc With_init() | +| classes.py:71:16:71:25 | malloc With_del() | classes.py:71:16:71:25 | malloc With_del() | +| classes.py:86:17:86:27 | malloc With_repr() | classes.py:86:17:86:27 | malloc With_repr() | +| classes.py:101:16:101:25 | malloc With_str() | classes.py:101:16:101:25 | malloc With_str() | +| classes.py:102:5:102:17 | malloc str() | classes.py:102:5:102:17 | malloc str() | +| classes.py:116:18:116:29 | malloc With_bytes() | classes.py:116:18:116:29 | malloc With_bytes() | +| classes.py:117:5:117:21 | malloc bytes() | classes.py:117:5:117:21 | malloc bytes() | +| classes.py:132:19:132:31 | malloc With_format() | classes.py:132:19:132:31 | malloc With_format() | +| classes.py:138:19:138:31 | malloc With_format() | classes.py:138:19:138:31 | malloc With_format() | +| classes.py:143:19:143:31 | malloc With_format() | classes.py:143:19:143:31 | malloc With_format() | +| classes.py:159:15:159:23 | malloc With_lt() | classes.py:159:15:159:23 | malloc With_lt() | +| classes.py:176:15:176:23 | malloc With_le() | classes.py:176:15:176:23 | malloc With_le() | +| classes.py:193:15:193:23 | malloc With_eq() | classes.py:193:15:193:23 | malloc With_eq() | +| classes.py:209:15:209:23 | malloc With_ne() | classes.py:209:15:209:23 | malloc With_ne() | +| classes.py:225:15:225:23 | malloc With_gt() | classes.py:225:15:225:23 | malloc With_gt() | +| classes.py:242:15:242:23 | malloc With_ge() | classes.py:242:15:242:23 | malloc With_ge() | +| classes.py:258:17:258:27 | malloc With_hash() | classes.py:258:17:258:27 | malloc With_hash() | +| classes.py:263:17:263:27 | malloc With_hash() | classes.py:263:17:263:27 | malloc With_hash() | +| classes.py:264:9:264:24 | malloc set() | classes.py:264:9:264:24 | malloc set() | +| classes.py:268:17:268:27 | malloc With_hash() | classes.py:268:17:268:27 | malloc With_hash() | +| classes.py:269:9:269:30 | malloc frozenset() | classes.py:269:9:269:30 | malloc frozenset() | +| classes.py:273:17:273:27 | malloc With_hash() | classes.py:273:17:273:27 | malloc With_hash() | +| classes.py:274:9:274:28 | malloc dict() | classes.py:274:9:274:28 | malloc dict() | +| classes.py:288:17:288:27 | malloc With_bool() | classes.py:288:17:288:27 | malloc With_bool() | +| classes.py:289:5:289:19 | malloc bool() | classes.py:289:5:289:19 | malloc bool() | +| classes.py:293:17:293:27 | malloc With_bool() | classes.py:293:17:293:27 | malloc With_bool() | +| classes.py:311:20:311:33 | malloc With_getattr() | classes.py:311:20:311:33 | malloc With_getattr() | +| classes.py:327:25:327:43 | malloc With_getattribute() | classes.py:327:25:327:43 | malloc With_getattribute() | +| classes.py:343:20:343:33 | malloc With_setattr() | classes.py:343:20:343:33 | malloc With_setattr() | +| classes.py:359:20:359:33 | malloc With_delattr() | classes.py:359:20:359:33 | malloc With_delattr() | +| classes.py:374:16:374:25 | malloc With_dir() | classes.py:374:16:374:25 | malloc With_dir() | +| classes.py:399:16:399:25 | malloc With_get() | classes.py:399:16:399:25 | malloc With_get() | +| classes.py:401:12:401:17 | malloc arg3() | classes.py:401:12:401:17 | malloc arg3() | +| classes.py:417:16:417:25 | malloc With_set() | classes.py:417:16:417:25 | malloc With_set() | +| classes.py:419:12:419:18 | malloc Owner() | classes.py:419:12:419:18 | malloc Owner() | +| classes.py:435:19:435:31 | malloc With_delete() | classes.py:435:19:435:31 | malloc With_delete() | +| classes.py:437:12:437:18 | malloc Owner() | classes.py:437:12:437:18 | malloc Owner() | +| classes.py:453:21:453:35 | malloc With_set_name() | classes.py:453:21:453:35 | malloc With_set_name() | +| classes.py:454:5:454:53 | malloc type() | classes.py:454:5:454:53 | malloc type() | +| classes.py:454:29:454:52 | malloc dict() | classes.py:454:29:454:52 | malloc dict() | +| classes.py:473:5:473:47 | malloc type() | classes.py:473:5:473:47 | malloc type() | +| classes.py:513:26:513:45 | malloc With_instancecheck() | classes.py:513:26:513:45 | malloc With_instancecheck() | +| classes.py:530:26:530:45 | malloc With_subclasscheck() | classes.py:530:26:530:45 | malloc With_subclasscheck() | +| classes.py:548:26:548:51 | malloc Subscript() | classes.py:548:26:548:51 | malloc Subscript() | +| classes.py:561:17:561:27 | malloc With_call() | classes.py:561:17:561:27 | malloc With_call() | +| classes.py:577:16:577:25 | malloc With_len() | classes.py:577:16:577:25 | malloc With_len() | +| classes.py:582:16:582:25 | malloc With_len() | classes.py:582:16:582:25 | malloc With_len() | +| classes.py:583:5:583:18 | malloc bool() | classes.py:583:5:583:18 | malloc bool() | +| classes.py:587:16:587:25 | malloc With_len() | classes.py:587:16:587:25 | malloc With_len() | +| classes.py:604:24:604:41 | malloc With_length_hint() | classes.py:604:24:604:41 | malloc With_length_hint() | +| classes.py:620:20:620:33 | malloc With_getitem() | classes.py:612:21:612:24 | SSA variable self | +| classes.py:620:20:620:33 | malloc With_getitem() | classes.py:620:20:620:33 | malloc With_getitem() | | classes.py:622:5:622:16 | ControlFlowNode for with_getitem | classes.py:612:21:612:24 | SSA variable self | | classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:612:27:612:29 | SSA variable key | +| classes.py:637:20:637:33 | malloc With_setitem() | classes.py:629:21:629:24 | SSA variable self | +| classes.py:637:20:637:33 | malloc With_setitem() | classes.py:637:20:637:33 | malloc With_setitem() | | classes.py:640:5:640:16 | ControlFlowNode for with_setitem | classes.py:629:21:629:24 | SSA variable self | | classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:629:27:629:29 | SSA variable key | | classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:629:32:629:36 | SSA variable value | +| classes.py:654:20:654:33 | malloc With_delitem() | classes.py:647:21:647:24 | SSA variable self | +| classes.py:654:20:654:33 | malloc With_delitem() | classes.py:654:20:654:33 | malloc With_delitem() | | classes.py:656:9:656:20 | ControlFlowNode for with_delitem | classes.py:647:21:647:24 | SSA variable self | | classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:647:27:647:29 | SSA variable key | +| classes.py:671:20:671:33 | malloc With_missing() | classes.py:671:20:671:33 | malloc With_missing() | | classes.py:683:16:683:28 | ControlFlowNode for Attribute() | classes.py:683:16:683:28 | ControlFlowNode for Attribute() | +| classes.py:687:17:687:27 | malloc With_iter() | classes.py:687:17:687:27 | malloc With_iter() | +| classes.py:702:21:702:35 | malloc With_reversed() | classes.py:702:21:702:35 | malloc With_reversed() | +| classes.py:703:5:703:27 | malloc reversed() | classes.py:703:5:703:27 | malloc reversed() | +| classes.py:718:21:718:35 | malloc With_contains() | classes.py:718:21:718:35 | malloc With_contains() | +| classes.py:735:16:735:25 | malloc With_add() | classes.py:727:17:727:20 | SSA variable self | +| classes.py:735:16:735:25 | malloc With_add() | classes.py:727:23:727:27 | SSA variable other | +| classes.py:735:16:735:25 | malloc With_add() | classes.py:735:16:735:25 | malloc With_add() | | classes.py:737:5:737:12 | ControlFlowNode for with_add | classes.py:727:17:727:20 | SSA variable self | | classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:727:23:727:27 | SSA variable other | +| classes.py:752:16:752:25 | malloc With_sub() | classes.py:744:17:744:20 | SSA variable self | +| classes.py:752:16:752:25 | malloc With_sub() | classes.py:744:23:744:27 | SSA variable other | +| classes.py:752:16:752:25 | malloc With_sub() | classes.py:752:16:752:25 | malloc With_sub() | | classes.py:754:5:754:12 | ControlFlowNode for with_sub | classes.py:744:17:744:20 | SSA variable self | | classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:744:23:744:27 | SSA variable other | +| classes.py:769:16:769:25 | malloc With_mul() | classes.py:761:17:761:20 | SSA variable self | +| classes.py:769:16:769:25 | malloc With_mul() | classes.py:761:23:761:27 | SSA variable other | +| classes.py:769:16:769:25 | malloc With_mul() | classes.py:769:16:769:25 | malloc With_mul() | | classes.py:771:5:771:12 | ControlFlowNode for with_mul | classes.py:761:17:761:20 | SSA variable self | | classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:761:23:761:27 | SSA variable other | +| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:778:20:778:23 | SSA variable self | +| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:778:26:778:30 | SSA variable other | +| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:786:19:786:31 | malloc With_matmul() | | classes.py:788:5:788:15 | ControlFlowNode for with_matmul | classes.py:778:20:778:23 | SSA variable self | | classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:778:26:778:30 | SSA variable other | +| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:795:21:795:24 | SSA variable self | +| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:795:27:795:31 | SSA variable other | +| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:803:20:803:33 | malloc With_truediv() | | classes.py:805:5:805:16 | ControlFlowNode for with_truediv | classes.py:795:21:795:24 | SSA variable self | | classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:795:27:795:31 | SSA variable other | +| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:812:22:812:25 | SSA variable self | +| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:812:28:812:32 | SSA variable other | +| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:820:21:820:35 | malloc With_floordiv() | | classes.py:822:5:822:17 | ControlFlowNode for with_floordiv | classes.py:812:22:812:25 | SSA variable self | | classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:812:28:812:32 | SSA variable other | +| classes.py:837:16:837:25 | malloc With_mod() | classes.py:829:17:829:20 | SSA variable self | +| classes.py:837:16:837:25 | malloc With_mod() | classes.py:829:23:829:27 | SSA variable other | +| classes.py:837:16:837:25 | malloc With_mod() | classes.py:837:16:837:25 | malloc With_mod() | | classes.py:839:5:839:12 | ControlFlowNode for with_mod | classes.py:829:17:829:20 | SSA variable self | | classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:829:23:829:27 | SSA variable other | +| classes.py:854:19:854:31 | malloc With_divmod() | classes.py:854:19:854:31 | malloc With_divmod() | +| classes.py:871:16:871:25 | malloc With_pow() | classes.py:871:16:871:25 | malloc With_pow() | +| classes.py:877:16:877:25 | malloc With_pow() | classes.py:863:17:863:20 | SSA variable self | +| classes.py:877:16:877:25 | malloc With_pow() | classes.py:863:23:863:27 | SSA variable other | +| classes.py:877:16:877:25 | malloc With_pow() | classes.py:877:16:877:25 | malloc With_pow() | | classes.py:879:5:879:12 | ControlFlowNode for with_pow | classes.py:863:17:863:20 | SSA variable self | | classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:863:23:863:27 | SSA variable other | +| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:886:20:886:23 | SSA variable self | +| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:886:26:886:30 | SSA variable other | +| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:894:19:894:31 | malloc With_lshift() | | classes.py:896:5:896:15 | ControlFlowNode for with_lshift | classes.py:886:20:886:23 | SSA variable self | | classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:886:26:886:30 | SSA variable other | +| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:903:20:903:23 | SSA variable self | +| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:903:26:903:30 | SSA variable other | +| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:911:19:911:31 | malloc With_rshift() | | classes.py:913:5:913:15 | ControlFlowNode for with_rshift | classes.py:903:20:903:23 | SSA variable self | | classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:903:26:903:30 | SSA variable other | +| classes.py:928:16:928:25 | malloc With_and() | classes.py:920:17:920:20 | SSA variable self | +| classes.py:928:16:928:25 | malloc With_and() | classes.py:920:23:920:27 | SSA variable other | +| classes.py:928:16:928:25 | malloc With_and() | classes.py:928:16:928:25 | malloc With_and() | | classes.py:930:5:930:12 | ControlFlowNode for with_and | classes.py:920:17:920:20 | SSA variable self | | classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:920:23:920:27 | SSA variable other | +| classes.py:945:16:945:25 | malloc With_xor() | classes.py:937:17:937:20 | SSA variable self | +| classes.py:945:16:945:25 | malloc With_xor() | classes.py:937:23:937:27 | SSA variable other | +| classes.py:945:16:945:25 | malloc With_xor() | classes.py:945:16:945:25 | malloc With_xor() | | classes.py:947:5:947:12 | ControlFlowNode for with_xor | classes.py:937:17:937:20 | SSA variable self | | classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:937:23:937:27 | SSA variable other | +| classes.py:962:15:962:23 | malloc With_or() | classes.py:954:16:954:19 | SSA variable self | +| classes.py:962:15:962:23 | malloc With_or() | classes.py:954:22:954:26 | SSA variable other | +| classes.py:962:15:962:23 | malloc With_or() | classes.py:962:15:962:23 | malloc With_or() | | classes.py:964:5:964:11 | ControlFlowNode for with_or | classes.py:954:16:954:19 | SSA variable self | | classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:954:22:954:26 | SSA variable other | +| classes.py:979:17:979:27 | malloc With_radd() | classes.py:979:17:979:27 | malloc With_radd() | +| classes.py:996:17:996:27 | malloc With_rsub() | classes.py:996:17:996:27 | malloc With_rsub() | +| classes.py:1013:17:1013:27 | malloc With_rmul() | classes.py:1013:17:1013:27 | malloc With_rmul() | +| classes.py:1030:20:1030:33 | malloc With_rmatmul() | classes.py:1030:20:1030:33 | malloc With_rmatmul() | +| classes.py:1047:21:1047:35 | malloc With_rtruediv() | classes.py:1047:21:1047:35 | malloc With_rtruediv() | +| classes.py:1064:22:1064:37 | malloc With_rfloordiv() | classes.py:1064:22:1064:37 | malloc With_rfloordiv() | +| classes.py:1081:17:1081:27 | malloc With_rmod() | classes.py:1081:17:1081:27 | malloc With_rmod() | +| classes.py:1098:20:1098:33 | malloc With_rdivmod() | classes.py:1098:20:1098:33 | malloc With_rdivmod() | +| classes.py:1115:17:1115:27 | malloc With_rpow() | classes.py:1115:17:1115:27 | malloc With_rpow() | +| classes.py:1121:17:1121:27 | malloc With_rpow() | classes.py:1121:17:1121:27 | malloc With_rpow() | +| classes.py:1138:20:1138:33 | malloc With_rlshift() | classes.py:1138:20:1138:33 | malloc With_rlshift() | +| classes.py:1155:20:1155:33 | malloc With_rrshift() | classes.py:1155:20:1155:33 | malloc With_rrshift() | +| classes.py:1172:17:1172:27 | malloc With_rand() | classes.py:1172:17:1172:27 | malloc With_rand() | +| classes.py:1189:17:1189:27 | malloc With_rxor() | classes.py:1189:17:1189:27 | malloc With_rxor() | +| classes.py:1206:16:1206:25 | malloc With_ror() | classes.py:1206:16:1206:25 | malloc With_ror() | +| classes.py:1223:17:1223:27 | malloc With_iadd() | classes.py:1223:17:1223:27 | malloc With_iadd() | +| classes.py:1240:17:1240:27 | malloc With_isub() | classes.py:1240:17:1240:27 | malloc With_isub() | +| classes.py:1257:17:1257:27 | malloc With_imul() | classes.py:1257:17:1257:27 | malloc With_imul() | +| classes.py:1274:20:1274:33 | malloc With_imatmul() | classes.py:1274:20:1274:33 | malloc With_imatmul() | +| classes.py:1291:21:1291:35 | malloc With_itruediv() | classes.py:1291:21:1291:35 | malloc With_itruediv() | +| classes.py:1308:22:1308:37 | malloc With_ifloordiv() | classes.py:1308:22:1308:37 | malloc With_ifloordiv() | +| classes.py:1325:17:1325:27 | malloc With_imod() | classes.py:1325:17:1325:27 | malloc With_imod() | +| classes.py:1342:17:1342:27 | malloc With_ipow() | classes.py:1342:17:1342:27 | malloc With_ipow() | +| classes.py:1359:20:1359:33 | malloc With_ilshift() | classes.py:1359:20:1359:33 | malloc With_ilshift() | +| classes.py:1376:20:1376:33 | malloc With_irshift() | classes.py:1376:20:1376:33 | malloc With_irshift() | +| classes.py:1393:17:1393:27 | malloc With_iand() | classes.py:1393:17:1393:27 | malloc With_iand() | +| classes.py:1410:17:1410:27 | malloc With_ixor() | classes.py:1410:17:1410:27 | malloc With_ixor() | +| classes.py:1427:16:1427:25 | malloc With_ior() | classes.py:1427:16:1427:25 | malloc With_ior() | +| classes.py:1443:16:1443:25 | malloc With_neg() | classes.py:1443:16:1443:25 | malloc With_neg() | +| classes.py:1458:16:1458:25 | malloc With_pos() | classes.py:1458:16:1458:25 | malloc With_pos() | +| classes.py:1473:16:1473:25 | malloc With_abs() | classes.py:1473:16:1473:25 | malloc With_abs() | +| classes.py:1488:19:1488:31 | malloc With_invert() | classes.py:1488:19:1488:31 | malloc With_invert() | +| classes.py:1503:20:1503:33 | malloc With_complex() | classes.py:1503:20:1503:33 | malloc With_complex() | +| classes.py:1504:5:1504:25 | malloc complex() | classes.py:1504:5:1504:25 | malloc complex() | +| classes.py:1518:16:1518:25 | malloc With_int() | classes.py:1518:16:1518:25 | malloc With_int() | +| classes.py:1519:5:1519:17 | malloc int() | classes.py:1519:5:1519:17 | malloc int() | +| classes.py:1533:18:1533:29 | malloc With_float() | classes.py:1533:18:1533:29 | malloc With_float() | +| classes.py:1534:5:1534:21 | malloc float() | classes.py:1534:5:1534:21 | malloc float() | +| classes.py:1549:18:1549:29 | malloc With_index() | classes.py:1549:18:1549:29 | malloc With_index() | +| classes.py:1554:18:1554:29 | malloc With_index() | classes.py:1554:18:1554:29 | malloc With_index() | +| classes.py:1559:18:1559:29 | malloc With_index() | classes.py:1559:18:1559:29 | malloc With_index() | +| classes.py:1564:18:1564:29 | malloc With_index() | classes.py:1564:18:1564:29 | malloc With_index() | +| classes.py:1569:18:1569:29 | malloc With_index() | classes.py:1569:18:1569:29 | malloc With_index() | +| classes.py:1574:18:1574:29 | malloc With_index() | classes.py:1574:18:1574:29 | malloc With_index() | +| classes.py:1575:5:1575:19 | malloc int() | classes.py:1575:5:1575:19 | malloc int() | +| classes.py:1579:18:1579:29 | malloc With_index() | classes.py:1579:18:1579:29 | malloc With_index() | +| classes.py:1580:5:1580:21 | malloc float() | classes.py:1580:5:1580:21 | malloc float() | +| classes.py:1584:18:1584:29 | malloc With_index() | classes.py:1584:18:1584:29 | malloc With_index() | +| classes.py:1585:5:1585:23 | malloc complex() | classes.py:1585:5:1585:23 | malloc complex() | +| classes.py:1599:18:1599:29 | malloc With_round() | classes.py:1599:18:1599:29 | malloc With_round() | +| classes.py:1614:18:1614:29 | malloc With_trunc() | classes.py:1614:18:1614:29 | malloc With_trunc() | +| classes.py:1630:18:1630:29 | malloc With_floor() | classes.py:1630:18:1630:29 | malloc With_floor() | +| classes.py:1646:17:1646:27 | malloc With_ceil() | classes.py:1646:17:1646:27 | malloc With_ceil() | +| classes.py:1665:10:1665:21 | malloc With_enter() | classes.py:1665:10:1665:21 | malloc With_enter() | +| classes.py:1686:10:1686:20 | malloc With_exit() | classes.py:1686:10:1686:20 | malloc With_exit() | +| classes.py:1703:18:1703:29 | malloc With_await() | classes.py:1703:18:1703:29 | malloc With_await() | +| classes.py:1726:18:1726:29 | malloc With_aiter() | classes.py:1726:18:1726:29 | malloc With_aiter() | +| classes.py:1745:18:1745:29 | malloc With_anext() | classes.py:1745:18:1745:29 | malloc With_anext() | +| classes.py:1763:19:1763:31 | malloc With_aenter() | classes.py:1763:19:1763:31 | malloc With_aenter() | +| classes.py:1784:18:1784:29 | malloc With_aexit() | classes.py:1784:18:1784:29 | malloc With_aexit() | From 9aa0cfb35cd6e5455a321c3b67b7e8cca6a36ddd Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 18 Sep 2020 22:11:21 +0200 Subject: [PATCH 037/185] Python: class callable -> class call Only have one type of callable, but have an extra type of call. A constructor call directs to an init callable (should also handle `call` overrides at some point). --- .../dataflow/internal/DataFlowPrivate.qll | 115 ++---- .../dataflow/coverage/argumentRouting1.ql | 2 +- .../coverage/classesCallGraph.expected | 330 +++++++++--------- .../dataflow/fieldflow/allLocalFlow.expected | 16 +- .../dataflow/fieldflow/dataflow.expected | 16 + .../fieldflow/dataflowExplore.expected | 11 +- .../dataflow/fieldflow/globalStep.expected | 195 +++++++---- .../dataflow/fieldflow/localFlow.expected | 8 +- .../dataflow/fieldflow/postupdates.expected | 20 +- 9 files changed, 370 insertions(+), 343 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index c2766a3ef95..b598c25a5e3 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -50,15 +50,6 @@ class ReadPreUpdateNode extends PreUpdateNode, CfgNode { override string label() { result = "read" } } -class MallocNode extends PreUpdateNode, ImplicitSelfArgumentNode { - // ObjectCreationNode() { exists(ClassValue c | this.asCfgNode() = c.getACall()) } - override string toString() { - result = "malloc " + this.asCfgNode().(CallNode).getNode().(Call).toString() - } - - override string label() { result = "malloc" } -} - /** * A node associated with an object after an operation that might have * changed its state. @@ -191,67 +182,13 @@ private Node update(Node node) { // Global flow //-------- /** - * IPA type for DataFlowCallable. - * A callable is either a callable value or a class. + * A DataFlowCallable is any callable value. */ -newtype TDataFlowCallable = - TCallableValue(CallableValue callable) or - TClassValue(ClassValue c) - -/** Represents a callable */ -abstract class DataFlowCallable extends TDataFlowCallable { - /** Gets a textual representation of this element. */ - abstract string toString(); - - /** Gets a call to this callable. */ - abstract CallNode getACall(); - - /** Gets the scope of this callable */ - abstract Scope getScope(); - - /** Gets the specified parameter of this callable */ - abstract NameNode getParameter(int n); - - /** Gets the name of this callable. */ - abstract string getName(); -} - -class DataFlowCallableValue extends DataFlowCallable, TCallableValue { - CallableValue callable; - - DataFlowCallableValue() { this = TCallableValue(callable) } - - override string toString() { result = callable.toString() } - - override CallNode getACall() { result = callable.getACall() } - - override Scope getScope() { result = callable.getScope() } - - override NameNode getParameter(int n) { result = callable.getParameter(n) } - - override string getName() { result = callable.getName() } -} - -class DataFlowClassValue extends DataFlowCallable, TClassValue { - ClassValue c; - - DataFlowClassValue() { this = TClassValue(c) } - - override string toString() { result = c.toString() } - - override CallNode getACall() { result = c.getACall() } - - override Scope getScope() { result = c.getScope() } - - override NameNode getParameter(int n) { - result.getNode() = c.getScope().getInitMethod().getArg(n + 1).asName() - } - - override string getName() { result = c.getName() } -} +class DataFlowCallable = CallableValue; newtype TDataFlowCall = - TCallNode(CallNode call) or + TCallNode(CallNode call) { call = any(CallableValue c).getACall() } or + TClassCall(CallNode call) { call = any(ClassValue c).getACall() } or TSpecialCall(SpecialMethodCallNode special) abstract class DataFlowCall extends TDataFlowCall { @@ -292,6 +229,36 @@ class CallNodeCall extends DataFlowCall, TCallNode { override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() } } +/** Represents a call to a class. */ +class ClassCall extends DataFlowCall, TClassCall { + CallNode call; + ClassValue c; + + ClassCall() { + this = TClassCall(call) and + call = c.getACall() + } + + override string toString() { result = call.toString() } + + override ControlFlowNode getArg(int n) { + result = call.getArg(n - 1) + or + n = 0 and result = call + } + + override ControlFlowNode getNode() { result = call } + + override DataFlowCallable getCallable() { + exists(CallableValue callable | + result = callable and + c.getScope().getInitMethod() = callable.getScope() + ) + } + + override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() } +} + /** Represents a call to a special method. */ class SpecialCall extends DataFlowCall, TSpecialCall { SpecialMethodCallNode special; @@ -304,9 +271,7 @@ class SpecialCall extends DataFlowCall, TSpecialCall { override ControlFlowNode getNode() { result = special } - override DataFlowCallable getCallable() { - result = TCallableValue(special.getResolvedSpecialMethod()) - } + override DataFlowCallable getCallable() { result = special.getResolvedSpecialMethod() } override DataFlowCallable getEnclosingCallable() { result.getScope() = special.getNode().getScope() @@ -333,16 +298,6 @@ class ExplicitArgumentNode extends ArgumentNode { final override DataFlowCall getCall() { this.argumentOf(result, _) } } -class ImplicitSelfArgumentNode extends ArgumentNode { - ImplicitSelfArgumentNode() { exists(ClassValue cv | node = cv.getACall()) } - - /** Holds if this argument occurs at the given position in the given call. */ - override predicate argumentOf(DataFlowCall call, int pos) { call = TCallNode(node) and pos = -1 } - - /** Gets the call in which this node is an argument. */ - final override DataFlowCall getCall() { result = TCallNode(node) } -} - /** Gets a viable run-time target for the call `call`. */ DataFlowCallable viableCallable(DataFlowCall call) { result = call.getCallable() } diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql index f0db6f193f5..9b7b562c770 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql @@ -11,7 +11,7 @@ class ArgumentRoutingConfig extends DataFlow::Configuration { exists(AssignmentDefinition def, DataFlow::DataFlowCall call | def.getVariable() = node.(DataFlow::EssaNode).getVar() and def.getValue() = call.getNode() and - call.getCallable().getName().matches("With\\_%") + call.getNode().(CallNode).getNode().(Call).toString().matches("With\\_%") // TODO: Do not rely on toString ) and node.(DataFlow::EssaNode).getVar().getName().matches("with\\_%") } diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected index 650f271bd27..a34e34424a7 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected @@ -1,200 +1,200 @@ -| classes.py:41:16:41:22 | malloc super() | classes.py:41:16:41:22 | malloc super() | +| classes.py:41:16:41:22 | ControlFlowNode for super() | classes.py:41:16:41:22 | ControlFlowNode for super() | | classes.py:41:16:41:35 | ControlFlowNode for Attribute() | classes.py:41:16:41:35 | ControlFlowNode for Attribute() | -| classes.py:45:16:45:25 | malloc With_new() | classes.py:45:16:45:25 | malloc With_new() | -| classes.py:58:17:58:27 | malloc With_init() | classes.py:52:18:52:21 | SSA variable self | -| classes.py:58:17:58:27 | malloc With_init() | classes.py:58:17:58:27 | malloc With_init() | -| classes.py:71:16:71:25 | malloc With_del() | classes.py:71:16:71:25 | malloc With_del() | -| classes.py:86:17:86:27 | malloc With_repr() | classes.py:86:17:86:27 | malloc With_repr() | -| classes.py:101:16:101:25 | malloc With_str() | classes.py:101:16:101:25 | malloc With_str() | -| classes.py:102:5:102:17 | malloc str() | classes.py:102:5:102:17 | malloc str() | -| classes.py:116:18:116:29 | malloc With_bytes() | classes.py:116:18:116:29 | malloc With_bytes() | -| classes.py:117:5:117:21 | malloc bytes() | classes.py:117:5:117:21 | malloc bytes() | -| classes.py:132:19:132:31 | malloc With_format() | classes.py:132:19:132:31 | malloc With_format() | -| classes.py:138:19:138:31 | malloc With_format() | classes.py:138:19:138:31 | malloc With_format() | -| classes.py:143:19:143:31 | malloc With_format() | classes.py:143:19:143:31 | malloc With_format() | -| classes.py:159:15:159:23 | malloc With_lt() | classes.py:159:15:159:23 | malloc With_lt() | -| classes.py:176:15:176:23 | malloc With_le() | classes.py:176:15:176:23 | malloc With_le() | -| classes.py:193:15:193:23 | malloc With_eq() | classes.py:193:15:193:23 | malloc With_eq() | -| classes.py:209:15:209:23 | malloc With_ne() | classes.py:209:15:209:23 | malloc With_ne() | -| classes.py:225:15:225:23 | malloc With_gt() | classes.py:225:15:225:23 | malloc With_gt() | -| classes.py:242:15:242:23 | malloc With_ge() | classes.py:242:15:242:23 | malloc With_ge() | -| classes.py:258:17:258:27 | malloc With_hash() | classes.py:258:17:258:27 | malloc With_hash() | -| classes.py:263:17:263:27 | malloc With_hash() | classes.py:263:17:263:27 | malloc With_hash() | -| classes.py:264:9:264:24 | malloc set() | classes.py:264:9:264:24 | malloc set() | -| classes.py:268:17:268:27 | malloc With_hash() | classes.py:268:17:268:27 | malloc With_hash() | -| classes.py:269:9:269:30 | malloc frozenset() | classes.py:269:9:269:30 | malloc frozenset() | -| classes.py:273:17:273:27 | malloc With_hash() | classes.py:273:17:273:27 | malloc With_hash() | -| classes.py:274:9:274:28 | malloc dict() | classes.py:274:9:274:28 | malloc dict() | -| classes.py:288:17:288:27 | malloc With_bool() | classes.py:288:17:288:27 | malloc With_bool() | -| classes.py:289:5:289:19 | malloc bool() | classes.py:289:5:289:19 | malloc bool() | -| classes.py:293:17:293:27 | malloc With_bool() | classes.py:293:17:293:27 | malloc With_bool() | -| classes.py:311:20:311:33 | malloc With_getattr() | classes.py:311:20:311:33 | malloc With_getattr() | -| classes.py:327:25:327:43 | malloc With_getattribute() | classes.py:327:25:327:43 | malloc With_getattribute() | -| classes.py:343:20:343:33 | malloc With_setattr() | classes.py:343:20:343:33 | malloc With_setattr() | -| classes.py:359:20:359:33 | malloc With_delattr() | classes.py:359:20:359:33 | malloc With_delattr() | -| classes.py:374:16:374:25 | malloc With_dir() | classes.py:374:16:374:25 | malloc With_dir() | -| classes.py:399:16:399:25 | malloc With_get() | classes.py:399:16:399:25 | malloc With_get() | -| classes.py:401:12:401:17 | malloc arg3() | classes.py:401:12:401:17 | malloc arg3() | -| classes.py:417:16:417:25 | malloc With_set() | classes.py:417:16:417:25 | malloc With_set() | -| classes.py:419:12:419:18 | malloc Owner() | classes.py:419:12:419:18 | malloc Owner() | -| classes.py:435:19:435:31 | malloc With_delete() | classes.py:435:19:435:31 | malloc With_delete() | -| classes.py:437:12:437:18 | malloc Owner() | classes.py:437:12:437:18 | malloc Owner() | -| classes.py:453:21:453:35 | malloc With_set_name() | classes.py:453:21:453:35 | malloc With_set_name() | -| classes.py:454:5:454:53 | malloc type() | classes.py:454:5:454:53 | malloc type() | -| classes.py:454:29:454:52 | malloc dict() | classes.py:454:29:454:52 | malloc dict() | -| classes.py:473:5:473:47 | malloc type() | classes.py:473:5:473:47 | malloc type() | -| classes.py:513:26:513:45 | malloc With_instancecheck() | classes.py:513:26:513:45 | malloc With_instancecheck() | -| classes.py:530:26:530:45 | malloc With_subclasscheck() | classes.py:530:26:530:45 | malloc With_subclasscheck() | -| classes.py:548:26:548:51 | malloc Subscript() | classes.py:548:26:548:51 | malloc Subscript() | -| classes.py:561:17:561:27 | malloc With_call() | classes.py:561:17:561:27 | malloc With_call() | -| classes.py:577:16:577:25 | malloc With_len() | classes.py:577:16:577:25 | malloc With_len() | -| classes.py:582:16:582:25 | malloc With_len() | classes.py:582:16:582:25 | malloc With_len() | -| classes.py:583:5:583:18 | malloc bool() | classes.py:583:5:583:18 | malloc bool() | -| classes.py:587:16:587:25 | malloc With_len() | classes.py:587:16:587:25 | malloc With_len() | -| classes.py:604:24:604:41 | malloc With_length_hint() | classes.py:604:24:604:41 | malloc With_length_hint() | -| classes.py:620:20:620:33 | malloc With_getitem() | classes.py:612:21:612:24 | SSA variable self | -| classes.py:620:20:620:33 | malloc With_getitem() | classes.py:620:20:620:33 | malloc With_getitem() | +| classes.py:45:16:45:25 | ControlFlowNode for With_new() | classes.py:45:16:45:25 | ControlFlowNode for With_new() | +| classes.py:58:17:58:27 | ControlFlowNode for With_init() | classes.py:52:18:52:21 | SSA variable self | +| classes.py:58:17:58:27 | ControlFlowNode for With_init() | classes.py:58:17:58:27 | ControlFlowNode for With_init() | +| classes.py:71:16:71:25 | ControlFlowNode for With_del() | classes.py:71:16:71:25 | ControlFlowNode for With_del() | +| classes.py:86:17:86:27 | ControlFlowNode for With_repr() | classes.py:86:17:86:27 | ControlFlowNode for With_repr() | +| classes.py:101:16:101:25 | ControlFlowNode for With_str() | classes.py:101:16:101:25 | ControlFlowNode for With_str() | +| classes.py:102:5:102:17 | ControlFlowNode for str() | classes.py:102:5:102:17 | ControlFlowNode for str() | +| classes.py:116:18:116:29 | ControlFlowNode for With_bytes() | classes.py:116:18:116:29 | ControlFlowNode for With_bytes() | +| classes.py:117:5:117:21 | ControlFlowNode for bytes() | classes.py:117:5:117:21 | ControlFlowNode for bytes() | +| classes.py:132:19:132:31 | ControlFlowNode for With_format() | classes.py:132:19:132:31 | ControlFlowNode for With_format() | +| classes.py:138:19:138:31 | ControlFlowNode for With_format() | classes.py:138:19:138:31 | ControlFlowNode for With_format() | +| classes.py:143:19:143:31 | ControlFlowNode for With_format() | classes.py:143:19:143:31 | ControlFlowNode for With_format() | +| classes.py:159:15:159:23 | ControlFlowNode for With_lt() | classes.py:159:15:159:23 | ControlFlowNode for With_lt() | +| classes.py:176:15:176:23 | ControlFlowNode for With_le() | classes.py:176:15:176:23 | ControlFlowNode for With_le() | +| classes.py:193:15:193:23 | ControlFlowNode for With_eq() | classes.py:193:15:193:23 | ControlFlowNode for With_eq() | +| classes.py:209:15:209:23 | ControlFlowNode for With_ne() | classes.py:209:15:209:23 | ControlFlowNode for With_ne() | +| classes.py:225:15:225:23 | ControlFlowNode for With_gt() | classes.py:225:15:225:23 | ControlFlowNode for With_gt() | +| classes.py:242:15:242:23 | ControlFlowNode for With_ge() | classes.py:242:15:242:23 | ControlFlowNode for With_ge() | +| classes.py:258:17:258:27 | ControlFlowNode for With_hash() | classes.py:258:17:258:27 | ControlFlowNode for With_hash() | +| classes.py:263:17:263:27 | ControlFlowNode for With_hash() | classes.py:263:17:263:27 | ControlFlowNode for With_hash() | +| classes.py:264:9:264:24 | ControlFlowNode for set() | classes.py:264:9:264:24 | ControlFlowNode for set() | +| classes.py:268:17:268:27 | ControlFlowNode for With_hash() | classes.py:268:17:268:27 | ControlFlowNode for With_hash() | +| classes.py:269:9:269:30 | ControlFlowNode for frozenset() | classes.py:269:9:269:30 | ControlFlowNode for frozenset() | +| classes.py:273:17:273:27 | ControlFlowNode for With_hash() | classes.py:273:17:273:27 | ControlFlowNode for With_hash() | +| classes.py:274:9:274:28 | ControlFlowNode for dict() | classes.py:274:9:274:28 | ControlFlowNode for dict() | +| classes.py:288:17:288:27 | ControlFlowNode for With_bool() | classes.py:288:17:288:27 | ControlFlowNode for With_bool() | +| classes.py:289:5:289:19 | ControlFlowNode for bool() | classes.py:289:5:289:19 | ControlFlowNode for bool() | +| classes.py:293:17:293:27 | ControlFlowNode for With_bool() | classes.py:293:17:293:27 | ControlFlowNode for With_bool() | +| classes.py:311:20:311:33 | ControlFlowNode for With_getattr() | classes.py:311:20:311:33 | ControlFlowNode for With_getattr() | +| classes.py:327:25:327:43 | ControlFlowNode for With_getattribute() | classes.py:327:25:327:43 | ControlFlowNode for With_getattribute() | +| classes.py:343:20:343:33 | ControlFlowNode for With_setattr() | classes.py:343:20:343:33 | ControlFlowNode for With_setattr() | +| classes.py:359:20:359:33 | ControlFlowNode for With_delattr() | classes.py:359:20:359:33 | ControlFlowNode for With_delattr() | +| classes.py:374:16:374:25 | ControlFlowNode for With_dir() | classes.py:374:16:374:25 | ControlFlowNode for With_dir() | +| classes.py:399:16:399:25 | ControlFlowNode for With_get() | classes.py:399:16:399:25 | ControlFlowNode for With_get() | +| classes.py:401:12:401:17 | ControlFlowNode for arg3() | classes.py:401:12:401:17 | ControlFlowNode for arg3() | +| classes.py:417:16:417:25 | ControlFlowNode for With_set() | classes.py:417:16:417:25 | ControlFlowNode for With_set() | +| classes.py:419:12:419:18 | ControlFlowNode for Owner() | classes.py:419:12:419:18 | ControlFlowNode for Owner() | +| classes.py:435:19:435:31 | ControlFlowNode for With_delete() | classes.py:435:19:435:31 | ControlFlowNode for With_delete() | +| classes.py:437:12:437:18 | ControlFlowNode for Owner() | classes.py:437:12:437:18 | ControlFlowNode for Owner() | +| classes.py:453:21:453:35 | ControlFlowNode for With_set_name() | classes.py:453:21:453:35 | ControlFlowNode for With_set_name() | +| classes.py:454:5:454:53 | ControlFlowNode for type() | classes.py:454:5:454:53 | ControlFlowNode for type() | +| classes.py:454:29:454:52 | ControlFlowNode for dict() | classes.py:454:29:454:52 | ControlFlowNode for dict() | +| classes.py:473:5:473:47 | ControlFlowNode for type() | classes.py:473:5:473:47 | ControlFlowNode for type() | +| classes.py:513:26:513:45 | ControlFlowNode for With_instancecheck() | classes.py:513:26:513:45 | ControlFlowNode for With_instancecheck() | +| classes.py:530:26:530:45 | ControlFlowNode for With_subclasscheck() | classes.py:530:26:530:45 | ControlFlowNode for With_subclasscheck() | +| classes.py:548:26:548:51 | ControlFlowNode for Subscript() | classes.py:548:26:548:51 | ControlFlowNode for Subscript() | +| classes.py:561:17:561:27 | ControlFlowNode for With_call() | classes.py:561:17:561:27 | ControlFlowNode for With_call() | +| classes.py:577:16:577:25 | ControlFlowNode for With_len() | classes.py:577:16:577:25 | ControlFlowNode for With_len() | +| classes.py:582:16:582:25 | ControlFlowNode for With_len() | classes.py:582:16:582:25 | ControlFlowNode for With_len() | +| classes.py:583:5:583:18 | ControlFlowNode for bool() | classes.py:583:5:583:18 | ControlFlowNode for bool() | +| classes.py:587:16:587:25 | ControlFlowNode for With_len() | classes.py:587:16:587:25 | ControlFlowNode for With_len() | +| classes.py:604:24:604:41 | ControlFlowNode for With_length_hint() | classes.py:604:24:604:41 | ControlFlowNode for With_length_hint() | +| classes.py:620:20:620:33 | ControlFlowNode for With_getitem() | classes.py:612:21:612:24 | SSA variable self | +| classes.py:620:20:620:33 | ControlFlowNode for With_getitem() | classes.py:620:20:620:33 | ControlFlowNode for With_getitem() | | classes.py:622:5:622:16 | ControlFlowNode for with_getitem | classes.py:612:21:612:24 | SSA variable self | | classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:612:27:612:29 | SSA variable key | -| classes.py:637:20:637:33 | malloc With_setitem() | classes.py:629:21:629:24 | SSA variable self | -| classes.py:637:20:637:33 | malloc With_setitem() | classes.py:637:20:637:33 | malloc With_setitem() | +| classes.py:637:20:637:33 | ControlFlowNode for With_setitem() | classes.py:629:21:629:24 | SSA variable self | +| classes.py:637:20:637:33 | ControlFlowNode for With_setitem() | classes.py:637:20:637:33 | ControlFlowNode for With_setitem() | | classes.py:640:5:640:16 | ControlFlowNode for with_setitem | classes.py:629:21:629:24 | SSA variable self | | classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:629:27:629:29 | SSA variable key | | classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:629:32:629:36 | SSA variable value | -| classes.py:654:20:654:33 | malloc With_delitem() | classes.py:647:21:647:24 | SSA variable self | -| classes.py:654:20:654:33 | malloc With_delitem() | classes.py:654:20:654:33 | malloc With_delitem() | +| classes.py:654:20:654:33 | ControlFlowNode for With_delitem() | classes.py:647:21:647:24 | SSA variable self | +| classes.py:654:20:654:33 | ControlFlowNode for With_delitem() | classes.py:654:20:654:33 | ControlFlowNode for With_delitem() | | classes.py:656:9:656:20 | ControlFlowNode for with_delitem | classes.py:647:21:647:24 | SSA variable self | | classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:647:27:647:29 | SSA variable key | -| classes.py:671:20:671:33 | malloc With_missing() | classes.py:671:20:671:33 | malloc With_missing() | +| classes.py:671:20:671:33 | ControlFlowNode for With_missing() | classes.py:671:20:671:33 | ControlFlowNode for With_missing() | | classes.py:683:16:683:28 | ControlFlowNode for Attribute() | classes.py:683:16:683:28 | ControlFlowNode for Attribute() | -| classes.py:687:17:687:27 | malloc With_iter() | classes.py:687:17:687:27 | malloc With_iter() | -| classes.py:702:21:702:35 | malloc With_reversed() | classes.py:702:21:702:35 | malloc With_reversed() | -| classes.py:703:5:703:27 | malloc reversed() | classes.py:703:5:703:27 | malloc reversed() | -| classes.py:718:21:718:35 | malloc With_contains() | classes.py:718:21:718:35 | malloc With_contains() | -| classes.py:735:16:735:25 | malloc With_add() | classes.py:727:17:727:20 | SSA variable self | -| classes.py:735:16:735:25 | malloc With_add() | classes.py:727:23:727:27 | SSA variable other | -| classes.py:735:16:735:25 | malloc With_add() | classes.py:735:16:735:25 | malloc With_add() | +| classes.py:687:17:687:27 | ControlFlowNode for With_iter() | classes.py:687:17:687:27 | ControlFlowNode for With_iter() | +| classes.py:702:21:702:35 | ControlFlowNode for With_reversed() | classes.py:702:21:702:35 | ControlFlowNode for With_reversed() | +| classes.py:703:5:703:27 | ControlFlowNode for reversed() | classes.py:703:5:703:27 | ControlFlowNode for reversed() | +| classes.py:718:21:718:35 | ControlFlowNode for With_contains() | classes.py:718:21:718:35 | ControlFlowNode for With_contains() | +| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:727:17:727:20 | SSA variable self | +| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:727:23:727:27 | SSA variable other | +| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:735:16:735:25 | ControlFlowNode for With_add() | | classes.py:737:5:737:12 | ControlFlowNode for with_add | classes.py:727:17:727:20 | SSA variable self | | classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:727:23:727:27 | SSA variable other | -| classes.py:752:16:752:25 | malloc With_sub() | classes.py:744:17:744:20 | SSA variable self | -| classes.py:752:16:752:25 | malloc With_sub() | classes.py:744:23:744:27 | SSA variable other | -| classes.py:752:16:752:25 | malloc With_sub() | classes.py:752:16:752:25 | malloc With_sub() | +| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:744:17:744:20 | SSA variable self | +| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:744:23:744:27 | SSA variable other | +| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:752:16:752:25 | ControlFlowNode for With_sub() | | classes.py:754:5:754:12 | ControlFlowNode for with_sub | classes.py:744:17:744:20 | SSA variable self | | classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:744:23:744:27 | SSA variable other | -| classes.py:769:16:769:25 | malloc With_mul() | classes.py:761:17:761:20 | SSA variable self | -| classes.py:769:16:769:25 | malloc With_mul() | classes.py:761:23:761:27 | SSA variable other | -| classes.py:769:16:769:25 | malloc With_mul() | classes.py:769:16:769:25 | malloc With_mul() | +| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:761:17:761:20 | SSA variable self | +| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:761:23:761:27 | SSA variable other | +| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:769:16:769:25 | ControlFlowNode for With_mul() | | classes.py:771:5:771:12 | ControlFlowNode for with_mul | classes.py:761:17:761:20 | SSA variable self | | classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:761:23:761:27 | SSA variable other | -| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:778:20:778:23 | SSA variable self | -| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:778:26:778:30 | SSA variable other | -| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:786:19:786:31 | malloc With_matmul() | +| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:778:20:778:23 | SSA variable self | +| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:778:26:778:30 | SSA variable other | +| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | | classes.py:788:5:788:15 | ControlFlowNode for with_matmul | classes.py:778:20:778:23 | SSA variable self | | classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:778:26:778:30 | SSA variable other | -| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:795:21:795:24 | SSA variable self | -| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:795:27:795:31 | SSA variable other | -| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:803:20:803:33 | malloc With_truediv() | +| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:795:21:795:24 | SSA variable self | +| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:795:27:795:31 | SSA variable other | +| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | | classes.py:805:5:805:16 | ControlFlowNode for with_truediv | classes.py:795:21:795:24 | SSA variable self | | classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:795:27:795:31 | SSA variable other | -| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:812:22:812:25 | SSA variable self | -| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:812:28:812:32 | SSA variable other | -| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:820:21:820:35 | malloc With_floordiv() | +| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:812:22:812:25 | SSA variable self | +| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:812:28:812:32 | SSA variable other | +| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | | classes.py:822:5:822:17 | ControlFlowNode for with_floordiv | classes.py:812:22:812:25 | SSA variable self | | classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:812:28:812:32 | SSA variable other | -| classes.py:837:16:837:25 | malloc With_mod() | classes.py:829:17:829:20 | SSA variable self | -| classes.py:837:16:837:25 | malloc With_mod() | classes.py:829:23:829:27 | SSA variable other | -| classes.py:837:16:837:25 | malloc With_mod() | classes.py:837:16:837:25 | malloc With_mod() | +| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:829:17:829:20 | SSA variable self | +| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:829:23:829:27 | SSA variable other | +| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:837:16:837:25 | ControlFlowNode for With_mod() | | classes.py:839:5:839:12 | ControlFlowNode for with_mod | classes.py:829:17:829:20 | SSA variable self | | classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:829:23:829:27 | SSA variable other | -| classes.py:854:19:854:31 | malloc With_divmod() | classes.py:854:19:854:31 | malloc With_divmod() | -| classes.py:871:16:871:25 | malloc With_pow() | classes.py:871:16:871:25 | malloc With_pow() | -| classes.py:877:16:877:25 | malloc With_pow() | classes.py:863:17:863:20 | SSA variable self | -| classes.py:877:16:877:25 | malloc With_pow() | classes.py:863:23:863:27 | SSA variable other | -| classes.py:877:16:877:25 | malloc With_pow() | classes.py:877:16:877:25 | malloc With_pow() | +| classes.py:854:19:854:31 | ControlFlowNode for With_divmod() | classes.py:854:19:854:31 | ControlFlowNode for With_divmod() | +| classes.py:871:16:871:25 | ControlFlowNode for With_pow() | classes.py:871:16:871:25 | ControlFlowNode for With_pow() | +| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:863:17:863:20 | SSA variable self | +| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:863:23:863:27 | SSA variable other | +| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:877:16:877:25 | ControlFlowNode for With_pow() | | classes.py:879:5:879:12 | ControlFlowNode for with_pow | classes.py:863:17:863:20 | SSA variable self | | classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:863:23:863:27 | SSA variable other | -| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:886:20:886:23 | SSA variable self | -| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:886:26:886:30 | SSA variable other | -| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:894:19:894:31 | malloc With_lshift() | +| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:886:20:886:23 | SSA variable self | +| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:886:26:886:30 | SSA variable other | +| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | | classes.py:896:5:896:15 | ControlFlowNode for with_lshift | classes.py:886:20:886:23 | SSA variable self | | classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:886:26:886:30 | SSA variable other | -| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:903:20:903:23 | SSA variable self | -| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:903:26:903:30 | SSA variable other | -| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:911:19:911:31 | malloc With_rshift() | +| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:903:20:903:23 | SSA variable self | +| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:903:26:903:30 | SSA variable other | +| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | | classes.py:913:5:913:15 | ControlFlowNode for with_rshift | classes.py:903:20:903:23 | SSA variable self | | classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:903:26:903:30 | SSA variable other | -| classes.py:928:16:928:25 | malloc With_and() | classes.py:920:17:920:20 | SSA variable self | -| classes.py:928:16:928:25 | malloc With_and() | classes.py:920:23:920:27 | SSA variable other | -| classes.py:928:16:928:25 | malloc With_and() | classes.py:928:16:928:25 | malloc With_and() | +| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:920:17:920:20 | SSA variable self | +| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:920:23:920:27 | SSA variable other | +| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:928:16:928:25 | ControlFlowNode for With_and() | | classes.py:930:5:930:12 | ControlFlowNode for with_and | classes.py:920:17:920:20 | SSA variable self | | classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:920:23:920:27 | SSA variable other | -| classes.py:945:16:945:25 | malloc With_xor() | classes.py:937:17:937:20 | SSA variable self | -| classes.py:945:16:945:25 | malloc With_xor() | classes.py:937:23:937:27 | SSA variable other | -| classes.py:945:16:945:25 | malloc With_xor() | classes.py:945:16:945:25 | malloc With_xor() | +| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:937:17:937:20 | SSA variable self | +| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:937:23:937:27 | SSA variable other | +| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:945:16:945:25 | ControlFlowNode for With_xor() | | classes.py:947:5:947:12 | ControlFlowNode for with_xor | classes.py:937:17:937:20 | SSA variable self | | classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:937:23:937:27 | SSA variable other | -| classes.py:962:15:962:23 | malloc With_or() | classes.py:954:16:954:19 | SSA variable self | -| classes.py:962:15:962:23 | malloc With_or() | classes.py:954:22:954:26 | SSA variable other | -| classes.py:962:15:962:23 | malloc With_or() | classes.py:962:15:962:23 | malloc With_or() | +| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:954:16:954:19 | SSA variable self | +| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:954:22:954:26 | SSA variable other | +| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:962:15:962:23 | ControlFlowNode for With_or() | | classes.py:964:5:964:11 | ControlFlowNode for with_or | classes.py:954:16:954:19 | SSA variable self | | classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:954:22:954:26 | SSA variable other | -| classes.py:979:17:979:27 | malloc With_radd() | classes.py:979:17:979:27 | malloc With_radd() | -| classes.py:996:17:996:27 | malloc With_rsub() | classes.py:996:17:996:27 | malloc With_rsub() | -| classes.py:1013:17:1013:27 | malloc With_rmul() | classes.py:1013:17:1013:27 | malloc With_rmul() | -| classes.py:1030:20:1030:33 | malloc With_rmatmul() | classes.py:1030:20:1030:33 | malloc With_rmatmul() | -| classes.py:1047:21:1047:35 | malloc With_rtruediv() | classes.py:1047:21:1047:35 | malloc With_rtruediv() | -| classes.py:1064:22:1064:37 | malloc With_rfloordiv() | classes.py:1064:22:1064:37 | malloc With_rfloordiv() | -| classes.py:1081:17:1081:27 | malloc With_rmod() | classes.py:1081:17:1081:27 | malloc With_rmod() | -| classes.py:1098:20:1098:33 | malloc With_rdivmod() | classes.py:1098:20:1098:33 | malloc With_rdivmod() | -| classes.py:1115:17:1115:27 | malloc With_rpow() | classes.py:1115:17:1115:27 | malloc With_rpow() | -| classes.py:1121:17:1121:27 | malloc With_rpow() | classes.py:1121:17:1121:27 | malloc With_rpow() | -| classes.py:1138:20:1138:33 | malloc With_rlshift() | classes.py:1138:20:1138:33 | malloc With_rlshift() | -| classes.py:1155:20:1155:33 | malloc With_rrshift() | classes.py:1155:20:1155:33 | malloc With_rrshift() | -| classes.py:1172:17:1172:27 | malloc With_rand() | classes.py:1172:17:1172:27 | malloc With_rand() | -| classes.py:1189:17:1189:27 | malloc With_rxor() | classes.py:1189:17:1189:27 | malloc With_rxor() | -| classes.py:1206:16:1206:25 | malloc With_ror() | classes.py:1206:16:1206:25 | malloc With_ror() | -| classes.py:1223:17:1223:27 | malloc With_iadd() | classes.py:1223:17:1223:27 | malloc With_iadd() | -| classes.py:1240:17:1240:27 | malloc With_isub() | classes.py:1240:17:1240:27 | malloc With_isub() | -| classes.py:1257:17:1257:27 | malloc With_imul() | classes.py:1257:17:1257:27 | malloc With_imul() | -| classes.py:1274:20:1274:33 | malloc With_imatmul() | classes.py:1274:20:1274:33 | malloc With_imatmul() | -| classes.py:1291:21:1291:35 | malloc With_itruediv() | classes.py:1291:21:1291:35 | malloc With_itruediv() | -| classes.py:1308:22:1308:37 | malloc With_ifloordiv() | classes.py:1308:22:1308:37 | malloc With_ifloordiv() | -| classes.py:1325:17:1325:27 | malloc With_imod() | classes.py:1325:17:1325:27 | malloc With_imod() | -| classes.py:1342:17:1342:27 | malloc With_ipow() | classes.py:1342:17:1342:27 | malloc With_ipow() | -| classes.py:1359:20:1359:33 | malloc With_ilshift() | classes.py:1359:20:1359:33 | malloc With_ilshift() | -| classes.py:1376:20:1376:33 | malloc With_irshift() | classes.py:1376:20:1376:33 | malloc With_irshift() | -| classes.py:1393:17:1393:27 | malloc With_iand() | classes.py:1393:17:1393:27 | malloc With_iand() | -| classes.py:1410:17:1410:27 | malloc With_ixor() | classes.py:1410:17:1410:27 | malloc With_ixor() | -| classes.py:1427:16:1427:25 | malloc With_ior() | classes.py:1427:16:1427:25 | malloc With_ior() | -| classes.py:1443:16:1443:25 | malloc With_neg() | classes.py:1443:16:1443:25 | malloc With_neg() | -| classes.py:1458:16:1458:25 | malloc With_pos() | classes.py:1458:16:1458:25 | malloc With_pos() | -| classes.py:1473:16:1473:25 | malloc With_abs() | classes.py:1473:16:1473:25 | malloc With_abs() | -| classes.py:1488:19:1488:31 | malloc With_invert() | classes.py:1488:19:1488:31 | malloc With_invert() | -| classes.py:1503:20:1503:33 | malloc With_complex() | classes.py:1503:20:1503:33 | malloc With_complex() | -| classes.py:1504:5:1504:25 | malloc complex() | classes.py:1504:5:1504:25 | malloc complex() | -| classes.py:1518:16:1518:25 | malloc With_int() | classes.py:1518:16:1518:25 | malloc With_int() | -| classes.py:1519:5:1519:17 | malloc int() | classes.py:1519:5:1519:17 | malloc int() | -| classes.py:1533:18:1533:29 | malloc With_float() | classes.py:1533:18:1533:29 | malloc With_float() | -| classes.py:1534:5:1534:21 | malloc float() | classes.py:1534:5:1534:21 | malloc float() | -| classes.py:1549:18:1549:29 | malloc With_index() | classes.py:1549:18:1549:29 | malloc With_index() | -| classes.py:1554:18:1554:29 | malloc With_index() | classes.py:1554:18:1554:29 | malloc With_index() | -| classes.py:1559:18:1559:29 | malloc With_index() | classes.py:1559:18:1559:29 | malloc With_index() | -| classes.py:1564:18:1564:29 | malloc With_index() | classes.py:1564:18:1564:29 | malloc With_index() | -| classes.py:1569:18:1569:29 | malloc With_index() | classes.py:1569:18:1569:29 | malloc With_index() | -| classes.py:1574:18:1574:29 | malloc With_index() | classes.py:1574:18:1574:29 | malloc With_index() | -| classes.py:1575:5:1575:19 | malloc int() | classes.py:1575:5:1575:19 | malloc int() | -| classes.py:1579:18:1579:29 | malloc With_index() | classes.py:1579:18:1579:29 | malloc With_index() | -| classes.py:1580:5:1580:21 | malloc float() | classes.py:1580:5:1580:21 | malloc float() | -| classes.py:1584:18:1584:29 | malloc With_index() | classes.py:1584:18:1584:29 | malloc With_index() | -| classes.py:1585:5:1585:23 | malloc complex() | classes.py:1585:5:1585:23 | malloc complex() | -| classes.py:1599:18:1599:29 | malloc With_round() | classes.py:1599:18:1599:29 | malloc With_round() | -| classes.py:1614:18:1614:29 | malloc With_trunc() | classes.py:1614:18:1614:29 | malloc With_trunc() | -| classes.py:1630:18:1630:29 | malloc With_floor() | classes.py:1630:18:1630:29 | malloc With_floor() | -| classes.py:1646:17:1646:27 | malloc With_ceil() | classes.py:1646:17:1646:27 | malloc With_ceil() | -| classes.py:1665:10:1665:21 | malloc With_enter() | classes.py:1665:10:1665:21 | malloc With_enter() | -| classes.py:1686:10:1686:20 | malloc With_exit() | classes.py:1686:10:1686:20 | malloc With_exit() | -| classes.py:1703:18:1703:29 | malloc With_await() | classes.py:1703:18:1703:29 | malloc With_await() | -| classes.py:1726:18:1726:29 | malloc With_aiter() | classes.py:1726:18:1726:29 | malloc With_aiter() | -| classes.py:1745:18:1745:29 | malloc With_anext() | classes.py:1745:18:1745:29 | malloc With_anext() | -| classes.py:1763:19:1763:31 | malloc With_aenter() | classes.py:1763:19:1763:31 | malloc With_aenter() | -| classes.py:1784:18:1784:29 | malloc With_aexit() | classes.py:1784:18:1784:29 | malloc With_aexit() | +| classes.py:979:17:979:27 | ControlFlowNode for With_radd() | classes.py:979:17:979:27 | ControlFlowNode for With_radd() | +| classes.py:996:17:996:27 | ControlFlowNode for With_rsub() | classes.py:996:17:996:27 | ControlFlowNode for With_rsub() | +| classes.py:1013:17:1013:27 | ControlFlowNode for With_rmul() | classes.py:1013:17:1013:27 | ControlFlowNode for With_rmul() | +| classes.py:1030:20:1030:33 | ControlFlowNode for With_rmatmul() | classes.py:1030:20:1030:33 | ControlFlowNode for With_rmatmul() | +| classes.py:1047:21:1047:35 | ControlFlowNode for With_rtruediv() | classes.py:1047:21:1047:35 | ControlFlowNode for With_rtruediv() | +| classes.py:1064:22:1064:37 | ControlFlowNode for With_rfloordiv() | classes.py:1064:22:1064:37 | ControlFlowNode for With_rfloordiv() | +| classes.py:1081:17:1081:27 | ControlFlowNode for With_rmod() | classes.py:1081:17:1081:27 | ControlFlowNode for With_rmod() | +| classes.py:1098:20:1098:33 | ControlFlowNode for With_rdivmod() | classes.py:1098:20:1098:33 | ControlFlowNode for With_rdivmod() | +| classes.py:1115:17:1115:27 | ControlFlowNode for With_rpow() | classes.py:1115:17:1115:27 | ControlFlowNode for With_rpow() | +| classes.py:1121:17:1121:27 | ControlFlowNode for With_rpow() | classes.py:1121:17:1121:27 | ControlFlowNode for With_rpow() | +| classes.py:1138:20:1138:33 | ControlFlowNode for With_rlshift() | classes.py:1138:20:1138:33 | ControlFlowNode for With_rlshift() | +| classes.py:1155:20:1155:33 | ControlFlowNode for With_rrshift() | classes.py:1155:20:1155:33 | ControlFlowNode for With_rrshift() | +| classes.py:1172:17:1172:27 | ControlFlowNode for With_rand() | classes.py:1172:17:1172:27 | ControlFlowNode for With_rand() | +| classes.py:1189:17:1189:27 | ControlFlowNode for With_rxor() | classes.py:1189:17:1189:27 | ControlFlowNode for With_rxor() | +| classes.py:1206:16:1206:25 | ControlFlowNode for With_ror() | classes.py:1206:16:1206:25 | ControlFlowNode for With_ror() | +| classes.py:1223:17:1223:27 | ControlFlowNode for With_iadd() | classes.py:1223:17:1223:27 | ControlFlowNode for With_iadd() | +| classes.py:1240:17:1240:27 | ControlFlowNode for With_isub() | classes.py:1240:17:1240:27 | ControlFlowNode for With_isub() | +| classes.py:1257:17:1257:27 | ControlFlowNode for With_imul() | classes.py:1257:17:1257:27 | ControlFlowNode for With_imul() | +| classes.py:1274:20:1274:33 | ControlFlowNode for With_imatmul() | classes.py:1274:20:1274:33 | ControlFlowNode for With_imatmul() | +| classes.py:1291:21:1291:35 | ControlFlowNode for With_itruediv() | classes.py:1291:21:1291:35 | ControlFlowNode for With_itruediv() | +| classes.py:1308:22:1308:37 | ControlFlowNode for With_ifloordiv() | classes.py:1308:22:1308:37 | ControlFlowNode for With_ifloordiv() | +| classes.py:1325:17:1325:27 | ControlFlowNode for With_imod() | classes.py:1325:17:1325:27 | ControlFlowNode for With_imod() | +| classes.py:1342:17:1342:27 | ControlFlowNode for With_ipow() | classes.py:1342:17:1342:27 | ControlFlowNode for With_ipow() | +| classes.py:1359:20:1359:33 | ControlFlowNode for With_ilshift() | classes.py:1359:20:1359:33 | ControlFlowNode for With_ilshift() | +| classes.py:1376:20:1376:33 | ControlFlowNode for With_irshift() | classes.py:1376:20:1376:33 | ControlFlowNode for With_irshift() | +| classes.py:1393:17:1393:27 | ControlFlowNode for With_iand() | classes.py:1393:17:1393:27 | ControlFlowNode for With_iand() | +| classes.py:1410:17:1410:27 | ControlFlowNode for With_ixor() | classes.py:1410:17:1410:27 | ControlFlowNode for With_ixor() | +| classes.py:1427:16:1427:25 | ControlFlowNode for With_ior() | classes.py:1427:16:1427:25 | ControlFlowNode for With_ior() | +| classes.py:1443:16:1443:25 | ControlFlowNode for With_neg() | classes.py:1443:16:1443:25 | ControlFlowNode for With_neg() | +| classes.py:1458:16:1458:25 | ControlFlowNode for With_pos() | classes.py:1458:16:1458:25 | ControlFlowNode for With_pos() | +| classes.py:1473:16:1473:25 | ControlFlowNode for With_abs() | classes.py:1473:16:1473:25 | ControlFlowNode for With_abs() | +| classes.py:1488:19:1488:31 | ControlFlowNode for With_invert() | classes.py:1488:19:1488:31 | ControlFlowNode for With_invert() | +| classes.py:1503:20:1503:33 | ControlFlowNode for With_complex() | classes.py:1503:20:1503:33 | ControlFlowNode for With_complex() | +| classes.py:1504:5:1504:25 | ControlFlowNode for complex() | classes.py:1504:5:1504:25 | ControlFlowNode for complex() | +| classes.py:1518:16:1518:25 | ControlFlowNode for With_int() | classes.py:1518:16:1518:25 | ControlFlowNode for With_int() | +| classes.py:1519:5:1519:17 | ControlFlowNode for int() | classes.py:1519:5:1519:17 | ControlFlowNode for int() | +| classes.py:1533:18:1533:29 | ControlFlowNode for With_float() | classes.py:1533:18:1533:29 | ControlFlowNode for With_float() | +| classes.py:1534:5:1534:21 | ControlFlowNode for float() | classes.py:1534:5:1534:21 | ControlFlowNode for float() | +| classes.py:1549:18:1549:29 | ControlFlowNode for With_index() | classes.py:1549:18:1549:29 | ControlFlowNode for With_index() | +| classes.py:1554:18:1554:29 | ControlFlowNode for With_index() | classes.py:1554:18:1554:29 | ControlFlowNode for With_index() | +| classes.py:1559:18:1559:29 | ControlFlowNode for With_index() | classes.py:1559:18:1559:29 | ControlFlowNode for With_index() | +| classes.py:1564:18:1564:29 | ControlFlowNode for With_index() | classes.py:1564:18:1564:29 | ControlFlowNode for With_index() | +| classes.py:1569:18:1569:29 | ControlFlowNode for With_index() | classes.py:1569:18:1569:29 | ControlFlowNode for With_index() | +| classes.py:1574:18:1574:29 | ControlFlowNode for With_index() | classes.py:1574:18:1574:29 | ControlFlowNode for With_index() | +| classes.py:1575:5:1575:19 | ControlFlowNode for int() | classes.py:1575:5:1575:19 | ControlFlowNode for int() | +| classes.py:1579:18:1579:29 | ControlFlowNode for With_index() | classes.py:1579:18:1579:29 | ControlFlowNode for With_index() | +| classes.py:1580:5:1580:21 | ControlFlowNode for float() | classes.py:1580:5:1580:21 | ControlFlowNode for float() | +| classes.py:1584:18:1584:29 | ControlFlowNode for With_index() | classes.py:1584:18:1584:29 | ControlFlowNode for With_index() | +| classes.py:1585:5:1585:23 | ControlFlowNode for complex() | classes.py:1585:5:1585:23 | ControlFlowNode for complex() | +| classes.py:1599:18:1599:29 | ControlFlowNode for With_round() | classes.py:1599:18:1599:29 | ControlFlowNode for With_round() | +| classes.py:1614:18:1614:29 | ControlFlowNode for With_trunc() | classes.py:1614:18:1614:29 | ControlFlowNode for With_trunc() | +| classes.py:1630:18:1630:29 | ControlFlowNode for With_floor() | classes.py:1630:18:1630:29 | ControlFlowNode for With_floor() | +| classes.py:1646:17:1646:27 | ControlFlowNode for With_ceil() | classes.py:1646:17:1646:27 | ControlFlowNode for With_ceil() | +| classes.py:1665:10:1665:21 | ControlFlowNode for With_enter() | classes.py:1665:10:1665:21 | ControlFlowNode for With_enter() | +| classes.py:1686:10:1686:20 | ControlFlowNode for With_exit() | classes.py:1686:10:1686:20 | ControlFlowNode for With_exit() | +| classes.py:1703:18:1703:29 | ControlFlowNode for With_await() | classes.py:1703:18:1703:29 | ControlFlowNode for With_await() | +| classes.py:1726:18:1726:29 | ControlFlowNode for With_aiter() | classes.py:1726:18:1726:29 | ControlFlowNode for With_aiter() | +| classes.py:1745:18:1745:29 | ControlFlowNode for With_anext() | classes.py:1745:18:1745:29 | ControlFlowNode for With_anext() | +| classes.py:1763:19:1763:31 | ControlFlowNode for With_aenter() | classes.py:1763:19:1763:31 | ControlFlowNode for With_aenter() | +| classes.py:1784:18:1784:29 | ControlFlowNode for With_aexit() | classes.py:1784:18:1784:29 | ControlFlowNode for With_aexit() | diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected index 1e36d80a5ee..7cc18f44d0b 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected @@ -17,28 +17,28 @@ | test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | | test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj | | test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x | | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x | | test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a | | test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | | test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | | test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | | test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected index 47ab357dc2c..d6b2ba62757 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected @@ -1,4 +1,5 @@ edges +| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | @@ -8,7 +9,13 @@ edges | test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | +| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | +| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | nodes +| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | +| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] | | test.py:29:19:29:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] | @@ -20,6 +27,15 @@ nodes | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | | test.py:41:10:41:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | semmle.label | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | +| test.py:46:10:46:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | +| test.py:56:33:56:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | #select +| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | | | test.py:30:10:30:18 | ControlFlowNode for Attribute | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:30:10:30:18 | ControlFlowNode for Attribute | | | test.py:41:10:41:18 | ControlFlowNode for Attribute | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:41:10:41:18 | ControlFlowNode for Attribute | | +| test.py:46:10:46:16 | ControlFlowNode for Attribute | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:46:10:46:16 | ControlFlowNode for Attribute | | +| test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | | diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected index d68142f928f..f65f714063c 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected @@ -21,23 +21,20 @@ edges | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, ... (2)] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:45:11:45:23 | [post malloc] malloc MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | +| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post malloc] malloc MyObj() [Attribute foo] | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:17:45:22 | [post arg] ControlFlowNode for SOURCE [Attribute foo] | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | | test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | | test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:50:11:50:18 | [post malloc] malloc MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post malloc] malloc MyObj() [Attribute foo] | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:17:50:17 | [post arg] ControlFlowNode for x [Attribute foo] | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | | test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x | | test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE [Attribute foo] | #select | test.py:46:10:46:16 | ControlFlowNode for Attribute | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:46:10:46:16 | ControlFlowNode for Attribute | | diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected index 0fe90c99f8f..b0fedf775a3 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected @@ -1,7 +1,5 @@ | test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj | | test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj | -| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ | -| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ | | test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | | test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | | test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | @@ -14,10 +12,21 @@ | test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | | test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | | test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | +| test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | | test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj | | test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj | -| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ | -| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ | | test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj | | test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj | | test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | @@ -28,12 +37,14 @@ | test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | | test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | | test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | -| test.py:15:20:15:30 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:15:20:15:30 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | +| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | +| test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | +| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] | | test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | | test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | -| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj | -| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj | | test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self | | test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self | | test.py:21:1:21:19 | ControlFlowNode for FunctionExpr | test.py:21:5:21:10 | GSSA Variable setFoo | @@ -52,6 +63,7 @@ | test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | | test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | | test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | +| test.py:21:12:21:14 | SSA variable obj [Attribute foo] | test.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | | test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | | test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | | test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | @@ -60,6 +72,8 @@ | test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | | test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | | test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | +| test.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | test.py:22:12:22:18 | ControlFlowNode for Attribute | +| test.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | test.py:22:12:22:18 | ControlFlowNode for Attribute | | test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | | test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | | test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | @@ -85,30 +99,38 @@ | test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | | test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:27:13:27:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:13:27:23 | malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:13:27:23 | malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | +| test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | | test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | | test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | +| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | +| test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | test.py:21:12:21:14 | SSA variable obj [Attribute foo] | +| test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | @@ -143,32 +165,43 @@ | test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | | test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:14:18:14:21 | SSA variable self | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:14:18:14:21 | SSA variable self | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:9:36:19 | malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | +| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | +| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:36:5:36:5 | SSA variable a [Attribute obj] | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:38:5:38:9 | ControlFlowNode for Attribute | +| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:38:5:38:9 | ControlFlowNode for Attribute | +| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | +| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | | test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | @@ -203,18 +236,24 @@ | test.py:44:1:44:20 | GSSA Variable SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | | test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | | test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:45:11:45:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | +| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | +| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | +| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | +| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | | test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow | | test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow | | test.py:49:1:49:30 | GSSA Variable MyObj | test.py:50:11:50:15 | ControlFlowNode for MyObj | @@ -229,26 +268,44 @@ | test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | | test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:50:11:50:18 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | | test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | | test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | +| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | +| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | +| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | +| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | +| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | +| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | +| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | +| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | +| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a | +| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:52:12:52:12 | ControlFlowNode for a | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | @@ -263,3 +320,5 @@ | test.py:55:1:55:18 | GSSA Variable fields_with_local_flow | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow | | test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x | | test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x | +| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected index 6651727c229..03549a5b810 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected @@ -1,14 +1,14 @@ | examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | | examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | | examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj | -| examples.py:46:9:46:16 | [post malloc] malloc MyObj() | examples.py:46:3:46:5 | SSA variable obj | -| examples.py:46:9:46:16 | malloc MyObj() | examples.py:46:3:46:5 | SSA variable obj | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | +| examples.py:46:9:46:16 | [post arg] ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | | examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | | examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | | test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | | test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected index 4bbb7f3542b..434cd0b6bda 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected @@ -1,52 +1,52 @@ | examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:8:9:8:12 | ControlFlowNode for self | | examples.py:14:9:14:12 | [post store] ControlFlowNode for self | examples.py:14:9:14:12 | ControlFlowNode for self | -| examples.py:14:20:14:30 | [post malloc] malloc MyObj() | examples.py:14:20:14:30 | malloc MyObj() | +| examples.py:14:20:14:30 | [post arg] ControlFlowNode for MyObj() | examples.py:14:20:14:30 | ControlFlowNode for MyObj() | | examples.py:14:26:14:29 | [post arg] ControlFlowNode for Str | examples.py:14:26:14:29 | ControlFlowNode for Str | | examples.py:17:16:17:19 | [post read] ControlFlowNode for self | examples.py:17:16:17:19 | ControlFlowNode for self | | examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:22:12:22:14 | ControlFlowNode for obj | | examples.py:23:5:23:7 | [post store] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:25:9:25:19 | [post malloc] malloc MyObj() | examples.py:25:9:25:19 | malloc MyObj() | +| examples.py:25:9:25:19 | [post arg] ControlFlowNode for MyObj() | examples.py:25:9:25:19 | ControlFlowNode for MyObj() | | examples.py:25:15:25:18 | [post arg] ControlFlowNode for Str | examples.py:25:15:25:18 | ControlFlowNode for Str | | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj | | examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | | examples.py:28:6:28:10 | [post read] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | -| examples.py:33:5:33:15 | [post malloc] malloc NestedObj() | examples.py:33:5:33:15 | malloc NestedObj() | +| examples.py:33:5:33:15 | [post arg] ControlFlowNode for NestedObj() | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | | examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:35:1:35:1 | ControlFlowNode for a | | examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute | examples.py:35:1:35:5 | ControlFlowNode for Attribute | | examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | | examples.py:36:1:36:10 | [post store] ControlFlowNode for Attribute() | examples.py:36:1:36:10 | ControlFlowNode for Attribute() | | examples.py:38:6:38:6 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | | examples.py:38:6:38:10 | [post read] ControlFlowNode for Attribute | examples.py:38:6:38:10 | ControlFlowNode for Attribute | -| examples.py:41:7:41:19 | [post malloc] malloc MyObj() | examples.py:41:7:41:19 | malloc MyObj() | +| examples.py:41:7:41:19 | [post arg] ControlFlowNode for MyObj() | examples.py:41:7:41:19 | ControlFlowNode for MyObj() | | examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | | examples.py:42:6:42:8 | [post read] ControlFlowNode for obj | examples.py:42:6:42:8 | ControlFlowNode for obj | -| examples.py:46:9:46:16 | [post malloc] malloc MyObj() | examples.py:46:9:46:16 | malloc MyObj() | +| examples.py:46:9:46:16 | [post arg] ControlFlowNode for MyObj() | examples.py:46:9:46:16 | ControlFlowNode for MyObj() | | examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x | | examples.py:47:7:47:9 | [post read] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj | | examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | | test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:9:9:9:12 | ControlFlowNode for self | | test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:15:9:15:12 | ControlFlowNode for self | -| test.py:15:20:15:30 | [post malloc] malloc MyObj() | test.py:15:20:15:30 | malloc MyObj() | +| test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() | test.py:15:20:15:30 | ControlFlowNode for MyObj() | | test.py:15:26:15:29 | [post arg] ControlFlowNode for Str | test.py:15:26:15:29 | ControlFlowNode for Str | | test.py:18:16:18:19 | [post read] ControlFlowNode for self | test.py:18:16:18:19 | ControlFlowNode for self | | test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:22:12:22:14 | ControlFlowNode for obj | | test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:13:27:23 | malloc MyObj() | +| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:13:27:23 | ControlFlowNode for MyObj() | | test.py:27:19:27:22 | [post arg] ControlFlowNode for Str | test.py:27:19:27:22 | ControlFlowNode for Str | | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | | test.py:29:19:29:24 | [post arg] ControlFlowNode for SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE | | test.py:30:10:30:14 | [post read] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:9:36:19 | malloc NestedObj() | +| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | | test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:38:5:38:5 | ControlFlowNode for a | | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute | test.py:38:5:38:9 | ControlFlowNode for Attribute | | test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:39:5:39:14 | [post store] ControlFlowNode for Attribute() | test.py:39:5:39:14 | ControlFlowNode for Attribute() | | test.py:41:10:41:10 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:41:10:41:14 | [post read] ControlFlowNode for Attribute | test.py:41:10:41:14 | ControlFlowNode for Attribute | -| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:11:45:23 | malloc MyObj() | +| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:11:45:23 | ControlFlowNode for MyObj() | | test.py:45:17:45:22 | [post arg] ControlFlowNode for SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | | test.py:46:10:46:12 | [post read] ControlFlowNode for obj | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:11:50:18 | malloc MyObj() | +| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:11:50:18 | ControlFlowNode for MyObj() | | test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:51:9:51:11 | [post read] ControlFlowNode for obj | test.py:51:9:51:11 | ControlFlowNode for obj | | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE | From 87d4e13584297992d169cf7fc6816f94fb5543cf Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 17 Sep 2020 12:42:37 +0200 Subject: [PATCH 038/185] added support for ES2021 assignment operators --- .../src/com/semmle/jcorn/Parser.java | 20 +- .../extractor/tests/es2021/input/assign.js | 9 + .../extractor/tests/es2021/options.json | 3 + .../tests/es2021/output/trap/assign.js.trap | 633 ++++++++++++++++++ 4 files changed, 659 insertions(+), 6 deletions(-) create mode 100644 javascript/extractor/tests/es2021/input/assign.js create mode 100644 javascript/extractor/tests/es2021/options.json create mode 100644 javascript/extractor/tests/es2021/output/trap/assign.js.trap diff --git a/javascript/extractor/src/com/semmle/jcorn/Parser.java b/javascript/extractor/src/com/semmle/jcorn/Parser.java index a8348c3c307..7c018b6a456 100644 --- a/javascript/extractor/src/com/semmle/jcorn/Parser.java +++ b/javascript/extractor/src/com/semmle/jcorn/Parser.java @@ -531,9 +531,14 @@ public class Parser { int next2 = charAt(this.pos + 2); if (this.options.esnext()) { if (next == '.' && !('0' <= next2 && next2 <= '9')) // '?.', but not '?.X' where X is a digit - return this.finishOp(TokenType.questiondot, 2); - if (next == '?') // '??' - return this.finishOp(TokenType.questionquestion, 2); + return this.finishOp(TokenType.questiondot, 2); + if (next == '?') { // '??' + if (next2 == '=') { // ??= + return this.finishOp(TokenType.assign, 3); + } + return this.finishOp(TokenType.questionquestion, 2); + } + } return this.finishOp(TokenType.question, 1); } @@ -566,8 +571,11 @@ public class Parser { private Token readToken_pipe_amp(int code) { // '|&' int next = charAt(this.pos + 1); - if (next == code) + int next2 = charAt(this.pos + 2); + if (next == code) { // && || + if (next2 == 61) return this.finishOp(TokenType.assign, 3); // &&= ||= return this.finishOp(code == 124 ? TokenType.logicalOR : TokenType.logicalAND, 2); + } if (next == 61) return this.finishOp(TokenType.assign, 2); return this.finishOp(code == 124 ? TokenType.bitwiseOR : TokenType.bitwiseAND, 1); } @@ -709,8 +717,8 @@ public class Parser { case 42: // '%*' return this.readToken_mult_modulo_exp(code); - case 124: - case 38: // '|&' + case 124: // '|' + case 38: // '&' return this.readToken_pipe_amp(code); case 94: // '^' diff --git a/javascript/extractor/tests/es2021/input/assign.js b/javascript/extractor/tests/es2021/input/assign.js new file mode 100644 index 00000000000..7075534fe3b --- /dev/null +++ b/javascript/extractor/tests/es2021/input/assign.js @@ -0,0 +1,9 @@ +var x = 1; +var y = 2; +var foo = x && y; +var bar = x &&= y; +console.log(x); // 2 + +x &&= y; +x ||= y; +x ??= y; diff --git a/javascript/extractor/tests/es2021/options.json b/javascript/extractor/tests/es2021/options.json new file mode 100644 index 00000000000..075583ca1f6 --- /dev/null +++ b/javascript/extractor/tests/es2021/options.json @@ -0,0 +1,3 @@ +{ + "experimental": true +} diff --git a/javascript/extractor/tests/es2021/output/trap/assign.js.trap b/javascript/extractor/tests/es2021/output/trap/assign.js.trap new file mode 100644 index 00000000000..1fb2b771f15 --- /dev/null +++ b/javascript/extractor/tests/es2021/output/trap/assign.js.trap @@ -0,0 +1,633 @@ +#10000=@"/assign.js;sourcefile" +files(#10000,"/assign.js","assign","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +#20002=* +comments(#20002,0,#20001," 2","// 2") +#20003=@"loc,{#10000},5,17,5,20" +locations_default(#20003,#10000,5,17,5,20) +hasLocation(#20002,#20003) +#20004=* +lines(#20004,#20001,"var x = 1;"," +") +#20005=@"loc,{#10000},1,1,1,10" +locations_default(#20005,#10000,1,1,1,10) +hasLocation(#20004,#20005) +#20006=* +lines(#20006,#20001,"var y = 2;"," +") +#20007=@"loc,{#10000},2,1,2,10" +locations_default(#20007,#10000,2,1,2,10) +hasLocation(#20006,#20007) +#20008=* +lines(#20008,#20001,"var foo = x && y;"," +") +#20009=@"loc,{#10000},3,1,3,17" +locations_default(#20009,#10000,3,1,3,17) +hasLocation(#20008,#20009) +#20010=* +lines(#20010,#20001,"var bar = x &&= y;"," +") +#20011=@"loc,{#10000},4,1,4,18" +locations_default(#20011,#10000,4,1,4,18) +hasLocation(#20010,#20011) +#20012=* +lines(#20012,#20001,"console.log(x); // 2"," +") +#20013=@"loc,{#10000},5,1,5,20" +locations_default(#20013,#10000,5,1,5,20) +hasLocation(#20012,#20013) +#20014=* +lines(#20014,#20001,""," +") +#20015=@"loc,{#10000},6,1,6,0" +locations_default(#20015,#10000,6,1,6,0) +hasLocation(#20014,#20015) +#20016=* +lines(#20016,#20001,"x &&= y;"," +") +#20017=@"loc,{#10000},7,1,7,8" +locations_default(#20017,#10000,7,1,7,8) +hasLocation(#20016,#20017) +#20018=* +lines(#20018,#20001,"x ||= y;"," +") +#20019=@"loc,{#10000},8,1,8,8" +locations_default(#20019,#10000,8,1,8,8) +hasLocation(#20018,#20019) +#20020=* +lines(#20020,#20001,"x ??= y;"," +") +#20021=@"loc,{#10000},9,1,9,8" +locations_default(#20021,#10000,9,1,9,8) +hasLocation(#20020,#20021) +numlines(#20001,9,8,1) +#20022=* +tokeninfo(#20022,7,#20001,0,"var") +#20023=@"loc,{#10000},1,1,1,3" +locations_default(#20023,#10000,1,1,1,3) +hasLocation(#20022,#20023) +#20024=* +tokeninfo(#20024,6,#20001,1,"x") +#20025=@"loc,{#10000},1,5,1,5" +locations_default(#20025,#10000,1,5,1,5) +hasLocation(#20024,#20025) +#20026=* +tokeninfo(#20026,8,#20001,2,"=") +#20027=@"loc,{#10000},1,7,1,7" +locations_default(#20027,#10000,1,7,1,7) +hasLocation(#20026,#20027) +#20028=* +tokeninfo(#20028,3,#20001,3,"1") +#20029=@"loc,{#10000},1,9,1,9" +locations_default(#20029,#10000,1,9,1,9) +hasLocation(#20028,#20029) +#20030=* +tokeninfo(#20030,8,#20001,4,";") +#20031=@"loc,{#10000},1,10,1,10" +locations_default(#20031,#10000,1,10,1,10) +hasLocation(#20030,#20031) +#20032=* +tokeninfo(#20032,7,#20001,5,"var") +#20033=@"loc,{#10000},2,1,2,3" +locations_default(#20033,#10000,2,1,2,3) +hasLocation(#20032,#20033) +#20034=* +tokeninfo(#20034,6,#20001,6,"y") +#20035=@"loc,{#10000},2,5,2,5" +locations_default(#20035,#10000,2,5,2,5) +hasLocation(#20034,#20035) +#20036=* +tokeninfo(#20036,8,#20001,7,"=") +#20037=@"loc,{#10000},2,7,2,7" +locations_default(#20037,#10000,2,7,2,7) +hasLocation(#20036,#20037) +#20038=* +tokeninfo(#20038,3,#20001,8,"2") +#20039=@"loc,{#10000},2,9,2,9" +locations_default(#20039,#10000,2,9,2,9) +hasLocation(#20038,#20039) +#20040=* +tokeninfo(#20040,8,#20001,9,";") +#20041=@"loc,{#10000},2,10,2,10" +locations_default(#20041,#10000,2,10,2,10) +hasLocation(#20040,#20041) +#20042=* +tokeninfo(#20042,7,#20001,10,"var") +#20043=@"loc,{#10000},3,1,3,3" +locations_default(#20043,#10000,3,1,3,3) +hasLocation(#20042,#20043) +#20044=* +tokeninfo(#20044,6,#20001,11,"foo") +#20045=@"loc,{#10000},3,5,3,7" +locations_default(#20045,#10000,3,5,3,7) +hasLocation(#20044,#20045) +#20046=* +tokeninfo(#20046,8,#20001,12,"=") +#20047=@"loc,{#10000},3,9,3,9" +locations_default(#20047,#10000,3,9,3,9) +hasLocation(#20046,#20047) +#20048=* +tokeninfo(#20048,6,#20001,13,"x") +#20049=@"loc,{#10000},3,11,3,11" +locations_default(#20049,#10000,3,11,3,11) +hasLocation(#20048,#20049) +#20050=* +tokeninfo(#20050,8,#20001,14,"&&") +#20051=@"loc,{#10000},3,13,3,14" +locations_default(#20051,#10000,3,13,3,14) +hasLocation(#20050,#20051) +#20052=* +tokeninfo(#20052,6,#20001,15,"y") +#20053=@"loc,{#10000},3,16,3,16" +locations_default(#20053,#10000,3,16,3,16) +hasLocation(#20052,#20053) +#20054=* +tokeninfo(#20054,8,#20001,16,";") +#20055=@"loc,{#10000},3,17,3,17" +locations_default(#20055,#10000,3,17,3,17) +hasLocation(#20054,#20055) +#20056=* +tokeninfo(#20056,7,#20001,17,"var") +#20057=@"loc,{#10000},4,1,4,3" +locations_default(#20057,#10000,4,1,4,3) +hasLocation(#20056,#20057) +#20058=* +tokeninfo(#20058,6,#20001,18,"bar") +#20059=@"loc,{#10000},4,5,4,7" +locations_default(#20059,#10000,4,5,4,7) +hasLocation(#20058,#20059) +#20060=* +tokeninfo(#20060,8,#20001,19,"=") +#20061=@"loc,{#10000},4,9,4,9" +locations_default(#20061,#10000,4,9,4,9) +hasLocation(#20060,#20061) +#20062=* +tokeninfo(#20062,6,#20001,20,"x") +#20063=@"loc,{#10000},4,11,4,11" +locations_default(#20063,#10000,4,11,4,11) +hasLocation(#20062,#20063) +#20064=* +tokeninfo(#20064,8,#20001,21,"&&=") +#20065=@"loc,{#10000},4,13,4,15" +locations_default(#20065,#10000,4,13,4,15) +hasLocation(#20064,#20065) +#20066=* +tokeninfo(#20066,6,#20001,22,"y") +#20067=@"loc,{#10000},4,17,4,17" +locations_default(#20067,#10000,4,17,4,17) +hasLocation(#20066,#20067) +#20068=* +tokeninfo(#20068,8,#20001,23,";") +#20069=@"loc,{#10000},4,18,4,18" +locations_default(#20069,#10000,4,18,4,18) +hasLocation(#20068,#20069) +#20070=* +tokeninfo(#20070,6,#20001,24,"console") +#20071=@"loc,{#10000},5,1,5,7" +locations_default(#20071,#10000,5,1,5,7) +hasLocation(#20070,#20071) +#20072=* +tokeninfo(#20072,8,#20001,25,".") +#20073=@"loc,{#10000},5,8,5,8" +locations_default(#20073,#10000,5,8,5,8) +hasLocation(#20072,#20073) +#20074=* +tokeninfo(#20074,6,#20001,26,"log") +#20075=@"loc,{#10000},5,9,5,11" +locations_default(#20075,#10000,5,9,5,11) +hasLocation(#20074,#20075) +#20076=* +tokeninfo(#20076,8,#20001,27,"(") +#20077=@"loc,{#10000},5,12,5,12" +locations_default(#20077,#10000,5,12,5,12) +hasLocation(#20076,#20077) +#20078=* +tokeninfo(#20078,6,#20001,28,"x") +#20079=@"loc,{#10000},5,13,5,13" +locations_default(#20079,#10000,5,13,5,13) +hasLocation(#20078,#20079) +#20080=* +tokeninfo(#20080,8,#20001,29,")") +#20081=@"loc,{#10000},5,14,5,14" +locations_default(#20081,#10000,5,14,5,14) +hasLocation(#20080,#20081) +#20082=* +tokeninfo(#20082,8,#20001,30,";") +#20083=@"loc,{#10000},5,15,5,15" +locations_default(#20083,#10000,5,15,5,15) +hasLocation(#20082,#20083) +#20084=* +tokeninfo(#20084,6,#20001,31,"x") +#20085=@"loc,{#10000},7,1,7,1" +locations_default(#20085,#10000,7,1,7,1) +hasLocation(#20084,#20085) +next_token(#20002,#20084) +#20086=* +tokeninfo(#20086,8,#20001,32,"&&=") +#20087=@"loc,{#10000},7,3,7,5" +locations_default(#20087,#10000,7,3,7,5) +hasLocation(#20086,#20087) +#20088=* +tokeninfo(#20088,6,#20001,33,"y") +#20089=@"loc,{#10000},7,7,7,7" +locations_default(#20089,#10000,7,7,7,7) +hasLocation(#20088,#20089) +#20090=* +tokeninfo(#20090,8,#20001,34,";") +#20091=@"loc,{#10000},7,8,7,8" +locations_default(#20091,#10000,7,8,7,8) +hasLocation(#20090,#20091) +#20092=* +tokeninfo(#20092,6,#20001,35,"x") +#20093=@"loc,{#10000},8,1,8,1" +locations_default(#20093,#10000,8,1,8,1) +hasLocation(#20092,#20093) +#20094=* +tokeninfo(#20094,8,#20001,36,"||=") +#20095=@"loc,{#10000},8,3,8,5" +locations_default(#20095,#10000,8,3,8,5) +hasLocation(#20094,#20095) +#20096=* +tokeninfo(#20096,6,#20001,37,"y") +#20097=@"loc,{#10000},8,7,8,7" +locations_default(#20097,#10000,8,7,8,7) +hasLocation(#20096,#20097) +#20098=* +tokeninfo(#20098,8,#20001,38,";") +#20099=@"loc,{#10000},8,8,8,8" +locations_default(#20099,#10000,8,8,8,8) +hasLocation(#20098,#20099) +#20100=* +tokeninfo(#20100,6,#20001,39,"x") +#20101=@"loc,{#10000},9,1,9,1" +locations_default(#20101,#10000,9,1,9,1) +hasLocation(#20100,#20101) +#20102=* +tokeninfo(#20102,8,#20001,40,"??=") +#20103=@"loc,{#10000},9,3,9,5" +locations_default(#20103,#10000,9,3,9,5) +hasLocation(#20102,#20103) +#20104=* +tokeninfo(#20104,6,#20001,41,"y") +#20105=@"loc,{#10000},9,7,9,7" +locations_default(#20105,#10000,9,7,9,7) +hasLocation(#20104,#20105) +#20106=* +tokeninfo(#20106,8,#20001,42,";") +#20107=@"loc,{#10000},9,8,9,8" +locations_default(#20107,#10000,9,8,9,8) +hasLocation(#20106,#20107) +#20108=* +tokeninfo(#20108,0,#20001,43,"") +#20109=@"loc,{#10000},10,1,10,0" +locations_default(#20109,#10000,10,1,10,0) +hasLocation(#20108,#20109) +toplevels(#20001,0) +#20110=@"loc,{#10000},1,1,10,0" +locations_default(#20110,#10000,1,1,10,0) +hasLocation(#20001,#20110) +#20111=@"var;{x};{#20000}" +variables(#20111,"x",#20000) +#20112=@"var;{y};{#20000}" +variables(#20112,"y",#20000) +#20113=@"var;{foo};{#20000}" +variables(#20113,"foo",#20000) +#20114=@"var;{bar};{#20000}" +variables(#20114,"bar",#20000) +#20115=* +stmts(#20115,18,#20001,0,"var x = 1;") +hasLocation(#20115,#20005) +stmt_containers(#20115,#20001) +#20116=* +exprs(#20116,64,#20115,0,"x = 1") +#20117=@"loc,{#10000},1,5,1,9" +locations_default(#20117,#10000,1,5,1,9) +hasLocation(#20116,#20117) +enclosing_stmt(#20116,#20115) +expr_containers(#20116,#20001) +#20118=* +exprs(#20118,78,#20116,0,"x") +hasLocation(#20118,#20025) +enclosing_stmt(#20118,#20115) +expr_containers(#20118,#20001) +literals("x","x",#20118) +decl(#20118,#20111) +#20119=* +exprs(#20119,3,#20116,1,"1") +hasLocation(#20119,#20029) +enclosing_stmt(#20119,#20115) +expr_containers(#20119,#20001) +literals("1","1",#20119) +#20120=* +stmts(#20120,18,#20001,1,"var y = 2;") +hasLocation(#20120,#20007) +stmt_containers(#20120,#20001) +#20121=* +exprs(#20121,64,#20120,0,"y = 2") +#20122=@"loc,{#10000},2,5,2,9" +locations_default(#20122,#10000,2,5,2,9) +hasLocation(#20121,#20122) +enclosing_stmt(#20121,#20120) +expr_containers(#20121,#20001) +#20123=* +exprs(#20123,78,#20121,0,"y") +hasLocation(#20123,#20035) +enclosing_stmt(#20123,#20120) +expr_containers(#20123,#20001) +literals("y","y",#20123) +decl(#20123,#20112) +#20124=* +exprs(#20124,3,#20121,1,"2") +hasLocation(#20124,#20039) +enclosing_stmt(#20124,#20120) +expr_containers(#20124,#20001) +literals("2","2",#20124) +#20125=* +stmts(#20125,18,#20001,2,"var foo = x && y;") +hasLocation(#20125,#20009) +stmt_containers(#20125,#20001) +#20126=* +exprs(#20126,64,#20125,0,"foo = x && y") +#20127=@"loc,{#10000},3,5,3,16" +locations_default(#20127,#10000,3,5,3,16) +hasLocation(#20126,#20127) +enclosing_stmt(#20126,#20125) +expr_containers(#20126,#20001) +#20128=* +exprs(#20128,78,#20126,0,"foo") +hasLocation(#20128,#20045) +enclosing_stmt(#20128,#20125) +expr_containers(#20128,#20001) +literals("foo","foo",#20128) +decl(#20128,#20113) +#20129=* +exprs(#20129,44,#20126,1,"x && y") +#20130=@"loc,{#10000},3,11,3,16" +locations_default(#20130,#10000,3,11,3,16) +hasLocation(#20129,#20130) +enclosing_stmt(#20129,#20125) +expr_containers(#20129,#20001) +#20131=* +exprs(#20131,79,#20129,0,"x") +hasLocation(#20131,#20049) +enclosing_stmt(#20131,#20125) +expr_containers(#20131,#20001) +literals("x","x",#20131) +bind(#20131,#20111) +#20132=* +exprs(#20132,79,#20129,1,"y") +hasLocation(#20132,#20053) +enclosing_stmt(#20132,#20125) +expr_containers(#20132,#20001) +literals("y","y",#20132) +bind(#20132,#20112) +#20133=* +stmts(#20133,18,#20001,3,"var bar = x &&= y;") +hasLocation(#20133,#20011) +stmt_containers(#20133,#20001) +#20134=* +exprs(#20134,64,#20133,0,"bar = x &&= y") +#20135=@"loc,{#10000},4,5,4,17" +locations_default(#20135,#10000,4,5,4,17) +hasLocation(#20134,#20135) +enclosing_stmt(#20134,#20133) +expr_containers(#20134,#20001) +#20136=* +exprs(#20136,78,#20134,0,"bar") +hasLocation(#20136,#20059) +enclosing_stmt(#20136,#20133) +expr_containers(#20136,#20001) +literals("bar","bar",#20136) +decl(#20136,#20114) +#20137=* +exprs(#20137,116,#20134,1,"x &&= y") +#20138=@"loc,{#10000},4,11,4,17" +locations_default(#20138,#10000,4,11,4,17) +hasLocation(#20137,#20138) +enclosing_stmt(#20137,#20133) +expr_containers(#20137,#20001) +#20139=* +exprs(#20139,79,#20137,0,"x") +hasLocation(#20139,#20063) +enclosing_stmt(#20139,#20133) +expr_containers(#20139,#20001) +literals("x","x",#20139) +bind(#20139,#20111) +#20140=* +exprs(#20140,79,#20137,1,"y") +hasLocation(#20140,#20067) +enclosing_stmt(#20140,#20133) +expr_containers(#20140,#20001) +literals("y","y",#20140) +bind(#20140,#20112) +#20141=* +stmts(#20141,2,#20001,4,"console.log(x);") +#20142=@"loc,{#10000},5,1,5,15" +locations_default(#20142,#10000,5,1,5,15) +hasLocation(#20141,#20142) +stmt_containers(#20141,#20001) +#20143=* +exprs(#20143,13,#20141,0,"console.log(x)") +#20144=@"loc,{#10000},5,1,5,14" +locations_default(#20144,#10000,5,1,5,14) +hasLocation(#20143,#20144) +enclosing_stmt(#20143,#20141) +expr_containers(#20143,#20001) +#20145=* +exprs(#20145,14,#20143,-1,"console.log") +#20146=@"loc,{#10000},5,1,5,11" +locations_default(#20146,#10000,5,1,5,11) +hasLocation(#20145,#20146) +enclosing_stmt(#20145,#20141) +expr_containers(#20145,#20001) +#20147=* +exprs(#20147,79,#20145,0,"console") +hasLocation(#20147,#20071) +enclosing_stmt(#20147,#20141) +expr_containers(#20147,#20001) +literals("console","console",#20147) +#20148=@"var;{console};{#20000}" +variables(#20148,"console",#20000) +bind(#20147,#20148) +#20149=* +exprs(#20149,0,#20145,1,"log") +hasLocation(#20149,#20075) +enclosing_stmt(#20149,#20141) +expr_containers(#20149,#20001) +literals("log","log",#20149) +#20150=* +exprs(#20150,79,#20143,0,"x") +hasLocation(#20150,#20079) +enclosing_stmt(#20150,#20141) +expr_containers(#20150,#20001) +literals("x","x",#20150) +bind(#20150,#20111) +#20151=* +stmts(#20151,2,#20001,5,"x &&= y;") +hasLocation(#20151,#20017) +stmt_containers(#20151,#20001) +#20152=* +exprs(#20152,116,#20151,0,"x &&= y") +#20153=@"loc,{#10000},7,1,7,7" +locations_default(#20153,#10000,7,1,7,7) +hasLocation(#20152,#20153) +enclosing_stmt(#20152,#20151) +expr_containers(#20152,#20001) +#20154=* +exprs(#20154,79,#20152,0,"x") +hasLocation(#20154,#20085) +enclosing_stmt(#20154,#20151) +expr_containers(#20154,#20001) +literals("x","x",#20154) +bind(#20154,#20111) +#20155=* +exprs(#20155,79,#20152,1,"y") +hasLocation(#20155,#20089) +enclosing_stmt(#20155,#20151) +expr_containers(#20155,#20001) +literals("y","y",#20155) +bind(#20155,#20112) +#20156=* +stmts(#20156,2,#20001,6,"x ||= y;") +hasLocation(#20156,#20019) +stmt_containers(#20156,#20001) +#20157=* +exprs(#20157,117,#20156,0,"x ||= y") +#20158=@"loc,{#10000},8,1,8,7" +locations_default(#20158,#10000,8,1,8,7) +hasLocation(#20157,#20158) +enclosing_stmt(#20157,#20156) +expr_containers(#20157,#20001) +#20159=* +exprs(#20159,79,#20157,0,"x") +hasLocation(#20159,#20093) +enclosing_stmt(#20159,#20156) +expr_containers(#20159,#20001) +literals("x","x",#20159) +bind(#20159,#20111) +#20160=* +exprs(#20160,79,#20157,1,"y") +hasLocation(#20160,#20097) +enclosing_stmt(#20160,#20156) +expr_containers(#20160,#20001) +literals("y","y",#20160) +bind(#20160,#20112) +#20161=* +stmts(#20161,2,#20001,7,"x ??= y;") +hasLocation(#20161,#20021) +stmt_containers(#20161,#20001) +#20162=* +exprs(#20162,118,#20161,0,"x ??= y") +#20163=@"loc,{#10000},9,1,9,7" +locations_default(#20163,#10000,9,1,9,7) +hasLocation(#20162,#20163) +enclosing_stmt(#20162,#20161) +expr_containers(#20162,#20001) +#20164=* +exprs(#20164,79,#20162,0,"x") +hasLocation(#20164,#20101) +enclosing_stmt(#20164,#20161) +expr_containers(#20164,#20001) +literals("x","x",#20164) +bind(#20164,#20111) +#20165=* +exprs(#20165,79,#20162,1,"y") +hasLocation(#20165,#20105) +enclosing_stmt(#20165,#20161) +expr_containers(#20165,#20001) +literals("y","y",#20165) +bind(#20165,#20112) +#20166=* +entry_cfg_node(#20166,#20001) +#20167=@"loc,{#10000},1,1,1,0" +locations_default(#20167,#10000,1,1,1,0) +hasLocation(#20166,#20167) +#20168=* +exit_cfg_node(#20168,#20001) +hasLocation(#20168,#20109) +successor(#20161,#20164) +successor(#20164,#20165) +successor(#20164,#20168) +successor(#20165,#20164) +successor(#20162,#20168) +successor(#20156,#20159) +#20169=* +guard_node(#20169,1,#20159) +hasLocation(#20169,#20093) +successor(#20169,#20161) +#20170=* +guard_node(#20170,0,#20159) +hasLocation(#20170,#20093) +successor(#20170,#20160) +successor(#20159,#20169) +successor(#20159,#20170) +successor(#20160,#20159) +successor(#20157,#20161) +successor(#20151,#20154) +#20171=* +guard_node(#20171,1,#20154) +hasLocation(#20171,#20085) +successor(#20171,#20155) +#20172=* +guard_node(#20172,0,#20154) +hasLocation(#20172,#20085) +successor(#20172,#20156) +successor(#20154,#20171) +successor(#20154,#20172) +successor(#20155,#20154) +successor(#20152,#20156) +successor(#20141,#20147) +successor(#20150,#20143) +successor(#20149,#20145) +successor(#20147,#20149) +successor(#20145,#20150) +successor(#20143,#20151) +successor(#20133,#20136) +#20173=* +guard_node(#20173,1,#20139) +hasLocation(#20173,#20063) +successor(#20173,#20140) +#20174=* +guard_node(#20174,0,#20139) +hasLocation(#20174,#20063) +successor(#20174,#20134) +successor(#20139,#20173) +successor(#20139,#20174) +successor(#20140,#20139) +successor(#20137,#20134) +successor(#20136,#20139) +successor(#20134,#20141) +successor(#20125,#20128) +successor(#20129,#20131) +#20175=* +guard_node(#20175,1,#20131) +hasLocation(#20175,#20049) +successor(#20175,#20132) +#20176=* +guard_node(#20176,0,#20131) +hasLocation(#20176,#20049) +successor(#20176,#20126) +successor(#20131,#20175) +successor(#20131,#20176) +successor(#20132,#20126) +successor(#20128,#20129) +successor(#20126,#20133) +successor(#20120,#20123) +successor(#20124,#20121) +successor(#20123,#20124) +successor(#20121,#20125) +successor(#20115,#20118) +successor(#20119,#20116) +successor(#20118,#20119) +successor(#20116,#20120) +successor(#20166,#20115) +numlines(#10000,9,8,1) +filetype(#10000,"javascript") From 0dbdbfa6596a026c4403635b8ad0dac98a80e73e Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 17 Sep 2020 13:13:55 +0200 Subject: [PATCH 039/185] bump extractor version --- javascript/extractor/src/com/semmle/js/extractor/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index 359e07d3194..a7b209bc7ce 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -43,7 +43,7 @@ public class Main { * A version identifier that should be updated every time the extractor changes in such a way that * it may produce different tuples for the same file under the same {@link ExtractorConfig}. */ - public static final String EXTRACTOR_VERSION = "2020-09-12"; + public static final String EXTRACTOR_VERSION = "2020-09-17"; public static final Pattern NEWLINE = Pattern.compile("\n"); From b09015380ac778d9376bf54a5f253af064bb1813 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 17 Sep 2020 14:00:44 +0200 Subject: [PATCH 040/185] add support for String.prototype.replaceAll --- .../ql/src/Expressions/StringInsteadOfRegex.ql | 2 +- .../ql/src/RegExp/IdentityReplacement.ql | 2 +- .../CWE-020/IncompleteUrlSchemeCheck.ql | 2 +- .../Security/CWE-116/IncompleteSanitization.ql | 1 + .../src/semmle/javascript/StandardLibrary.qll | 6 +++--- .../javascript/dataflow/TaintTracking.qll | 6 ++++-- .../heuristics/AdditionalTaintSteps.qll | 4 +--- .../CleartextLoggingCustomizations.qll | 12 ++++-------- .../dataflow/TaintedPathCustomizations.qll | 18 ++++++++---------- .../security/dataflow/UnsafeJQueryPlugin.qll | 5 ++--- .../javascript/security/dataflow/Xss.qll | 5 ++--- .../PolynomialReDoSCustomizations.qll | 1 + .../InterProceduralFlow/TaintTracking.expected | 2 ++ .../library-tests/InterProceduralFlow/tst.js | 3 +++ .../ReDoS/PolynomialBackTracking.expected | 1 + .../Performance/ReDoS/PolynomialReDoS.expected | 5 +++++ .../Performance/ReDoS/polynomial-redos.js | 2 +- 17 files changed, 41 insertions(+), 36 deletions(-) diff --git a/javascript/ql/src/Expressions/StringInsteadOfRegex.ql b/javascript/ql/src/Expressions/StringInsteadOfRegex.ql index f81b8b1a5ae..f1efb37e330 100644 --- a/javascript/ql/src/Expressions/StringInsteadOfRegex.ql +++ b/javascript/ql/src/Expressions/StringInsteadOfRegex.ql @@ -31,7 +31,7 @@ predicate isStringSplitOrReplace(MethodCallExpr mce) { mce.getMethodName() = name and mce.getNumArgument() = arity | - name = "replace" and arity = 2 + name = ["replace", "replaceAll"] and arity = 2 or name = "split" and (arity = 1 or arity = 2) diff --git a/javascript/ql/src/RegExp/IdentityReplacement.ql b/javascript/ql/src/RegExp/IdentityReplacement.ql index f8c62a1ab3a..2a6e354cc9c 100644 --- a/javascript/ql/src/RegExp/IdentityReplacement.ql +++ b/javascript/ql/src/RegExp/IdentityReplacement.ql @@ -70,7 +70,7 @@ predicate regExpMatchesString(RegExpTerm t, string s) { from MethodCallExpr repl, string s, string friendly where - repl.getMethodName() = "replace" and + repl.getMethodName() = ["replace", "replaceAll"] and matchesString(repl.getArgument(0), s) and repl.getArgument(1).getStringValue() = s and (if s = "" then friendly = "the empty string" else friendly = "'" + s + "'") diff --git a/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql b/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql index 617692f3adb..0675b189cd1 100644 --- a/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql +++ b/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql @@ -75,7 +75,7 @@ DataFlow::Node schemeCheck(DataFlow::Node nd, DangerousScheme scheme) { exists(DataFlow::MethodCallNode stringop | stringop.getMethodName().matches("trim%") or stringop.getMethodName().matches("to%Case") or - stringop.getMethodName() = "replace" + stringop.getMethodName() = ["replace", "replaceAll"] | result = schemeCheck(stringop, scheme) and nd = stringop.getReceiver() diff --git a/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql b/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql index a1b466cab9f..9494dcde0e5 100644 --- a/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql +++ b/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql @@ -79,6 +79,7 @@ predicate allBackslashesEscaped(DataFlow::Node nd) { // flow through string methods exists(DataFlow::MethodCallNode mc, string m | m = "replace" or + m = "replaceAll" or m = "slice" or m = "substr" or m = "substring" or diff --git a/javascript/ql/src/semmle/javascript/StandardLibrary.qll b/javascript/ql/src/semmle/javascript/StandardLibrary.qll index b28d1f27439..6a57bda6461 100644 --- a/javascript/ql/src/semmle/javascript/StandardLibrary.qll +++ b/javascript/ql/src/semmle/javascript/StandardLibrary.qll @@ -102,7 +102,7 @@ private class IteratorExceptionStep extends DataFlow::MethodCallNode, DataFlow:: */ class StringReplaceCall extends DataFlow::MethodCallNode { StringReplaceCall() { - getMethodName() = "replace" and + getMethodName() = ["replace", "replaceAll"] and (getNumArgument() = 2 or getReceiver().mayHaveStringValue(_)) } @@ -128,9 +128,9 @@ class StringReplaceCall extends DataFlow::MethodCallNode { /** * Holds if this is a global replacement, that is, the first argument is a regular expression - * with the `g` flag. + * with the `g` flag, or this is a call to `.replaceAll()`. */ - predicate isGlobal() { getRegExp().isGlobal() } + predicate isGlobal() { getRegExp().isGlobal() or getMethodName() = "replaceAll" } /** * Holds if this call to `replace` replaces `old` with `new`. diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index f0763c0893f..6698800918c 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -427,6 +427,7 @@ module TaintTracking { name = "padStart" or name = "repeat" or name = "replace" or + name = "replaceAll" or name = "slice" or name = "small" or name = "split" or @@ -452,7 +453,7 @@ module TaintTracking { exists(int i | pred.asExpr() = succ.getAstNode().(MethodCallExpr).getArgument(i) | name = "concat" or - name = "replace" and i = 1 + name = ["replace", "replaceAll"] and i = 1 ) ) or @@ -707,7 +708,8 @@ module TaintTracking { */ private ControlFlowNode getACaptureSetter(DataFlow::Node input) { exists(DataFlow::MethodCallNode call | result = call.asExpr() | - call.getMethodName() = ["search", "replace", "match"] and input = call.getReceiver() + call.getMethodName() = ["search", "replace", "replaceAll", "match"] and + input = call.getReceiver() or call.getMethodName() = ["test", "exec"] and input = call.getArgument(0) ) diff --git a/javascript/ql/src/semmle/javascript/heuristics/AdditionalTaintSteps.qll b/javascript/ql/src/semmle/javascript/heuristics/AdditionalTaintSteps.qll index e920fb10613..5cda6f0d867 100644 --- a/javascript/ql/src/semmle/javascript/heuristics/AdditionalTaintSteps.qll +++ b/javascript/ql/src/semmle/javascript/heuristics/AdditionalTaintSteps.qll @@ -15,9 +15,7 @@ abstract class HeuristicAdditionalTaintStep extends DataFlow::ValueNode { } * A call to `tainted.replace(x, y)` that preserves taint. */ private class HeuristicStringManipulationTaintStep extends HeuristicAdditionalTaintStep, - TaintTracking::AdditionalTaintStep, DataFlow::MethodCallNode { - HeuristicStringManipulationTaintStep() { getMethodName() = "replace" } - + TaintTracking::AdditionalTaintStep, StringReplaceCall { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { pred = getReceiver() and succ = this } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll index 6fc13631ef2..eea099a7a79 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll @@ -34,15 +34,11 @@ module CleartextLogging { /** * A call to `.replace()` that seems to mask sensitive information. */ - class MaskingReplacer extends Barrier, DataFlow::MethodCallNode { + class MaskingReplacer extends Barrier, StringReplaceCall { MaskingReplacer() { - this.getCalleeName() = "replace" and - exists(RegExpLiteral reg | - reg = this.getArgument(0).getALocalSource().asExpr() and - reg.isGlobal() and - any(RegExpDot term).getLiteral() = reg - ) and - exists(this.getArgument(1).getStringValue()) + this.isGlobal() and + exists(this.getRawReplacement().getStringValue()) and + any(RegExpDot term).getLiteral() = getRegExp().asExpr() } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index daf0e9324ee..76b9e350427 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -218,12 +218,12 @@ module TaintedPath { output = this or // non-global replace or replace of something other than /\.\./g, /[/]/g, or /[\.]/g. - this.getCalleeName() = "replace" and + this instanceof StringReplaceCall and input = getReceiver() and output = this and not exists(RegExpLiteral literal, RegExpTerm term | - getArgument(0).getALocalSource().asExpr() = literal and - literal.isGlobal() and + this.(StringReplaceCall).getRegExp().asExpr() = literal and + this.(StringReplaceCall).isGlobal() and literal.getRoot() = term | term.getAMatchedString() = "/" or @@ -247,16 +247,15 @@ module TaintedPath { /** * A call that removes all instances of "../" in the prefix of the string. */ - class DotDotSlashPrefixRemovingReplace extends DataFlow::CallNode { + class DotDotSlashPrefixRemovingReplace extends StringReplaceCall { DataFlow::Node input; DataFlow::Node output; DotDotSlashPrefixRemovingReplace() { - this.getCalleeName() = "replace" and input = getReceiver() and output = this and exists(RegExpLiteral literal, RegExpTerm term | - getArgument(0).getALocalSource().asExpr() = literal and + getRegExp().asExpr() = literal and (term instanceof RegExpStar or term instanceof RegExpPlus) and term.getChild(0) = getADotDotSlashMatcher() | @@ -298,17 +297,16 @@ module TaintedPath { /** * A call that removes all "." or ".." from a path, without also removing all forward slashes. */ - class DotRemovingReplaceCall extends DataFlow::CallNode { + class DotRemovingReplaceCall extends StringReplaceCall { DataFlow::Node input; DataFlow::Node output; DotRemovingReplaceCall() { - this.getCalleeName() = "replace" and input = getReceiver() and output = this and + isGlobal() and exists(RegExpLiteral literal, RegExpTerm term | - getArgument(0).getALocalSource().asExpr() = literal and - literal.isGlobal() and + getRegExp().asExpr() = literal and literal.getRoot() = term and not term.getAMatchedString() = "/" | diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeJQueryPlugin.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeJQueryPlugin.qll index a2a7246b4a7..05c33b71330 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeJQueryPlugin.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeJQueryPlugin.qll @@ -39,10 +39,9 @@ module UnsafeJQueryPlugin { StringConcatenation::taintStep(pred, succ, _, any(int i | i >= 1)) or // prefixing through a poor-mans templating system: - exists(DataFlow::MethodCallNode replace | + exists(StringReplaceCall replace | replace = succ and - pred = replace.getArgument(1) and - replace.getMethodName() = "replace" + pred = replace.getRawReplacement() ) } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll b/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll index 54e7b7de47d..346ff52c862 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll @@ -33,11 +33,10 @@ module Shared { * The XSS queries do not attempt to reason about correctness or completeness of sanitizers, * so any such replacement stops taint propagation. */ - class MetacharEscapeSanitizer extends Sanitizer, DataFlow::MethodCallNode { + class MetacharEscapeSanitizer extends Sanitizer, StringReplaceCall { MetacharEscapeSanitizer() { - getMethodName() = "replace" and exists(RegExpConstant c | - c.getLiteral() = getArgument(0).getALocalSource().asExpr() and + c.getLiteral() = getRegExp().asExpr() and c.getValue().regexpMatch("['\"&<>]") ) } diff --git a/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll b/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll index 5507f9e6981..ea3d9db3ada 100644 --- a/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll @@ -51,6 +51,7 @@ module PolynomialReDoS { name = "split" or name = "matchAll" or name = "replace" or + name = "replaceAll" or name = "search" ) or diff --git a/javascript/ql/test/library-tests/InterProceduralFlow/TaintTracking.expected b/javascript/ql/test/library-tests/InterProceduralFlow/TaintTracking.expected index c3ea1cdb5f2..823db35f84d 100644 --- a/javascript/ql/test/library-tests/InterProceduralFlow/TaintTracking.expected +++ b/javascript/ql/test/library-tests/InterProceduralFlow/TaintTracking.expected @@ -86,6 +86,8 @@ | tst.js:2:17:2:22 | "src1" | tst.js:61:16:61:18 | o.r | | tst.js:2:17:2:22 | "src1" | tst.js:68:16:68:22 | inner() | | tst.js:2:17:2:22 | "src1" | tst.js:80:16:80:22 | outer() | +| tst.js:2:17:2:22 | "src1" | tst.js:87:16:87:43 | source1 ... /g, "") | +| tst.js:2:17:2:22 | "src1" | tst.js:88:16:88:46 | "foo".r ... ource1) | | underscore.js:2:17:2:22 | "src1" | underscore.js:3:15:3:28 | _.max(source1) | | underscore.js:5:17:5:22 | "src2" | underscore.js:6:15:6:34 | _.union([], source2) | | underscore.js:5:17:5:22 | "src2" | underscore.js:7:15:7:32 | _.zip(source2, []) | diff --git a/javascript/ql/test/library-tests/InterProceduralFlow/tst.js b/javascript/ql/test/library-tests/InterProceduralFlow/tst.js index a557538bd11..a8fc56e3d52 100644 --- a/javascript/ql/test/library-tests/InterProceduralFlow/tst.js +++ b/javascript/ql/test/library-tests/InterProceduralFlow/tst.js @@ -83,4 +83,7 @@ o.notTracked = source1; var sink22 = o.notTracked; + + var sink23 = source1.replaceAll(/f/g, ""); + var sink24 = "foo".replaceAll(/f/g, source1); })(); diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected index 98f5cb00ed4..1d78b51e8bf 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected @@ -17,6 +17,7 @@ | polynomial-redos.js:31:42:31:43 | -+ | it can start matching anywhere | | polynomial-redos.js:32:45:32:47 | \\n* | it can start matching anywhere | | polynomial-redos.js:33:17:33:20 | (.)* | it can start matching anywhere | +| polynomial-redos.js:48:22:48:24 | \\s* | it can start matching anywhere | | regexplib/address.js:18:26:18:31 | [ \\w]* | it can start matching anywhere after the start of the preceeding '[ \\w]{3,}' | | regexplib/address.js:20:144:20:147 | [ ]+ | it can start matching anywhere after the start of the preceeding '[a-zA-Z0-9 \\-.]{6,}' | | regexplib/address.js:24:26:24:31 | [ \\w]* | it can start matching anywhere after the start of the preceeding '[ \\w]{3,}' | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected index 4d3a9a12da6..e8ba1a56d67 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected @@ -28,6 +28,8 @@ nodes | polynomial-redos.js:30:2:30:8 | tainted | | polynomial-redos.js:33:2:33:8 | tainted | | polynomial-redos.js:33:2:33:8 | tainted | +| polynomial-redos.js:48:2:48:8 | tainted | +| polynomial-redos.js:48:2:48:8 | tainted | edges | polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:7:2:7:8 | tainted | | polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:7:2:7:8 | tainted | @@ -55,6 +57,8 @@ edges | polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:30:2:30:8 | tainted | | polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:33:2:33:8 | tainted | | polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:33:2:33:8 | tainted | +| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:48:2:48:8 | tainted | +| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:48:2:48:8 | tainted | | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:5:6:5:32 | tainted | | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:5:6:5:32 | tainted | #select @@ -72,3 +76,4 @@ edges | polynomial-redos.js:27:77:27:83 | tainted | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:27:77:27:83 | tainted | This expensive $@ use depends on $@. | polynomial-redos.js:27:14:27:22 | [A-Z]{2,} | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | | polynomial-redos.js:30:2:30:8 | tainted | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:30:2:30:8 | tainted | This expensive $@ use depends on $@. | polynomial-redos.js:30:19:30:22 | [?]+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | | polynomial-redos.js:33:2:33:8 | tainted | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:33:2:33:8 | tainted | This expensive $@ use depends on $@. | polynomial-redos.js:33:17:33:20 | (.)* | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | +| polynomial-redos.js:48:2:48:8 | tainted | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:48:2:48:8 | tainted | This expensive $@ use depends on $@. | polynomial-redos.js:48:22:48:24 | \\s* | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/polynomial-redos.js b/javascript/ql/test/query-tests/Performance/ReDoS/polynomial-redos.js index 4175bd48a50..1dcce05f3c7 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/polynomial-redos.js +++ b/javascript/ql/test/query-tests/Performance/ReDoS/polynomial-redos.js @@ -45,5 +45,5 @@ app.use(function(req, res) { tainted.match(/^(?:\.?[a-zA-Z_][a-zA-Z_0-9]*)+$/); // NOT OK - but not flagged tainted.match(/^(?:\.?[a-zA-Z_][a-zA-Z_0-9]*)(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*$/); // OK - + tainted.replaceAll(/\s*\n\s*/g, ' '); // NOT OK }); From 9f1b3d61b9fa6a87969cfedf4bdf38e3388a19a6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 17 Sep 2020 14:04:07 +0200 Subject: [PATCH 041/185] add test for numeric separators --- .../extractor/tests/es2021/input/numeric.js | 8 + .../tests/es2021/output/trap/numeric.js.trap | 435 ++++++++++++++++++ 2 files changed, 443 insertions(+) create mode 100644 javascript/extractor/tests/es2021/input/numeric.js create mode 100644 javascript/extractor/tests/es2021/output/trap/numeric.js.trap diff --git a/javascript/extractor/tests/es2021/input/numeric.js b/javascript/extractor/tests/es2021/input/numeric.js new file mode 100644 index 00000000000..e7e9d031f50 --- /dev/null +++ b/javascript/extractor/tests/es2021/input/numeric.js @@ -0,0 +1,8 @@ +1_000_000_000 // Ah, so a billion +101_475_938.38 // And this is hundreds of millions + +let fee = 123_00; // $123 (12300 cents, apparently) +let fee = 12_300; // $12,300 (woah, that fee!) +let amount = 12345_00; // 12,345 (1234500 cents, apparently) +let amount = 123_4500; // 123.45 (4-fixed financial) +let amount = 1_234_500; // 1,234,500 diff --git a/javascript/extractor/tests/es2021/output/trap/numeric.js.trap b/javascript/extractor/tests/es2021/output/trap/numeric.js.trap new file mode 100644 index 00000000000..afcf1627f2d --- /dev/null +++ b/javascript/extractor/tests/es2021/output/trap/numeric.js.trap @@ -0,0 +1,435 @@ +#10000=@"/numeric.js;sourcefile" +files(#10000,"/numeric.js","numeric","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +#20002=* +comments(#20002,0,#20001," Ah, so a billion","// Ah, so a billion") +#20003=@"loc,{#10000},1,25,1,43" +locations_default(#20003,#10000,1,25,1,43) +hasLocation(#20002,#20003) +#20004=* +comments(#20004,0,#20001," And this is hundreds of millions","// And ... illions") +#20005=@"loc,{#10000},2,25,2,59" +locations_default(#20005,#10000,2,25,2,59) +hasLocation(#20004,#20005) +#20006=* +comments(#20006,0,#20001," $123 (12300 cents, apparently)","// $123 ... rently)") +#20007=@"loc,{#10000},4,25,4,57" +locations_default(#20007,#10000,4,25,4,57) +hasLocation(#20006,#20007) +#20008=* +comments(#20008,0,#20001," $12,300 (woah, that fee!)","// $12, ... t fee!)") +#20009=@"loc,{#10000},5,25,5,52" +locations_default(#20009,#10000,5,25,5,52) +hasLocation(#20008,#20009) +#20010=* +comments(#20010,0,#20001," 12,345 (1234500 cents, apparently)","// 12,3 ... rently)") +#20011=@"loc,{#10000},6,25,6,61" +locations_default(#20011,#10000,6,25,6,61) +hasLocation(#20010,#20011) +#20012=* +comments(#20012,0,#20001," 123.45 (4-fixed financial)","// 123. ... ancial)") +#20013=@"loc,{#10000},7,25,7,53" +locations_default(#20013,#10000,7,25,7,53) +hasLocation(#20012,#20013) +#20014=* +comments(#20014,0,#20001," 1,234,500","// 1,234,500") +#20015=@"loc,{#10000},8,25,8,36" +locations_default(#20015,#10000,8,25,8,36) +hasLocation(#20014,#20015) +#20016=* +lines(#20016,#20001,"1_000_000_000 // Ah, so a billion"," +") +#20017=@"loc,{#10000},1,1,1,43" +locations_default(#20017,#10000,1,1,1,43) +hasLocation(#20016,#20017) +#20018=* +lines(#20018,#20001,"101_475_938.38 // And this is hundreds of millions"," +") +#20019=@"loc,{#10000},2,1,2,59" +locations_default(#20019,#10000,2,1,2,59) +hasLocation(#20018,#20019) +#20020=* +lines(#20020,#20001,""," +") +#20021=@"loc,{#10000},3,1,3,0" +locations_default(#20021,#10000,3,1,3,0) +hasLocation(#20020,#20021) +#20022=* +lines(#20022,#20001,"let fee = 123_00; // $123 (12300 cents, apparently)"," +") +#20023=@"loc,{#10000},4,1,4,57" +locations_default(#20023,#10000,4,1,4,57) +hasLocation(#20022,#20023) +#20024=* +lines(#20024,#20001,"let fee = 12_300; // $12,300 (woah, that fee!)"," +") +#20025=@"loc,{#10000},5,1,5,52" +locations_default(#20025,#10000,5,1,5,52) +hasLocation(#20024,#20025) +#20026=* +lines(#20026,#20001,"let amount = 12345_00; // 12,345 (1234500 cents, apparently)"," +") +#20027=@"loc,{#10000},6,1,6,61" +locations_default(#20027,#10000,6,1,6,61) +hasLocation(#20026,#20027) +#20028=* +lines(#20028,#20001,"let amount = 123_4500; // 123.45 (4-fixed financial)"," +") +#20029=@"loc,{#10000},7,1,7,53" +locations_default(#20029,#10000,7,1,7,53) +hasLocation(#20028,#20029) +#20030=* +lines(#20030,#20001,"let amount = 1_234_500; // 1,234,500"," +") +#20031=@"loc,{#10000},8,1,8,36" +locations_default(#20031,#10000,8,1,8,36) +hasLocation(#20030,#20031) +numlines(#20001,8,7,7) +#20032=* +tokeninfo(#20032,3,#20001,0,"1_000_000_000") +#20033=@"loc,{#10000},1,1,1,13" +locations_default(#20033,#10000,1,1,1,13) +hasLocation(#20032,#20033) +#20034=* +tokeninfo(#20034,3,#20001,1,"101_475_938.38") +#20035=@"loc,{#10000},2,1,2,14" +locations_default(#20035,#10000,2,1,2,14) +hasLocation(#20034,#20035) +next_token(#20002,#20034) +#20036=* +tokeninfo(#20036,7,#20001,2,"let") +#20037=@"loc,{#10000},4,1,4,3" +locations_default(#20037,#10000,4,1,4,3) +hasLocation(#20036,#20037) +next_token(#20004,#20036) +#20038=* +tokeninfo(#20038,6,#20001,3,"fee") +#20039=@"loc,{#10000},4,5,4,7" +locations_default(#20039,#10000,4,5,4,7) +hasLocation(#20038,#20039) +#20040=* +tokeninfo(#20040,8,#20001,4,"=") +#20041=@"loc,{#10000},4,9,4,9" +locations_default(#20041,#10000,4,9,4,9) +hasLocation(#20040,#20041) +#20042=* +tokeninfo(#20042,3,#20001,5,"123_00") +#20043=@"loc,{#10000},4,11,4,16" +locations_default(#20043,#10000,4,11,4,16) +hasLocation(#20042,#20043) +#20044=* +tokeninfo(#20044,8,#20001,6,";") +#20045=@"loc,{#10000},4,17,4,17" +locations_default(#20045,#10000,4,17,4,17) +hasLocation(#20044,#20045) +#20046=* +tokeninfo(#20046,7,#20001,7,"let") +#20047=@"loc,{#10000},5,1,5,3" +locations_default(#20047,#10000,5,1,5,3) +hasLocation(#20046,#20047) +next_token(#20006,#20046) +#20048=* +tokeninfo(#20048,6,#20001,8,"fee") +#20049=@"loc,{#10000},5,5,5,7" +locations_default(#20049,#10000,5,5,5,7) +hasLocation(#20048,#20049) +#20050=* +tokeninfo(#20050,8,#20001,9,"=") +#20051=@"loc,{#10000},5,9,5,9" +locations_default(#20051,#10000,5,9,5,9) +hasLocation(#20050,#20051) +#20052=* +tokeninfo(#20052,3,#20001,10,"12_300") +#20053=@"loc,{#10000},5,11,5,16" +locations_default(#20053,#10000,5,11,5,16) +hasLocation(#20052,#20053) +#20054=* +tokeninfo(#20054,8,#20001,11,";") +#20055=@"loc,{#10000},5,17,5,17" +locations_default(#20055,#10000,5,17,5,17) +hasLocation(#20054,#20055) +#20056=* +tokeninfo(#20056,7,#20001,12,"let") +#20057=@"loc,{#10000},6,1,6,3" +locations_default(#20057,#10000,6,1,6,3) +hasLocation(#20056,#20057) +next_token(#20008,#20056) +#20058=* +tokeninfo(#20058,6,#20001,13,"amount") +#20059=@"loc,{#10000},6,5,6,10" +locations_default(#20059,#10000,6,5,6,10) +hasLocation(#20058,#20059) +#20060=* +tokeninfo(#20060,8,#20001,14,"=") +#20061=@"loc,{#10000},6,12,6,12" +locations_default(#20061,#10000,6,12,6,12) +hasLocation(#20060,#20061) +#20062=* +tokeninfo(#20062,3,#20001,15,"12345_00") +#20063=@"loc,{#10000},6,14,6,21" +locations_default(#20063,#10000,6,14,6,21) +hasLocation(#20062,#20063) +#20064=* +tokeninfo(#20064,8,#20001,16,";") +#20065=@"loc,{#10000},6,22,6,22" +locations_default(#20065,#10000,6,22,6,22) +hasLocation(#20064,#20065) +#20066=* +tokeninfo(#20066,7,#20001,17,"let") +#20067=@"loc,{#10000},7,1,7,3" +locations_default(#20067,#10000,7,1,7,3) +hasLocation(#20066,#20067) +next_token(#20010,#20066) +#20068=* +tokeninfo(#20068,6,#20001,18,"amount") +#20069=@"loc,{#10000},7,5,7,10" +locations_default(#20069,#10000,7,5,7,10) +hasLocation(#20068,#20069) +#20070=* +tokeninfo(#20070,8,#20001,19,"=") +#20071=@"loc,{#10000},7,12,7,12" +locations_default(#20071,#10000,7,12,7,12) +hasLocation(#20070,#20071) +#20072=* +tokeninfo(#20072,3,#20001,20,"123_4500") +#20073=@"loc,{#10000},7,14,7,21" +locations_default(#20073,#10000,7,14,7,21) +hasLocation(#20072,#20073) +#20074=* +tokeninfo(#20074,8,#20001,21,";") +#20075=@"loc,{#10000},7,22,7,22" +locations_default(#20075,#10000,7,22,7,22) +hasLocation(#20074,#20075) +#20076=* +tokeninfo(#20076,7,#20001,22,"let") +#20077=@"loc,{#10000},8,1,8,3" +locations_default(#20077,#10000,8,1,8,3) +hasLocation(#20076,#20077) +next_token(#20012,#20076) +#20078=* +tokeninfo(#20078,6,#20001,23,"amount") +#20079=@"loc,{#10000},8,5,8,10" +locations_default(#20079,#10000,8,5,8,10) +hasLocation(#20078,#20079) +#20080=* +tokeninfo(#20080,8,#20001,24,"=") +#20081=@"loc,{#10000},8,12,8,12" +locations_default(#20081,#10000,8,12,8,12) +hasLocation(#20080,#20081) +#20082=* +tokeninfo(#20082,3,#20001,25,"1_234_500") +#20083=@"loc,{#10000},8,14,8,22" +locations_default(#20083,#10000,8,14,8,22) +hasLocation(#20082,#20083) +#20084=* +tokeninfo(#20084,8,#20001,26,";") +#20085=@"loc,{#10000},8,23,8,23" +locations_default(#20085,#10000,8,23,8,23) +hasLocation(#20084,#20085) +#20086=* +tokeninfo(#20086,0,#20001,27,"") +#20087=@"loc,{#10000},9,1,9,0" +locations_default(#20087,#10000,9,1,9,0) +hasLocation(#20086,#20087) +next_token(#20014,#20086) +toplevels(#20001,0) +#20088=@"loc,{#10000},1,1,9,0" +locations_default(#20088,#10000,1,1,9,0) +hasLocation(#20001,#20088) +#20089=@"var;{fee};{#20000}" +variables(#20089,"fee",#20000) +#20090=@"var;{amount};{#20000}" +variables(#20090,"amount",#20000) +#20091=* +stmts(#20091,2,#20001,0,"1_000_000_000") +hasLocation(#20091,#20033) +stmt_containers(#20091,#20001) +#20092=* +exprs(#20092,3,#20091,0,"1_000_000_000") +hasLocation(#20092,#20033) +enclosing_stmt(#20092,#20091) +expr_containers(#20092,#20001) +literals("1000000000","1_000_000_000",#20092) +#20093=* +stmts(#20093,2,#20001,1,"101_475_938.38") +hasLocation(#20093,#20035) +stmt_containers(#20093,#20001) +#20094=* +exprs(#20094,3,#20093,0,"101_475_938.38") +hasLocation(#20094,#20035) +enclosing_stmt(#20094,#20093) +expr_containers(#20094,#20001) +literals("1.0147593838E8","101_475_938.38",#20094) +#20095=* +stmts(#20095,23,#20001,2,"let fee = 123_00;") +#20096=@"loc,{#10000},4,1,4,17" +locations_default(#20096,#10000,4,1,4,17) +hasLocation(#20095,#20096) +stmt_containers(#20095,#20001) +#20097=* +exprs(#20097,64,#20095,0,"fee = 123_00") +#20098=@"loc,{#10000},4,5,4,16" +locations_default(#20098,#10000,4,5,4,16) +hasLocation(#20097,#20098) +enclosing_stmt(#20097,#20095) +expr_containers(#20097,#20001) +#20099=* +exprs(#20099,78,#20097,0,"fee") +hasLocation(#20099,#20039) +enclosing_stmt(#20099,#20095) +expr_containers(#20099,#20001) +literals("fee","fee",#20099) +decl(#20099,#20089) +#20100=* +exprs(#20100,3,#20097,1,"123_00") +hasLocation(#20100,#20043) +enclosing_stmt(#20100,#20095) +expr_containers(#20100,#20001) +literals("12300","123_00",#20100) +#20101=* +stmts(#20101,23,#20001,3,"let fee = 12_300;") +#20102=@"loc,{#10000},5,1,5,17" +locations_default(#20102,#10000,5,1,5,17) +hasLocation(#20101,#20102) +stmt_containers(#20101,#20001) +#20103=* +exprs(#20103,64,#20101,0,"fee = 12_300") +#20104=@"loc,{#10000},5,5,5,16" +locations_default(#20104,#10000,5,5,5,16) +hasLocation(#20103,#20104) +enclosing_stmt(#20103,#20101) +expr_containers(#20103,#20001) +#20105=* +exprs(#20105,78,#20103,0,"fee") +hasLocation(#20105,#20049) +enclosing_stmt(#20105,#20101) +expr_containers(#20105,#20001) +literals("fee","fee",#20105) +decl(#20105,#20089) +#20106=* +exprs(#20106,3,#20103,1,"12_300") +hasLocation(#20106,#20053) +enclosing_stmt(#20106,#20101) +expr_containers(#20106,#20001) +literals("12300","12_300",#20106) +#20107=* +stmts(#20107,23,#20001,4,"let amo ... 345_00;") +#20108=@"loc,{#10000},6,1,6,22" +locations_default(#20108,#10000,6,1,6,22) +hasLocation(#20107,#20108) +stmt_containers(#20107,#20001) +#20109=* +exprs(#20109,64,#20107,0,"amount = 12345_00") +#20110=@"loc,{#10000},6,5,6,21" +locations_default(#20110,#10000,6,5,6,21) +hasLocation(#20109,#20110) +enclosing_stmt(#20109,#20107) +expr_containers(#20109,#20001) +#20111=* +exprs(#20111,78,#20109,0,"amount") +hasLocation(#20111,#20059) +enclosing_stmt(#20111,#20107) +expr_containers(#20111,#20001) +literals("amount","amount",#20111) +decl(#20111,#20090) +#20112=* +exprs(#20112,3,#20109,1,"12345_00") +hasLocation(#20112,#20063) +enclosing_stmt(#20112,#20107) +expr_containers(#20112,#20001) +literals("1234500","12345_00",#20112) +#20113=* +stmts(#20113,23,#20001,5,"let amo ... 3_4500;") +#20114=@"loc,{#10000},7,1,7,22" +locations_default(#20114,#10000,7,1,7,22) +hasLocation(#20113,#20114) +stmt_containers(#20113,#20001) +#20115=* +exprs(#20115,64,#20113,0,"amount = 123_4500") +#20116=@"loc,{#10000},7,5,7,21" +locations_default(#20116,#10000,7,5,7,21) +hasLocation(#20115,#20116) +enclosing_stmt(#20115,#20113) +expr_containers(#20115,#20001) +#20117=* +exprs(#20117,78,#20115,0,"amount") +hasLocation(#20117,#20069) +enclosing_stmt(#20117,#20113) +expr_containers(#20117,#20001) +literals("amount","amount",#20117) +decl(#20117,#20090) +#20118=* +exprs(#20118,3,#20115,1,"123_4500") +hasLocation(#20118,#20073) +enclosing_stmt(#20118,#20113) +expr_containers(#20118,#20001) +literals("1234500","123_4500",#20118) +#20119=* +stmts(#20119,23,#20001,6,"let amo ... 34_500;") +#20120=@"loc,{#10000},8,1,8,23" +locations_default(#20120,#10000,8,1,8,23) +hasLocation(#20119,#20120) +stmt_containers(#20119,#20001) +#20121=* +exprs(#20121,64,#20119,0,"amount = 1_234_500") +#20122=@"loc,{#10000},8,5,8,22" +locations_default(#20122,#10000,8,5,8,22) +hasLocation(#20121,#20122) +enclosing_stmt(#20121,#20119) +expr_containers(#20121,#20001) +#20123=* +exprs(#20123,78,#20121,0,"amount") +hasLocation(#20123,#20079) +enclosing_stmt(#20123,#20119) +expr_containers(#20123,#20001) +literals("amount","amount",#20123) +decl(#20123,#20090) +#20124=* +exprs(#20124,3,#20121,1,"1_234_500") +hasLocation(#20124,#20083) +enclosing_stmt(#20124,#20119) +expr_containers(#20124,#20001) +literals("1234500","1_234_500",#20124) +#20125=* +entry_cfg_node(#20125,#20001) +#20126=@"loc,{#10000},1,1,1,0" +locations_default(#20126,#10000,1,1,1,0) +hasLocation(#20125,#20126) +#20127=* +exit_cfg_node(#20127,#20001) +hasLocation(#20127,#20087) +successor(#20119,#20123) +successor(#20124,#20121) +successor(#20123,#20124) +successor(#20121,#20127) +successor(#20113,#20117) +successor(#20118,#20115) +successor(#20117,#20118) +successor(#20115,#20119) +successor(#20107,#20111) +successor(#20112,#20109) +successor(#20111,#20112) +successor(#20109,#20113) +successor(#20101,#20105) +successor(#20106,#20103) +successor(#20105,#20106) +successor(#20103,#20107) +successor(#20095,#20099) +successor(#20100,#20097) +successor(#20099,#20100) +successor(#20097,#20101) +successor(#20093,#20094) +successor(#20094,#20095) +successor(#20091,#20092) +successor(#20092,#20093) +successor(#20125,#20091) +numlines(#10000,8,7,7) +filetype(#10000,"javascript") From 4bc91c4439a36f09fb6ea249e3d9703c76c6bdc6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 17 Sep 2020 14:10:07 +0200 Subject: [PATCH 042/185] add support for Promise.any --- javascript/ql/src/semmle/javascript/Promises.qll | 4 ++-- javascript/ql/test/library-tests/Promises/flow2.js | 6 ++++++ .../ql/test/library-tests/Promises/tests.expected | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/Promises.qll b/javascript/ql/src/semmle/javascript/Promises.qll index a8231d75dce..4f730b36f0a 100644 --- a/javascript/ql/src/semmle/javascript/Promises.qll +++ b/javascript/ql/src/semmle/javascript/Promises.qll @@ -117,11 +117,11 @@ class ResolvedES2015PromiseDefinition extends ResolvedPromiseDefinition { } /** - * An aggregated promise produced either by `Promise.all` or `Promise.race`. + * An aggregated promise produced either by `Promise.all`, `Promise.race`, or `Promise.any`. */ class AggregateES2015PromiseDefinition extends PromiseCreationCall { AggregateES2015PromiseDefinition() { - exists(string m | m = "all" or m = "race" | + exists(string m | m = "all" or m = "race" or m = "any" | this = DataFlow::globalVarRef("Promise").getAMemberCall(m) ) } diff --git a/javascript/ql/test/library-tests/Promises/flow2.js b/javascript/ql/test/library-tests/Promises/flow2.js index ef91740e0d5..ccafb83fd3f 100644 --- a/javascript/ql/test/library-tests/Promises/flow2.js +++ b/javascript/ql/test/library-tests/Promises/flow2.js @@ -18,4 +18,10 @@ var [clean3, tainted3] = await Promise.all(["clean", Promise.resolve(source)]); sink(clean3); // OK sink(tainted3); // NOT OK - but only flagged by taint-tracking + + var tainted4 = await Promise.race(["clean", Promise.resolve(source)]); + sink(tainted4); // NOT OK - but only flagged by taint-tracking + + var tainted5 = await Promise.any(["clean", Promise.resolve(source)]); + sink(tainted5); // NOT OK - but only flagged by taint-tracking }); \ No newline at end of file diff --git a/javascript/ql/test/library-tests/Promises/tests.expected b/javascript/ql/test/library-tests/Promises/tests.expected index 21c685e0c40..00320cc83fc 100644 --- a/javascript/ql/test/library-tests/Promises/tests.expected +++ b/javascript/ql/test/library-tests/Promises/tests.expected @@ -9,6 +9,12 @@ test_ResolvedPromiseDefinition | flow2.js:18:33:18:79 | Promise ... urce)]) | flow2.js:18:46:18:52 | "clean" | | flow2.js:18:33:18:79 | Promise ... urce)]) | flow2.js:18:55:18:77 | Promise ... source) | | flow2.js:18:55:18:77 | Promise ... source) | flow2.js:18:71:18:76 | source | +| flow2.js:22:23:22:70 | Promise ... urce)]) | flow2.js:22:37:22:43 | "clean" | +| flow2.js:22:23:22:70 | Promise ... urce)]) | flow2.js:22:46:22:68 | Promise ... source) | +| flow2.js:22:46:22:68 | Promise ... source) | flow2.js:22:62:22:67 | source | +| flow2.js:25:23:25:69 | Promise ... urce)]) | flow2.js:25:36:25:42 | "clean" | +| flow2.js:25:23:25:69 | Promise ... urce)]) | flow2.js:25:45:25:67 | Promise ... source) | +| flow2.js:25:45:25:67 | Promise ... source) | flow2.js:25:61:25:66 | source | | flow.js:4:11:4:33 | Promise ... source) | flow.js:4:27:4:32 | source | | flow.js:20:2:20:24 | Promise ... source) | flow.js:20:18:20:23 | source | | flow.js:22:2:22:24 | Promise ... source) | flow.js:22:18:22:23 | source | @@ -201,6 +207,8 @@ flow | flow2.js:2:15:2:22 | "source" | flow2.js:6:8:6:13 | arr[0] | | flow2.js:2:15:2:22 | "source" | flow2.js:12:7:12:13 | tainted | | flow2.js:2:15:2:22 | "source" | flow2.js:16:7:16:14 | tainted2 | +| flow2.js:2:15:2:22 | "source" | flow2.js:23:7:23:14 | tainted4 | +| flow2.js:2:15:2:22 | "source" | flow2.js:26:7:26:14 | tainted5 | | flow.js:2:15:2:22 | "source" | flow.js:5:7:5:14 | await p1 | | flow.js:2:15:2:22 | "source" | flow.js:8:7:8:14 | await p2 | | flow.js:2:15:2:22 | "source" | flow.js:17:8:17:8 | e | @@ -255,6 +263,12 @@ typetrack | flow2.js:18:27:18:79 | await P ... urce)]) | flow2.js:18:33:18:79 | Promise ... urce)]) | load $PromiseResolveField$ | | flow2.js:18:33:18:79 | Promise ... urce)]) | flow2.js:18:45:18:78 | ["clean ... ource)] | copy $PromiseResolveField$ | | flow2.js:18:33:18:79 | Promise ... urce)]) | flow2.js:18:45:18:78 | ["clean ... ource)] | store $PromiseResolveField$ | +| flow2.js:22:17:22:70 | await P ... urce)]) | flow2.js:22:23:22:70 | Promise ... urce)]) | load $PromiseResolveField$ | +| flow2.js:22:23:22:70 | Promise ... urce)]) | flow2.js:22:46:22:68 | Promise ... source) | copy $PromiseResolveField$ | +| flow2.js:22:23:22:70 | Promise ... urce)]) | flow2.js:22:46:22:68 | Promise ... source) | store $PromiseResolveField$ | +| flow2.js:25:17:25:69 | await P ... urce)]) | flow2.js:25:23:25:69 | Promise ... urce)]) | load $PromiseResolveField$ | +| flow2.js:25:23:25:69 | Promise ... urce)]) | flow2.js:25:45:25:67 | Promise ... source) | copy $PromiseResolveField$ | +| flow2.js:25:23:25:69 | Promise ... urce)]) | flow2.js:25:45:25:67 | Promise ... source) | store $PromiseResolveField$ | | flow.js:20:2:20:43 | Promise ... ink(x)) | flow.js:20:36:20:42 | sink(x) | copy $PromiseResolveField$ | | flow.js:20:2:20:43 | Promise ... ink(x)) | flow.js:20:36:20:42 | sink(x) | store $PromiseResolveField$ | | flow.js:20:31:20:31 | x | flow.js:20:2:20:24 | Promise ... source) | load $PromiseResolveField$ | From 4571ba38a5f1249be8e2a766bc567bc5e655e644 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 21 Sep 2020 10:51:36 +0200 Subject: [PATCH 043/185] add change-note for es2021 --- change-notes/1.26/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.26/analysis-javascript.md b/change-notes/1.26/analysis-javascript.md index 392e4bebeb7..81ceb96b0ac 100644 --- a/change-notes/1.26/analysis-javascript.md +++ b/change-notes/1.26/analysis-javascript.md @@ -18,6 +18,7 @@ - [underscore](https://www.npmjs.com/package/underscore) * Analyzing files with the ".cjs" extension is now supported. +* ES2021 features are now supported. ## New queries From 441fbe32154b93702eaba6c8ad23658aaa0812fc Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Thu, 3 Sep 2020 15:17:06 +0200 Subject: [PATCH 044/185] Add Java test file for sign analysis --- .../dataflow/sign-analysis/A.java | 12 ++++++++++++ .../sign-analysis/SignAnalysis.expected | 3 +++ .../dataflow/sign-analysis/SignAnalysis.ql | 19 +++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 java/ql/test/library-tests/dataflow/sign-analysis/A.java create mode 100644 java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected create mode 100644 java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/A.java b/java/ql/test/library-tests/dataflow/sign-analysis/A.java new file mode 100644 index 00000000000..158f4d5a18d --- /dev/null +++ b/java/ql/test/library-tests/dataflow/sign-analysis/A.java @@ -0,0 +1,12 @@ +public class A { + int f1(int x, int y) { + if (x < 0) { + return x; // strictly negative + } + if (x < y) { + return y; // y is strictly positive because of the bound on x above + } + + return 0; + } +} diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected new file mode 100644 index 00000000000..1e3fe9acf27 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected @@ -0,0 +1,3 @@ +| A.java:4:14:4:14 | x | negative strictlyNegative | +| A.java:6:9:6:9 | x | positive | +| A.java:7:14:7:14 | y | positive strictlyPositive | diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql new file mode 100644 index 00000000000..7dd564dd77d --- /dev/null +++ b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql @@ -0,0 +1,19 @@ +import java +import semmle.code.java.dataflow.SignAnalysis + +string getASignString(Expr i) { + positive(i) and + result = "positive" + or + negative(i) and + result = "negative" + or + strictlyPositive(i) and + result = "strictlyPositive" + or + strictlyNegative(i) and + result = "strictlyNegative" +} + +from Expr e +select e, strictconcat(string s | s = getASignString(e) | s, " ") From 8bf4a4209c9026c3b87a59a24df26c8c093c4552 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Wed, 2 Sep 2020 16:52:17 +0200 Subject: [PATCH 045/185] C#: Sign analysis Synced between Java and C# through `identical-files.json`. --- config/identical-files.json | 20 +- csharp/ql/src/Linq/Helpers.qll | 11 + .../code/csharp/commons/ComparisonTest.qll | 2 +- .../semmle/code/csharp/controlflow/Guards.qll | 27 + .../semmle/code/csharp/dataflow/Nullness.qll | 2 - .../src/semmle/code/csharp/dataflow/SSA.qll | 7 + .../code/csharp/dataflow/SignAnalysis.qll | 9 + .../internal/rangeanalysis/ConstantUtils.qll | 64 ++ .../dataflow/internal/rangeanalysis/Sign.qll | 219 +++++++ .../rangeanalysis/SignAnalysisCommon.qll | 293 +++++++++ .../rangeanalysis/SignAnalysisSpecific.qll | 283 +++++++++ .../rangeanalysis/SsaReadPositionCommon.qll | 57 ++ .../rangeanalysis/SsaReadPositionSpecific.qll | 16 + .../internal/rangeanalysis/SsaUtils.qll | 42 ++ .../code/csharp/exprs/ComparisonOperation.qll | 3 + .../signanalysis/MissingSign.expected | 4 + .../dataflow/signanalysis/MissingSign.ql | 15 + .../dataflow/signanalysis/SignAnalysis.cs | 455 ++++++++++++++ .../signanalysis/SignAnalysis.expected | 217 +++++++ .../dataflow/signanalysis/SignAnalysis.ql | 21 + .../code/java/dataflow/ModulusAnalysis.qll | 1 + .../code/java/dataflow/RangeAnalysis.qll | 1 + .../semmle/code/java/dataflow/RangeUtils.qll | 58 +- .../code/java/dataflow/SignAnalysis.qll | 594 +----------------- .../dataflow/internal/rangeanalysis/Sign.qll | 219 +++++++ .../rangeanalysis/SignAnalysisCommon.qll | 293 +++++++++ .../rangeanalysis/SignAnalysisSpecific.qll | 235 +++++++ .../rangeanalysis/SsaReadPositionCommon.qll | 57 ++ .../rangeanalysis/SsaReadPositionSpecific.qll | 15 + .../sign-analysis/SignAnalysis.expected | 4 +- .../dataflow/sign-analysis/SignAnalysis.ql | 12 +- 31 files changed, 2592 insertions(+), 664 deletions(-) create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/SignAnalysis.qll create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll create mode 100644 csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected create mode 100644 csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql create mode 100644 csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs create mode 100644 csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected create mode 100644 csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll diff --git a/config/identical-files.json b/config/identical-files.json index 7e62497c9bf..e0a4e8dabf4 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -50,6 +50,18 @@ "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll", "python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll" ], + "SsaReadPosition Java/C#": [ + "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll", + "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll" + ], + "Sign Java/C#": [ + "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll", + "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll" + ], + "SignAnalysis Java/C#": [ + "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll", + "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll" + ], "C++ SubBasicBlocks": [ "cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll", "cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll" @@ -87,7 +99,7 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll", "csharp/ql/src/experimental/ir/implementation/raw/Operand.qll", - "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll" + "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll" ], "IR IRType": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll", @@ -109,11 +121,11 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll", "csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll" ], - "IR TInstruction":[ + "IR TInstruction": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll", "csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll" ], - "IR TIRVariable":[ + "IR TIRVariable": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll", "csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll" ], @@ -381,4 +393,4 @@ "javascript/ql/src/Comments/CommentedOutCodeReferences.qhelp", "python/ql/src/Lexical/CommentedOutCodeReferences.qhelp" ] -} +} \ No newline at end of file diff --git a/csharp/ql/src/Linq/Helpers.qll b/csharp/ql/src/Linq/Helpers.qll index 41d1e305ca6..e1b1beb1222 100644 --- a/csharp/ql/src/Linq/Helpers.qll +++ b/csharp/ql/src/Linq/Helpers.qll @@ -132,6 +132,17 @@ class AnyCall extends MethodCall { } } +/** A LINQ Count(...) call. */ +class CountCall extends MethodCall { + CountCall() { + exists(Method m | + m = getTarget() and + isEnumerableType(m.getDeclaringType()) and + m.hasName("Count") + ) + } +} + /** A variable of type IEnumerable<T>, for some T. */ class IEnumerableSequence extends Variable { IEnumerableSequence() { isIEnumerableType(getType()) } diff --git a/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll b/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll index 5d083679f9d..ea7d250abb0 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll @@ -2,7 +2,7 @@ * Provides classes for capturing various ways of performing comparison tests. */ -import csharp +private import csharp private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.system.Collections private import semmle.code.csharp.frameworks.system.collections.Generic diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll index 89042f7de07..f81b3b09212 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll @@ -31,6 +31,33 @@ class Guard extends Expr { predicate controlsNode(ControlFlow::Nodes::ElementNode cfn, AccessOrCallExpr sub, AbstractValue v) { isGuardedByNode(cfn, this, sub, v) } + + /** + * Holds if guard is an equality test between `e1` and `e2`. If the test is + * negated, that is `!=`, then `polarity` is false, otherwise `polarity` is + * true. + */ + predicate isEquality(Expr e1, Expr e2, boolean polarity) { + exists(Expr exp1, Expr exp2 | equalityGuard(exp1, exp2, polarity) | + e1 = exp1 and e2 = exp2 + or + e2 = exp1 and e1 = exp2 + ) + } + + private predicate equalityGuard(Expr e1, Expr e2, boolean polarity) { + exists(ComparisonTest eqtest | + eqtest.getExpr() = this and + eqtest.getAnArgument() = e1 and + eqtest.getAnArgument() = e2 and + not e1 = e2 and + ( + polarity = true and eqtest.getComparisonKind().isEquality() + or + polarity = false and eqtest.getComparisonKind().isInequality() + ) + ) + } } /** An abstract value. */ diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll index a537d08aabc..ed10e700966 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll @@ -21,10 +21,8 @@ import csharp private import ControlFlow private import internal.CallableReturns private import semmle.code.csharp.commons.Assertions -private import semmle.code.csharp.commons.ComparisonTest private import semmle.code.csharp.controlflow.Guards as G private import semmle.code.csharp.controlflow.Guards::AbstractValues -private import semmle.code.csharp.dataflow.SSA private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.Test diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll index adcfdfaaca7..50a69857d0c 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll @@ -2537,6 +2537,13 @@ module Ssa { ) } + /** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */ + predicate hasInputFromBlock(Definition inp, BasicBlock bb) { + this.getAnInput() = inp and + this.getBasicBlock().getAPredecessor() = bb and + inp.isLiveAtEndOfBlock(bb) + } + override string toString() { result = getToStringPrefix(this) + "SSA phi(" + getSourceVariable() + ")" } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/SignAnalysis.qll b/csharp/ql/src/semmle/code/csharp/dataflow/SignAnalysis.qll new file mode 100644 index 00000000000..3742f87de25 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/SignAnalysis.qll @@ -0,0 +1,9 @@ +/** + * Provides sign analysis to determine whether expression are always positive + * or negative. + * + * The analysis is implemented as an abstract interpretation over the + * three-valued domain `{negative, zero, positive}`. + */ + +import semmle.code.csharp.dataflow.internal.rangeanalysis.SignAnalysisCommon diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll new file mode 100644 index 00000000000..e283909155c --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll @@ -0,0 +1,64 @@ +/** + * Provides classes and predicates to represent constant integer expressions. + */ + +private import csharp +private import Ssa + +/** + * Holds if property `p` matches `property` in `baseClass` or any overrides. + */ +predicate propertyOverrides(Property p, string baseClass, string property) { + exists(Property p2 | + p2.getSourceDeclaration().getDeclaringType().hasQualifiedName(baseClass) and + p2.hasName(property) + | + p.overridesOrImplementsOrEquals(p2) + ) +} + +/** + * Holds if expression `e` is either + * - a compile time constant with integer value `val`, or + * - a read of a compile time constant with integer value `val`, or + * - a read of the `Length` of an array with `val` lengths. + */ +private predicate constantIntegerExpr(Expr e, int val) { + e.getValue().toInt() = val + or + exists(ExplicitDefinition v, Expr src | + e = v.getARead() and + src = v.getADefinition().getSource() and + constantIntegerExpr(src, val) + ) + or + isArrayLengthAccess(e, val) +} + +private int getArrayLength(ArrayCreation arrCreation, int index) { + constantIntegerExpr(arrCreation.getLengthArgument(index), result) +} + +private int getArrayLengthRec(ArrayCreation arrCreation, int index) { + index = 0 and result = getArrayLength(arrCreation, 0) + or + index > 0 and + result = getArrayLength(arrCreation, index) * getArrayLengthRec(arrCreation, index - 1) +} + +private predicate isArrayLengthAccess(PropertyAccess pa, int length) { + propertyOverrides(pa.getTarget(), "System.Array", "Length") and + exists(ExplicitDefinition arr, ArrayCreation arrCreation | + getArrayLengthRec(arrCreation, arrCreation.getNumberOfLengthArguments() - 1) = length and + arrCreation = arr.getADefinition().getSource() and + pa.getQualifier() = arr.getARead() + ) +} + +/** An expression that always has the same integer value. */ +class ConstantIntegerExpr extends Expr { + ConstantIntegerExpr() { constantIntegerExpr(this, _) } + + /** Gets the integer value of this expression. */ + int getIntValue() { constantIntegerExpr(this, result) } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll new file mode 100644 index 00000000000..10ca946a044 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll @@ -0,0 +1,219 @@ +newtype TSign = + TNeg() or + TZero() or + TPos() + +/** Class representing expression signs (+, -, 0). */ +class Sign extends TSign { + /** Gets the string representation of this sign. */ + string toString() { + result = "-" and this = TNeg() + or + result = "0" and this = TZero() + or + result = "+" and this = TPos() + } + + /** Gets a possible sign after incrementing an expression that has this sign. */ + Sign inc() { + this = TNeg() and result = TNeg() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TPos() + or + this = TPos() and result = TPos() + } + + /** Gets a possible sign after decrementing an expression that has this sign. */ + Sign dec() { result.inc() = this } + + /** Gets a possible sign after negating an expression that has this sign. */ + Sign neg() { + this = TNeg() and result = TPos() + or + this = TZero() and result = TZero() + or + this = TPos() and result = TNeg() + } + + /** + * Gets a possible sign after bitwise complementing an expression that has this + * sign. + */ + Sign bitnot() { + this = TNeg() and result = TPos() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TNeg() + or + this = TPos() and result = TNeg() + } + + /** + * Gets a possible sign after adding an expression with sign `s` to an expression + * that has this sign. + */ + Sign add(Sign s) { + this = TZero() and result = s + or + s = TZero() and result = this + or + this = s and this = result + or + this = TPos() and s = TNeg() + or + this = TNeg() and s = TPos() + } + + /** + * Gets a possible sign after multiplying an expression with sign `s` to an expression + * that has this sign. + */ + Sign mul(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + /** + * Gets a possible sign after integer dividing an expression that has this sign + * by an expression with sign `s`. + */ + Sign div(Sign s) { + result = TZero() and s = TNeg() // ex: 3 / -5 = 0 + or + result = TZero() and s = TPos() // ex: 3 / 5 = 0 + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + /** + * Gets a possible sign after modulo dividing an expression that has this sign + * by an expression with sign `s`. + */ + Sign rem(Sign s) { + result = TZero() and s = TNeg() + or + result = TZero() and s = TPos() + or + result = this and s = TNeg() + or + result = this and s = TPos() + } + + /** + * Gets a possible sign after bitwise `and` of an expression that has this sign + * and an expression with sign `s`. + */ + Sign bitand(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TZero() and this = TPos() + or + result = TZero() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TNeg() + or + result = TPos() and this = TPos() and s = TPos() + } + + /** + * Gets a possible sign after bitwise `or` of an expression that has this sign + * and an expression with sign `s`. + */ + Sign bitor(Sign s) { + result = TZero() and this = TZero() and s = TZero() + or + result = TNeg() and this = TNeg() + or + result = TNeg() and s = TNeg() + or + result = TPos() and this = TPos() and s = TZero() + or + result = TPos() and this = TZero() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + } + + /** + * Gets a possible sign after bitwise `xor` of an expression that has this sign + * and an expression with sign `s`. + */ + Sign bitxor(Sign s) { + result = TZero() and this = s + or + result = this and s = TZero() + or + result = s and this = TZero() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + /** + * Gets a possible sign after left shift of an expression that has this sign + * by an expression with sign `s`. + */ + Sign lshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + this != TZero() and s != TZero() + } + + /** + * Gets a possible sign after right shift of an expression that has this sign + * by an expression with sign `s`. + */ + Sign rshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result = TNeg() and this = TNeg() + or + result != TNeg() and this = TPos() and s != TZero() + } + + /** + * Gets a possible sign after unsigned right shift of an expression that has + * this sign by an expression with sign `s`. + */ + Sign urshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result != TZero() and this = TNeg() and s != TZero() + or + result != TNeg() and this = TPos() and s != TZero() + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll new file mode 100644 index 00000000000..66b38433eef --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll @@ -0,0 +1,293 @@ +/** + * Provides sign analysis to determine whether expression are always positive + * or negative. + * + * The analysis is implemented as an abstract interpretation over the + * three-valued domain `{negative, zero, positive}`. + */ + +private import SignAnalysisSpecific::Private +private import SsaReadPositionCommon +private import Sign + +/** Gets the sign of `e` if this can be directly determined. */ +Sign certainExprSign(Expr e) { + exists(int i | e.(ConstantIntegerExpr).getIntValue() = i | + i < 0 and result = TNeg() + or + i = 0 and result = TZero() + or + i > 0 and result = TPos() + ) + or + not exists(e.(ConstantIntegerExpr).getIntValue()) and + ( + exists(float f | f = getNonIntegerValue(e) | + f < 0 and result = TNeg() + or + f = 0 and result = TZero() + or + f > 0 and result = TPos() + ) + or + exists(string charlit | charlit = getCharValue(e) | + if charlit.regexpMatch("\\u0000") then result = TZero() else result = TPos() + ) + or + containerSizeAccess(e) and + (result = TPos() or result = TZero()) + or + positiveExpression(e) and result = TPos() + ) +} + +/** Holds if the sign of `e` is too complicated to determine. */ +predicate unknownSign(Expr e) { + not exists(certainExprSign(e)) and + ( + exists(IntegerLiteral lit | lit = e and not exists(lit.getValue().toInt())) + or + exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat())) + or + exists(CastExpr cast, Type fromtyp | + cast = e and + fromtyp = cast.getExpr().getType() and + not fromtyp instanceof NumericOrCharType + ) + or + unknownIntegerAccess(e) + ) +} + +/** + * Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate lowerBound(Expr lowerbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) { + exists(boolean testIsTrue, ComparisonExpr comp | + pos.hasReadOfVar(v) and + guardControlsSsaRead(getComparisonGuard(comp), pos, testIsTrue) and + not unknownSign(lowerbound) + | + testIsTrue = true and + comp.getLesserOperand() = lowerbound and + comp.getGreaterOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = true else isStrict = false) + or + testIsTrue = false and + comp.getGreaterOperand() = lowerbound and + comp.getLesserOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = false else isStrict = true) + ) +} + +/** + * Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate upperBound(Expr upperbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) { + exists(boolean testIsTrue, ComparisonExpr comp | + pos.hasReadOfVar(v) and + guardControlsSsaRead(getComparisonGuard(comp), pos, testIsTrue) and + not unknownSign(upperbound) + | + testIsTrue = true and + comp.getGreaterOperand() = upperbound and + comp.getLesserOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = true else isStrict = false) + or + testIsTrue = false and + comp.getLesserOperand() = upperbound and + comp.getGreaterOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = false else isStrict = true) + ) +} + +/** + * Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is + * restricted to only include bounds for which we might determine a sign. The + * boolean `isEq` gives the polarity: + * - `isEq = true` : `v = eqbound` + * - `isEq = false` : `v != eqbound` + */ +private predicate eqBound(Expr eqbound, SsaVariable v, SsaReadPosition pos, boolean isEq) { + exists(Guard guard, boolean testIsTrue, boolean polarity | + pos.hasReadOfVar(v) and + guardControlsSsaRead(guard, pos, testIsTrue) and + guard.isEquality(eqbound, ssaRead(v, 0), polarity) and + isEq = polarity.booleanXor(testIsTrue).booleanNot() and + not unknownSign(eqbound) + ) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in + * order for `v` to be positive. + */ +private predicate posBound(Expr bound, SsaVariable v, SsaReadPosition pos) { + upperBound(bound, v, pos, _) or + eqBound(bound, v, pos, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in + * order for `v` to be negative. + */ +private predicate negBound(Expr bound, SsaVariable v, SsaReadPosition pos) { + lowerBound(bound, v, pos, _) or + eqBound(bound, v, pos, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v` + * can be zero. + */ +private predicate zeroBound(Expr bound, SsaVariable v, SsaReadPosition pos) { + lowerBound(bound, v, pos, _) or + upperBound(bound, v, pos, _) or + eqBound(bound, v, pos, _) +} + +/** Holds if `bound` allows `v` to be positive at `pos`. */ +private predicate posBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { + posBound(bound, v, pos) and TPos() = exprSign(bound) +} + +/** Holds if `bound` allows `v` to be negative at `pos`. */ +private predicate negBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { + negBound(bound, v, pos) and TNeg() = exprSign(bound) +} + +/** Holds if `bound` allows `v` to be zero at `pos`. */ +private predicate zeroBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { + lowerBound(bound, v, pos, _) and TNeg() = exprSign(bound) + or + lowerBound(bound, v, pos, false) and TZero() = exprSign(bound) + or + upperBound(bound, v, pos, _) and TPos() = exprSign(bound) + or + upperBound(bound, v, pos, false) and TZero() = exprSign(bound) + or + eqBound(bound, v, pos, true) and TZero() = exprSign(bound) + or + eqBound(bound, v, pos, false) and TZero() != exprSign(bound) +} + +/** + * Holds if there is a bound that might restrict whether `v` has the sign `s` + * at `pos`. + */ +private predicate hasGuard(SsaVariable v, SsaReadPosition pos, Sign s) { + s = TPos() and posBound(_, v, pos) + or + s = TNeg() and negBound(_, v, pos) + or + s = TZero() and zeroBound(_, v, pos) +} + +pragma[noinline] +private Sign guardedSsaSign(SsaVariable v, SsaReadPosition pos) { + // SSA variable can have sign `result` + result = ssaDefSign(v) and + pos.hasReadOfVar(v) and + // there are guards at this position on `v` that might restrict it to be sign `result`. + // (So we need to check if they are satisfied) + hasGuard(v, pos, result) +} + +pragma[noinline] +private Sign unguardedSsaSign(SsaVariable v, SsaReadPosition pos) { + // SSA variable can have sign `result` + result = ssaDefSign(v) and + pos.hasReadOfVar(v) and + // there's no guard at this position on `v` that might restrict it to be sign `result`. + not hasGuard(v, pos, result) +} + +/** + * Gets the sign of `v` at read position `pos`, when there's at least one guard + * on `v` at position `pos`. Each bound corresponding to a given sign must be met + * in order for `v` to be of that sign. + */ +private Sign guardedSsaSignOk(SsaVariable v, SsaReadPosition pos) { + result = TPos() and + forex(Expr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos)) + or + result = TNeg() and + forex(Expr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos)) + or + result = TZero() and + forex(Expr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos)) +} + +/** Gets a possible sign for `v` at `pos`. */ +Sign ssaSign(SsaVariable v, SsaReadPosition pos) { + result = unguardedSsaSign(v, pos) + or + result = guardedSsaSign(v, pos) and + result = guardedSsaSignOk(v, pos) +} + +/** Gets a possible sign for `v`. */ +pragma[nomagic] +Sign ssaDefSign(SsaVariable v) { + result = explicitSsaDefSign(v) + or + result = implicitSsaDefSign(v) + or + exists(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge | + v = phi and + edge.phiInput(phi, inp) and + result = ssaSign(inp, edge) + ) +} + +/** Gets a possible sign for `e`. */ +cached +Sign exprSign(Expr e) { + result = certainExprSign(e) + or + not exists(certainExprSign(e)) and + ( + unknownSign(e) + or + exists(SsaVariable v | getARead(v) = e | result = ssaVariableSign(v, e)) + or + e = + any(VarAccess access | + not exists(SsaVariable v | getARead(v) = access) and + ( + result = fieldSign(getField(access.(FieldAccess))) or + not access instanceof FieldAccess + ) + ) + or + result = specificSubExprSign(e) + ) +} + +/** Holds if `e` can be positive and cannot be negative. */ +predicate positive(Expr e) { + exprSign(e) = TPos() and + not exprSign(e) = TNeg() +} + +/** Holds if `e` can be negative and cannot be positive. */ +predicate negative(Expr e) { + exprSign(e) = TNeg() and + not exprSign(e) = TPos() +} + +/** Holds if `e` is strictly positive. */ +predicate strictlyPositive(Expr e) { + exprSign(e) = TPos() and + not exprSign(e) = TNeg() and + not exprSign(e) = TZero() +} + +/** Holds if `e` is strictly negative. */ +predicate strictlyNegative(Expr e) { + exprSign(e) = TNeg() and + not exprSign(e) = TPos() and + not exprSign(e) = TZero() +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll new file mode 100644 index 00000000000..75adec96909 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -0,0 +1,283 @@ +/** + * Provides C#-specific definitions for use in sign analysis. + */ +module Private { + private import SsaUtils as SU + private import csharp as CS + private import ConstantUtils as CU + private import semmle.code.csharp.controlflow.Guards as G + import Impl + + class Guard = G::Guard; + + class ConstantIntegerExpr = CU::ConstantIntegerExpr; + + class SsaVariable = CS::Ssa::Definition; + + class SsaPhiNode = CS::Ssa::PhiNode; + + class VarAccess = CS::AssignableAccess; + + class FieldAccess = CS::FieldAccess; + + class CharacterLiteral = CS::CharLiteral; + + class IntegerLiteral = CS::IntegerLiteral; + + class LongLiteral = CS::LongLiteral; + + class CastExpr = CS::CastExpr; + + class Type = CS::Type; + + class Expr = CS::Expr; + + predicate ssaRead = SU::ssaRead/2; +} + +private module Impl { + private import csharp + private import SsaUtils + private import ConstantUtils + private import semmle.code.csharp.controlflow.Guards + private import Linq.Helpers + private import Sign + private import SignAnalysisCommon + private import SsaReadPositionCommon + private import semmle.code.csharp.commons.ComparisonTest + + private class BooleanValue = AbstractValues::BooleanValue; + + float getNonIntegerValue(Expr e) { + exists(string s | + s = e.getValue() and + result = s.toFloat() and + not exists(s.toInt()) + ) + } + + string getCharValue(Expr e) { result = e.getValue() and e.getType() instanceof CharType } + + predicate containerSizeAccess(Expr e) { + exists(Property p | p = e.(PropertyAccess).getTarget() | + propertyOverrides(p, "System.Collections.Generic.IEnumerable<>", "Count") or + propertyOverrides(p, "System.Collections.ICollection", "Count") or + propertyOverrides(p, "System.String", "Length") or + propertyOverrides(p, "System.Array", "Length") + ) + or + e instanceof CountCall + } + + predicate positiveExpression(Expr e) { e instanceof SizeofExpr } + + class NumericOrCharType extends Type { + NumericOrCharType() { + this instanceof CharType or + this instanceof IntegralType or + this instanceof FloatingPointType or + this instanceof DecimalType or + this instanceof Enum or + this instanceof PointerType // should be similar to unsigned integers + } + } + + Sign explicitSsaDefSign(Ssa::ExplicitDefinition v) { + exists(AssignableDefinition def | def = v.getADefinition() | + result = exprSign(def.getSource()) + or + not exists(def.getSource()) and + not def.getElement() instanceof MutatorOperation + or + result = exprSign(def.getElement().(IncrementOperation).getOperand()).inc() + or + result = exprSign(def.getElement().(DecrementOperation).getOperand()).dec() + ) + } + + Sign implicitSsaDefSign(Ssa::ImplicitDefinition v) { + result = fieldSign(v.getSourceVariable().getAssignable()) or + not v.getSourceVariable().getAssignable() instanceof Field + } + + pragma[inline] + Sign ssaVariableSign(Ssa::Definition v, Expr e) { + result = ssaSign(v, any(SsaReadPositionBlock bb | getAnExpression(bb) = e)) + } + + /** Gets a possible sign for `f`. */ + Sign fieldSign(Field f) { + if f.fromSource() and f.isEffectivelyPrivate() + then + result = exprSign(f.getAnAssignedValue()) + or + any(IncrementOperation inc).getOperand() = f.getAnAccess() and result = fieldSign(f).inc() + or + any(DecrementOperation dec).getOperand() = f.getAnAccess() and result = fieldSign(f).dec() + or + exists(AssignOperation a | a.getLValue() = f.getAnAccess() | result = exprSign(a)) + or + not exists(f.getInitializer()) and result = TZero() + else any() + } + + predicate unknownIntegerAccess(Expr e) { + e.getType() instanceof NumericOrCharType and + not e = getARead(_) and + not e instanceof FieldAccess and + // The expression types that are listed here are the ones handled in `specificSubExprSign`. + // Keep them in sync. + not e instanceof AssignExpr and + not e instanceof AssignOperation and + not e instanceof UnaryPlusExpr and + not e instanceof PostIncrExpr and + not e instanceof PostDecrExpr and + not e instanceof PreIncrExpr and + not e instanceof PreDecrExpr and + not e instanceof UnaryMinusExpr and + not e instanceof ComplementExpr and + not e instanceof AddExpr and + not e instanceof SubExpr and + not e instanceof MulExpr and + not e instanceof DivExpr and + not e instanceof RemExpr and + not e instanceof BitwiseAndExpr and + not e instanceof BitwiseOrExpr and + not e instanceof BitwiseXorExpr and + not e instanceof LShiftExpr and + not e instanceof RShiftExpr and + not e instanceof ConditionalExpr and + not e instanceof RefExpr and + not e instanceof LocalVariableDeclAndInitExpr and + not e instanceof SwitchCaseExpr and + not e instanceof CastExpr and + not e instanceof SwitchExpr and + not e instanceof NullCoalescingExpr + } + + Sign specificSubExprSign(Expr e) { + // The expression types that are handled here should be excluded in `unknownIntegerAccess`. + // Keep them in sync. + result = exprSign(e.(AssignExpr).getRValue()) + or + result = exprSign(e.(AssignOperation).getExpandedAssignment()) + or + result = exprSign(e.(UnaryPlusExpr).getOperand()) + or + result = exprSign(e.(PostIncrExpr).getOperand()) + or + result = exprSign(e.(PostDecrExpr).getOperand()) + or + result = exprSign(e.(PreIncrExpr).getOperand()).inc() + or + result = exprSign(e.(PreDecrExpr).getOperand()).dec() + or + result = exprSign(e.(UnaryMinusExpr).getOperand()).neg() + or + result = exprSign(e.(ComplementExpr).getOperand()).bitnot() + or + e = + any(DivExpr div | + result = exprSign(div.getLeftOperand()) and + result != TZero() and + div.getRightOperand().(RealLiteral).getValue().toFloat() = 0 + ) + or + exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) | + e instanceof AddExpr and result = s1.add(s2) + or + e instanceof SubExpr and result = s1.add(s2.neg()) + or + e instanceof MulExpr and result = s1.mul(s2) + or + e instanceof DivExpr and result = s1.div(s2) + or + e instanceof RemExpr and result = s1.rem(s2) + or + e instanceof BitwiseAndExpr and result = s1.bitand(s2) + or + e instanceof BitwiseOrExpr and result = s1.bitor(s2) + or + e instanceof BitwiseXorExpr and result = s1.bitxor(s2) + or + e instanceof LShiftExpr and result = s1.lshift(s2) + or + e instanceof RShiftExpr and result = s1.rshift(s2) + ) + or + result = exprSign(e.(ConditionalExpr).getAChild()) + or + result = exprSign(e.(NullCoalescingExpr).getAChild()) + or + result = exprSign(e.(SwitchExpr).getACase().getBody()) + or + result = exprSign(e.(CastExpr).getExpr()) + or + result = exprSign(e.(SwitchCaseExpr).getBody()) + or + result = exprSign(e.(LocalVariableDeclAndInitExpr).getInitializer()) + or + result = exprSign(e.(RefExpr).getExpr()) + } + + private Sign binaryOpLhsSign(BinaryOperation e) { result = exprSign(e.getLeftOperand()) } + + private Sign binaryOpRhsSign(BinaryOperation e) { result = exprSign(e.getRightOperand()) } + + pragma[noinline] + private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) { + lhs = binaryOpLhsSign(e) and + rhs = binaryOpRhsSign(e) + } + + Expr getARead(Ssa::Definition v) { result = v.getARead() } + + Field getField(FieldAccess fa) { result = fa.getTarget() } + + Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode().getElement() } + + Guard getComparisonGuard(ComparisonExpr ce) { result = ce.getExpr() } + + /** + * Holds if `guard` controls the position `controlled` with the value `testIsTrue`. + */ + predicate guardControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) { + exists(BooleanValue b | b.getValue() = testIsTrue | + guard.controlsNode(controlled.(SsaReadPositionBlock).getBlock().getANode(), _, b) + ) + } + + /** A relational comparison */ + class ComparisonExpr extends ComparisonTest { + private boolean strict; + + ComparisonExpr() { + this.getComparisonKind() = + any(ComparisonKind ck | + ck.isLessThan() and strict = true + or + ck.isLessThanEquals() and + strict = false + ) + } + + /** + * Gets the operand on the "greater" (or "greater-or-equal") side + * of this relational expression, that is, the side that is larger + * if the overall expression evaluates to `true`; for example on + * `x <= 20` this is the `20`, and on `y > 0` it is `y`. + */ + Expr getGreaterOperand() { result = this.getSecondArgument() } + + /** + * Gets the operand on the "lesser" (or "lesser-or-equal") side + * of this relational expression, that is, the side that is smaller + * if the overall expression evaluates to `true`; for example on + * `x <= 20` this is `x`, and on `y > 0` it is the `0`. + */ + Expr getLesserOperand() { result = this.getFirstArgument() } + + /** Holds if this comparison is strict, i.e. `<` or `>`. */ + predicate isStrict() { strict = true } + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll new file mode 100644 index 00000000000..558ecd1b88b --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll @@ -0,0 +1,57 @@ +/** + * Provides classes for representing a position at which an SSA variable is read. + */ + +private import SsaReadPositionSpecific + +private newtype TSsaReadPosition = + TSsaReadPositionBlock(BasicBlock bb) { bb = getAReadBasicBlock(_) } or + TSsaReadPositionPhiInputEdge(BasicBlock bbOrig, BasicBlock bbPhi) { + exists(SsaPhiNode phi | phi.hasInputFromBlock(_, bbOrig) and bbPhi = phi.getBasicBlock()) + } + +/** + * A position at which an SSA variable is read. This includes both ordinary + * reads occurring in basic blocks and input to phi nodes occurring along an + * edge between two basic blocks. + */ +class SsaReadPosition extends TSsaReadPosition { + /** Holds if `v` is read at this position. */ + abstract predicate hasReadOfVar(SsaVariable v); + + /** Gets a textual representation of this SSA read position. */ + abstract string toString(); +} + +/** A basic block in which an SSA variable is read. */ +class SsaReadPositionBlock extends SsaReadPosition, TSsaReadPositionBlock { + /** Gets the basic block corresponding to this position. */ + BasicBlock getBlock() { this = TSsaReadPositionBlock(result) } + + override predicate hasReadOfVar(SsaVariable v) { getBlock() = getAReadBasicBlock(v) } + + override string toString() { result = "block" } +} + +/** + * An edge between two basic blocks where the latter block has an SSA phi + * definition. The edge therefore has a read of an SSA variable serving as the + * input to the phi node. + */ +class SsaReadPositionPhiInputEdge extends SsaReadPosition, TSsaReadPositionPhiInputEdge { + /** Gets the source of the edge. */ + BasicBlock getOrigBlock() { this = TSsaReadPositionPhiInputEdge(result, _) } + + /** Gets the target of the edge. */ + BasicBlock getPhiBlock() { this = TSsaReadPositionPhiInputEdge(_, result) } + + override predicate hasReadOfVar(SsaVariable v) { this.phiInput(_, v) } + + /** Holds if `inp` is an input to `phi` along this edge. */ + predicate phiInput(SsaPhiNode phi, SsaVariable inp) { + phi.hasInputFromBlock(inp, getOrigBlock()) and + getPhiBlock() = phi.getBasicBlock() + } + + override string toString() { result = "edge" } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll new file mode 100644 index 00000000000..d7df9781b2a --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll @@ -0,0 +1,16 @@ +/** + * Provides C#-specific definitions for use in the `SsaReadPosition`. + */ + +private import csharp + +class SsaVariable = Ssa::Definition; + +class SsaPhiNode = Ssa::PhiNode; + +class BasicBlock = Ssa::BasicBlock; + +/** Gets a basic block in which SSA variable `v` is read. */ +BasicBlock getAReadBasicBlock(SsaVariable v) { + result = v.getARead().getAControlFlowNode().getBasicBlock() +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll new file mode 100644 index 00000000000..7f1811c1f37 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll @@ -0,0 +1,42 @@ +/** + * Provides utility predicates to extend the core SSA functionality. + */ + +private import csharp +private import Ssa +private import ConstantUtils + +/** + * Gets an expression that equals `v - delta`. + */ +Expr ssaRead(Definition v, int delta) { + result = v.getARead() and delta = 0 + or + exists(AddExpr add, int d1, ConstantIntegerExpr c | + result = add and + delta = d1 - c.getIntValue() + | + add.getLeftOperand() = ssaRead(v, d1) and add.getRightOperand() = c + or + add.getRightOperand() = ssaRead(v, d1) and add.getLeftOperand() = c + ) + or + exists(SubExpr sub, int d1, ConstantIntegerExpr c | + result = sub and + sub.getLeftOperand() = ssaRead(v, d1) and + sub.getRightOperand() = c and + delta = d1 + c.getIntValue() + ) + or + v.(ExplicitDefinition).getADefinition().getExpr().(PreIncrExpr) = result and delta = 0 + or + v.(ExplicitDefinition).getADefinition().getExpr().(PreDecrExpr) = result and delta = 0 + or + v.(ExplicitDefinition).getADefinition().getExpr().(PostIncrExpr) = result and delta = 1 // x++ === ++x - 1 + or + v.(ExplicitDefinition).getADefinition().getExpr().(PostDecrExpr) = result and delta = -1 // x-- === --x + 1 + or + v.(ExplicitDefinition).getADefinition().getExpr().(Assignment) = result and delta = 0 + or + result.(AssignExpr).getRValue() = ssaRead(v, delta) +} diff --git a/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll b/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll index e48aabd76a7..8b94ef5b4d7 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll @@ -57,6 +57,9 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr { * `x <= 20` this is `x`, and on `y > 0` it is the `0`. */ Expr getLesserOperand() { none() } + + /** Holds if this comparison is strict, i.e. `<` or `>`. */ + predicate isStrict() { this instanceof LTExpr or this instanceof GTExpr } } /** diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected new file mode 100644 index 00000000000..72f1bdadeda --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected @@ -0,0 +1,4 @@ +| SignAnalysis.cs:428:23:428:24 | access to constant A | +| SignAnalysis.cs:448:22:448:29 | Byte* to = ... | +| SignAnalysis.cs:450:38:450:44 | (...) ... | +| SignAnalysis.cs:450:43:450:44 | access to local variable to | diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql new file mode 100644 index 00000000000..b769f3eff6e --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql @@ -0,0 +1,15 @@ +import csharp +import semmle.code.csharp.dataflow.SignAnalysis + +from Expr e +where + not exists(exprSign(e)) and + ( + e.getType() instanceof CharType or + e.getType() instanceof IntegralType or + e.getType() instanceof FloatingPointType or + e.getType() instanceof DecimalType or + e.getType() instanceof Enum or + e.getType() instanceof PointerType + ) +select e diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs new file mode 100644 index 00000000000..13a94a18aae --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs @@ -0,0 +1,455 @@ +using System; +using System.Linq; +using System.Diagnostics; + +class SignAnalysis +{ + static int GetRandomValue() { return (new System.Random()).Next(0, 1000) - 500; } + + int RandomValue { get => GetRandomValue(); } + + int random = GetRandomValue(); + + int SsaSources(int p, int[] values) + { + var v = GetRandomValue(); + if (v < 0) + { + return v; + } + + v = RandomValue; + if (v < 0) + { + return v; + } + + v = p; + if (v < 0) + { + return v; + } + + v = random; + if (v < 0) + { + return v; + } + + v = values[0]; + if (v < 0) + { + return v; + } + + int x = values[1]; + v = x; + if (v < 0) + { + return v; + } + + return 0; + } + + void Operations(int i, int j, bool b) + { + if (i < 0 && j < 0) + { + var x = i + j; + System.Console.WriteLine(x); // strictly neg + x = i * j; + System.Console.WriteLine(x); // strictly pos + x = i / j; + System.Console.WriteLine(x); // pos + x = i - j; + System.Console.WriteLine(x); // no clue + x = i % j; + System.Console.WriteLine(x); // neg + x = i++; + System.Console.WriteLine(x); // strictly neg + x = i--; + System.Console.WriteLine(x); // neg + x = -i; + System.Console.WriteLine(x); // strictly pos + x = +i; + System.Console.WriteLine(x); // strictly neg + var l = (long)i; + System.Console.WriteLine(l); // strictly neg + + x = i; + x += i; + System.Console.WriteLine(x); // strictly neg + } + + if (i < 0 && j > 0) + { + var x = i + j; + System.Console.WriteLine(x); + x = i * j; + System.Console.WriteLine(x); // strictly neg + x = i / j; + System.Console.WriteLine(x); // neg + x = i - j; + System.Console.WriteLine(x); // strictly neg + x = i % j; + System.Console.WriteLine(x); // neg + x = b ? i : j; + System.Console.WriteLine(x); // any (except 0) + } + } + + void NumericalTypes() + { + var f = 4.2f; + System.Console.WriteLine(f); + var d = 4.2; + System.Console.WriteLine(d); + var de = 4.2m; + System.Console.WriteLine(de); + var c = 'a'; + System.Console.WriteLine(c); + } + + int f0; + + int f1; + + void Field0() + { + f0++; + System.Console.WriteLine(f0); // strictly positive + f0 = 0; + } + + void Field1() + { + f1++; + System.Console.WriteLine(f1); // no clue + f1 = -10; + } + + void Field2() + { + System.Console.WriteLine(f1); // no clue + } + + void Ctor() + { + var i = new Int32(); // const 0 value + i++; + System.Console.WriteLine(i); // strictly pos + } + + int Guards(int x, int y) + { + if (x < 0) + { + return x; // strictly negative + } + + if (y == 1) + { + return y; // strictly positive + } + + if (y is -1) + { + return y; // strictly negative [MISSING] + } + + if (x < y) + { + return y; // strictly positive + } + + var b = y == 1; + if (b) + { + return y; // strictly positive + } + + return 0; + } + + void Inconsistent() + { + var i = 1; + if (i < 0) + { + System.Console.WriteLine(i); // reported as strictly pos, although unreachable + } + } + + void SpecialValues(int[] ints) + { + System.Console.WriteLine(ints.Length); // positive + ints = new int[] { 1, 2, 3 }; + System.Console.WriteLine(ints.Length); // 3, so strictly positive + System.Console.WriteLine(ints.Count()); // positive + System.Console.WriteLine(ints.Count(i => i > 1)); // positive + + var s = "abc"; + System.Console.WriteLine(s.Length); // positive, could be strictly positive + + var enumerable = Enumerable.Empty(); + System.Console.WriteLine(enumerable.Count()); // positive + + var i = new int[,] { { 1, 1 }, { 1, 2 }, { 1, 3 } }; + System.Console.WriteLine(i.Length); // 6, so strictly positive + } + + void Phi1(int i) + { + if (i > 0) + { + System.Console.WriteLine(i); // strictly positive + } + else + { + System.Console.WriteLine(i); // negative + } + System.Console.WriteLine(i); // any + } + + void Phi2(int i) + { + if (i > 0) + { + System.Console.WriteLine(i); // strictly positive + } + else + { + if (i < 0) // negative + { + System.Console.WriteLine(i); // strictly negative + return; + } + } + System.Console.WriteLine(i); // positive, not found + } + + void Phi3(int i) + { + if (i > 0) + { + System.Console.WriteLine(i); // strictly positive + } + else + { + if (i < 0) // negative + { + System.Console.WriteLine(i); // strictly negative + } + else + { + System.Console.WriteLine(i); // zero, nothing is reported + } + } + } + + void Loop(int i, int j, int k) + { + if (i > 0) + { + while (i >= 0) // any + { + i--; // positive + System.Console.WriteLine(i); // any + } + System.Console.WriteLine(i); // strictly neg + } + + if (j > 0) + { + while (j > 0) + { + j--; // strictly pos + System.Console.WriteLine(j); // positive + } + System.Console.WriteLine(j); // reported negative, can only be 0 + } + + if (k > 0) + { + while (k > 0) + { + k--; // strictly pos + System.Console.WriteLine(k); // positive + + if (k == 5) // positive + { + break; + } + } + System.Console.WriteLine(k); // any + } + } + + void Assert(int i, bool b) + { + Debug.Assert(i > 0); + System.Console.WriteLine(i); // strictly positive + + if (b) + System.Console.WriteLine(i); // strictly positive + } + + void CheckedUnchecked(int i) + { + var x = unchecked(-1 * i * i); + if (x < 0) + { + System.Console.WriteLine(x); // strictly negative + } + + x = checked(-1 * i * i); + if (x < 0) + { + System.Console.WriteLine(x); // strictly negative + } + } + + void CharMinMax() + { + var min = char.MinValue; + var max = char.MaxValue; + var c = min + 1; + System.Console.WriteLine(c); // strictly positive + c = min - 1; + System.Console.WriteLine(c); // strictly negative + c = max + 1; + System.Console.WriteLine(c); // strictly positive + } + + void NullCoalesce(int? v) + { + if (v > 0) + { + var x = v ?? 1; + System.Console.WriteLine(x); // strictly positive + } + + if (v == null) + { + var x = v ?? 1; + System.Console.WriteLine(x); // strictly positive + } + + if (v < 0) + { + var x = v ?? 0; + System.Console.WriteLine(x); // negative + } + } + + async System.Threading.Tasks.Task Await() + { + var i = await System.Threading.Tasks.Task.FromResult(5); + if (i < 0) + { + System.Console.WriteLine(i); // strictly negative + } + } + + void Unsigned(uint i) + { + if (i != 0) // positive + { + System.Console.WriteLine(i); // strictly positive + } + } + + public int MyField = 0; + + void FieldAccess() + { + var x = new SignAnalysis(); + var y = x.MyField; + if (y < 0) + { + System.Console.WriteLine(y); // strictly negative + } + } + + private static unsafe void Pointer(float d) + { + float* dp = &d; + var x = *dp; + if (x < 0) + { + System.Console.WriteLine(x); // strictly negative + } + } + + [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit, Size = 15)] + struct MyStruct { } + + unsafe void Sizeof() + { + var x = sizeof(MyStruct); + System.Console.WriteLine(x); // strictly positive + } + + void SwitchCase(string s) + { + var x = s switch + { + "x" => 0, + _ => 2 + }; + System.Console.WriteLine(x); // positive + } + + void Capture() + { + var i = 1; + void Capture() + { + if (i > 0) + Console.WriteLine(i); // strictly positive + } + Capture(); + + if (i > 0) + Console.WriteLine(i); // strictly positive + } + + public struct MyStruct2 { public int F; } + void RefExpression(MyStruct2 s) + { + ref var x = ref s.F; + if (x < 0) + { + Console.WriteLine(x); // strictly negative + } + } + + enum MyEnum { A = 12, B, C } + void EnumOp(MyEnum x, MyEnum y) + { + var i = x - y; + if (i < 0) + { + System.Console.WriteLine(i); // strictly negative + } + } + + unsafe void PointerCast(byte* src, byte* dst) + { + var x = (int)(src-dst); + if (x < 0) + { + System.Console.WriteLine(x); // strictly negative + } + + byte[] buf = new byte[10]; + + fixed (byte* to = buf) + { + System.Console.WriteLine((int)to); + } + } +} + +// semmle-extractor-options: /r:System.Linq.dll \ No newline at end of file diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected new file mode 100644 index 00000000000..91d429a45ef --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected @@ -0,0 +1,217 @@ +| SignAnalysis.cs:7:72:7:75 | 1000 | strictlyPositive | +| SignAnalysis.cs:7:80:7:82 | 500 | strictlyPositive | +| SignAnalysis.cs:18:20:18:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:24:20:24:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:30:20:30:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:36:20:36:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:42:20:42:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:45:24:45:24 | 1 | strictlyPositive | +| SignAnalysis.cs:49:20:49:20 | access to local variable v | strictlyNegative | +| SignAnalysis.cs:59:17:59:25 | Int32 x = ... | strictlyNegative | +| SignAnalysis.cs:59:21:59:21 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:59:21:59:25 | ... + ... | strictlyNegative | +| SignAnalysis.cs:59:25:59:25 | access to parameter j | strictlyNegative | +| SignAnalysis.cs:60:38:60:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:61:13:61:21 | ... = ... | strictlyPositive | +| SignAnalysis.cs:61:17:61:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:61:17:61:21 | ... * ... | strictlyPositive | +| SignAnalysis.cs:61:21:61:21 | access to parameter j | strictlyNegative | +| SignAnalysis.cs:62:38:62:38 | access to local variable x | strictlyPositive | +| SignAnalysis.cs:63:13:63:21 | ... = ... | positive | +| SignAnalysis.cs:63:17:63:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:63:17:63:21 | ... / ... | positive | +| SignAnalysis.cs:63:21:63:21 | access to parameter j | strictlyNegative | +| SignAnalysis.cs:64:38:64:38 | access to local variable x | positive | +| SignAnalysis.cs:65:17:65:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:65:21:65:21 | access to parameter j | strictlyNegative | +| SignAnalysis.cs:67:13:67:21 | ... = ... | negative | +| SignAnalysis.cs:67:17:67:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:67:17:67:21 | ... % ... | negative | +| SignAnalysis.cs:67:21:67:21 | access to parameter j | strictlyNegative | +| SignAnalysis.cs:68:38:68:38 | access to local variable x | negative | +| SignAnalysis.cs:69:13:69:19 | ... = ... | strictlyNegative | +| SignAnalysis.cs:69:17:69:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:69:17:69:19 | ...++ | strictlyNegative | +| SignAnalysis.cs:70:38:70:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:71:13:71:19 | ... = ... | negative | +| SignAnalysis.cs:71:17:71:17 | access to parameter i | negative | +| SignAnalysis.cs:71:17:71:19 | ...-- | negative | +| SignAnalysis.cs:72:38:72:38 | access to local variable x | negative | +| SignAnalysis.cs:73:13:73:18 | ... = ... | strictlyPositive | +| SignAnalysis.cs:73:17:73:18 | -... | strictlyPositive | +| SignAnalysis.cs:73:18:73:18 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:74:38:74:38 | access to local variable x | strictlyPositive | +| SignAnalysis.cs:75:13:75:18 | ... = ... | strictlyNegative | +| SignAnalysis.cs:75:17:75:18 | +... | strictlyNegative | +| SignAnalysis.cs:75:18:75:18 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:76:38:76:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:77:17:77:27 | Int64 l = ... | strictlyNegative | +| SignAnalysis.cs:77:21:77:27 | (...) ... | strictlyNegative | +| SignAnalysis.cs:77:27:77:27 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:78:38:78:38 | access to local variable l | strictlyNegative | +| SignAnalysis.cs:80:13:80:17 | ... = ... | strictlyNegative | +| SignAnalysis.cs:80:17:80:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:81:13:81:13 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:81:13:81:18 | ... + ... | strictlyNegative | +| SignAnalysis.cs:81:13:81:18 | ... += ... | strictlyNegative | +| SignAnalysis.cs:81:13:81:18 | ... = ... | strictlyNegative | +| SignAnalysis.cs:81:18:81:18 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:82:38:82:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:87:21:87:21 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:87:25:87:25 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:89:13:89:21 | ... = ... | strictlyNegative | +| SignAnalysis.cs:89:17:89:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:89:17:89:21 | ... * ... | strictlyNegative | +| SignAnalysis.cs:89:21:89:21 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:90:38:90:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:91:13:91:21 | ... = ... | negative | +| SignAnalysis.cs:91:17:91:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:91:17:91:21 | ... / ... | negative | +| SignAnalysis.cs:91:21:91:21 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:92:38:92:38 | access to local variable x | negative | +| SignAnalysis.cs:93:13:93:21 | ... = ... | strictlyNegative | +| SignAnalysis.cs:93:17:93:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:93:17:93:21 | ... - ... | strictlyNegative | +| SignAnalysis.cs:93:21:93:21 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:94:38:94:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:95:13:95:21 | ... = ... | negative | +| SignAnalysis.cs:95:17:95:17 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:95:17:95:21 | ... % ... | negative | +| SignAnalysis.cs:95:21:95:21 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:96:38:96:38 | access to local variable x | negative | +| SignAnalysis.cs:97:21:97:21 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:97:25:97:25 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:104:13:104:20 | Single f = ... | strictlyPositive | +| SignAnalysis.cs:104:17:104:20 | 4.2 | strictlyPositive | +| SignAnalysis.cs:105:34:105:34 | access to local variable f | strictlyPositive | +| SignAnalysis.cs:106:13:106:19 | Double d = ... | strictlyPositive | +| SignAnalysis.cs:106:17:106:19 | 4.2 | strictlyPositive | +| SignAnalysis.cs:107:34:107:34 | access to local variable d | strictlyPositive | +| SignAnalysis.cs:108:13:108:21 | Decimal de = ... | strictlyPositive | +| SignAnalysis.cs:108:18:108:21 | 4.2 | strictlyPositive | +| SignAnalysis.cs:109:34:109:35 | access to local variable de | strictlyPositive | +| SignAnalysis.cs:110:13:110:19 | Char c = ... | strictlyPositive | +| SignAnalysis.cs:110:17:110:19 | a | strictlyPositive | +| SignAnalysis.cs:111:34:111:34 | access to local variable c | strictlyPositive | +| SignAnalysis.cs:120:9:120:10 | access to field f0 | positive | +| SignAnalysis.cs:120:9:120:12 | ...++ | positive | +| SignAnalysis.cs:121:34:121:35 | access to field f0 | strictlyPositive | +| SignAnalysis.cs:122:9:122:10 | access to field f0 | positive | +| SignAnalysis.cs:129:9:129:16 | ... = ... | strictlyNegative | +| SignAnalysis.cs:129:14:129:16 | -... | strictlyNegative | +| SignAnalysis.cs:129:15:129:16 | 10 | strictlyPositive | +| SignAnalysis.cs:141:34:141:34 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:148:20:148:20 | access to parameter x | strictlyNegative | +| SignAnalysis.cs:151:18:151:18 | 1 | strictlyPositive | +| SignAnalysis.cs:153:20:153:20 | access to parameter y | strictlyPositive | +| SignAnalysis.cs:156:18:156:19 | -... | strictlyNegative | +| SignAnalysis.cs:156:19:156:19 | 1 | strictlyPositive | +| SignAnalysis.cs:161:13:161:13 | access to parameter x | positive | +| SignAnalysis.cs:163:20:163:20 | access to parameter y | strictlyPositive | +| SignAnalysis.cs:166:22:166:22 | 1 | strictlyPositive | +| SignAnalysis.cs:169:20:169:20 | access to parameter y | strictlyPositive | +| SignAnalysis.cs:177:13:177:17 | Int32 i = ... | strictlyPositive | +| SignAnalysis.cs:177:17:177:17 | 1 | strictlyPositive | +| SignAnalysis.cs:178:13:178:13 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:180:38:180:38 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:186:34:186:44 | access to property Length | positive | +| SignAnalysis.cs:187:16:187:36 | 3 | strictlyPositive | +| SignAnalysis.cs:187:28:187:28 | 1 | strictlyPositive | +| SignAnalysis.cs:187:31:187:31 | 2 | strictlyPositive | +| SignAnalysis.cs:187:34:187:34 | 3 | strictlyPositive | +| SignAnalysis.cs:188:34:188:44 | access to property Length | strictlyPositive | +| SignAnalysis.cs:189:34:189:45 | call to method Count | positive | +| SignAnalysis.cs:190:34:190:55 | call to method Count | positive | +| SignAnalysis.cs:190:54:190:54 | 1 | strictlyPositive | +| SignAnalysis.cs:193:34:193:41 | access to property Length | positive | +| SignAnalysis.cs:196:34:196:51 | call to method Count | positive | +| SignAnalysis.cs:198:17:198:59 | 2 | strictlyPositive | +| SignAnalysis.cs:198:17:198:59 | 3 | strictlyPositive | +| SignAnalysis.cs:198:32:198:32 | 1 | strictlyPositive | +| SignAnalysis.cs:198:35:198:35 | 1 | strictlyPositive | +| SignAnalysis.cs:198:42:198:42 | 1 | strictlyPositive | +| SignAnalysis.cs:198:45:198:45 | 2 | strictlyPositive | +| SignAnalysis.cs:198:52:198:52 | 1 | strictlyPositive | +| SignAnalysis.cs:198:55:198:55 | 3 | strictlyPositive | +| SignAnalysis.cs:199:34:199:41 | access to property Length | strictlyPositive | +| SignAnalysis.cs:206:38:206:38 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:210:38:210:38 | access to parameter i | negative | +| SignAnalysis.cs:219:38:219:38 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:223:17:223:17 | access to parameter i | negative | +| SignAnalysis.cs:225:42:225:42 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:236:38:236:38 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:240:17:240:17 | access to parameter i | negative | +| SignAnalysis.cs:242:42:242:42 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:257:17:257:17 | access to parameter i | positive | +| SignAnalysis.cs:257:17:257:19 | ...-- | positive | +| SignAnalysis.cs:260:38:260:38 | access to parameter i | strictlyNegative | +| SignAnalysis.cs:267:17:267:17 | access to parameter j | strictlyPositive | +| SignAnalysis.cs:267:17:267:19 | ...-- | strictlyPositive | +| SignAnalysis.cs:268:42:268:42 | access to parameter j | positive | +| SignAnalysis.cs:270:38:270:38 | access to parameter j | negative | +| SignAnalysis.cs:277:17:277:17 | access to parameter k | strictlyPositive | +| SignAnalysis.cs:277:17:277:19 | ...-- | strictlyPositive | +| SignAnalysis.cs:278:42:278:42 | access to parameter k | positive | +| SignAnalysis.cs:280:21:280:21 | access to parameter k | positive | +| SignAnalysis.cs:280:26:280:26 | 5 | strictlyPositive | +| SignAnalysis.cs:291:22:291:22 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:292:34:292:34 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:295:38:295:38 | access to parameter i | strictlyPositive | +| SignAnalysis.cs:300:27:300:28 | -... | strictlyNegative | +| SignAnalysis.cs:300:28:300:28 | 1 | strictlyPositive | +| SignAnalysis.cs:303:38:303:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:306:21:306:22 | -... | strictlyNegative | +| SignAnalysis.cs:306:22:306:22 | 1 | strictlyPositive | +| SignAnalysis.cs:309:38:309:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:316:13:316:31 | Char max = ... | strictlyPositive | +| SignAnalysis.cs:316:19:316:31 | access to constant MaxValue | strictlyPositive | +| SignAnalysis.cs:317:13:317:23 | Int32 c = ... | strictlyPositive | +| SignAnalysis.cs:317:17:317:23 | ... + ... | strictlyPositive | +| SignAnalysis.cs:317:23:317:23 | 1 | strictlyPositive | +| SignAnalysis.cs:318:34:318:34 | access to local variable c | strictlyPositive | +| SignAnalysis.cs:319:9:319:19 | ... = ... | strictlyNegative | +| SignAnalysis.cs:319:13:319:19 | ... - ... | strictlyNegative | +| SignAnalysis.cs:319:19:319:19 | 1 | strictlyPositive | +| SignAnalysis.cs:320:34:320:34 | access to local variable c | strictlyNegative | +| SignAnalysis.cs:321:9:321:19 | ... = ... | strictlyPositive | +| SignAnalysis.cs:321:13:321:15 | (...) ... | strictlyPositive | +| SignAnalysis.cs:321:13:321:15 | access to local variable max | strictlyPositive | +| SignAnalysis.cs:321:13:321:19 | ... + ... | strictlyPositive | +| SignAnalysis.cs:321:19:321:19 | 1 | strictlyPositive | +| SignAnalysis.cs:322:34:322:34 | access to local variable c | strictlyPositive | +| SignAnalysis.cs:329:17:329:26 | Int32 x = ... | strictlyPositive | +| SignAnalysis.cs:329:21:329:21 | access to parameter v | strictlyPositive | +| SignAnalysis.cs:329:21:329:26 | ... ?? ... | strictlyPositive | +| SignAnalysis.cs:329:26:329:26 | 1 | strictlyPositive | +| SignAnalysis.cs:330:38:330:38 | access to local variable x | strictlyPositive | +| SignAnalysis.cs:335:17:335:26 | Int32 x = ... | strictlyPositive | +| SignAnalysis.cs:335:21:335:26 | ... ?? ... | strictlyPositive | +| SignAnalysis.cs:335:26:335:26 | 1 | strictlyPositive | +| SignAnalysis.cs:336:38:336:38 | access to local variable x | strictlyPositive | +| SignAnalysis.cs:341:17:341:26 | Int32 x = ... | negative | +| SignAnalysis.cs:341:21:341:21 | access to parameter v | strictlyNegative | +| SignAnalysis.cs:341:21:341:26 | ... ?? ... | negative | +| SignAnalysis.cs:342:38:342:38 | access to local variable x | negative | +| SignAnalysis.cs:348:62:348:62 | 5 | strictlyPositive | +| SignAnalysis.cs:351:38:351:38 | access to local variable i | strictlyNegative | +| SignAnalysis.cs:371:38:371:38 | access to local variable y | strictlyNegative | +| SignAnalysis.cs:381:38:381:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:385:50:385:99 | access to constant Explicit | strictlyPositive | +| SignAnalysis.cs:385:109:385:110 | 15 | strictlyPositive | +| SignAnalysis.cs:390:13:390:32 | Int32 x = ... | strictlyPositive | +| SignAnalysis.cs:390:17:390:32 | sizeof(..) | strictlyPositive | +| SignAnalysis.cs:391:34:391:34 | access to local variable x | strictlyPositive | +| SignAnalysis.cs:396:13:400:9 | Int32 x = ... | positive | +| SignAnalysis.cs:396:17:400:9 | ... switch { ... } | positive | +| SignAnalysis.cs:399:13:399:18 | ... => ... | strictlyPositive | +| SignAnalysis.cs:399:18:399:18 | 2 | strictlyPositive | +| SignAnalysis.cs:401:34:401:34 | access to local variable x | positive | +| SignAnalysis.cs:406:13:406:17 | Int32 i = ... | strictlyPositive | +| SignAnalysis.cs:406:17:406:17 | 1 | strictlyPositive | +| SignAnalysis.cs:410:35:410:35 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:414:13:414:13 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:415:31:415:31 | access to local variable i | strictlyPositive | +| SignAnalysis.cs:424:31:424:31 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:434:38:434:38 | access to local variable i | strictlyNegative | +| SignAnalysis.cs:443:38:443:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:446:31:446:32 | 10 | strictlyPositive | diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql new file mode 100644 index 00000000000..4350e8f1742 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql @@ -0,0 +1,21 @@ +import csharp +import semmle.code.csharp.dataflow.SignAnalysis + +string getASignString(Expr e) { + positive(e) and + not strictlyPositive(e) and + result = "positive" + or + negative(e) and + not strictlyNegative(e) and + result = "negative" + or + strictlyPositive(e) and + result = "strictlyPositive" + or + strictlyNegative(e) and + result = "strictlyNegative" +} + +from Expr e +select e, strictconcat(string s | s = getASignString(e) | s, " ") diff --git a/java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll b/java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll index 1fb3eb201a8..b153e9fbd04 100644 --- a/java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll +++ b/java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll @@ -6,6 +6,7 @@ import java private import SSA +private import semmle.code.java.dataflow.internal.rangeanalysis.SsaReadPositionCommon private import RangeUtils private import semmle.code.java.controlflow.Guards import Bound diff --git a/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll b/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll index cbd59f8302a..07860c76ee4 100644 --- a/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll +++ b/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll @@ -66,6 +66,7 @@ import java private import SSA private import RangeUtils +private import semmle.code.java.dataflow.internal.rangeanalysis.SsaReadPositionCommon private import semmle.code.java.controlflow.internal.GuardsLogic private import SignAnalysis private import ModulusAnalysis diff --git a/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll b/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll index e7da9891b47..631145a2aa2 100644 --- a/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll +++ b/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll @@ -5,6 +5,7 @@ import java private import SSA private import semmle.code.java.controlflow.internal.GuardsLogic +private import semmle.code.java.dataflow.internal.rangeanalysis.SsaReadPositionCommon /** * Holds if `v` is an input to `phi` that is not along a back edge, and the @@ -127,63 +128,6 @@ Expr ssaRead(SsaVariable v, int delta) { result.(AssignExpr).getSource() = ssaRead(v, delta) } -private newtype TSsaReadPosition = - TSsaReadPositionBlock(BasicBlock bb) { exists(SsaVariable v | bb = v.getAUse().getBasicBlock()) } or - TSsaReadPositionPhiInputEdge(BasicBlock bbOrig, BasicBlock bbPhi) { - exists(SsaPhiNode phi | phi.hasInputFromBlock(_, bbOrig) and bbPhi = phi.getBasicBlock()) - } - -/** - * A position at which an SSA variable is read. This includes both ordinary - * reads occurring in basic blocks and input to phi nodes occurring along an - * edge between two basic blocks. - */ -class SsaReadPosition extends TSsaReadPosition { - /** Holds if `v` is read at this position. */ - abstract predicate hasReadOfVar(SsaVariable v); - - /** Gets a textual representation of this SSA read position. */ - abstract string toString(); -} - -/** A basic block in which an SSA variable is read. */ -class SsaReadPositionBlock extends SsaReadPosition, TSsaReadPositionBlock { - /** Gets the basic block corresponding to this position. */ - BasicBlock getBlock() { this = TSsaReadPositionBlock(result) } - - override predicate hasReadOfVar(SsaVariable v) { getBlock() = v.getAUse().getBasicBlock() } - - override string toString() { result = "block" } -} - -/** - * An edge between two basic blocks where the latter block has an SSA phi - * definition. The edge therefore has a read of an SSA variable serving as the - * input to the phi node. - */ -class SsaReadPositionPhiInputEdge extends SsaReadPosition, TSsaReadPositionPhiInputEdge { - /** Gets the head of the edge. */ - BasicBlock getOrigBlock() { this = TSsaReadPositionPhiInputEdge(result, _) } - - /** Gets the tail of the edge. */ - BasicBlock getPhiBlock() { this = TSsaReadPositionPhiInputEdge(_, result) } - - override predicate hasReadOfVar(SsaVariable v) { - exists(SsaPhiNode phi | - phi.hasInputFromBlock(v, getOrigBlock()) and - getPhiBlock() = phi.getBasicBlock() - ) - } - - /** Holds if `inp` is an input to `phi` along this edge. */ - predicate phiInput(SsaPhiNode phi, SsaVariable inp) { - phi.hasInputFromBlock(inp, getOrigBlock()) and - getPhiBlock() = phi.getBasicBlock() - } - - override string toString() { result = "edge" } -} - /** * Holds if `inp` is an input to `phi` along a back edge. */ diff --git a/java/ql/src/semmle/code/java/dataflow/SignAnalysis.qll b/java/ql/src/semmle/code/java/dataflow/SignAnalysis.qll index 595f9c623f2..9cd629f4ef9 100644 --- a/java/ql/src/semmle/code/java/dataflow/SignAnalysis.qll +++ b/java/ql/src/semmle/code/java/dataflow/SignAnalysis.qll @@ -6,596 +6,4 @@ * three-valued domain `{negative, zero, positive}`. */ -import java -private import SSA -private import RangeUtils -private import semmle.code.java.controlflow.Guards -private import semmle.code.java.Reflection -private import semmle.code.java.Collections -private import semmle.code.java.Maps - -private newtype TSign = - TNeg() or - TZero() or - TPos() - -private class Sign extends TSign { - string toString() { - result = "-" and this = TNeg() - or - result = "0" and this = TZero() - or - result = "+" and this = TPos() - } - - Sign inc() { - this = TNeg() and result = TNeg() - or - this = TNeg() and result = TZero() - or - this = TZero() and result = TPos() - or - this = TPos() and result = TPos() - } - - Sign dec() { result.inc() = this } - - Sign neg() { - this = TNeg() and result = TPos() - or - this = TZero() and result = TZero() - or - this = TPos() and result = TNeg() - } - - Sign bitnot() { - this = TNeg() and result = TPos() - or - this = TNeg() and result = TZero() - or - this = TZero() and result = TNeg() - or - this = TPos() and result = TNeg() - } - - Sign add(Sign s) { - this = TZero() and result = s - or - s = TZero() and result = this - or - this = s and this = result - or - this = TPos() and s = TNeg() - or - this = TNeg() and s = TPos() - } - - Sign mul(Sign s) { - result = TZero() and this = TZero() - or - result = TZero() and s = TZero() - or - result = TNeg() and this = TPos() and s = TNeg() - or - result = TNeg() and this = TNeg() and s = TPos() - or - result = TPos() and this = TPos() and s = TPos() - or - result = TPos() and this = TNeg() and s = TNeg() - } - - Sign div(Sign s) { - result = TZero() and s = TNeg() - or - result = TZero() and s = TPos() - or - result = TNeg() and this = TPos() and s = TNeg() - or - result = TNeg() and this = TNeg() and s = TPos() - or - result = TPos() and this = TPos() and s = TPos() - or - result = TPos() and this = TNeg() and s = TNeg() - } - - Sign rem(Sign s) { - result = TZero() and s = TNeg() - or - result = TZero() and s = TPos() - or - result = this and s = TNeg() - or - result = this and s = TPos() - } - - Sign bitand(Sign s) { - result = TZero() and this = TZero() - or - result = TZero() and s = TZero() - or - result = TZero() and this = TPos() - or - result = TZero() and s = TPos() - or - result = TNeg() and this = TNeg() and s = TNeg() - or - result = TPos() and this = TNeg() and s = TPos() - or - result = TPos() and this = TPos() and s = TNeg() - or - result = TPos() and this = TPos() and s = TPos() - } - - Sign bitor(Sign s) { - result = TZero() and this = TZero() and s = TZero() - or - result = TNeg() and this = TNeg() - or - result = TNeg() and s = TNeg() - or - result = TPos() and this = TPos() and s = TZero() - or - result = TPos() and this = TZero() and s = TPos() - or - result = TPos() and this = TPos() and s = TPos() - } - - Sign bitxor(Sign s) { - result = TZero() and this = s - or - result = this and s = TZero() - or - result = s and this = TZero() - or - result = TPos() and this = TPos() and s = TPos() - or - result = TNeg() and this = TNeg() and s = TPos() - or - result = TNeg() and this = TPos() and s = TNeg() - or - result = TPos() and this = TNeg() and s = TNeg() - } - - Sign lshift(Sign s) { - result = TZero() and this = TZero() - or - result = this and s = TZero() - or - this != TZero() and s != TZero() - } - - Sign rshift(Sign s) { - result = TZero() and this = TZero() - or - result = this and s = TZero() - or - result = TNeg() and this = TNeg() - or - result != TNeg() and this = TPos() and s != TZero() - } - - Sign urshift(Sign s) { - result = TZero() and this = TZero() - or - result = this and s = TZero() - or - result != TZero() and this = TNeg() and s != TZero() - or - result != TNeg() and this = TPos() and s != TZero() - } -} - -/** Gets the sign of `e` if this can be directly determined. */ -private Sign certainExprSign(Expr e) { - exists(int i | e.(ConstantIntegerExpr).getIntValue() = i | - i < 0 and result = TNeg() - or - i = 0 and result = TZero() - or - i > 0 and result = TPos() - ) - or - not exists(e.(ConstantIntegerExpr).getIntValue()) and - ( - exists(float f | - f = e.(LongLiteral).getValue().toFloat() or - f = e.(FloatingPointLiteral).getValue().toFloat() or - f = e.(DoubleLiteral).getValue().toFloat() - | - f < 0 and result = TNeg() - or - f = 0 and result = TZero() - or - f > 0 and result = TPos() - ) - or - exists(string charlit | charlit = e.(CharacterLiteral).getValue() | - if charlit = "\\0" or charlit = "\\u0000" then result = TZero() else result = TPos() - ) - or - e.(MethodAccess).getMethod() instanceof StringLengthMethod and - (result = TPos() or result = TZero()) - or - e.(MethodAccess).getMethod() instanceof CollectionSizeMethod and - (result = TPos() or result = TZero()) - or - e.(MethodAccess).getMethod() instanceof MapSizeMethod and - (result = TPos() or result = TZero()) - ) -} - -/** Holds if the sign of `e` is too complicated to determine. */ -private predicate unknownSign(Expr e) { - not exists(e.(ConstantIntegerExpr).getIntValue()) and - ( - exists(IntegerLiteral lit | lit = e and not exists(lit.getValue().toInt())) - or - exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat())) - or - exists(CastExpr cast, Type fromtyp | - cast = e and - fromtyp = cast.getExpr().getType() and - not fromtyp instanceof NumericOrCharType - ) - or - e instanceof ArrayAccess and e.getType() instanceof NumericOrCharType - or - e instanceof MethodAccess and e.getType() instanceof NumericOrCharType - or - e instanceof ClassInstanceExpr and e.getType() instanceof NumericOrCharType - ) -} - -/** - * Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted - * to only include bounds for which we might determine a sign. - */ -private predicate lowerBound(Expr lowerbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) { - exists(boolean testIsTrue, ComparisonExpr comp | - pos.hasReadOfVar(v) and - guardControlsSsaRead(comp, pos, testIsTrue) and - not unknownSign(lowerbound) - | - testIsTrue = true and - comp.getLesserOperand() = lowerbound and - comp.getGreaterOperand() = ssaRead(v, 0) and - (if comp.isStrict() then isStrict = true else isStrict = false) - or - testIsTrue = false and - comp.getGreaterOperand() = lowerbound and - comp.getLesserOperand() = ssaRead(v, 0) and - (if comp.isStrict() then isStrict = false else isStrict = true) - ) -} - -/** - * Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted - * to only include bounds for which we might determine a sign. - */ -private predicate upperBound(Expr upperbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) { - exists(boolean testIsTrue, ComparisonExpr comp | - pos.hasReadOfVar(v) and - guardControlsSsaRead(comp, pos, testIsTrue) and - not unknownSign(upperbound) - | - testIsTrue = true and - comp.getGreaterOperand() = upperbound and - comp.getLesserOperand() = ssaRead(v, 0) and - (if comp.isStrict() then isStrict = true else isStrict = false) - or - testIsTrue = false and - comp.getLesserOperand() = upperbound and - comp.getGreaterOperand() = ssaRead(v, 0) and - (if comp.isStrict() then isStrict = false else isStrict = true) - ) -} - -/** - * Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is - * restricted to only include bounds for which we might determine a sign. The - * boolean `isEq` gives the polarity: - * - `isEq = true` : `v = eqbound` - * - `isEq = false` : `v != eqbound` - */ -private predicate eqBound(Expr eqbound, SsaVariable v, SsaReadPosition pos, boolean isEq) { - exists(Guard guard, boolean testIsTrue, boolean polarity | - pos.hasReadOfVar(v) and - guardControlsSsaRead(guard, pos, testIsTrue) and - guard.isEquality(eqbound, ssaRead(v, 0), polarity) and - isEq = polarity.booleanXor(testIsTrue).booleanNot() and - not unknownSign(eqbound) - ) -} - -/** - * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in - * order for `v` to be positive. - */ -private predicate posBound(Expr bound, SsaVariable v, SsaReadPosition pos) { - upperBound(bound, v, pos, _) or - eqBound(bound, v, pos, true) -} - -/** - * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in - * order for `v` to be negative. - */ -private predicate negBound(Expr bound, SsaVariable v, SsaReadPosition pos) { - lowerBound(bound, v, pos, _) or - eqBound(bound, v, pos, true) -} - -/** - * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v` - * can be zero. - */ -private predicate zeroBound(Expr bound, SsaVariable v, SsaReadPosition pos) { - lowerBound(bound, v, pos, _) or - upperBound(bound, v, pos, _) or - eqBound(bound, v, pos, _) -} - -/** Holds if `bound` allows `v` to be positive at `pos`. */ -private predicate posBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { - posBound(bound, v, pos) and TPos() = exprSign(bound) -} - -/** Holds if `bound` allows `v` to be negative at `pos`. */ -private predicate negBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { - negBound(bound, v, pos) and TNeg() = exprSign(bound) -} - -/** Holds if `bound` allows `v` to be zero at `pos`. */ -private predicate zeroBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { - lowerBound(bound, v, pos, _) and TNeg() = exprSign(bound) - or - lowerBound(bound, v, pos, false) and TZero() = exprSign(bound) - or - upperBound(bound, v, pos, _) and TPos() = exprSign(bound) - or - upperBound(bound, v, pos, false) and TZero() = exprSign(bound) - or - eqBound(bound, v, pos, true) and TZero() = exprSign(bound) - or - eqBound(bound, v, pos, false) and TZero() != exprSign(bound) -} - -/** - * Holds if there is a bound that might restrict whether `v` has the sign `s` - * at `pos`. - */ -private predicate hasGuard(SsaVariable v, SsaReadPosition pos, Sign s) { - s = TPos() and posBound(_, v, pos) - or - s = TNeg() and negBound(_, v, pos) - or - s = TZero() and zeroBound(_, v, pos) -} - -pragma[noinline] -private Sign guardedSsaSign(SsaVariable v, SsaReadPosition pos) { - result = ssaDefSign(v) and - pos.hasReadOfVar(v) and - hasGuard(v, pos, result) -} - -pragma[noinline] -private Sign unguardedSsaSign(SsaVariable v, SsaReadPosition pos) { - result = ssaDefSign(v) and - pos.hasReadOfVar(v) and - not hasGuard(v, pos, result) -} - -private Sign guardedSsaSignOk(SsaVariable v, SsaReadPosition pos) { - result = TPos() and - forex(Expr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos)) - or - result = TNeg() and - forex(Expr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos)) - or - result = TZero() and - forex(Expr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos)) -} - -/** Gets a possible sign for `v` at `pos`. */ -private Sign ssaSign(SsaVariable v, SsaReadPosition pos) { - result = unguardedSsaSign(v, pos) - or - result = guardedSsaSign(v, pos) and - result = guardedSsaSignOk(v, pos) -} - -/** Gets a possible sign for `v`. */ -pragma[nomagic] -private Sign ssaDefSign(SsaVariable v) { - exists(VariableUpdate def | def = v.(SsaExplicitUpdate).getDefiningExpr() | - result = exprSign(def.(VariableAssign).getSource()) - or - exists(EnhancedForStmt for | def = for.getVariable()) - or - result = exprSign(def.(PostIncExpr).getExpr()).inc() - or - result = exprSign(def.(PreIncExpr).getExpr()).inc() - or - result = exprSign(def.(PostDecExpr).getExpr()).dec() - or - result = exprSign(def.(PreDecExpr).getExpr()).dec() - or - exists(AssignOp a | a = def and result = exprSign(a)) - ) - or - result = fieldSign(v.(SsaImplicitUpdate).getSourceVariable().getVariable()) - or - result = fieldSign(v.(SsaImplicitInit).getSourceVariable().getVariable()) - or - exists(Parameter p | v.(SsaImplicitInit).isParameterDefinition(p)) - or - exists(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge | - v = phi and - edge.phiInput(phi, inp) and - result = ssaSign(inp, edge) - ) -} - -/** Gets a possible sign for `f`. */ -private Sign fieldSign(Field f) { - result = exprSign(f.getAnAssignedValue()) - or - exists(PostIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc()) - or - exists(PreIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc()) - or - exists(PostDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec()) - or - exists(PreDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec()) - or - exists(AssignOp a | a.getDest() = f.getAnAccess() | result = exprSign(a)) - or - exists(ReflectiveFieldAccess rfa | rfa.inferAccessedField() = f) - or - if f.fromSource() - then not exists(f.getInitializer()) and result = TZero() - else - if f instanceof ArrayLengthField - then result != TNeg() - else - if f.hasName("MAX_VALUE") - then result = TPos() - else - if f.hasName("MIN_VALUE") - then result = TNeg() - else any() -} - -/** Gets a possible sign for `e`. */ -cached -private Sign exprSign(Expr e) { - result = certainExprSign(e) - or - not exists(certainExprSign(e)) and - ( - unknownSign(e) - or - exists(SsaVariable v | v.getAUse() = e | - result = ssaSign(v, any(SsaReadPositionBlock bb | bb.getBlock() = e.getBasicBlock())) - or - not exists(e.getBasicBlock()) and result = ssaDefSign(v) - ) - or - exists(FieldAccess fa | fa = e | - not exists(SsaVariable v | v.getAUse() = fa) and - result = fieldSign(fa.getField()) - ) - or - exists(VarAccess va | va = e | - not exists(SsaVariable v | v.getAUse() = va) and - not va instanceof FieldAccess - ) - or - result = exprSign(e.(AssignExpr).getSource()) - or - result = exprSign(e.(PlusExpr).getExpr()) - or - result = exprSign(e.(PostIncExpr).getExpr()) - or - result = exprSign(e.(PostDecExpr).getExpr()) - or - result = exprSign(e.(PreIncExpr).getExpr()).inc() - or - result = exprSign(e.(PreDecExpr).getExpr()).dec() - or - result = exprSign(e.(MinusExpr).getExpr()).neg() - or - result = exprSign(e.(BitNotExpr).getExpr()).bitnot() - or - exists(DivExpr div | - div = e and - result = exprSign(div.getLeftOperand()) and - result != TZero() - | - div.getRightOperand().(FloatingPointLiteral).getValue().toFloat() = 0 or - div.getRightOperand().(DoubleLiteral).getValue().toFloat() = 0 - ) - or - exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) | - (e instanceof AssignAddExpr or e instanceof AddExpr) and - result = s1.add(s2) - or - (e instanceof AssignSubExpr or e instanceof SubExpr) and - result = s1.add(s2.neg()) - or - (e instanceof AssignMulExpr or e instanceof MulExpr) and - result = s1.mul(s2) - or - (e instanceof AssignDivExpr or e instanceof DivExpr) and - result = s1.div(s2) - or - (e instanceof AssignRemExpr or e instanceof RemExpr) and - result = s1.rem(s2) - or - (e instanceof AssignAndExpr or e instanceof AndBitwiseExpr) and - result = s1.bitand(s2) - or - (e instanceof AssignOrExpr or e instanceof OrBitwiseExpr) and - result = s1.bitor(s2) - or - (e instanceof AssignXorExpr or e instanceof XorBitwiseExpr) and - result = s1.bitxor(s2) - or - (e instanceof AssignLShiftExpr or e instanceof LShiftExpr) and - result = s1.lshift(s2) - or - (e instanceof AssignRShiftExpr or e instanceof RShiftExpr) and - result = s1.rshift(s2) - or - (e instanceof AssignURShiftExpr or e instanceof URShiftExpr) and - result = s1.urshift(s2) - ) - or - result = exprSign(e.(ChooseExpr).getAResultExpr()) - or - result = exprSign(e.(CastExpr).getExpr()) - ) -} - -private Sign binaryOpLhsSign(Expr e) { - result = exprSign(e.(BinaryExpr).getLeftOperand()) or - result = exprSign(e.(AssignOp).getDest()) -} - -private Sign binaryOpRhsSign(Expr e) { - result = exprSign(e.(BinaryExpr).getRightOperand()) or - result = exprSign(e.(AssignOp).getRhs()) -} - -pragma[noinline] -private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) { - lhs = binaryOpLhsSign(e) and - rhs = binaryOpRhsSign(e) -} - -/** Holds if `e` can be positive and cannot be negative. */ -predicate positive(Expr e) { - exprSign(e) = TPos() and - not exprSign(e) = TNeg() -} - -/** Holds if `e` can be negative and cannot be positive. */ -predicate negative(Expr e) { - exprSign(e) = TNeg() and - not exprSign(e) = TPos() -} - -/** Holds if `e` is strictly positive. */ -predicate strictlyPositive(Expr e) { - exprSign(e) = TPos() and - not exprSign(e) = TNeg() and - not exprSign(e) = TZero() -} - -/** Holds if `e` is strictly negative. */ -predicate strictlyNegative(Expr e) { - exprSign(e) = TNeg() and - not exprSign(e) = TPos() and - not exprSign(e) = TZero() -} +import semmle.code.java.dataflow.internal.rangeanalysis.SignAnalysisCommon diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll new file mode 100644 index 00000000000..10ca946a044 --- /dev/null +++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll @@ -0,0 +1,219 @@ +newtype TSign = + TNeg() or + TZero() or + TPos() + +/** Class representing expression signs (+, -, 0). */ +class Sign extends TSign { + /** Gets the string representation of this sign. */ + string toString() { + result = "-" and this = TNeg() + or + result = "0" and this = TZero() + or + result = "+" and this = TPos() + } + + /** Gets a possible sign after incrementing an expression that has this sign. */ + Sign inc() { + this = TNeg() and result = TNeg() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TPos() + or + this = TPos() and result = TPos() + } + + /** Gets a possible sign after decrementing an expression that has this sign. */ + Sign dec() { result.inc() = this } + + /** Gets a possible sign after negating an expression that has this sign. */ + Sign neg() { + this = TNeg() and result = TPos() + or + this = TZero() and result = TZero() + or + this = TPos() and result = TNeg() + } + + /** + * Gets a possible sign after bitwise complementing an expression that has this + * sign. + */ + Sign bitnot() { + this = TNeg() and result = TPos() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TNeg() + or + this = TPos() and result = TNeg() + } + + /** + * Gets a possible sign after adding an expression with sign `s` to an expression + * that has this sign. + */ + Sign add(Sign s) { + this = TZero() and result = s + or + s = TZero() and result = this + or + this = s and this = result + or + this = TPos() and s = TNeg() + or + this = TNeg() and s = TPos() + } + + /** + * Gets a possible sign after multiplying an expression with sign `s` to an expression + * that has this sign. + */ + Sign mul(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + /** + * Gets a possible sign after integer dividing an expression that has this sign + * by an expression with sign `s`. + */ + Sign div(Sign s) { + result = TZero() and s = TNeg() // ex: 3 / -5 = 0 + or + result = TZero() and s = TPos() // ex: 3 / 5 = 0 + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + /** + * Gets a possible sign after modulo dividing an expression that has this sign + * by an expression with sign `s`. + */ + Sign rem(Sign s) { + result = TZero() and s = TNeg() + or + result = TZero() and s = TPos() + or + result = this and s = TNeg() + or + result = this and s = TPos() + } + + /** + * Gets a possible sign after bitwise `and` of an expression that has this sign + * and an expression with sign `s`. + */ + Sign bitand(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TZero() and this = TPos() + or + result = TZero() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TNeg() + or + result = TPos() and this = TPos() and s = TPos() + } + + /** + * Gets a possible sign after bitwise `or` of an expression that has this sign + * and an expression with sign `s`. + */ + Sign bitor(Sign s) { + result = TZero() and this = TZero() and s = TZero() + or + result = TNeg() and this = TNeg() + or + result = TNeg() and s = TNeg() + or + result = TPos() and this = TPos() and s = TZero() + or + result = TPos() and this = TZero() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + } + + /** + * Gets a possible sign after bitwise `xor` of an expression that has this sign + * and an expression with sign `s`. + */ + Sign bitxor(Sign s) { + result = TZero() and this = s + or + result = this and s = TZero() + or + result = s and this = TZero() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + /** + * Gets a possible sign after left shift of an expression that has this sign + * by an expression with sign `s`. + */ + Sign lshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + this != TZero() and s != TZero() + } + + /** + * Gets a possible sign after right shift of an expression that has this sign + * by an expression with sign `s`. + */ + Sign rshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result = TNeg() and this = TNeg() + or + result != TNeg() and this = TPos() and s != TZero() + } + + /** + * Gets a possible sign after unsigned right shift of an expression that has + * this sign by an expression with sign `s`. + */ + Sign urshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result != TZero() and this = TNeg() and s != TZero() + or + result != TNeg() and this = TPos() and s != TZero() + } +} diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll new file mode 100644 index 00000000000..66b38433eef --- /dev/null +++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll @@ -0,0 +1,293 @@ +/** + * Provides sign analysis to determine whether expression are always positive + * or negative. + * + * The analysis is implemented as an abstract interpretation over the + * three-valued domain `{negative, zero, positive}`. + */ + +private import SignAnalysisSpecific::Private +private import SsaReadPositionCommon +private import Sign + +/** Gets the sign of `e` if this can be directly determined. */ +Sign certainExprSign(Expr e) { + exists(int i | e.(ConstantIntegerExpr).getIntValue() = i | + i < 0 and result = TNeg() + or + i = 0 and result = TZero() + or + i > 0 and result = TPos() + ) + or + not exists(e.(ConstantIntegerExpr).getIntValue()) and + ( + exists(float f | f = getNonIntegerValue(e) | + f < 0 and result = TNeg() + or + f = 0 and result = TZero() + or + f > 0 and result = TPos() + ) + or + exists(string charlit | charlit = getCharValue(e) | + if charlit.regexpMatch("\\u0000") then result = TZero() else result = TPos() + ) + or + containerSizeAccess(e) and + (result = TPos() or result = TZero()) + or + positiveExpression(e) and result = TPos() + ) +} + +/** Holds if the sign of `e` is too complicated to determine. */ +predicate unknownSign(Expr e) { + not exists(certainExprSign(e)) and + ( + exists(IntegerLiteral lit | lit = e and not exists(lit.getValue().toInt())) + or + exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat())) + or + exists(CastExpr cast, Type fromtyp | + cast = e and + fromtyp = cast.getExpr().getType() and + not fromtyp instanceof NumericOrCharType + ) + or + unknownIntegerAccess(e) + ) +} + +/** + * Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate lowerBound(Expr lowerbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) { + exists(boolean testIsTrue, ComparisonExpr comp | + pos.hasReadOfVar(v) and + guardControlsSsaRead(getComparisonGuard(comp), pos, testIsTrue) and + not unknownSign(lowerbound) + | + testIsTrue = true and + comp.getLesserOperand() = lowerbound and + comp.getGreaterOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = true else isStrict = false) + or + testIsTrue = false and + comp.getGreaterOperand() = lowerbound and + comp.getLesserOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = false else isStrict = true) + ) +} + +/** + * Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate upperBound(Expr upperbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) { + exists(boolean testIsTrue, ComparisonExpr comp | + pos.hasReadOfVar(v) and + guardControlsSsaRead(getComparisonGuard(comp), pos, testIsTrue) and + not unknownSign(upperbound) + | + testIsTrue = true and + comp.getGreaterOperand() = upperbound and + comp.getLesserOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = true else isStrict = false) + or + testIsTrue = false and + comp.getLesserOperand() = upperbound and + comp.getGreaterOperand() = ssaRead(v, 0) and + (if comp.isStrict() then isStrict = false else isStrict = true) + ) +} + +/** + * Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is + * restricted to only include bounds for which we might determine a sign. The + * boolean `isEq` gives the polarity: + * - `isEq = true` : `v = eqbound` + * - `isEq = false` : `v != eqbound` + */ +private predicate eqBound(Expr eqbound, SsaVariable v, SsaReadPosition pos, boolean isEq) { + exists(Guard guard, boolean testIsTrue, boolean polarity | + pos.hasReadOfVar(v) and + guardControlsSsaRead(guard, pos, testIsTrue) and + guard.isEquality(eqbound, ssaRead(v, 0), polarity) and + isEq = polarity.booleanXor(testIsTrue).booleanNot() and + not unknownSign(eqbound) + ) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in + * order for `v` to be positive. + */ +private predicate posBound(Expr bound, SsaVariable v, SsaReadPosition pos) { + upperBound(bound, v, pos, _) or + eqBound(bound, v, pos, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in + * order for `v` to be negative. + */ +private predicate negBound(Expr bound, SsaVariable v, SsaReadPosition pos) { + lowerBound(bound, v, pos, _) or + eqBound(bound, v, pos, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v` + * can be zero. + */ +private predicate zeroBound(Expr bound, SsaVariable v, SsaReadPosition pos) { + lowerBound(bound, v, pos, _) or + upperBound(bound, v, pos, _) or + eqBound(bound, v, pos, _) +} + +/** Holds if `bound` allows `v` to be positive at `pos`. */ +private predicate posBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { + posBound(bound, v, pos) and TPos() = exprSign(bound) +} + +/** Holds if `bound` allows `v` to be negative at `pos`. */ +private predicate negBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { + negBound(bound, v, pos) and TNeg() = exprSign(bound) +} + +/** Holds if `bound` allows `v` to be zero at `pos`. */ +private predicate zeroBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) { + lowerBound(bound, v, pos, _) and TNeg() = exprSign(bound) + or + lowerBound(bound, v, pos, false) and TZero() = exprSign(bound) + or + upperBound(bound, v, pos, _) and TPos() = exprSign(bound) + or + upperBound(bound, v, pos, false) and TZero() = exprSign(bound) + or + eqBound(bound, v, pos, true) and TZero() = exprSign(bound) + or + eqBound(bound, v, pos, false) and TZero() != exprSign(bound) +} + +/** + * Holds if there is a bound that might restrict whether `v` has the sign `s` + * at `pos`. + */ +private predicate hasGuard(SsaVariable v, SsaReadPosition pos, Sign s) { + s = TPos() and posBound(_, v, pos) + or + s = TNeg() and negBound(_, v, pos) + or + s = TZero() and zeroBound(_, v, pos) +} + +pragma[noinline] +private Sign guardedSsaSign(SsaVariable v, SsaReadPosition pos) { + // SSA variable can have sign `result` + result = ssaDefSign(v) and + pos.hasReadOfVar(v) and + // there are guards at this position on `v` that might restrict it to be sign `result`. + // (So we need to check if they are satisfied) + hasGuard(v, pos, result) +} + +pragma[noinline] +private Sign unguardedSsaSign(SsaVariable v, SsaReadPosition pos) { + // SSA variable can have sign `result` + result = ssaDefSign(v) and + pos.hasReadOfVar(v) and + // there's no guard at this position on `v` that might restrict it to be sign `result`. + not hasGuard(v, pos, result) +} + +/** + * Gets the sign of `v` at read position `pos`, when there's at least one guard + * on `v` at position `pos`. Each bound corresponding to a given sign must be met + * in order for `v` to be of that sign. + */ +private Sign guardedSsaSignOk(SsaVariable v, SsaReadPosition pos) { + result = TPos() and + forex(Expr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos)) + or + result = TNeg() and + forex(Expr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos)) + or + result = TZero() and + forex(Expr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos)) +} + +/** Gets a possible sign for `v` at `pos`. */ +Sign ssaSign(SsaVariable v, SsaReadPosition pos) { + result = unguardedSsaSign(v, pos) + or + result = guardedSsaSign(v, pos) and + result = guardedSsaSignOk(v, pos) +} + +/** Gets a possible sign for `v`. */ +pragma[nomagic] +Sign ssaDefSign(SsaVariable v) { + result = explicitSsaDefSign(v) + or + result = implicitSsaDefSign(v) + or + exists(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge | + v = phi and + edge.phiInput(phi, inp) and + result = ssaSign(inp, edge) + ) +} + +/** Gets a possible sign for `e`. */ +cached +Sign exprSign(Expr e) { + result = certainExprSign(e) + or + not exists(certainExprSign(e)) and + ( + unknownSign(e) + or + exists(SsaVariable v | getARead(v) = e | result = ssaVariableSign(v, e)) + or + e = + any(VarAccess access | + not exists(SsaVariable v | getARead(v) = access) and + ( + result = fieldSign(getField(access.(FieldAccess))) or + not access instanceof FieldAccess + ) + ) + or + result = specificSubExprSign(e) + ) +} + +/** Holds if `e` can be positive and cannot be negative. */ +predicate positive(Expr e) { + exprSign(e) = TPos() and + not exprSign(e) = TNeg() +} + +/** Holds if `e` can be negative and cannot be positive. */ +predicate negative(Expr e) { + exprSign(e) = TNeg() and + not exprSign(e) = TPos() +} + +/** Holds if `e` is strictly positive. */ +predicate strictlyPositive(Expr e) { + exprSign(e) = TPos() and + not exprSign(e) = TNeg() and + not exprSign(e) = TZero() +} + +/** Holds if `e` is strictly negative. */ +predicate strictlyNegative(Expr e) { + exprSign(e) = TNeg() and + not exprSign(e) = TPos() and + not exprSign(e) = TZero() +} diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll new file mode 100644 index 00000000000..16784ccf0ec --- /dev/null +++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -0,0 +1,235 @@ +/** + * Provides Java-specific definitions for use in sign analysis. + */ +module Private { + import semmle.code.java.dataflow.RangeUtils as RU + private import semmle.code.java.dataflow.SSA as Ssa + private import semmle.code.java.controlflow.Guards as G + private import java as J + import Impl + + class ConstantIntegerExpr = RU::ConstantIntegerExpr; + + class Guard = G::Guard; + + class SsaVariable = Ssa::SsaVariable; + + class SsaPhiNode = Ssa::SsaPhiNode; + + class VarAccess = J::VarAccess; + + class FieldAccess = J::FieldAccess; + + class CharacterLiteral = J::CharacterLiteral; + + class IntegerLiteral = J::IntegerLiteral; + + class LongLiteral = J::LongLiteral; + + class CastExpr = J::CastExpr; + + class Type = J::Type; + + class Expr = J::Expr; + + class ComparisonExpr = J::ComparisonExpr; + + class NumericOrCharType = J::NumericOrCharType; + + predicate ssaRead = RU::ssaRead/2; + + predicate guardControlsSsaRead = RU::guardControlsSsaRead/3; +} + +private module Impl { + private import java + private import semmle.code.java.dataflow.RangeUtils + private import semmle.code.java.dataflow.SSA + private import semmle.code.java.controlflow.Guards + private import semmle.code.java.Reflection + private import semmle.code.java.Collections + private import semmle.code.java.Maps + private import Sign + private import SignAnalysisCommon + private import SsaReadPositionCommon + + float getNonIntegerValue(Expr e) { + result = e.(LongLiteral).getValue().toFloat() or + result = e.(FloatingPointLiteral).getValue().toFloat() or + result = e.(DoubleLiteral).getValue().toFloat() + } + + string getCharValue(Expr e) { result = e.(CharacterLiteral).getValue() } + + predicate containerSizeAccess(Expr e) { + e.(MethodAccess).getMethod() instanceof StringLengthMethod + or + e.(MethodAccess).getMethod() instanceof CollectionSizeMethod + or + e.(MethodAccess).getMethod() instanceof MapSizeMethod + } + + predicate positiveExpression(Expr e) { none() } + + predicate unknownIntegerAccess(Expr e) { + e instanceof ArrayAccess and e.getType() instanceof NumericOrCharType + or + e instanceof MethodAccess and e.getType() instanceof NumericOrCharType + or + e instanceof ClassInstanceExpr and e.getType() instanceof NumericOrCharType + } + + Sign explicitSsaDefSign(SsaVariable v) { + exists(VariableUpdate def | def = v.(SsaExplicitUpdate).getDefiningExpr() | + result = exprSign(def.(VariableAssign).getSource()) + or + exists(EnhancedForStmt for | def = for.getVariable()) + or + result = exprSign(def.(PostIncExpr).getExpr()).inc() + or + result = exprSign(def.(PreIncExpr).getExpr()).inc() + or + result = exprSign(def.(PostDecExpr).getExpr()).dec() + or + result = exprSign(def.(PreDecExpr).getExpr()).dec() + or + exists(AssignOp a | a = def and result = exprSign(a)) + ) + } + + Sign implicitSsaDefSign(SsaVariable v) { + result = fieldSign(v.(SsaImplicitUpdate).getSourceVariable().getVariable()) + or + result = fieldSign(v.(SsaImplicitInit).getSourceVariable().getVariable()) + or + exists(Parameter p | v.(SsaImplicitInit).isParameterDefinition(p)) + } + + pragma[inline] + Sign ssaVariableSign(SsaVariable v, Expr e) { + result = ssaSign(v, any(SsaReadPositionBlock bb | getAnExpression(bb) = e)) + or + not exists(SsaReadPositionBlock bb | getAnExpression(bb) = e) and + result = ssaDefSign(v) + } + + /** Gets a possible sign for `f`. */ + Sign fieldSign(Field f) { + result = exprSign(f.getAnAssignedValue()) + or + exists(PostIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc()) + or + exists(PreIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc()) + or + exists(PostDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec()) + or + exists(PreDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec()) + or + exists(AssignOp a | a.getDest() = f.getAnAccess() | result = exprSign(a)) + or + exists(ReflectiveFieldAccess rfa | rfa.inferAccessedField() = f) + or + if f.fromSource() + then not exists(f.getInitializer()) and result = TZero() + else + if f instanceof ArrayLengthField + then result != TNeg() + else + if f.hasName("MAX_VALUE") + then result = TPos() + else + if f.hasName("MIN_VALUE") + then result = TNeg() + else any() + } + + Sign specificSubExprSign(Expr e) { + result = exprSign(e.(AssignExpr).getSource()) + or + result = exprSign(e.(PlusExpr).getExpr()) + or + result = exprSign(e.(PostIncExpr).getExpr()) + or + result = exprSign(e.(PostDecExpr).getExpr()) + or + result = exprSign(e.(PreIncExpr).getExpr()).inc() + or + result = exprSign(e.(PreDecExpr).getExpr()).dec() + or + result = exprSign(e.(MinusExpr).getExpr()).neg() + or + result = exprSign(e.(BitNotExpr).getExpr()).bitnot() + or + exists(DivExpr div | + div = e and + result = exprSign(div.getLeftOperand()) and + result != TZero() + | + div.getRightOperand().(FloatingPointLiteral).getValue().toFloat() = 0 or + div.getRightOperand().(DoubleLiteral).getValue().toFloat() = 0 + ) + or + exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) | + (e instanceof AssignAddExpr or e instanceof AddExpr) and + result = s1.add(s2) + or + (e instanceof AssignSubExpr or e instanceof SubExpr) and + result = s1.add(s2.neg()) + or + (e instanceof AssignMulExpr or e instanceof MulExpr) and + result = s1.mul(s2) + or + (e instanceof AssignDivExpr or e instanceof DivExpr) and + result = s1.div(s2) + or + (e instanceof AssignRemExpr or e instanceof RemExpr) and + result = s1.rem(s2) + or + (e instanceof AssignAndExpr or e instanceof AndBitwiseExpr) and + result = s1.bitand(s2) + or + (e instanceof AssignOrExpr or e instanceof OrBitwiseExpr) and + result = s1.bitor(s2) + or + (e instanceof AssignXorExpr or e instanceof XorBitwiseExpr) and + result = s1.bitxor(s2) + or + (e instanceof AssignLShiftExpr or e instanceof LShiftExpr) and + result = s1.lshift(s2) + or + (e instanceof AssignRShiftExpr or e instanceof RShiftExpr) and + result = s1.rshift(s2) + or + (e instanceof AssignURShiftExpr or e instanceof URShiftExpr) and + result = s1.urshift(s2) + ) + or + result = exprSign(e.(ChooseExpr).getAResultExpr()) + or + result = exprSign(e.(CastExpr).getExpr()) + } + + private Sign binaryOpLhsSign(Expr e) { + result = exprSign(e.(BinaryExpr).getLeftOperand()) or + result = exprSign(e.(AssignOp).getDest()) + } + + private Sign binaryOpRhsSign(Expr e) { + result = exprSign(e.(BinaryExpr).getRightOperand()) or + result = exprSign(e.(AssignOp).getRhs()) + } + + pragma[noinline] + private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) { + lhs = binaryOpLhsSign(e) and + rhs = binaryOpRhsSign(e) + } + + Expr getARead(SsaVariable v) { result = v.getAUse() } + + Field getField(FieldAccess fa) { result = fa.getField() } + + Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode() } + + Guard getComparisonGuard(ComparisonExpr ce) { result = ce } +} diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll new file mode 100644 index 00000000000..558ecd1b88b --- /dev/null +++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll @@ -0,0 +1,57 @@ +/** + * Provides classes for representing a position at which an SSA variable is read. + */ + +private import SsaReadPositionSpecific + +private newtype TSsaReadPosition = + TSsaReadPositionBlock(BasicBlock bb) { bb = getAReadBasicBlock(_) } or + TSsaReadPositionPhiInputEdge(BasicBlock bbOrig, BasicBlock bbPhi) { + exists(SsaPhiNode phi | phi.hasInputFromBlock(_, bbOrig) and bbPhi = phi.getBasicBlock()) + } + +/** + * A position at which an SSA variable is read. This includes both ordinary + * reads occurring in basic blocks and input to phi nodes occurring along an + * edge between two basic blocks. + */ +class SsaReadPosition extends TSsaReadPosition { + /** Holds if `v` is read at this position. */ + abstract predicate hasReadOfVar(SsaVariable v); + + /** Gets a textual representation of this SSA read position. */ + abstract string toString(); +} + +/** A basic block in which an SSA variable is read. */ +class SsaReadPositionBlock extends SsaReadPosition, TSsaReadPositionBlock { + /** Gets the basic block corresponding to this position. */ + BasicBlock getBlock() { this = TSsaReadPositionBlock(result) } + + override predicate hasReadOfVar(SsaVariable v) { getBlock() = getAReadBasicBlock(v) } + + override string toString() { result = "block" } +} + +/** + * An edge between two basic blocks where the latter block has an SSA phi + * definition. The edge therefore has a read of an SSA variable serving as the + * input to the phi node. + */ +class SsaReadPositionPhiInputEdge extends SsaReadPosition, TSsaReadPositionPhiInputEdge { + /** Gets the source of the edge. */ + BasicBlock getOrigBlock() { this = TSsaReadPositionPhiInputEdge(result, _) } + + /** Gets the target of the edge. */ + BasicBlock getPhiBlock() { this = TSsaReadPositionPhiInputEdge(_, result) } + + override predicate hasReadOfVar(SsaVariable v) { this.phiInput(_, v) } + + /** Holds if `inp` is an input to `phi` along this edge. */ + predicate phiInput(SsaPhiNode phi, SsaVariable inp) { + phi.hasInputFromBlock(inp, getOrigBlock()) and + getPhiBlock() = phi.getBasicBlock() + } + + override string toString() { result = "edge" } +} diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll new file mode 100644 index 00000000000..dcfc3d69e32 --- /dev/null +++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll @@ -0,0 +1,15 @@ +/** + * Provides Java-specific definitions for use in the `SsaReadPosition`. + */ + +private import semmle.code.java.dataflow.SSA as Ssa +private import semmle.code.java.controlflow.BasicBlocks as BB + +class SsaVariable = Ssa::SsaVariable; + +class SsaPhiNode = Ssa::SsaPhiNode; + +class BasicBlock = BB::BasicBlock; + +/** Gets a basic block in which SSA variable `v` is read. */ +BasicBlock getAReadBasicBlock(SsaVariable v) { result = v.getAUse().getBasicBlock() } diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected index 1e3fe9acf27..a1dedfa95b1 100644 --- a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected +++ b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected @@ -1,3 +1,3 @@ -| A.java:4:14:4:14 | x | negative strictlyNegative | +| A.java:4:14:4:14 | x | strictlyNegative | | A.java:6:9:6:9 | x | positive | -| A.java:7:14:7:14 | y | positive strictlyPositive | +| A.java:7:14:7:14 | y | strictlyPositive | diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql index 7dd564dd77d..bc4fed33dc8 100644 --- a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql +++ b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql @@ -1,17 +1,19 @@ import java import semmle.code.java.dataflow.SignAnalysis -string getASignString(Expr i) { - positive(i) and +string getASignString(Expr e) { + positive(e) and + not strictlyPositive(e) and result = "positive" or - negative(i) and + negative(e) and + not strictlyNegative(e) and result = "negative" or - strictlyPositive(i) and + strictlyPositive(e) and result = "strictlyPositive" or - strictlyNegative(i) and + strictlyNegative(e) and result = "strictlyNegative" } From 06dbec78f7fe8be7b82aa55ef9dbc1b42b1f52bc Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 15 Sep 2020 12:47:42 +0200 Subject: [PATCH 046/185] C#: Add `Guard::controlsBasicBlock()` and simplify `Guard::isEquality()` --- .../semmle/code/csharp/controlflow/Guards.qll | 125 ++++++++++-------- .../rangeanalysis/SignAnalysisSpecific.qll | 2 +- .../dataflow/signanalysis/SignAnalysis.cs | 4 +- .../signanalysis/SignAnalysis.expected | 3 +- .../Nullness/EqualityCheck.expected | 4 + 5 files changed, 75 insertions(+), 63 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll index f81b3b09212..65a3bfe7213 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll @@ -33,29 +33,21 @@ class Guard extends Expr { } /** - * Holds if guard is an equality test between `e1` and `e2`. If the test is + * Holds if basic block `bb` is guarded by this expression having value `v`. + */ + predicate controlsBasicBlock(BasicBlock bb, AbstractValue v) { + Internal::guardControls(this, bb, v) + } + + /** + * Holds if this guard is an equality test between `e1` and `e2`. If the test is * negated, that is `!=`, then `polarity` is false, otherwise `polarity` is * true. */ predicate isEquality(Expr e1, Expr e2, boolean polarity) { - exists(Expr exp1, Expr exp2 | equalityGuard(exp1, exp2, polarity) | - e1 = exp1 and e2 = exp2 - or - e2 = exp1 and e1 = exp2 - ) - } - - private predicate equalityGuard(Expr e1, Expr e2, boolean polarity) { - exists(ComparisonTest eqtest | - eqtest.getExpr() = this and - eqtest.getAnArgument() = e1 and - eqtest.getAnArgument() = e2 and - not e1 = e2 and - ( - polarity = true and eqtest.getComparisonKind().isEquality() - or - polarity = false and eqtest.getComparisonKind().isInequality() - ) + exists(BooleanValue v | + this = Internal::getAnEqualityCheck(e1, v, e2) and + polarity = v.getValue() ) } } @@ -970,13 +962,14 @@ module Internal { e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand()) } - /** Holds if basic block `bb` only is reached when guard `g` has abstract value `v`. */ - private predicate guardControls(Guard g, BasicBlock bb, AbstractValue v) { - exists(ControlFlowElement cfe, ConditionalSuccessor s, AbstractValue v0, Guard g0 | - cfe.controlsBlock(bb, s) - | - v0.branch(cfe, s, g0) and - impliesSteps(g0, v0, g, v) + pragma[noinline] + private predicate assertionControlsNodeInSameBasicBlock0( + Guard g, AbstractValue v, BasicBlock bb, int i + ) { + exists(Assertion a, Guard g0, AbstractValue v0 | + asserts(a, g0, v0) and + impliesSteps(g0, v0, g, v) and + bb.getNode(i) = a.getAControlFlowNode() ) } @@ -984,17 +977,13 @@ module Internal { * Holds if control flow node `cfn` only is reached when guard `g` evaluates to `v`, * because of an assertion. */ - private predicate guardAssertionControlsNode(Guard g, ControlFlow::Node cfn, AbstractValue v) { - exists(Assertion a, Guard g0, AbstractValue v0 | - asserts(a, g0, v0) and - impliesSteps(g0, v0, g, v) - | - a.strictlyDominates(cfn.getBasicBlock()) - or - exists(BasicBlock bb, int i, int j | bb.getNode(i) = a.getAControlFlowNode() | - bb.getNode(j) = cfn and - j > i - ) + private predicate assertionControlsNodeInSameBasicBlock( + Guard g, ControlFlow::Node cfn, AbstractValue v + ) { + exists(BasicBlock bb, int i, int j | + assertionControlsNodeInSameBasicBlock0(g, v, bb, i) and + bb.getNode(j) = cfn and + j > i ) } @@ -1004,7 +993,7 @@ module Internal { */ private predicate guardAssertionControlsElement(Guard g, ControlFlowElement cfe, AbstractValue v) { forex(ControlFlow::Node cfn | cfn = cfe.getAControlFlowNode() | - guardAssertionControlsNode(g, cfn, v) + assertionControlsNodeInSameBasicBlock(g, cfn, v) ) } @@ -1318,24 +1307,6 @@ module Internal { ) } - /** - * Gets an expression that tests whether expression `e1` is equal to - * expression `e2`. - * - * If the returned expression has abstract value `v`, then expression `e1` is - * guaranteed to be equal to `e2`, and if the returned expression has abstract - * value `v.getDualValue()`, then this expression is guaranteed to be - * non-equal to `e`. - * - * For example, if the expression `x != ""` evaluates to `false` then the - * expression `x` is guaranteed to be equal to `""`. - */ - Expr getAnEqualityCheck(Expr e1, AbstractValue v, Expr e2) { - result = getABooleanEqualityCheck(e1, v, e2) - or - result = getAMatchingEqualityCheck(e1, v, e2) - } - private Expr getAnEqualityCheckVal(Expr e, AbstractValue v, AbstractValue vExpr) { result = getAnEqualityCheck(e, v, vExpr.getAnExpr()) } @@ -1491,6 +1462,29 @@ module Internal { not e = any(LocalVariableDeclStmt s).getAVariableDeclExpr() } + /** + * Gets an expression that tests whether expression `e1` is equal to + * expression `e2`. + * + * If the returned expression has abstract value `v`, then expression `e1` is + * guaranteed to be equal to `e2`, and if the returned expression has abstract + * value `v.getDualValue()`, then this expression is guaranteed to be + * non-equal to `e`. + * + * For example, if the expression `x != ""` evaluates to `false` then the + * expression `x` is guaranteed to be equal to `""`. + */ + cached + Expr getAnEqualityCheck(Expr e1, AbstractValue v, Expr e2) { + result = getABooleanEqualityCheck(e1, v, e2) + or + result = getABooleanEqualityCheck(e2, v, e1) + or + result = getAMatchingEqualityCheck(e1, v, e2) + or + result = getAMatchingEqualityCheck(e2, v, e1) + } + cached predicate isCustomNullCheck(Call call, Expr arg, BooleanValue v, boolean isNull) { exists(Callable callable, Parameter p | @@ -1776,7 +1770,7 @@ module Internal { exists(Guard g | e = getAChildExprStar(g) | guardControls(g, bb, _) or - guardAssertionControlsNode(g, bb.getANode(), _) + assertionControlsNodeInSameBasicBlock(g, bb.getANode(), _) ) } } @@ -1785,6 +1779,21 @@ module Internal { private module Cached { private import semmle.code.csharp.Caching + /** Holds if basic block `bb` only is reached when guard `g` has abstract value `v`. */ + cached + predicate guardControls(Guard g, BasicBlock bb, AbstractValue v) { + exists(AbstractValue v0, Guard g0 | impliesSteps(g0, v0, g, v) | + exists(ControlFlowElement cfe, ConditionalSuccessor s | + v0.branch(cfe, s, g0) and cfe.controlsBlock(bb, s) + ) + or + exists(Assertion a | + asserts(a, g0, v0) and + a.strictlyDominates(bb) + ) + ) + } + pragma[noinline] private predicate isGuardedByNode0( ControlFlow::Node cfn, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, @@ -1840,7 +1849,7 @@ module Internal { ) { isGuardedByNode0(guarded, _, g, sub, v) or - guardAssertionControlsNode(g, guarded, v) and + assertionControlsNodeInSameBasicBlock(g, guarded, v) and exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded.getElement())) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll index 75adec96909..a47434eed0d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -243,7 +243,7 @@ private module Impl { */ predicate guardControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) { exists(BooleanValue b | b.getValue() = testIsTrue | - guard.controlsNode(controlled.(SsaReadPositionBlock).getBlock().getANode(), _, b) + guard.controlsBasicBlock(controlled.(SsaReadPositionBlock).getBlock(), b) ) } diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs index 13a94a18aae..5a2df6428ff 100644 --- a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs @@ -155,7 +155,7 @@ class SignAnalysis if (y is -1) { - return y; // strictly negative [MISSING] + return y; // strictly negative } if (x < y) @@ -289,7 +289,7 @@ class SignAnalysis void Assert(int i, bool b) { Debug.Assert(i > 0); - System.Console.WriteLine(i); // strictly positive + System.Console.WriteLine(i); // strictly positive [MISSING] if (b) System.Console.WriteLine(i); // strictly positive diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected index 91d429a45ef..31220df1a3c 100644 --- a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected @@ -106,6 +106,7 @@ | SignAnalysis.cs:153:20:153:20 | access to parameter y | strictlyPositive | | SignAnalysis.cs:156:18:156:19 | -... | strictlyNegative | | SignAnalysis.cs:156:19:156:19 | 1 | strictlyPositive | +| SignAnalysis.cs:158:20:158:20 | access to parameter y | strictlyNegative | | SignAnalysis.cs:161:13:161:13 | access to parameter x | positive | | SignAnalysis.cs:163:20:163:20 | access to parameter y | strictlyPositive | | SignAnalysis.cs:166:22:166:22 | 1 | strictlyPositive | @@ -154,8 +155,6 @@ | SignAnalysis.cs:278:42:278:42 | access to parameter k | positive | | SignAnalysis.cs:280:21:280:21 | access to parameter k | positive | | SignAnalysis.cs:280:26:280:26 | 5 | strictlyPositive | -| SignAnalysis.cs:291:22:291:22 | access to parameter i | strictlyPositive | -| SignAnalysis.cs:292:34:292:34 | access to parameter i | strictlyPositive | | SignAnalysis.cs:295:38:295:38 | access to parameter i | strictlyPositive | | SignAnalysis.cs:300:27:300:28 | -... | strictlyNegative | | SignAnalysis.cs:300:28:300:28 | 1 | strictlyPositive | diff --git a/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected b/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected index 5f739e93c2b..dc70346eea5 100644 --- a/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected +++ b/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected @@ -191,8 +191,11 @@ | E.cs:85:18:85:29 | ... != ... | false | E.cs:85:18:85:21 | access to parameter vals | E.cs:85:26:85:29 | null | | E.cs:85:18:85:29 | ... != ... | false | E.cs:85:26:85:29 | null | E.cs:85:18:85:21 | access to parameter vals | | E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_A | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:92:18:92:27 | access to constant MY_CONST_A | +| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_A | E.cs:92:18:92:27 | access to constant MY_CONST_A | E.cs:90:17:90:27 | access to local variable switchguard | | E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_B | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:97:18:97:27 | access to constant MY_CONST_B | +| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_B | E.cs:97:18:97:27 | access to constant MY_CONST_B | E.cs:90:17:90:27 | access to local variable switchguard | | E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_C | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:95:18:95:27 | access to constant MY_CONST_C | +| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_C | E.cs:95:18:95:27 | access to constant MY_CONST_C | E.cs:90:17:90:27 | access to local variable switchguard | | E.cs:126:21:126:29 | ... == ... | true | E.cs:126:21:126:24 | access to local variable step | E.cs:126:29:126:29 | 0 | | E.cs:126:21:126:29 | ... == ... | true | E.cs:126:29:126:29 | 0 | E.cs:126:21:126:24 | access to local variable step | | E.cs:153:13:153:24 | ... != ... | false | E.cs:153:13:153:16 | access to local variable obj2 | E.cs:153:21:153:24 | null | @@ -216,6 +219,7 @@ | E.cs:293:13:293:24 | ... == ... | true | E.cs:293:15:293:19 | call to method M2 | E.cs:293:24:293:24 | (...) ... | | E.cs:293:13:293:24 | ... == ... | true | E.cs:293:24:293:24 | (...) ... | E.cs:293:15:293:19 | call to method M2 | | E.cs:321:13:321:30 | ... is ... | true | E.cs:321:14:321:21 | ... ?? ... | E.cs:321:27:321:30 | null | +| E.cs:321:13:321:30 | ... is ... | true | E.cs:321:27:321:30 | null | E.cs:321:14:321:21 | ... ?? ... | | E.cs:355:13:355:21 | dynamic call to operator != | false | E.cs:355:13:355:13 | access to local variable x | E.cs:355:18:355:21 | null | | E.cs:355:13:355:21 | dynamic call to operator != | false | E.cs:355:18:355:21 | null | E.cs:355:13:355:13 | access to local variable x | | E.cs:362:13:362:29 | ... != ... | false | E.cs:362:13:362:13 | access to local variable x | E.cs:362:18:362:29 | (...) ... | From 73d2d9b1f86fafee048949059ef5d74b5400b9c7 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 21 Sep 2020 17:32:22 +0200 Subject: [PATCH 047/185] Python: Make constructor calls post-update nodes --- .../dataflow/internal/DataFlowPrivate.qll | 101 +++++++---- .../dataflow/internal/DataFlowPublic.qll | 6 +- .../coverage/classesCallGraph.expected | 162 +----------------- .../dataflow/coverage/dataflow.expected | 12 ++ .../dataflow/coverage/datamodel.py | 2 +- .../dataflow/fieldflow/allLocalFlow.expected | 4 - .../dataflow/fieldflow/dataflow.expected | 16 +- .../fieldflow/dataflowExplore.expected | 8 +- .../dataflow/fieldflow/globalStep.expected | 121 ++++++------- .../dataflow/fieldflow/localFlow.expected | 2 - .../dataflow/fieldflow/postupdates.expected | 20 +-- 11 files changed, 168 insertions(+), 286 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index b598c25a5e3..22d483bea7a 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -16,18 +16,42 @@ class DataFlowCfgNode extends ControlFlowNode { DataFlowCfgNode() { isExpressionNode(this) } } -/** A data flow node which should have an associated post-update node. */ -abstract class PreUpdateNode extends Node { +/** A data flow node for which we should synthesise an associated pre-update node. */ +abstract class NeedsSyntheticPreUpdateNode extends Node { + abstract string label(); +} + +class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode { + NeedsSyntheticPreUpdateNode post; + + SyntheticPreUpdateNode() { this = TSyntheticPreUpdateNode(post) } + + /** Gets the node for which this is a synthetic pre-update node. */ + Node getPostUpdateNode() { result = post } + + override string toString() { result = "[pre " + post.label() + "] " + post.toString() } + + override Scope getScope() { result = post.getScope() } + + override Location getLocation() { result = post.getLocation() } +} + +/** A data flow node for which we should synthesise an associated post-update node. */ +abstract class NeedsSyntheticPostUpdateNode extends Node { abstract string label(); } /** An argument might have its value changed as a result of a call. */ -class ArgumentPreUpdateNode extends PreUpdateNode, ExplicitArgumentNode { +class ArgumentPreUpdateNode extends NeedsSyntheticPostUpdateNode, ArgumentNode { + // Certain arguments, such as implicit self arguments are already post-update nodes + // and should not have an extra node synthesised. + ArgumentPreUpdateNode() { this.isNotPostUpdate() } + override string label() { result = "arg" } } /** An object might have its value changed after a store. */ -class StorePreUpdateNode extends PreUpdateNode, CfgNode { +class StorePreUpdateNode extends NeedsSyntheticPostUpdateNode, CfgNode { StorePreUpdateNode() { exists(Attribute a | node = a.getObject().getAFlowNode() and @@ -39,7 +63,7 @@ class StorePreUpdateNode extends PreUpdateNode, CfgNode { } /** A node marking the state change of an object after a read. */ -class ReadPreUpdateNode extends PreUpdateNode, CfgNode { +class ReadPreUpdateNode extends NeedsSyntheticPostUpdateNode, CfgNode { ReadPreUpdateNode() { exists(Attribute a | node = a.getObject().getAFlowNode() and @@ -58,16 +82,21 @@ class ReadPreUpdateNode extends PreUpdateNode, CfgNode { * (which might have mutated the argument), or the qualifier of a field after * an update to the field. * - * Nodes corresponding to AST elements, for example `ExprNode`, usually refer - * to the value before the update. + * Nodes corresponding to AST elements, for example `ExprNode`s, usually refer + * to the value before the update with the exception of `ObjectCreationNode`s, + * which represents the value after the constructor has run. */ -class PostUpdateNode extends Node, TPostUpdateNode { - PreUpdateNode pre; - - PostUpdateNode() { this = TPostUpdateNode(pre) } - +abstract class PostUpdateNode extends Node { /** Gets the node before the state update. */ - Node getPreUpdateNode() { result = pre } + abstract Node getPreUpdateNode(); +} + +class SyntheticPostUpdateNode extends PostUpdateNode, TSyntheticPostUpdateNode { + NeedsSyntheticPostUpdateNode pre; + + SyntheticPostUpdateNode() { this = TSyntheticPostUpdateNode(pre) } + + override Node getPreUpdateNode() { result = pre } override string toString() { result = "[post " + pre.label() + "] " + pre.toString() } @@ -76,6 +105,14 @@ class PostUpdateNode extends Node, TPostUpdateNode { override Location getLocation() { result = pre.getLocation() } } +class ObjectCreationNode extends PostUpdateNode, NeedsSyntheticPreUpdateNode, CfgNode { + ObjectCreationNode() { node.(CallNode) = any(ClassValue c).getACall() } + + override Node getPreUpdateNode() { result.(SyntheticPreUpdateNode).getPostUpdateNode() = this } + + override string label() { result = "objCreate" } +} + class DataFlowExpr = Expr; /** @@ -199,7 +236,7 @@ abstract class DataFlowCall extends TDataFlowCall { abstract DataFlowCallable getCallable(); /** Get the specified argument to this call. */ - abstract ControlFlowNode getArg(int n); + abstract Node getArg(int n); /** Get the control flow node representing this call. */ abstract ControlFlowNode getNode(); @@ -220,7 +257,7 @@ class CallNodeCall extends DataFlowCall, TCallNode { override string toString() { result = call.toString() } - override ControlFlowNode getArg(int n) { result = call.getArg(n) } + override Node getArg(int n) { result = TCfgNode(call.getArg(n)) } override ControlFlowNode getNode() { result = call } @@ -241,10 +278,10 @@ class ClassCall extends DataFlowCall, TClassCall { override string toString() { result = call.toString() } - override ControlFlowNode getArg(int n) { - result = call.getArg(n - 1) + override Node getArg(int n) { + n > 0 and result = TCfgNode(call.getArg(n - 1)) or - n = 0 and result = call + n = 0 and result = TSyntheticPreUpdateNode(TCfgNode(call)) } override ControlFlowNode getNode() { result = call } @@ -267,7 +304,7 @@ class SpecialCall extends DataFlowCall, TSpecialCall { override string toString() { result = special.toString() } - override ControlFlowNode getArg(int n) { result = special.(SpecialMethod::Potential).getArg(n) } + override Node getArg(int n) { result = TCfgNode(special.(SpecialMethod::Potential).getArg(n)) } override ControlFlowNode getNode() { result = special } @@ -279,23 +316,23 @@ class SpecialCall extends DataFlowCall, TSpecialCall { } /** A data flow node that represents a call argument. */ -abstract class ArgumentNode extends CfgNode { - /** Holds if this argument occurs at the given position in the given call. */ - abstract predicate argumentOf(DataFlowCall call, int pos); - - /** Gets the call in which this node is an argument. */ - abstract DataFlowCall getCall(); -} - -/** A data flow node that represents a call argument. */ -class ExplicitArgumentNode extends ArgumentNode { - ExplicitArgumentNode() { exists(DataFlowCall call, int pos | node = call.getArg(pos)) } +class ArgumentNode extends Node { + ArgumentNode() { this = any(DataFlowCall c).getArg(_) } /** Holds if this argument occurs at the given position in the given call. */ - override predicate argumentOf(DataFlowCall call, int pos) { node = call.getArg(pos) } + predicate argumentOf(DataFlowCall call, int pos) { this = call.getArg(pos) } /** Gets the call in which this node is an argument. */ - final override DataFlowCall getCall() { this.argumentOf(result, _) } + final DataFlowCall getCall() { this.argumentOf(result, _) } + + predicate isNotPostUpdate() { + // Avoid argument 0 of class calls as those have non-synthetic post-update nodes. + exists(CallNodeCall c | this = c.getArg(_)) + or + exists(ClassCall c, int n | n > 0 | this = c.getArg(n)) + or + exists(SpecialCall c | this = c.getArg(_)) + } } /** Gets a viable run-time target for the call `call`. */ diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index e9e143ca7ad..038721d6f52 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -23,8 +23,10 @@ newtype TNode = TEssaNode(EssaVariable var) or /** A node corresponding to a control flow node. */ TCfgNode(DataFlowCfgNode node) or - /** A node representing the value of an object after a state change */ - TPostUpdateNode(PreUpdateNode pre) + /** A synthetic node representing the value of an object before a state change */ + TSyntheticPreUpdateNode(NeedsSyntheticPreUpdateNode post) or + /** A synthetic node representing the value of an object after a state change */ + TSyntheticPostUpdateNode(NeedsSyntheticPostUpdateNode pre) /** * An element, viewed as a node in a data flow graph. Either an SSA variable diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected index a34e34424a7..cf688c17a49 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected @@ -1,200 +1,40 @@ -| classes.py:41:16:41:22 | ControlFlowNode for super() | classes.py:41:16:41:22 | ControlFlowNode for super() | | classes.py:41:16:41:35 | ControlFlowNode for Attribute() | classes.py:41:16:41:35 | ControlFlowNode for Attribute() | -| classes.py:45:16:45:25 | ControlFlowNode for With_new() | classes.py:45:16:45:25 | ControlFlowNode for With_new() | -| classes.py:58:17:58:27 | ControlFlowNode for With_init() | classes.py:52:18:52:21 | SSA variable self | -| classes.py:58:17:58:27 | ControlFlowNode for With_init() | classes.py:58:17:58:27 | ControlFlowNode for With_init() | -| classes.py:71:16:71:25 | ControlFlowNode for With_del() | classes.py:71:16:71:25 | ControlFlowNode for With_del() | -| classes.py:86:17:86:27 | ControlFlowNode for With_repr() | classes.py:86:17:86:27 | ControlFlowNode for With_repr() | -| classes.py:101:16:101:25 | ControlFlowNode for With_str() | classes.py:101:16:101:25 | ControlFlowNode for With_str() | -| classes.py:102:5:102:17 | ControlFlowNode for str() | classes.py:102:5:102:17 | ControlFlowNode for str() | -| classes.py:116:18:116:29 | ControlFlowNode for With_bytes() | classes.py:116:18:116:29 | ControlFlowNode for With_bytes() | -| classes.py:117:5:117:21 | ControlFlowNode for bytes() | classes.py:117:5:117:21 | ControlFlowNode for bytes() | -| classes.py:132:19:132:31 | ControlFlowNode for With_format() | classes.py:132:19:132:31 | ControlFlowNode for With_format() | -| classes.py:138:19:138:31 | ControlFlowNode for With_format() | classes.py:138:19:138:31 | ControlFlowNode for With_format() | -| classes.py:143:19:143:31 | ControlFlowNode for With_format() | classes.py:143:19:143:31 | ControlFlowNode for With_format() | -| classes.py:159:15:159:23 | ControlFlowNode for With_lt() | classes.py:159:15:159:23 | ControlFlowNode for With_lt() | -| classes.py:176:15:176:23 | ControlFlowNode for With_le() | classes.py:176:15:176:23 | ControlFlowNode for With_le() | -| classes.py:193:15:193:23 | ControlFlowNode for With_eq() | classes.py:193:15:193:23 | ControlFlowNode for With_eq() | -| classes.py:209:15:209:23 | ControlFlowNode for With_ne() | classes.py:209:15:209:23 | ControlFlowNode for With_ne() | -| classes.py:225:15:225:23 | ControlFlowNode for With_gt() | classes.py:225:15:225:23 | ControlFlowNode for With_gt() | -| classes.py:242:15:242:23 | ControlFlowNode for With_ge() | classes.py:242:15:242:23 | ControlFlowNode for With_ge() | -| classes.py:258:17:258:27 | ControlFlowNode for With_hash() | classes.py:258:17:258:27 | ControlFlowNode for With_hash() | -| classes.py:263:17:263:27 | ControlFlowNode for With_hash() | classes.py:263:17:263:27 | ControlFlowNode for With_hash() | +| classes.py:58:17:58:27 | [pre objCreate] ControlFlowNode for With_init() | classes.py:52:18:52:21 | SSA variable self | | classes.py:264:9:264:24 | ControlFlowNode for set() | classes.py:264:9:264:24 | ControlFlowNode for set() | -| classes.py:268:17:268:27 | ControlFlowNode for With_hash() | classes.py:268:17:268:27 | ControlFlowNode for With_hash() | | classes.py:269:9:269:30 | ControlFlowNode for frozenset() | classes.py:269:9:269:30 | ControlFlowNode for frozenset() | -| classes.py:273:17:273:27 | ControlFlowNode for With_hash() | classes.py:273:17:273:27 | ControlFlowNode for With_hash() | | classes.py:274:9:274:28 | ControlFlowNode for dict() | classes.py:274:9:274:28 | ControlFlowNode for dict() | -| classes.py:288:17:288:27 | ControlFlowNode for With_bool() | classes.py:288:17:288:27 | ControlFlowNode for With_bool() | -| classes.py:289:5:289:19 | ControlFlowNode for bool() | classes.py:289:5:289:19 | ControlFlowNode for bool() | -| classes.py:293:17:293:27 | ControlFlowNode for With_bool() | classes.py:293:17:293:27 | ControlFlowNode for With_bool() | -| classes.py:311:20:311:33 | ControlFlowNode for With_getattr() | classes.py:311:20:311:33 | ControlFlowNode for With_getattr() | -| classes.py:327:25:327:43 | ControlFlowNode for With_getattribute() | classes.py:327:25:327:43 | ControlFlowNode for With_getattribute() | -| classes.py:343:20:343:33 | ControlFlowNode for With_setattr() | classes.py:343:20:343:33 | ControlFlowNode for With_setattr() | -| classes.py:359:20:359:33 | ControlFlowNode for With_delattr() | classes.py:359:20:359:33 | ControlFlowNode for With_delattr() | -| classes.py:374:16:374:25 | ControlFlowNode for With_dir() | classes.py:374:16:374:25 | ControlFlowNode for With_dir() | -| classes.py:399:16:399:25 | ControlFlowNode for With_get() | classes.py:399:16:399:25 | ControlFlowNode for With_get() | -| classes.py:401:12:401:17 | ControlFlowNode for arg3() | classes.py:401:12:401:17 | ControlFlowNode for arg3() | -| classes.py:417:16:417:25 | ControlFlowNode for With_set() | classes.py:417:16:417:25 | ControlFlowNode for With_set() | -| classes.py:419:12:419:18 | ControlFlowNode for Owner() | classes.py:419:12:419:18 | ControlFlowNode for Owner() | -| classes.py:435:19:435:31 | ControlFlowNode for With_delete() | classes.py:435:19:435:31 | ControlFlowNode for With_delete() | -| classes.py:437:12:437:18 | ControlFlowNode for Owner() | classes.py:437:12:437:18 | ControlFlowNode for Owner() | -| classes.py:453:21:453:35 | ControlFlowNode for With_set_name() | classes.py:453:21:453:35 | ControlFlowNode for With_set_name() | -| classes.py:454:5:454:53 | ControlFlowNode for type() | classes.py:454:5:454:53 | ControlFlowNode for type() | | classes.py:454:29:454:52 | ControlFlowNode for dict() | classes.py:454:29:454:52 | ControlFlowNode for dict() | -| classes.py:473:5:473:47 | ControlFlowNode for type() | classes.py:473:5:473:47 | ControlFlowNode for type() | -| classes.py:513:26:513:45 | ControlFlowNode for With_instancecheck() | classes.py:513:26:513:45 | ControlFlowNode for With_instancecheck() | -| classes.py:530:26:530:45 | ControlFlowNode for With_subclasscheck() | classes.py:530:26:530:45 | ControlFlowNode for With_subclasscheck() | -| classes.py:548:26:548:51 | ControlFlowNode for Subscript() | classes.py:548:26:548:51 | ControlFlowNode for Subscript() | -| classes.py:561:17:561:27 | ControlFlowNode for With_call() | classes.py:561:17:561:27 | ControlFlowNode for With_call() | -| classes.py:577:16:577:25 | ControlFlowNode for With_len() | classes.py:577:16:577:25 | ControlFlowNode for With_len() | -| classes.py:582:16:582:25 | ControlFlowNode for With_len() | classes.py:582:16:582:25 | ControlFlowNode for With_len() | -| classes.py:583:5:583:18 | ControlFlowNode for bool() | classes.py:583:5:583:18 | ControlFlowNode for bool() | -| classes.py:587:16:587:25 | ControlFlowNode for With_len() | classes.py:587:16:587:25 | ControlFlowNode for With_len() | -| classes.py:604:24:604:41 | ControlFlowNode for With_length_hint() | classes.py:604:24:604:41 | ControlFlowNode for With_length_hint() | -| classes.py:620:20:620:33 | ControlFlowNode for With_getitem() | classes.py:612:21:612:24 | SSA variable self | -| classes.py:620:20:620:33 | ControlFlowNode for With_getitem() | classes.py:620:20:620:33 | ControlFlowNode for With_getitem() | | classes.py:622:5:622:16 | ControlFlowNode for with_getitem | classes.py:612:21:612:24 | SSA variable self | | classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:612:27:612:29 | SSA variable key | -| classes.py:637:20:637:33 | ControlFlowNode for With_setitem() | classes.py:629:21:629:24 | SSA variable self | -| classes.py:637:20:637:33 | ControlFlowNode for With_setitem() | classes.py:637:20:637:33 | ControlFlowNode for With_setitem() | | classes.py:640:5:640:16 | ControlFlowNode for with_setitem | classes.py:629:21:629:24 | SSA variable self | | classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:629:27:629:29 | SSA variable key | | classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:629:32:629:36 | SSA variable value | -| classes.py:654:20:654:33 | ControlFlowNode for With_delitem() | classes.py:647:21:647:24 | SSA variable self | -| classes.py:654:20:654:33 | ControlFlowNode for With_delitem() | classes.py:654:20:654:33 | ControlFlowNode for With_delitem() | | classes.py:656:9:656:20 | ControlFlowNode for with_delitem | classes.py:647:21:647:24 | SSA variable self | | classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:647:27:647:29 | SSA variable key | -| classes.py:671:20:671:33 | ControlFlowNode for With_missing() | classes.py:671:20:671:33 | ControlFlowNode for With_missing() | | classes.py:683:16:683:28 | ControlFlowNode for Attribute() | classes.py:683:16:683:28 | ControlFlowNode for Attribute() | -| classes.py:687:17:687:27 | ControlFlowNode for With_iter() | classes.py:687:17:687:27 | ControlFlowNode for With_iter() | -| classes.py:702:21:702:35 | ControlFlowNode for With_reversed() | classes.py:702:21:702:35 | ControlFlowNode for With_reversed() | -| classes.py:703:5:703:27 | ControlFlowNode for reversed() | classes.py:703:5:703:27 | ControlFlowNode for reversed() | -| classes.py:718:21:718:35 | ControlFlowNode for With_contains() | classes.py:718:21:718:35 | ControlFlowNode for With_contains() | -| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:727:17:727:20 | SSA variable self | -| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:727:23:727:27 | SSA variable other | -| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:735:16:735:25 | ControlFlowNode for With_add() | | classes.py:737:5:737:12 | ControlFlowNode for with_add | classes.py:727:17:727:20 | SSA variable self | | classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:727:23:727:27 | SSA variable other | -| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:744:17:744:20 | SSA variable self | -| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:744:23:744:27 | SSA variable other | -| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:752:16:752:25 | ControlFlowNode for With_sub() | | classes.py:754:5:754:12 | ControlFlowNode for with_sub | classes.py:744:17:744:20 | SSA variable self | | classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:744:23:744:27 | SSA variable other | -| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:761:17:761:20 | SSA variable self | -| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:761:23:761:27 | SSA variable other | -| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:769:16:769:25 | ControlFlowNode for With_mul() | | classes.py:771:5:771:12 | ControlFlowNode for with_mul | classes.py:761:17:761:20 | SSA variable self | | classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:761:23:761:27 | SSA variable other | -| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:778:20:778:23 | SSA variable self | -| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:778:26:778:30 | SSA variable other | -| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | | classes.py:788:5:788:15 | ControlFlowNode for with_matmul | classes.py:778:20:778:23 | SSA variable self | | classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:778:26:778:30 | SSA variable other | -| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:795:21:795:24 | SSA variable self | -| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:795:27:795:31 | SSA variable other | -| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | | classes.py:805:5:805:16 | ControlFlowNode for with_truediv | classes.py:795:21:795:24 | SSA variable self | | classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:795:27:795:31 | SSA variable other | -| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:812:22:812:25 | SSA variable self | -| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:812:28:812:32 | SSA variable other | -| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | | classes.py:822:5:822:17 | ControlFlowNode for with_floordiv | classes.py:812:22:812:25 | SSA variable self | | classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:812:28:812:32 | SSA variable other | -| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:829:17:829:20 | SSA variable self | -| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:829:23:829:27 | SSA variable other | -| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:837:16:837:25 | ControlFlowNode for With_mod() | | classes.py:839:5:839:12 | ControlFlowNode for with_mod | classes.py:829:17:829:20 | SSA variable self | | classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:829:23:829:27 | SSA variable other | -| classes.py:854:19:854:31 | ControlFlowNode for With_divmod() | classes.py:854:19:854:31 | ControlFlowNode for With_divmod() | -| classes.py:871:16:871:25 | ControlFlowNode for With_pow() | classes.py:871:16:871:25 | ControlFlowNode for With_pow() | -| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:863:17:863:20 | SSA variable self | -| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:863:23:863:27 | SSA variable other | -| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:877:16:877:25 | ControlFlowNode for With_pow() | | classes.py:879:5:879:12 | ControlFlowNode for with_pow | classes.py:863:17:863:20 | SSA variable self | | classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:863:23:863:27 | SSA variable other | -| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:886:20:886:23 | SSA variable self | -| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:886:26:886:30 | SSA variable other | -| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | | classes.py:896:5:896:15 | ControlFlowNode for with_lshift | classes.py:886:20:886:23 | SSA variable self | | classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:886:26:886:30 | SSA variable other | -| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:903:20:903:23 | SSA variable self | -| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:903:26:903:30 | SSA variable other | -| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | | classes.py:913:5:913:15 | ControlFlowNode for with_rshift | classes.py:903:20:903:23 | SSA variable self | | classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:903:26:903:30 | SSA variable other | -| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:920:17:920:20 | SSA variable self | -| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:920:23:920:27 | SSA variable other | -| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:928:16:928:25 | ControlFlowNode for With_and() | | classes.py:930:5:930:12 | ControlFlowNode for with_and | classes.py:920:17:920:20 | SSA variable self | | classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:920:23:920:27 | SSA variable other | -| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:937:17:937:20 | SSA variable self | -| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:937:23:937:27 | SSA variable other | -| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:945:16:945:25 | ControlFlowNode for With_xor() | | classes.py:947:5:947:12 | ControlFlowNode for with_xor | classes.py:937:17:937:20 | SSA variable self | | classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:937:23:937:27 | SSA variable other | -| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:954:16:954:19 | SSA variable self | -| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:954:22:954:26 | SSA variable other | -| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:962:15:962:23 | ControlFlowNode for With_or() | | classes.py:964:5:964:11 | ControlFlowNode for with_or | classes.py:954:16:954:19 | SSA variable self | | classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:954:22:954:26 | SSA variable other | -| classes.py:979:17:979:27 | ControlFlowNode for With_radd() | classes.py:979:17:979:27 | ControlFlowNode for With_radd() | -| classes.py:996:17:996:27 | ControlFlowNode for With_rsub() | classes.py:996:17:996:27 | ControlFlowNode for With_rsub() | -| classes.py:1013:17:1013:27 | ControlFlowNode for With_rmul() | classes.py:1013:17:1013:27 | ControlFlowNode for With_rmul() | -| classes.py:1030:20:1030:33 | ControlFlowNode for With_rmatmul() | classes.py:1030:20:1030:33 | ControlFlowNode for With_rmatmul() | -| classes.py:1047:21:1047:35 | ControlFlowNode for With_rtruediv() | classes.py:1047:21:1047:35 | ControlFlowNode for With_rtruediv() | -| classes.py:1064:22:1064:37 | ControlFlowNode for With_rfloordiv() | classes.py:1064:22:1064:37 | ControlFlowNode for With_rfloordiv() | -| classes.py:1081:17:1081:27 | ControlFlowNode for With_rmod() | classes.py:1081:17:1081:27 | ControlFlowNode for With_rmod() | -| classes.py:1098:20:1098:33 | ControlFlowNode for With_rdivmod() | classes.py:1098:20:1098:33 | ControlFlowNode for With_rdivmod() | -| classes.py:1115:17:1115:27 | ControlFlowNode for With_rpow() | classes.py:1115:17:1115:27 | ControlFlowNode for With_rpow() | -| classes.py:1121:17:1121:27 | ControlFlowNode for With_rpow() | classes.py:1121:17:1121:27 | ControlFlowNode for With_rpow() | -| classes.py:1138:20:1138:33 | ControlFlowNode for With_rlshift() | classes.py:1138:20:1138:33 | ControlFlowNode for With_rlshift() | -| classes.py:1155:20:1155:33 | ControlFlowNode for With_rrshift() | classes.py:1155:20:1155:33 | ControlFlowNode for With_rrshift() | -| classes.py:1172:17:1172:27 | ControlFlowNode for With_rand() | classes.py:1172:17:1172:27 | ControlFlowNode for With_rand() | -| classes.py:1189:17:1189:27 | ControlFlowNode for With_rxor() | classes.py:1189:17:1189:27 | ControlFlowNode for With_rxor() | -| classes.py:1206:16:1206:25 | ControlFlowNode for With_ror() | classes.py:1206:16:1206:25 | ControlFlowNode for With_ror() | -| classes.py:1223:17:1223:27 | ControlFlowNode for With_iadd() | classes.py:1223:17:1223:27 | ControlFlowNode for With_iadd() | -| classes.py:1240:17:1240:27 | ControlFlowNode for With_isub() | classes.py:1240:17:1240:27 | ControlFlowNode for With_isub() | -| classes.py:1257:17:1257:27 | ControlFlowNode for With_imul() | classes.py:1257:17:1257:27 | ControlFlowNode for With_imul() | -| classes.py:1274:20:1274:33 | ControlFlowNode for With_imatmul() | classes.py:1274:20:1274:33 | ControlFlowNode for With_imatmul() | -| classes.py:1291:21:1291:35 | ControlFlowNode for With_itruediv() | classes.py:1291:21:1291:35 | ControlFlowNode for With_itruediv() | -| classes.py:1308:22:1308:37 | ControlFlowNode for With_ifloordiv() | classes.py:1308:22:1308:37 | ControlFlowNode for With_ifloordiv() | -| classes.py:1325:17:1325:27 | ControlFlowNode for With_imod() | classes.py:1325:17:1325:27 | ControlFlowNode for With_imod() | -| classes.py:1342:17:1342:27 | ControlFlowNode for With_ipow() | classes.py:1342:17:1342:27 | ControlFlowNode for With_ipow() | -| classes.py:1359:20:1359:33 | ControlFlowNode for With_ilshift() | classes.py:1359:20:1359:33 | ControlFlowNode for With_ilshift() | -| classes.py:1376:20:1376:33 | ControlFlowNode for With_irshift() | classes.py:1376:20:1376:33 | ControlFlowNode for With_irshift() | -| classes.py:1393:17:1393:27 | ControlFlowNode for With_iand() | classes.py:1393:17:1393:27 | ControlFlowNode for With_iand() | -| classes.py:1410:17:1410:27 | ControlFlowNode for With_ixor() | classes.py:1410:17:1410:27 | ControlFlowNode for With_ixor() | -| classes.py:1427:16:1427:25 | ControlFlowNode for With_ior() | classes.py:1427:16:1427:25 | ControlFlowNode for With_ior() | -| classes.py:1443:16:1443:25 | ControlFlowNode for With_neg() | classes.py:1443:16:1443:25 | ControlFlowNode for With_neg() | -| classes.py:1458:16:1458:25 | ControlFlowNode for With_pos() | classes.py:1458:16:1458:25 | ControlFlowNode for With_pos() | -| classes.py:1473:16:1473:25 | ControlFlowNode for With_abs() | classes.py:1473:16:1473:25 | ControlFlowNode for With_abs() | -| classes.py:1488:19:1488:31 | ControlFlowNode for With_invert() | classes.py:1488:19:1488:31 | ControlFlowNode for With_invert() | -| classes.py:1503:20:1503:33 | ControlFlowNode for With_complex() | classes.py:1503:20:1503:33 | ControlFlowNode for With_complex() | -| classes.py:1504:5:1504:25 | ControlFlowNode for complex() | classes.py:1504:5:1504:25 | ControlFlowNode for complex() | -| classes.py:1518:16:1518:25 | ControlFlowNode for With_int() | classes.py:1518:16:1518:25 | ControlFlowNode for With_int() | -| classes.py:1519:5:1519:17 | ControlFlowNode for int() | classes.py:1519:5:1519:17 | ControlFlowNode for int() | -| classes.py:1533:18:1533:29 | ControlFlowNode for With_float() | classes.py:1533:18:1533:29 | ControlFlowNode for With_float() | -| classes.py:1534:5:1534:21 | ControlFlowNode for float() | classes.py:1534:5:1534:21 | ControlFlowNode for float() | -| classes.py:1549:18:1549:29 | ControlFlowNode for With_index() | classes.py:1549:18:1549:29 | ControlFlowNode for With_index() | -| classes.py:1554:18:1554:29 | ControlFlowNode for With_index() | classes.py:1554:18:1554:29 | ControlFlowNode for With_index() | -| classes.py:1559:18:1559:29 | ControlFlowNode for With_index() | classes.py:1559:18:1559:29 | ControlFlowNode for With_index() | -| classes.py:1564:18:1564:29 | ControlFlowNode for With_index() | classes.py:1564:18:1564:29 | ControlFlowNode for With_index() | -| classes.py:1569:18:1569:29 | ControlFlowNode for With_index() | classes.py:1569:18:1569:29 | ControlFlowNode for With_index() | -| classes.py:1574:18:1574:29 | ControlFlowNode for With_index() | classes.py:1574:18:1574:29 | ControlFlowNode for With_index() | -| classes.py:1575:5:1575:19 | ControlFlowNode for int() | classes.py:1575:5:1575:19 | ControlFlowNode for int() | -| classes.py:1579:18:1579:29 | ControlFlowNode for With_index() | classes.py:1579:18:1579:29 | ControlFlowNode for With_index() | -| classes.py:1580:5:1580:21 | ControlFlowNode for float() | classes.py:1580:5:1580:21 | ControlFlowNode for float() | -| classes.py:1584:18:1584:29 | ControlFlowNode for With_index() | classes.py:1584:18:1584:29 | ControlFlowNode for With_index() | -| classes.py:1585:5:1585:23 | ControlFlowNode for complex() | classes.py:1585:5:1585:23 | ControlFlowNode for complex() | -| classes.py:1599:18:1599:29 | ControlFlowNode for With_round() | classes.py:1599:18:1599:29 | ControlFlowNode for With_round() | -| classes.py:1614:18:1614:29 | ControlFlowNode for With_trunc() | classes.py:1614:18:1614:29 | ControlFlowNode for With_trunc() | -| classes.py:1630:18:1630:29 | ControlFlowNode for With_floor() | classes.py:1630:18:1630:29 | ControlFlowNode for With_floor() | -| classes.py:1646:17:1646:27 | ControlFlowNode for With_ceil() | classes.py:1646:17:1646:27 | ControlFlowNode for With_ceil() | -| classes.py:1665:10:1665:21 | ControlFlowNode for With_enter() | classes.py:1665:10:1665:21 | ControlFlowNode for With_enter() | -| classes.py:1686:10:1686:20 | ControlFlowNode for With_exit() | classes.py:1686:10:1686:20 | ControlFlowNode for With_exit() | -| classes.py:1703:18:1703:29 | ControlFlowNode for With_await() | classes.py:1703:18:1703:29 | ControlFlowNode for With_await() | -| classes.py:1726:18:1726:29 | ControlFlowNode for With_aiter() | classes.py:1726:18:1726:29 | ControlFlowNode for With_aiter() | -| classes.py:1745:18:1745:29 | ControlFlowNode for With_anext() | classes.py:1745:18:1745:29 | ControlFlowNode for With_anext() | -| classes.py:1763:19:1763:31 | ControlFlowNode for With_aenter() | classes.py:1763:19:1763:31 | ControlFlowNode for With_aenter() | -| classes.py:1784:18:1784:29 | ControlFlowNode for With_aexit() | classes.py:1784:18:1784:29 | ControlFlowNode for With_aexit() | diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index 0b1dfb6a5be..32020110bcd 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -13,6 +13,11 @@ edges | datamodel.py:80:6:80:26 | GSSA Variable SOURCE | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | +| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | +| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | +| datamodel.py:155:1:155:10 | GSSA Variable customized [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | +| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:155:1:155:10 | GSSA Variable customized [Attribute b] | +| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | | test.py:36:10:36:26 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:37:9:37:9 | ControlFlowNode for x [Tuple element at index 1] | | test.py:36:21:36:26 | ControlFlowNode for SOURCE | test.py:36:10:36:26 | ControlFlowNode for Tuple [Tuple element at index 1] | | test.py:37:9:37:9 | ControlFlowNode for x [Tuple element at index 1] | test.py:37:9:37:12 | ControlFlowNode for Subscript | @@ -127,6 +132,12 @@ nodes | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | semmle.label | [post store] ControlFlowNode for self [Attribute b] | +| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| datamodel.py:155:1:155:10 | GSSA Variable customized [Attribute b] | semmle.label | GSSA Variable customized [Attribute b] | +| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] | +| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] | +| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | test.py:36:10:36:26 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | | test.py:36:21:36:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:37:9:37:9 | ControlFlowNode for x [Tuple element at index 1] | semmle.label | ControlFlowNode for x [Tuple element at index 1] | @@ -263,6 +274,7 @@ nodes | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | | | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | | +| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | | | test.py:38:10:38:10 | ControlFlowNode for y | test.py:36:21:36:26 | ControlFlowNode for SOURCE | test.py:38:10:38:10 | ControlFlowNode for y | | | test.py:51:10:51:10 | ControlFlowNode for x | test.py:50:9:50:14 | ControlFlowNode for SOURCE | test.py:51:10:51:10 | ControlFlowNode for x | | | test.py:58:10:58:10 | ControlFlowNode for x | test.py:57:9:57:16 | ControlFlowNode for Str | test.py:58:10:58:10 | ControlFlowNode for x | | diff --git a/python/ql/test/experimental/dataflow/coverage/datamodel.py b/python/ql/test/experimental/dataflow/coverage/datamodel.py index a3fc54d11a0..f5f3680dd55 100644 --- a/python/ql/test/experimental/dataflow/coverage/datamodel.py +++ b/python/ql/test/experimental/dataflow/coverage/datamodel.py @@ -156,4 +156,4 @@ customized = Customized() SINK(Customized.a) SINK_F(Customized.b) SINK(customized.a) -SINK(customized.b) +SINK(customized.b) # Flow found diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected index 7cc18f44d0b..ed0e6a4ea1d 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected @@ -18,7 +18,6 @@ | test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj | | test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | | test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x | @@ -26,7 +25,6 @@ | test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a | | test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | @@ -34,11 +32,9 @@ | test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | | test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | | test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | | test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | | test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected index d6b2ba62757..c56353dbd4f 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected @@ -1,4 +1,8 @@ edges +| examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] | examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | +| examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] | +| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | examples.py:42:6:42:12 | ControlFlowNode for Attribute | | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | @@ -9,11 +13,16 @@ edges | test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | -| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | | test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | nodes +| examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] | semmle.label | GSSA Variable obj [Attribute foo] | +| examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | +| examples.py:42:6:42:12 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] | @@ -27,13 +36,14 @@ nodes | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | | test.py:41:10:41:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | semmle.label | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | | test.py:45:17:45:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | | test.py:46:10:46:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | | test.py:56:33:56:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | #select +| examples.py:42:6:42:12 | ControlFlowNode for Attribute | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:42:6:42:12 | ControlFlowNode for Attribute | | | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | | | test.py:30:10:30:18 | ControlFlowNode for Attribute | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:30:10:30:18 | ControlFlowNode for Attribute | | | test.py:41:10:41:18 | ControlFlowNode for Attribute | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:41:10:41:18 | ControlFlowNode for Attribute | | diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected index f65f714063c..1d6e5f42120 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected @@ -21,16 +21,16 @@ edges | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, ... (2)] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | | test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | | test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected index b0fedf775a3..1add30ba17d 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected @@ -12,17 +12,18 @@ | test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | | test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | | test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | ControlFlowNode for MyObj() | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:15:20:15:30 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | | test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | | test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj | @@ -37,14 +38,17 @@ | test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | | test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | | test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | -| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | -| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | -| test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | -| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | +| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | +| test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | +| test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | | test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] | +| test.py:15:20:15:30 | ControlFlowNode for MyObj() [Attribute foo] | test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | +| test.py:15:20:15:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:15:20:15:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | | test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | | test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | +| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:15:20:15:30 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self | | test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self | | test.py:21:1:21:19 | ControlFlowNode for FunctionExpr | test.py:21:5:21:10 | GSSA Variable setFoo | @@ -101,8 +105,6 @@ | test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | | test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | | test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | | test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | | test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | @@ -111,20 +113,14 @@ | test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | | test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | | test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:27:13:27:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:27:13:27:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | | test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | | test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | -| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj | | test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | @@ -165,11 +161,12 @@ | test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | | test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | | test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | | test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self | | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | @@ -180,24 +177,22 @@ | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:36:5:36:5 | SSA variable a [Attribute obj] | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:36:5:36:5 | SSA variable a [Attribute obj] | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | +| test.py:36:9:36:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self | +| test.py:36:9:36:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | +| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:38:5:38:9 | ControlFlowNode for Attribute | | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:38:5:38:9 | ControlFlowNode for Attribute | | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | @@ -237,21 +232,17 @@ | test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | | test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | | test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | | test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | | test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | | test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | -| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:45:11:45:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:45:11:45:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | | test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow | @@ -270,26 +261,22 @@ | test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | | test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | | test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | | test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | | test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:50:11:50:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | +| test.py:50:11:50:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | | test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | | test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected index 03549a5b810..93a4d590d01 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected @@ -2,13 +2,11 @@ | examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | | examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj | | examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | -| examples.py:46:9:46:16 | [post arg] ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | | examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | | examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | | test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | | test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | | test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | | test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | | test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected index 434cd0b6bda..4dcabdd4b86 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected @@ -1,52 +1,52 @@ | examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:8:9:8:12 | ControlFlowNode for self | | examples.py:14:9:14:12 | [post store] ControlFlowNode for self | examples.py:14:9:14:12 | ControlFlowNode for self | -| examples.py:14:20:14:30 | [post arg] ControlFlowNode for MyObj() | examples.py:14:20:14:30 | ControlFlowNode for MyObj() | +| examples.py:14:20:14:30 | ControlFlowNode for MyObj() | examples.py:14:20:14:30 | [pre objCreate] ControlFlowNode for MyObj() | | examples.py:14:26:14:29 | [post arg] ControlFlowNode for Str | examples.py:14:26:14:29 | ControlFlowNode for Str | | examples.py:17:16:17:19 | [post read] ControlFlowNode for self | examples.py:17:16:17:19 | ControlFlowNode for self | | examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:22:12:22:14 | ControlFlowNode for obj | | examples.py:23:5:23:7 | [post store] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:25:9:25:19 | [post arg] ControlFlowNode for MyObj() | examples.py:25:9:25:19 | ControlFlowNode for MyObj() | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:25:9:25:19 | [pre objCreate] ControlFlowNode for MyObj() | | examples.py:25:15:25:18 | [post arg] ControlFlowNode for Str | examples.py:25:15:25:18 | ControlFlowNode for Str | | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj | | examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | | examples.py:28:6:28:10 | [post read] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | -| examples.py:33:5:33:15 | [post arg] ControlFlowNode for NestedObj() | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:33:5:33:15 | [pre objCreate] ControlFlowNode for NestedObj() | | examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:35:1:35:1 | ControlFlowNode for a | | examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute | examples.py:35:1:35:5 | ControlFlowNode for Attribute | | examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | | examples.py:36:1:36:10 | [post store] ControlFlowNode for Attribute() | examples.py:36:1:36:10 | ControlFlowNode for Attribute() | | examples.py:38:6:38:6 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | | examples.py:38:6:38:10 | [post read] ControlFlowNode for Attribute | examples.py:38:6:38:10 | ControlFlowNode for Attribute | -| examples.py:41:7:41:19 | [post arg] ControlFlowNode for MyObj() | examples.py:41:7:41:19 | ControlFlowNode for MyObj() | +| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:41:7:41:19 | [pre objCreate] ControlFlowNode for MyObj() | | examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | | examples.py:42:6:42:8 | [post read] ControlFlowNode for obj | examples.py:42:6:42:8 | ControlFlowNode for obj | -| examples.py:46:9:46:16 | [post arg] ControlFlowNode for MyObj() | examples.py:46:9:46:16 | ControlFlowNode for MyObj() | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:9:46:16 | [pre objCreate] ControlFlowNode for MyObj() | | examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x | | examples.py:47:7:47:9 | [post read] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj | | examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | | test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:9:9:9:12 | ControlFlowNode for self | | test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:15:9:15:12 | ControlFlowNode for self | -| test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() | test.py:15:20:15:30 | ControlFlowNode for MyObj() | +| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:15:20:15:30 | [pre objCreate] ControlFlowNode for MyObj() | | test.py:15:26:15:29 | [post arg] ControlFlowNode for Str | test.py:15:26:15:29 | ControlFlowNode for Str | | test.py:18:16:18:19 | [post read] ControlFlowNode for self | test.py:18:16:18:19 | ControlFlowNode for self | | test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:22:12:22:14 | ControlFlowNode for obj | | test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:13:27:23 | ControlFlowNode for MyObj() | +| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:13:27:23 | [pre objCreate] ControlFlowNode for MyObj() | | test.py:27:19:27:22 | [post arg] ControlFlowNode for Str | test.py:27:19:27:22 | ControlFlowNode for Str | | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | | test.py:29:19:29:24 | [post arg] ControlFlowNode for SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE | | test.py:30:10:30:14 | [post read] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | +| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:9:36:19 | [pre objCreate] ControlFlowNode for NestedObj() | | test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:38:5:38:5 | ControlFlowNode for a | | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute | test.py:38:5:38:9 | ControlFlowNode for Attribute | | test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | | test.py:39:5:39:14 | [post store] ControlFlowNode for Attribute() | test.py:39:5:39:14 | ControlFlowNode for Attribute() | | test.py:41:10:41:10 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | | test.py:41:10:41:14 | [post read] ControlFlowNode for Attribute | test.py:41:10:41:14 | ControlFlowNode for Attribute | -| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:11:45:23 | ControlFlowNode for MyObj() | +| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:11:45:23 | [pre objCreate] ControlFlowNode for MyObj() | | test.py:45:17:45:22 | [post arg] ControlFlowNode for SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | | test.py:46:10:46:12 | [post read] ControlFlowNode for obj | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:11:50:18 | ControlFlowNode for MyObj() | +| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:11:50:18 | [pre objCreate] ControlFlowNode for MyObj() | | test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:50:17:50:17 | ControlFlowNode for x | | test.py:51:9:51:11 | [post read] ControlFlowNode for obj | test.py:51:9:51:11 | ControlFlowNode for obj | | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE | From 08b51e67c42eb257c7e9ad76516021ae206dc07f Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 21 Sep 2020 17:44:36 +0200 Subject: [PATCH 048/185] Python: Update test annotation --- python/ql/test/experimental/dataflow/coverage/classes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 350c10d5b04..830ad72d1c5 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -50,8 +50,8 @@ def test_new(): class With_init: def __init__(self): - SINK1(self) # Flow not found - OK() # Call not found + SINK1(self) + OK() def test_init(): From e70bb20f349ccfd43df7670f290d52dfcb52d734 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 15 Sep 2020 12:25:07 +0100 Subject: [PATCH 049/185] JS: Support XML extraction when run with codeql --- .../com/semmle/js/extractor/AutoBuild.java | 29 +++++++++++++++---- .../js/extractor/EnvironmentVariables.java | 14 +++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 28c96f806f7..d97dda5daf3 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -60,6 +60,7 @@ import com.semmle.util.io.WholeIO; import com.semmle.util.io.csv.CSVReader; import com.semmle.util.language.LegacyLanguage; import com.semmle.util.process.Env; +import com.semmle.util.process.Env.OS; import com.semmle.util.projectstructure.ProjectLayout; import com.semmle.util.trap.TrapWriter; @@ -1239,11 +1240,29 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set protected void extractXml() throws IOException { if (xmlExtensions.isEmpty()) return; List cmd = new ArrayList<>(); - cmd.add("odasa"); - cmd.add("index"); - cmd.add("--xml"); - cmd.add("--extensions"); - cmd.addAll(xmlExtensions); + if (EnvironmentVariables.getCodeQLDist() == null) { + // Use the legacy odasa XML extractor + cmd.add("odasa"); + cmd.add("index"); + cmd.add("--xml"); + cmd.add("--extensions"); + cmd.addAll(xmlExtensions); + } else { + String command = Env.getOS() == OS.WINDOWS ? "codeql.cmd" : "codeql"; + cmd.add(EnvironmentVariables.getCodeQLDist() + "/" + command); + cmd.add("database"); + cmd.add("index-files"); + cmd.add("--language"); + cmd.add("xml"); + cmd.add("--size-limit"); + cmd.add("10m"); + for (String extension : xmlExtensions) { + cmd.add("--include-extension"); + cmd.add(extension); + } + cmd.add("--"); + cmd.add(EnvironmentVariables.getWipDatabase()); + } ProcessBuilder pb = new ProcessBuilder(cmd); try { pb.redirectError(Redirect.INHERIT); diff --git a/javascript/extractor/src/com/semmle/js/extractor/EnvironmentVariables.java b/javascript/extractor/src/com/semmle/js/extractor/EnvironmentVariables.java index 6f8e7124b3c..8ffcb65831c 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/EnvironmentVariables.java +++ b/javascript/extractor/src/com/semmle/js/extractor/EnvironmentVariables.java @@ -14,6 +14,11 @@ public class EnvironmentVariables { public static final String LGTM_WORKSPACE_ENV_VAR = "LGTM_WORKSPACE"; + public static final String CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE_ENV_VAR = + "CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE"; + + public static final String CODEQL_DIST_ENV_VAR = "CODEQL_DIST"; + /** * Gets the extractor root based on the CODEQL_EXTRACTOR_JAVASCRIPT_ROOT or * SEMMLE_DIST or environment variable, or null if neither is set. @@ -49,4 +54,13 @@ public class EnvironmentVariables { throw new UserError(CODEQL_EXTRACTOR_JAVASCRIPT_SCRATCH_DIR_ENV_VAR + " or " + LGTM_WORKSPACE_ENV_VAR + " must be set"); } + + public static String getCodeQLDist() { + return Env.systemEnv().getNonEmpty(CODEQL_DIST_ENV_VAR); + } + + /** Gets the output database directory. */ + public static String getWipDatabase() { + return Env.systemEnv().getNonEmpty(CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE_ENV_VAR); + } } From 4243504c8baa3330ee0a7cedd8590704039f6eff Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 21 Sep 2020 23:20:16 +0200 Subject: [PATCH 050/185] improve join-order for HTTP::isDecoratedCall --- .../ql/src/semmle/javascript/frameworks/HTTP.qll | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll index e552bbcb85c..28a79edb5e7 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll @@ -240,14 +240,22 @@ module HTTP { */ private predicate isDecoratedCall(DataFlow::CallNode call, DataFlow::FunctionNode decoratee) { // indirect route-handler `result` is given to function `outer`, which returns function `inner` which calls the function `pred`. - exists(int i, Function outer, Function inner | + exists(int i, DataFlow::FunctionNode outer, HTTP::RouteHandlerCandidate inner | decoratee = call.getArgument(i).getALocalSource() and - outer = call.getACallee() and - inner = outer.getAReturnedExpr() and - isAForwardingRouteHandlerCall(DataFlow::parameterNode(outer.getParameter(i)), inner.flow()) + outer.getFunction() = call.getACallee() and + outer = returnsARouteHandler(inner) and + isAForwardingRouteHandlerCall(outer.getParameter(i), inner) ) } + /** + * Gets a function that returns the route-handler-candidate `routeHandler`. + */ + pragma[noinline] + private DataFlow::FunctionNode returnsARouteHandler(HTTP::RouteHandlerCandidate routeHandler) { + routeHandler = result.getAReturn().getALocalSource() + } + /** * Holds if `f` looks like a route-handler and a call to `callee` inside `f` forwards all of the parameters from `f` to that call. */ From 913881b17b869602b45e37d5044949e1ba72bde8 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 21 Sep 2020 16:10:37 -0700 Subject: [PATCH 051/185] C++: Add test for iterator false positive --- .../dataflow/taint-tests/localTaint.expected | 476 ++++++++++-------- .../dataflow/taint-tests/taint.expected | 28 +- .../dataflow/taint-tests/test_diff.expected | 28 +- .../dataflow/taint-tests/vector.cpp | 14 +- 4 files changed, 298 insertions(+), 248 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index ae2bc9b563e..365216bce3c 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -3701,224 +3701,258 @@ | vector.cpp:330:3:330:6 | iter | vector.cpp:330:2:330:2 | call to operator* | TAINT | | vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:2 | call to operator* [post update] | TAINT | | vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:17 | ... = ... | | -| vector.cpp:333:38:333:38 | b | vector.cpp:368:5:368:5 | b | | -| vector.cpp:334:22:334:24 | call to vector | vector.cpp:336:34:336:35 | v1 | | -| vector.cpp:334:22:334:24 | call to vector | vector.cpp:338:7:338:8 | v1 | | -| vector.cpp:334:22:334:24 | call to vector | vector.cpp:389:1:389:1 | v1 | | -| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:38:340:39 | v2 | | -| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:56:340:57 | v2 | | -| vector.cpp:334:30:334:32 | call to vector | vector.cpp:343:7:343:8 | v2 | | -| vector.cpp:334:30:334:32 | call to vector | vector.cpp:389:1:389:1 | v2 | | -| vector.cpp:334:38:334:40 | call to vector | vector.cpp:345:15:345:16 | v3 | | -| vector.cpp:334:38:334:40 | call to vector | vector.cpp:348:7:348:8 | v3 | | -| vector.cpp:334:38:334:40 | call to vector | vector.cpp:389:1:389:1 | v3 | | -| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:38:350:39 | v4 | | -| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:56:350:57 | v4 | | -| vector.cpp:334:46:334:48 | call to vector | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:334:46:334:48 | call to vector | vector.cpp:389:1:389:1 | v4 | | -| vector.cpp:334:54:334:56 | call to vector | vector.cpp:355:34:355:35 | v5 | | -| vector.cpp:334:54:334:56 | call to vector | vector.cpp:357:7:357:8 | v5 | | -| vector.cpp:334:54:334:56 | call to vector | vector.cpp:359:7:359:8 | v5 | | -| vector.cpp:334:54:334:56 | call to vector | vector.cpp:389:1:389:1 | v5 | | -| vector.cpp:334:62:334:64 | call to vector | vector.cpp:361:34:361:35 | v6 | | -| vector.cpp:334:62:334:64 | call to vector | vector.cpp:363:7:363:8 | v6 | | -| vector.cpp:334:62:334:64 | call to vector | vector.cpp:364:2:364:3 | v6 | | -| vector.cpp:334:62:334:64 | call to vector | vector.cpp:365:7:365:8 | v6 | | -| vector.cpp:334:62:334:64 | call to vector | vector.cpp:389:1:389:1 | v6 | | -| vector.cpp:334:70:334:72 | call to vector | vector.cpp:367:34:367:35 | v7 | | -| vector.cpp:334:70:334:72 | call to vector | vector.cpp:370:8:370:9 | v7 | | -| vector.cpp:334:70:334:72 | call to vector | vector.cpp:373:8:373:9 | v7 | | -| vector.cpp:334:70:334:72 | call to vector | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:334:70:334:72 | call to vector | vector.cpp:389:1:389:1 | v7 | | -| vector.cpp:334:78:334:80 | call to vector | vector.cpp:377:34:377:35 | v8 | | -| vector.cpp:334:78:334:80 | call to vector | vector.cpp:379:7:379:8 | v8 | | -| vector.cpp:334:78:334:80 | call to vector | vector.cpp:381:7:381:8 | v8 | | -| vector.cpp:334:78:334:80 | call to vector | vector.cpp:389:1:389:1 | v8 | | -| vector.cpp:334:86:334:88 | call to vector | vector.cpp:383:34:383:35 | v9 | | -| vector.cpp:334:86:334:88 | call to vector | vector.cpp:388:7:388:8 | v9 | | -| vector.cpp:334:86:334:88 | call to vector | vector.cpp:389:1:389:1 | v9 | | -| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:338:7:338:8 | v1 | | -| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:389:1:389:1 | v1 | | -| vector.cpp:336:34:336:35 | v1 | vector.cpp:336:37:336:41 | call to begin | TAINT | -| vector.cpp:336:37:336:41 | call to begin | vector.cpp:337:3:337:4 | i1 | | -| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:338:7:338:8 | v1 | | -| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v1 | | -| vector.cpp:337:2:337:15 | ... = ... | vector.cpp:337:2:337:2 | call to operator* [post update] | | -| vector.cpp:337:3:337:4 | i1 | vector.cpp:337:2:337:2 | call to operator* | TAINT | -| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:2 | call to operator* [post update] | TAINT | -| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:15 | ... = ... | | -| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:389:1:389:1 | v1 | | -| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | | -| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | | -| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | | -| vector.cpp:340:38:340:39 | v2 | vector.cpp:340:41:340:45 | call to begin | TAINT | -| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:50:340:51 | it | | -| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:68:340:69 | it | | -| vector.cpp:340:41:340:45 | call to begin | vector.cpp:341:4:341:5 | it | | -| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | | -| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | | -| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | | -| vector.cpp:340:56:340:57 | v2 | vector.cpp:340:59:340:61 | call to end | TAINT | -| vector.cpp:340:68:340:69 | it | vector.cpp:340:66:340:66 | call to operator++ | | -| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:50:340:51 | it | | -| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:68:340:69 | it | | -| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:341:4:341:5 | it | | -| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:340:56:340:57 | v2 | | -| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:343:7:343:8 | v2 | | -| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v2 | | -| vector.cpp:341:3:341:16 | ... = ... | vector.cpp:341:3:341:3 | call to operator* [post update] | | -| vector.cpp:341:4:341:5 | it | vector.cpp:341:3:341:3 | call to operator* | TAINT | -| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:3 | call to operator* [post update] | TAINT | -| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:16 | ... = ... | | -| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | | -| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator* | TAINT | -| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator++ | | -| vector.cpp:345:15:345:15 | (__end) | vector.cpp:345:15:345:15 | call to iterator | | -| vector.cpp:345:15:345:15 | (__range) | vector.cpp:345:15:345:15 | call to begin | TAINT | -| vector.cpp:345:15:345:15 | (__range) | vector.cpp:345:15:345:15 | call to end | TAINT | -| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | | -| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | | -| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | | -| vector.cpp:345:15:345:15 | call to end | vector.cpp:345:15:345:15 | (__end) | | -| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | | -| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | | -| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | | -| vector.cpp:345:15:345:15 | ref arg (__range) | vector.cpp:345:15:345:15 | (__range) | | -| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | | -| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | | -| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | call to operator* | TAINT | -| vector.cpp:346:3:346:14 | ... = ... | vector.cpp:346:3:346:3 | x [post update] | | -| vector.cpp:346:7:346:12 | call to source | vector.cpp:346:3:346:14 | ... = ... | | -| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:389:1:389:1 | v3 | | -| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | | -| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | | -| vector.cpp:350:38:350:39 | v4 | vector.cpp:350:41:350:45 | call to begin | TAINT | -| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:50:350:51 | it | | -| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:68:350:69 | it | | -| vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:32:351:33 | it | | -| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | | -| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | | -| vector.cpp:350:56:350:57 | v4 | vector.cpp:350:59:350:61 | call to end | TAINT | -| vector.cpp:350:68:350:69 | it | vector.cpp:350:66:350:66 | call to operator++ | | -| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:50:350:51 | it | | -| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | | -| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:32:351:33 | it | | -| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:350:56:350:57 | v4 | | -| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:389:1:389:1 | v4 | | -| vector.cpp:351:32:351:33 | it | vector.cpp:351:32:351:33 | call to iterator | | -| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:350:56:350:57 | v4 | | -| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:353:7:353:8 | v4 | | -| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:389:1:389:1 | v4 | | -| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | | -| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:357:7:357:8 | v5 | | -| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | | -| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | | -| vector.cpp:355:34:355:35 | v5 | vector.cpp:355:37:355:41 | call to begin | TAINT | -| vector.cpp:355:37:355:41 | call to begin | vector.cpp:356:3:356:4 | i5 | | -| vector.cpp:355:37:355:41 | call to begin | vector.cpp:358:3:358:4 | i5 | | -| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:357:7:357:8 | v5 | | -| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | | -| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v5 | | -| vector.cpp:356:2:356:15 | ... = ... | vector.cpp:356:2:356:2 | call to operator* [post update] | | -| vector.cpp:356:3:356:4 | i5 | vector.cpp:356:2:356:2 | call to operator* | TAINT | -| vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:2 | call to operator* [post update] | TAINT | -| vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:15 | ... = ... | | -| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | | -| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | | -| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | | -| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v5 | | -| vector.cpp:358:2:358:8 | ... = ... | vector.cpp:358:2:358:2 | call to operator* [post update] | | -| vector.cpp:358:3:358:4 | i5 | vector.cpp:358:2:358:2 | call to operator* | TAINT | -| vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:2 | call to operator* [post update] | TAINT | -| vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:8 | ... = ... | | -| vector.cpp:359:7:359:8 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | | -| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:363:7:363:8 | v6 | | -| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | | -| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | | -| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | | -| vector.cpp:361:34:361:35 | v6 | vector.cpp:361:37:361:41 | call to begin | TAINT | -| vector.cpp:361:37:361:41 | call to begin | vector.cpp:362:3:362:4 | i6 | | -| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v6 | | -| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:364:2:364:3 | v6 | | -| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:365:7:365:8 | v6 | | -| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v6 | | -| vector.cpp:362:2:362:15 | ... = ... | vector.cpp:362:2:362:2 | call to operator* [post update] | | -| vector.cpp:362:3:362:4 | i6 | vector.cpp:362:2:362:2 | call to operator* | TAINT | -| vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:2 | call to operator* [post update] | TAINT | -| vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:15 | ... = ... | | -| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | | -| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | | -| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | | -| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | | -| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | | -| vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:2:364:3 | ref arg v6 | TAINT | -| vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:5:364:5 | call to operator= | TAINT | -| vector.cpp:365:7:365:8 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | | -| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:370:8:370:9 | v7 | | -| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:373:8:373:9 | v7 | | -| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | | -| vector.cpp:367:34:367:35 | v7 | vector.cpp:367:37:367:41 | call to begin | TAINT | -| vector.cpp:367:37:367:41 | call to begin | vector.cpp:369:4:369:5 | i7 | | -| vector.cpp:367:37:367:41 | call to begin | vector.cpp:372:4:372:5 | i7 | | -| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:370:8:370:9 | v7 | | -| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v7 | | -| vector.cpp:369:3:369:16 | ... = ... | vector.cpp:369:3:369:3 | call to operator* [post update] | | -| vector.cpp:369:4:369:5 | i7 | vector.cpp:369:3:369:3 | call to operator* | TAINT | -| vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:3 | call to operator* [post update] | TAINT | -| vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:16 | ... = ... | | -| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | | -| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:373:8:373:9 | v7 | | -| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v7 | | -| vector.cpp:372:3:372:9 | ... = ... | vector.cpp:372:3:372:3 | call to operator* [post update] | | -| vector.cpp:372:4:372:5 | i7 | vector.cpp:372:3:372:3 | call to operator* | TAINT | -| vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:3 | call to operator* [post update] | TAINT | -| vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:9 | ... = ... | | -| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | | -| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | | -| vector.cpp:375:7:375:8 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | | -| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:379:7:379:8 | v8 | | -| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | | -| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | | -| vector.cpp:377:34:377:35 | v8 | vector.cpp:377:37:377:41 | call to begin | TAINT | -| vector.cpp:377:37:377:41 | call to begin | vector.cpp:378:3:378:4 | i8 | | -| vector.cpp:377:37:377:41 | call to begin | vector.cpp:380:3:380:4 | i8 | | -| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:379:7:379:8 | v8 | | -| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | | -| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v8 | | -| vector.cpp:378:2:378:15 | ... = ... | vector.cpp:378:2:378:2 | call to operator* [post update] | | -| vector.cpp:378:3:378:4 | i8 | vector.cpp:378:2:378:2 | call to operator* | TAINT | -| vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:2 | call to operator* [post update] | TAINT | -| vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:15 | ... = ... | | -| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | | -| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | | -| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | | -| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v8 | | -| vector.cpp:380:2:380:8 | ... = ... | vector.cpp:380:2:380:2 | call to operator* [post update] | | -| vector.cpp:380:3:380:4 | i8 | vector.cpp:380:2:380:2 | call to operator* | TAINT | -| vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:2 | call to operator* [post update] | TAINT | -| vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:8 | ... = ... | | -| vector.cpp:381:7:381:8 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | | -| vector.cpp:383:34:383:35 | ref arg v9 | vector.cpp:388:7:388:8 | v9 | | -| vector.cpp:383:34:383:35 | ref arg v9 | vector.cpp:389:1:389:1 | v9 | | -| vector.cpp:383:34:383:35 | v9 | vector.cpp:383:37:383:41 | call to begin | TAINT | -| vector.cpp:383:37:383:41 | call to begin | vector.cpp:385:3:385:4 | i9 | | -| vector.cpp:383:37:383:41 | call to begin | vector.cpp:386:31:386:32 | i9 | | -| vector.cpp:385:2:385:2 | call to operator* [post update] | vector.cpp:388:7:388:8 | v9 | | -| vector.cpp:385:2:385:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v9 | | -| vector.cpp:385:2:385:15 | ... = ... | vector.cpp:385:2:385:2 | call to operator* [post update] | | -| vector.cpp:385:3:385:4 | i9 | vector.cpp:385:2:385:2 | call to operator* | TAINT | -| vector.cpp:385:8:385:13 | call to source | vector.cpp:385:2:385:2 | call to operator* [post update] | TAINT | -| vector.cpp:385:8:385:13 | call to source | vector.cpp:385:2:385:15 | ... = ... | | -| vector.cpp:386:31:386:32 | call to iterator [post update] | vector.cpp:388:7:388:8 | v9 | | -| vector.cpp:386:31:386:32 | call to iterator [post update] | vector.cpp:389:1:389:1 | v9 | | -| vector.cpp:386:31:386:32 | i9 | vector.cpp:386:31:386:32 | call to iterator | | -| vector.cpp:386:31:386:32 | i9 [post update] | vector.cpp:388:7:388:8 | v9 | | -| vector.cpp:386:31:386:32 | i9 [post update] | vector.cpp:389:1:389:1 | v9 | | -| vector.cpp:388:7:388:8 | ref arg v9 | vector.cpp:389:1:389:1 | v9 | | +| vector.cpp:333:64:333:67 | iter | vector.cpp:333:64:333:67 | iter | | +| vector.cpp:333:64:333:67 | iter | vector.cpp:334:3:334:6 | iter | | +| vector.cpp:333:74:333:74 | i | vector.cpp:334:10:334:10 | i | | +| vector.cpp:334:2:334:2 | call to operator* [post update] | vector.cpp:333:64:333:67 | iter | | +| vector.cpp:334:2:334:10 | ... = ... | vector.cpp:334:2:334:2 | call to operator* [post update] | | +| vector.cpp:334:3:334:6 | iter | vector.cpp:334:2:334:2 | call to operator* | TAINT | +| vector.cpp:334:10:334:10 | i | vector.cpp:334:2:334:2 | call to operator* [post update] | TAINT | +| vector.cpp:334:10:334:10 | i | vector.cpp:334:2:334:10 | ... = ... | | +| vector.cpp:337:38:337:38 | b | vector.cpp:372:5:372:5 | b | | +| vector.cpp:338:22:338:24 | call to vector | vector.cpp:340:34:340:35 | v1 | | +| vector.cpp:338:22:338:24 | call to vector | vector.cpp:342:7:342:8 | v1 | | +| vector.cpp:338:22:338:24 | call to vector | vector.cpp:401:1:401:1 | v1 | | +| vector.cpp:338:30:338:32 | call to vector | vector.cpp:344:38:344:39 | v2 | | +| vector.cpp:338:30:338:32 | call to vector | vector.cpp:344:56:344:57 | v2 | | +| vector.cpp:338:30:338:32 | call to vector | vector.cpp:347:7:347:8 | v2 | | +| vector.cpp:338:30:338:32 | call to vector | vector.cpp:401:1:401:1 | v2 | | +| vector.cpp:338:38:338:40 | call to vector | vector.cpp:349:15:349:16 | v3 | | +| vector.cpp:338:38:338:40 | call to vector | vector.cpp:352:7:352:8 | v3 | | +| vector.cpp:338:38:338:40 | call to vector | vector.cpp:401:1:401:1 | v3 | | +| vector.cpp:338:46:338:48 | call to vector | vector.cpp:354:38:354:39 | v4 | | +| vector.cpp:338:46:338:48 | call to vector | vector.cpp:354:56:354:57 | v4 | | +| vector.cpp:338:46:338:48 | call to vector | vector.cpp:357:7:357:8 | v4 | | +| vector.cpp:338:46:338:48 | call to vector | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:338:54:338:56 | call to vector | vector.cpp:359:34:359:35 | v5 | | +| vector.cpp:338:54:338:56 | call to vector | vector.cpp:361:7:361:8 | v5 | | +| vector.cpp:338:54:338:56 | call to vector | vector.cpp:363:7:363:8 | v5 | | +| vector.cpp:338:54:338:56 | call to vector | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:338:62:338:64 | call to vector | vector.cpp:365:34:365:35 | v6 | | +| vector.cpp:338:62:338:64 | call to vector | vector.cpp:367:7:367:8 | v6 | | +| vector.cpp:338:62:338:64 | call to vector | vector.cpp:368:2:368:3 | v6 | | +| vector.cpp:338:62:338:64 | call to vector | vector.cpp:369:7:369:8 | v6 | | +| vector.cpp:338:62:338:64 | call to vector | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:338:70:338:72 | call to vector | vector.cpp:371:34:371:35 | v7 | | +| vector.cpp:338:70:338:72 | call to vector | vector.cpp:374:8:374:9 | v7 | | +| vector.cpp:338:70:338:72 | call to vector | vector.cpp:377:8:377:9 | v7 | | +| vector.cpp:338:70:338:72 | call to vector | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:338:70:338:72 | call to vector | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:338:78:338:80 | call to vector | vector.cpp:381:34:381:35 | v8 | | +| vector.cpp:338:78:338:80 | call to vector | vector.cpp:383:7:383:8 | v8 | | +| vector.cpp:338:78:338:80 | call to vector | vector.cpp:385:7:385:8 | v8 | | +| vector.cpp:338:78:338:80 | call to vector | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:338:86:338:88 | call to vector | vector.cpp:387:34:387:35 | v9 | | +| vector.cpp:338:86:338:88 | call to vector | vector.cpp:392:7:392:8 | v9 | | +| vector.cpp:338:86:338:88 | call to vector | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:338:95:338:97 | call to vector | vector.cpp:394:35:394:37 | v10 | | +| vector.cpp:338:95:338:97 | call to vector | vector.cpp:396:7:396:9 | v10 | | +| vector.cpp:338:95:338:97 | call to vector | vector.cpp:401:1:401:1 | v10 | | +| vector.cpp:338:104:338:106 | call to vector | vector.cpp:398:35:398:37 | v11 | | +| vector.cpp:338:104:338:106 | call to vector | vector.cpp:400:7:400:9 | v11 | | +| vector.cpp:338:104:338:106 | call to vector | vector.cpp:401:1:401:1 | v11 | | +| vector.cpp:340:34:340:35 | ref arg v1 | vector.cpp:342:7:342:8 | v1 | | +| vector.cpp:340:34:340:35 | ref arg v1 | vector.cpp:401:1:401:1 | v1 | | +| vector.cpp:340:34:340:35 | v1 | vector.cpp:340:37:340:41 | call to begin | TAINT | +| vector.cpp:340:37:340:41 | call to begin | vector.cpp:341:3:341:4 | i1 | | +| vector.cpp:341:2:341:2 | call to operator* [post update] | vector.cpp:342:7:342:8 | v1 | | +| vector.cpp:341:2:341:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v1 | | +| vector.cpp:341:2:341:15 | ... = ... | vector.cpp:341:2:341:2 | call to operator* [post update] | | +| vector.cpp:341:3:341:4 | i1 | vector.cpp:341:2:341:2 | call to operator* | TAINT | +| vector.cpp:341:8:341:13 | call to source | vector.cpp:341:2:341:2 | call to operator* [post update] | TAINT | +| vector.cpp:341:8:341:13 | call to source | vector.cpp:341:2:341:15 | ... = ... | | +| vector.cpp:342:7:342:8 | ref arg v1 | vector.cpp:401:1:401:1 | v1 | | +| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:344:56:344:57 | v2 | | +| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:347:7:347:8 | v2 | | +| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | | +| vector.cpp:344:38:344:39 | v2 | vector.cpp:344:41:344:45 | call to begin | TAINT | +| vector.cpp:344:41:344:45 | call to begin | vector.cpp:344:50:344:51 | it | | +| vector.cpp:344:41:344:45 | call to begin | vector.cpp:344:68:344:69 | it | | +| vector.cpp:344:41:344:45 | call to begin | vector.cpp:345:4:345:5 | it | | +| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:344:56:344:57 | v2 | | +| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:347:7:347:8 | v2 | | +| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | | +| vector.cpp:344:56:344:57 | v2 | vector.cpp:344:59:344:61 | call to end | TAINT | +| vector.cpp:344:68:344:69 | it | vector.cpp:344:66:344:66 | call to operator++ | | +| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:344:50:344:51 | it | | +| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:344:68:344:69 | it | | +| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:345:4:345:5 | it | | +| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:344:56:344:57 | v2 | | +| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:347:7:347:8 | v2 | | +| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v2 | | +| vector.cpp:345:3:345:16 | ... = ... | vector.cpp:345:3:345:3 | call to operator* [post update] | | +| vector.cpp:345:4:345:5 | it | vector.cpp:345:3:345:3 | call to operator* | TAINT | +| vector.cpp:345:9:345:14 | call to source | vector.cpp:345:3:345:3 | call to operator* [post update] | TAINT | +| vector.cpp:345:9:345:14 | call to source | vector.cpp:345:3:345:16 | ... = ... | | +| vector.cpp:347:7:347:8 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | | +| vector.cpp:349:15:349:15 | (__begin) | vector.cpp:349:15:349:15 | call to operator* | TAINT | +| vector.cpp:349:15:349:15 | (__begin) | vector.cpp:349:15:349:15 | call to operator++ | | +| vector.cpp:349:15:349:15 | (__end) | vector.cpp:349:15:349:15 | call to iterator | | +| vector.cpp:349:15:349:15 | (__range) | vector.cpp:349:15:349:15 | call to begin | TAINT | +| vector.cpp:349:15:349:15 | (__range) | vector.cpp:349:15:349:15 | call to end | TAINT | +| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | call to end | vector.cpp:349:15:349:15 | (__end) | | +| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | | +| vector.cpp:349:15:349:15 | ref arg (__range) | vector.cpp:349:15:349:15 | (__range) | | +| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | (__range) | | +| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | (__range) | | +| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | call to operator* | TAINT | +| vector.cpp:350:3:350:14 | ... = ... | vector.cpp:350:3:350:3 | x [post update] | | +| vector.cpp:350:7:350:12 | call to source | vector.cpp:350:3:350:14 | ... = ... | | +| vector.cpp:352:7:352:8 | ref arg v3 | vector.cpp:401:1:401:1 | v3 | | +| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:354:56:354:57 | v4 | | +| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:357:7:357:8 | v4 | | +| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:354:38:354:39 | v4 | vector.cpp:354:41:354:45 | call to begin | TAINT | +| vector.cpp:354:41:354:45 | call to begin | vector.cpp:354:50:354:51 | it | | +| vector.cpp:354:41:354:45 | call to begin | vector.cpp:354:68:354:69 | it | | +| vector.cpp:354:41:354:45 | call to begin | vector.cpp:355:32:355:33 | it | | +| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:354:56:354:57 | v4 | | +| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:357:7:357:8 | v4 | | +| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:354:56:354:57 | v4 | vector.cpp:354:59:354:61 | call to end | TAINT | +| vector.cpp:354:68:354:69 | it | vector.cpp:354:66:354:66 | call to operator++ | | +| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:354:50:354:51 | it | | +| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:354:68:354:69 | it | | +| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:355:32:355:33 | it | | +| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:354:56:354:57 | v4 | | +| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:357:7:357:8 | v4 | | +| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:355:32:355:33 | it | vector.cpp:355:32:355:33 | call to iterator | | +| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:354:56:354:57 | v4 | | +| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:357:7:357:8 | v4 | | +| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:357:7:357:8 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | | +| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:361:7:361:8 | v5 | | +| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:363:7:363:8 | v5 | | +| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:359:34:359:35 | v5 | vector.cpp:359:37:359:41 | call to begin | TAINT | +| vector.cpp:359:37:359:41 | call to begin | vector.cpp:360:3:360:4 | i5 | | +| vector.cpp:359:37:359:41 | call to begin | vector.cpp:362:3:362:4 | i5 | | +| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:361:7:361:8 | v5 | | +| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v5 | | +| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:360:2:360:15 | ... = ... | vector.cpp:360:2:360:2 | call to operator* [post update] | | +| vector.cpp:360:3:360:4 | i5 | vector.cpp:360:2:360:2 | call to operator* | TAINT | +| vector.cpp:360:8:360:13 | call to source | vector.cpp:360:2:360:2 | call to operator* [post update] | TAINT | +| vector.cpp:360:8:360:13 | call to source | vector.cpp:360:2:360:15 | ... = ... | | +| vector.cpp:361:7:361:8 | ref arg v5 | vector.cpp:363:7:363:8 | v5 | | +| vector.cpp:361:7:361:8 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v5 | | +| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:362:2:362:8 | ... = ... | vector.cpp:362:2:362:2 | call to operator* [post update] | | +| vector.cpp:362:3:362:4 | i5 | vector.cpp:362:2:362:2 | call to operator* | TAINT | +| vector.cpp:362:8:362:8 | 1 | vector.cpp:362:2:362:2 | call to operator* [post update] | TAINT | +| vector.cpp:362:8:362:8 | 1 | vector.cpp:362:2:362:8 | ... = ... | | +| vector.cpp:363:7:363:8 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | | +| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:367:7:367:8 | v6 | | +| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:368:2:368:3 | v6 | | +| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | | +| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:365:34:365:35 | v6 | vector.cpp:365:37:365:41 | call to begin | TAINT | +| vector.cpp:365:37:365:41 | call to begin | vector.cpp:366:3:366:4 | i6 | | +| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:367:7:367:8 | v6 | | +| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:368:2:368:3 | v6 | | +| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:369:7:369:8 | v6 | | +| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:366:2:366:15 | ... = ... | vector.cpp:366:2:366:2 | call to operator* [post update] | | +| vector.cpp:366:3:366:4 | i6 | vector.cpp:366:2:366:2 | call to operator* | TAINT | +| vector.cpp:366:8:366:13 | call to source | vector.cpp:366:2:366:2 | call to operator* [post update] | TAINT | +| vector.cpp:366:8:366:13 | call to source | vector.cpp:366:2:366:15 | ... = ... | | +| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:368:2:368:3 | v6 | | +| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | | +| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:368:2:368:3 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | | +| vector.cpp:368:2:368:3 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:368:7:368:26 | call to vector | vector.cpp:368:2:368:3 | ref arg v6 | TAINT | +| vector.cpp:368:7:368:26 | call to vector | vector.cpp:368:5:368:5 | call to operator= | TAINT | +| vector.cpp:369:7:369:8 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | | +| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:374:8:374:9 | v7 | | +| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:377:8:377:9 | v7 | | +| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:371:34:371:35 | v7 | vector.cpp:371:37:371:41 | call to begin | TAINT | +| vector.cpp:371:37:371:41 | call to begin | vector.cpp:373:4:373:5 | i7 | | +| vector.cpp:371:37:371:41 | call to begin | vector.cpp:376:4:376:5 | i7 | | +| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:374:8:374:9 | v7 | | +| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:373:3:373:16 | ... = ... | vector.cpp:373:3:373:3 | call to operator* [post update] | | +| vector.cpp:373:4:373:5 | i7 | vector.cpp:373:3:373:3 | call to operator* | TAINT | +| vector.cpp:373:9:373:14 | call to source | vector.cpp:373:3:373:3 | call to operator* [post update] | TAINT | +| vector.cpp:373:9:373:14 | call to source | vector.cpp:373:3:373:16 | ... = ... | | +| vector.cpp:374:8:374:9 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:374:8:374:9 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:377:8:377:9 | v7 | | +| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:376:3:376:9 | ... = ... | vector.cpp:376:3:376:3 | call to operator* [post update] | | +| vector.cpp:376:4:376:5 | i7 | vector.cpp:376:3:376:3 | call to operator* | TAINT | +| vector.cpp:376:9:376:9 | 1 | vector.cpp:376:3:376:3 | call to operator* [post update] | TAINT | +| vector.cpp:376:9:376:9 | 1 | vector.cpp:376:3:376:9 | ... = ... | | +| vector.cpp:377:8:377:9 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | | +| vector.cpp:377:8:377:9 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:379:7:379:8 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | | +| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:383:7:383:8 | v8 | | +| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:385:7:385:8 | v8 | | +| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:381:34:381:35 | v8 | vector.cpp:381:37:381:41 | call to begin | TAINT | +| vector.cpp:381:37:381:41 | call to begin | vector.cpp:382:3:382:4 | i8 | | +| vector.cpp:381:37:381:41 | call to begin | vector.cpp:384:3:384:4 | i8 | | +| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:383:7:383:8 | v8 | | +| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:385:7:385:8 | v8 | | +| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:382:2:382:15 | ... = ... | vector.cpp:382:2:382:2 | call to operator* [post update] | | +| vector.cpp:382:3:382:4 | i8 | vector.cpp:382:2:382:2 | call to operator* | TAINT | +| vector.cpp:382:8:382:13 | call to source | vector.cpp:382:2:382:2 | call to operator* [post update] | TAINT | +| vector.cpp:382:8:382:13 | call to source | vector.cpp:382:2:382:15 | ... = ... | | +| vector.cpp:383:7:383:8 | ref arg v8 | vector.cpp:385:7:385:8 | v8 | | +| vector.cpp:383:7:383:8 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:384:2:384:2 | call to operator* [post update] | vector.cpp:385:7:385:8 | v8 | | +| vector.cpp:384:2:384:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:384:2:384:8 | ... = ... | vector.cpp:384:2:384:2 | call to operator* [post update] | | +| vector.cpp:384:3:384:4 | i8 | vector.cpp:384:2:384:2 | call to operator* | TAINT | +| vector.cpp:384:8:384:8 | 1 | vector.cpp:384:2:384:2 | call to operator* [post update] | TAINT | +| vector.cpp:384:8:384:8 | 1 | vector.cpp:384:2:384:8 | ... = ... | | +| vector.cpp:385:7:385:8 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | | +| vector.cpp:387:34:387:35 | ref arg v9 | vector.cpp:392:7:392:8 | v9 | | +| vector.cpp:387:34:387:35 | ref arg v9 | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:387:34:387:35 | v9 | vector.cpp:387:37:387:41 | call to begin | TAINT | +| vector.cpp:387:37:387:41 | call to begin | vector.cpp:389:3:389:4 | i9 | | +| vector.cpp:387:37:387:41 | call to begin | vector.cpp:390:31:390:32 | i9 | | +| vector.cpp:389:2:389:2 | call to operator* [post update] | vector.cpp:392:7:392:8 | v9 | | +| vector.cpp:389:2:389:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:389:2:389:15 | ... = ... | vector.cpp:389:2:389:2 | call to operator* [post update] | | +| vector.cpp:389:3:389:4 | i9 | vector.cpp:389:2:389:2 | call to operator* | TAINT | +| vector.cpp:389:8:389:13 | call to source | vector.cpp:389:2:389:2 | call to operator* [post update] | TAINT | +| vector.cpp:389:8:389:13 | call to source | vector.cpp:389:2:389:15 | ... = ... | | +| vector.cpp:390:31:390:32 | call to iterator [post update] | vector.cpp:392:7:392:8 | v9 | | +| vector.cpp:390:31:390:32 | call to iterator [post update] | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:390:31:390:32 | i9 | vector.cpp:390:31:390:32 | call to iterator | | +| vector.cpp:390:31:390:32 | i9 [post update] | vector.cpp:392:7:392:8 | v9 | | +| vector.cpp:390:31:390:32 | i9 [post update] | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:392:7:392:8 | ref arg v9 | vector.cpp:401:1:401:1 | v9 | | +| vector.cpp:394:35:394:37 | ref arg v10 | vector.cpp:396:7:396:9 | v10 | | +| vector.cpp:394:35:394:37 | ref arg v10 | vector.cpp:401:1:401:1 | v10 | | +| vector.cpp:394:35:394:37 | v10 | vector.cpp:394:39:394:43 | call to begin | TAINT | +| vector.cpp:394:39:394:43 | call to begin | vector.cpp:395:33:395:35 | i10 | | +| vector.cpp:395:33:395:35 | call to iterator [post update] | vector.cpp:396:7:396:9 | v10 | | +| vector.cpp:395:33:395:35 | call to iterator [post update] | vector.cpp:401:1:401:1 | v10 | | +| vector.cpp:395:33:395:35 | i10 | vector.cpp:395:33:395:35 | call to iterator | | +| vector.cpp:395:33:395:35 | i10 [post update] | vector.cpp:396:7:396:9 | v10 | | +| vector.cpp:395:33:395:35 | i10 [post update] | vector.cpp:401:1:401:1 | v10 | | +| vector.cpp:396:7:396:9 | ref arg v10 | vector.cpp:401:1:401:1 | v10 | | +| vector.cpp:398:35:398:37 | ref arg v11 | vector.cpp:400:7:400:9 | v11 | | +| vector.cpp:398:35:398:37 | ref arg v11 | vector.cpp:401:1:401:1 | v11 | | +| vector.cpp:398:35:398:37 | v11 | vector.cpp:398:39:398:43 | call to begin | TAINT | +| vector.cpp:398:39:398:43 | call to begin | vector.cpp:399:33:399:35 | i11 | | +| vector.cpp:399:33:399:35 | call to iterator [post update] | vector.cpp:400:7:400:9 | v11 | | +| vector.cpp:399:33:399:35 | call to iterator [post update] | vector.cpp:401:1:401:1 | v11 | | +| vector.cpp:399:33:399:35 | i11 | vector.cpp:399:33:399:35 | call to iterator | | +| vector.cpp:399:33:399:35 | i11 [post update] | vector.cpp:400:7:400:9 | v11 | | +| vector.cpp:399:33:399:35 | i11 [post update] | vector.cpp:401:1:401:1 | v11 | | +| vector.cpp:400:7:400:9 | ref arg v11 | vector.cpp:401:1:401:1 | v11 | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index 2eca13801c6..f91b718bdda 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -375,16 +375,18 @@ | vector.cpp:312:7:312:7 | d | vector.cpp:303:14:303:19 | call to source | | vector.cpp:324:7:324:8 | v2 | vector.cpp:318:15:318:20 | call to source | | vector.cpp:326:7:326:8 | v4 | vector.cpp:318:15:318:20 | call to source | -| vector.cpp:338:7:338:8 | v1 | vector.cpp:337:8:337:13 | call to source | -| vector.cpp:343:7:343:8 | v2 | vector.cpp:341:9:341:14 | call to source | -| vector.cpp:353:7:353:8 | v4 | vector.cpp:330:10:330:15 | call to source | -| vector.cpp:357:7:357:8 | v5 | vector.cpp:356:8:356:13 | call to source | -| vector.cpp:359:7:359:8 | v5 | vector.cpp:356:8:356:13 | call to source | -| vector.cpp:363:7:363:8 | v6 | vector.cpp:362:8:362:13 | call to source | -| vector.cpp:365:7:365:8 | v6 | vector.cpp:362:8:362:13 | call to source | -| vector.cpp:370:8:370:9 | v7 | vector.cpp:369:9:369:14 | call to source | -| vector.cpp:375:7:375:8 | v7 | vector.cpp:369:9:369:14 | call to source | -| vector.cpp:379:7:379:8 | v8 | vector.cpp:378:8:378:13 | call to source | -| vector.cpp:381:7:381:8 | v8 | vector.cpp:378:8:378:13 | call to source | -| vector.cpp:388:7:388:8 | v9 | vector.cpp:330:10:330:15 | call to source | -| vector.cpp:388:7:388:8 | v9 | vector.cpp:385:8:385:13 | call to source | +| vector.cpp:342:7:342:8 | v1 | vector.cpp:341:8:341:13 | call to source | +| vector.cpp:347:7:347:8 | v2 | vector.cpp:345:9:345:14 | call to source | +| vector.cpp:357:7:357:8 | v4 | vector.cpp:330:10:330:15 | call to source | +| vector.cpp:361:7:361:8 | v5 | vector.cpp:360:8:360:13 | call to source | +| vector.cpp:363:7:363:8 | v5 | vector.cpp:360:8:360:13 | call to source | +| vector.cpp:367:7:367:8 | v6 | vector.cpp:366:8:366:13 | call to source | +| vector.cpp:369:7:369:8 | v6 | vector.cpp:366:8:366:13 | call to source | +| vector.cpp:374:8:374:9 | v7 | vector.cpp:373:9:373:14 | call to source | +| vector.cpp:379:7:379:8 | v7 | vector.cpp:373:9:373:14 | call to source | +| vector.cpp:383:7:383:8 | v8 | vector.cpp:382:8:382:13 | call to source | +| vector.cpp:385:7:385:8 | v8 | vector.cpp:382:8:382:13 | call to source | +| vector.cpp:392:7:392:8 | v9 | vector.cpp:330:10:330:15 | call to source | +| vector.cpp:392:7:392:8 | v9 | vector.cpp:389:8:389:13 | call to source | +| vector.cpp:396:7:396:9 | v10 | vector.cpp:399:38:399:43 | call to source | +| vector.cpp:400:7:400:9 | v11 | vector.cpp:399:38:399:43 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index db6bf0f3cf0..6397c1d7830 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -300,16 +300,18 @@ | vector.cpp:312:7:312:7 | vector.cpp:303:14:303:19 | AST only | | vector.cpp:324:7:324:8 | vector.cpp:318:15:318:20 | AST only | | vector.cpp:326:7:326:8 | vector.cpp:318:15:318:20 | AST only | -| vector.cpp:338:7:338:8 | vector.cpp:337:8:337:13 | AST only | -| vector.cpp:343:7:343:8 | vector.cpp:341:9:341:14 | AST only | -| vector.cpp:353:7:353:8 | vector.cpp:330:10:330:15 | AST only | -| vector.cpp:357:7:357:8 | vector.cpp:356:8:356:13 | AST only | -| vector.cpp:359:7:359:8 | vector.cpp:356:8:356:13 | AST only | -| vector.cpp:363:7:363:8 | vector.cpp:362:8:362:13 | AST only | -| vector.cpp:365:7:365:8 | vector.cpp:362:8:362:13 | AST only | -| vector.cpp:370:8:370:9 | vector.cpp:369:9:369:14 | AST only | -| vector.cpp:375:7:375:8 | vector.cpp:369:9:369:14 | AST only | -| vector.cpp:379:7:379:8 | vector.cpp:378:8:378:13 | AST only | -| vector.cpp:381:7:381:8 | vector.cpp:378:8:378:13 | AST only | -| vector.cpp:388:7:388:8 | vector.cpp:330:10:330:15 | AST only | -| vector.cpp:388:7:388:8 | vector.cpp:385:8:385:13 | AST only | +| vector.cpp:342:7:342:8 | vector.cpp:341:8:341:13 | AST only | +| vector.cpp:347:7:347:8 | vector.cpp:345:9:345:14 | AST only | +| vector.cpp:357:7:357:8 | vector.cpp:330:10:330:15 | AST only | +| vector.cpp:361:7:361:8 | vector.cpp:360:8:360:13 | AST only | +| vector.cpp:363:7:363:8 | vector.cpp:360:8:360:13 | AST only | +| vector.cpp:367:7:367:8 | vector.cpp:366:8:366:13 | AST only | +| vector.cpp:369:7:369:8 | vector.cpp:366:8:366:13 | AST only | +| vector.cpp:374:8:374:9 | vector.cpp:373:9:373:14 | AST only | +| vector.cpp:379:7:379:8 | vector.cpp:373:9:373:14 | AST only | +| vector.cpp:383:7:383:8 | vector.cpp:382:8:382:13 | AST only | +| vector.cpp:385:7:385:8 | vector.cpp:382:8:382:13 | AST only | +| vector.cpp:392:7:392:8 | vector.cpp:330:10:330:15 | AST only | +| vector.cpp:392:7:392:8 | vector.cpp:389:8:389:13 | AST only | +| vector.cpp:396:7:396:9 | vector.cpp:399:38:399:43 | AST only | +| vector.cpp:400:7:400:9 | vector.cpp:399:38:399:43 | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp index 92cd5f4c9f5..0cc6570a175 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp @@ -330,8 +330,12 @@ void taint_vector_output_iterator(std::vector::iterator iter) { *iter = source(); } +void vector_iterator_assign_wrapper(std::vector::iterator iter, int i) { + *iter = i; +} + void test_vector_output_iterator(int b) { - std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10), v9(10); + std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10), v9(10), v10(10), v11(10); std::vector::iterator i1 = v1.begin(); *i1 = source(); @@ -386,4 +390,12 @@ void test_vector_output_iterator(int b) { taint_vector_output_iterator(i9); sink(v9); + + std::vector::iterator i10 = v10.begin(); + vector_iterator_assign_wrapper(i10, 10); + sink(v10); // FALSE POSITIVE + + std::vector::iterator i11 = v11.begin(); + vector_iterator_assign_wrapper(i11, source()); + sink(v11); // tainted [NOT DETECTED by IR] } From 9e3bfe1968c3c39339bf638ce47a709c9a1effc4 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 21 Sep 2020 16:17:16 -0700 Subject: [PATCH 052/185] C++: Fix iterator flow context sensitivity --- .../cpp/dataflow/internal/DataFlowPrivate.qll | 6 ++- .../cpp/dataflow/internal/DataFlowUtil.qll | 42 ++++++++++++------- .../code/cpp/dataflow/internal/FlowVar.qll | 7 ++++ .../dataflow/internal/TaintTrackingUtil.qll | 5 --- .../dataflow/taint-tests/taint.expected | 1 - .../dataflow/taint-tests/test_diff.expected | 1 - 6 files changed, 37 insertions(+), 25 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll index 42c31bca69c..e737f8b2d94 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -1,6 +1,8 @@ private import cpp private import DataFlowUtil private import DataFlowDispatch +private import FlowVar + /** Gets the instance argument of a non-static call. */ private Node getInstanceArgument(Call call) { @@ -106,7 +108,7 @@ private class ExprOutNode extends OutNode, ExprNode { override DataFlowCall getCall() { result = this.getExpr() } } -private class RefOutNode extends OutNode, DefinitionByReferenceNode { +private class RefOutNode extends OutNode, DefinitionByReferenceOrIteratorNode { /** Gets the underlying call. */ override DataFlowCall getCall() { result = this.getArgument().getParent() } } @@ -120,7 +122,7 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { kind = TNormalReturnKind() or exists(int i | - result.asDefiningArgument() = call.getArgument(i) and + result.(DefinitionByReferenceOrIteratorNode).getArgument() = call.getArgument(i) and kind = TRefReturnKind(i) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 963f1a17826..25a37f9db0f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -182,29 +182,23 @@ class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode { override predicate isParameterOf(Function fun, int i) { f = fun and i = -1 } } -/** - * A node that represents the value of a variable after a function call that - * may have changed the variable because it's passed by reference. - * - * A typical example would be a call `f(&x)`. Firstly, there will be flow into - * `x` from previous definitions of `x`. Secondly, there will be a - * `DefinitionByReferenceNode` to represent the value of `x` after the call has - * returned. This node will have its `getArgument()` equal to `&x`. - */ -class DefinitionByReferenceNode extends PartialDefinitionNode { +class DefinitionByReferenceOrIteratorNode extends PartialDefinitionNode { Expr inner; Expr argument; - DefinitionByReferenceNode() { - this.getPartialDefinition().(DefinitionByReference).definesExpressions(inner, argument) + DefinitionByReferenceOrIteratorNode() { + this.getPartialDefinition().definesExpressions(inner, argument) and + ( + this.getPartialDefinition() instanceof DefinitionByReference + or + this.getPartialDefinition() instanceof DefinitionByIterator + ) } override Function getFunction() { result = inner.getEnclosingFunction() } override Type getType() { result = inner.getType() } - override string toString() { result = "ref arg " + argument.toString() } - override Location getLocation() { result = argument.getLocation() } override ExprNode getPreUpdateNode() { result.getExpr() = argument } @@ -221,6 +215,22 @@ class DefinitionByReferenceNode extends PartialDefinitionNode { } } + +/** + * A node that represents the value of a variable after a function call that + * may have changed the variable because it's passed by reference. + * + * A typical example would be a call `f(&x)`. Firstly, there will be flow into + * `x` from previous definitions of `x`. Secondly, there will be a + * `DefinitionByReferenceNode` to represent the value of `x` after the call has + * returned. This node will have its `getArgument()` equal to `&x`. + */ +class DefinitionByReferenceNode extends DefinitionByReferenceOrIteratorNode { + override VariablePartialDefinition pd; + + override string toString() { result = "ref arg " + argument.toString() } +} + /** * The value of an uninitialized local variable, viewed as a node in a data * flow graph. @@ -551,10 +561,10 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { or // In `f(&x->a)`, this step provides the flow from post-`&` to post-`x->a`, // from which there is field flow to `x` via reverse read. - exists(VariablePartialDefinition def, Expr inner, Expr outer | + exists(PartialDefinition def, Expr inner, Expr outer | def.definesExpressions(inner, outer) and inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and - outer = nodeFrom.(VariablePartialDefinitionNode).getPreUpdateNode().asExpr() + outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr() ) or // Reverse flow: data that flows from the post-update node of a reference diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index 13f11f8b275..15c15595c4f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -243,6 +243,13 @@ private module PartialDefinitions { } } + /** + * A partial definition that's a definition by reference. + */ + class DefinitionByIterator extends IteratorPartialDefinition { + DefinitionByIterator() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) } + } + /** * A partial definition that's a definition by reference. */ diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll index 3190d3e9eba..17cb9b88104 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -31,11 +31,6 @@ predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) { */ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { localAdditionalTaintStep(src, sink) - or - exists(FunctionCall call, int i | - sink.(DataFlow::IteratorPartialDefinitionNode).getPartialDefinition().definesExpressions(_, call.getArgument(i)) and - src.(DataFlow::RefParameterFinalValueNode).getParameter() = call.getTarget().getParameter(i) - ) } /** diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index f91b718bdda..033cd47145b 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -388,5 +388,4 @@ | vector.cpp:385:7:385:8 | v8 | vector.cpp:382:8:382:13 | call to source | | vector.cpp:392:7:392:8 | v9 | vector.cpp:330:10:330:15 | call to source | | vector.cpp:392:7:392:8 | v9 | vector.cpp:389:8:389:13 | call to source | -| vector.cpp:396:7:396:9 | v10 | vector.cpp:399:38:399:43 | call to source | | vector.cpp:400:7:400:9 | v11 | vector.cpp:399:38:399:43 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 6397c1d7830..3dc9b717e07 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -313,5 +313,4 @@ | vector.cpp:385:7:385:8 | vector.cpp:382:8:382:13 | AST only | | vector.cpp:392:7:392:8 | vector.cpp:330:10:330:15 | AST only | | vector.cpp:392:7:392:8 | vector.cpp:389:8:389:13 | AST only | -| vector.cpp:396:7:396:9 | vector.cpp:399:38:399:43 | AST only | | vector.cpp:400:7:400:9 | vector.cpp:399:38:399:43 | AST only | From 772a51508f09f8b8b7d2e256db11dab0fbfd33f6 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 21 Sep 2020 16:19:41 -0700 Subject: [PATCH 053/185] C++: Update test comment --- cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp index 0cc6570a175..b5dad811998 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp @@ -393,7 +393,7 @@ void test_vector_output_iterator(int b) { std::vector::iterator i10 = v10.begin(); vector_iterator_assign_wrapper(i10, 10); - sink(v10); // FALSE POSITIVE + sink(v10); std::vector::iterator i11 = v11.begin(); vector_iterator_assign_wrapper(i11, source()); From d8176bc00de78f51107f68bd73cc5b84a5549ee0 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 22 Sep 2020 09:53:35 +0200 Subject: [PATCH 054/185] C#: Change TrapStackBehaviour of local functions --- .../Semmle.Extraction.CSharp/Entities/LocalFunction.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs index 8b90f6905dd..edce7b66ce2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs @@ -51,5 +51,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.local_functions(this, symbol.Name, returnType, originalDefinition); ExtractRefReturn(trapFile); } + + public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NeedsLabel; } } From ec49c444ef99291d0b9f080c944dac1f43477649 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 22 Sep 2020 10:06:34 +0200 Subject: [PATCH 055/185] Apply suggestions from code review Co-authored-by: Esben Sparre Andreasen --- .../ql/src/semmle/javascript/frameworks/HTTP.qll | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll index 28a79edb5e7..bf13d8243c0 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll @@ -243,17 +243,19 @@ module HTTP { exists(int i, DataFlow::FunctionNode outer, HTTP::RouteHandlerCandidate inner | decoratee = call.getArgument(i).getALocalSource() and outer.getFunction() = call.getACallee() and - outer = returnsARouteHandler(inner) and + returnsRouteHandler(outer, inner) and isAForwardingRouteHandlerCall(outer.getParameter(i), inner) ) } /** - * Gets a function that returns the route-handler-candidate `routeHandler`. + * Holds if `fun` returns the route-handler-candidate `routeHandler`. */ pragma[noinline] - private DataFlow::FunctionNode returnsARouteHandler(HTTP::RouteHandlerCandidate routeHandler) { - routeHandler = result.getAReturn().getALocalSource() + private predicate returnsRouteHandler( + DataFlow::FunctionNode fun, HTTP::RouteHandlerCandidate routeHandler + ) { + routeHandler = fun.getAReturn().getALocalSource() } /** From 46ba4a1fa8ee4a8494fa2bdc5624b5c1ad995de7 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Thu, 17 Sep 2020 11:58:01 +0100 Subject: [PATCH 056/185] JavaScript: Expose another useful predicate on API-graph nodes. --- javascript/ql/src/semmle/javascript/ApiGraphs.qll | 3 +++ 1 file changed, 3 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll index 4c92212df57..160ff9e47a2 100644 --- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll @@ -223,6 +223,9 @@ module API { ) and length in [1 .. Impl::distanceFromRoot(this)] } + + /** Gets the shortest distance from the root to this node in the API graph. */ + int getDepth() { result = Impl::distanceFromRoot(this) } } /** The root node of an API graph. */ From dafd45f0f4c056e5d8ce199db111050c935f6b08 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Thu, 17 Sep 2020 12:07:00 +0100 Subject: [PATCH 057/185] JavaScript: Add a few metric queries for API graphs. --- javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql | 15 +++++++++++++++ javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql | 14 ++++++++++++++ .../src/meta/ApiGraphs/ApiGraphPointsToEdges.ql | 14 ++++++++++++++ .../ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql | 15 +++++++++++++++ .../ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql | 14 ++++++++++++++ 5 files changed, 72 insertions(+) create mode 100644 javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql create mode 100644 javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql create mode 100644 javascript/ql/src/meta/ApiGraphs/ApiGraphPointsToEdges.ql create mode 100644 javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql create mode 100644 javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql new file mode 100644 index 00000000000..16718a9d122 --- /dev/null +++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql @@ -0,0 +1,15 @@ +/** + * @name API-graph edges + * @description The number of edges (other than points-to edges) in the API graph. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/api-graph-edges + */ + +import javascript +import meta.MetaMetrics + +select projectRoot(), + count(API::Node pred, string lbl, API::Node succ | succ = pred.getASuccessor(lbl)) diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql new file mode 100644 index 00000000000..661c1414e1d --- /dev/null +++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql @@ -0,0 +1,14 @@ +/** + * @name API-graph nodes + * @description The number of nodes in the API graph. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/api-graph-nodes + */ + +import javascript +import meta.MetaMetrics + +select projectRoot(), count(API::Node nd) diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphPointsToEdges.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphPointsToEdges.ql new file mode 100644 index 00000000000..e410af5a4f1 --- /dev/null +++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphPointsToEdges.ql @@ -0,0 +1,14 @@ +/** + * @name API-graph points-to edges + * @description The number of points-to edges in the API graph. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/api-graph-points-to-edges + */ + +import javascript +import meta.MetaMetrics + +select projectRoot(), count(API::Node pred, API::Node succ | pred.refersTo(succ)) diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql new file mode 100644 index 00000000000..fcb98dc57af --- /dev/null +++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql @@ -0,0 +1,15 @@ +/** + * @name API-graph right-hand-side nodes + * @description The number of data-flow nodes corresponding to a right-hand side of + * a definition of an API-graph node. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/api-graph-rhs-nodes + */ + +import javascript +import meta.MetaMetrics + +select projectRoot(), count(any(API::Node nd).getARhs()) diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql new file mode 100644 index 00000000000..446df101364 --- /dev/null +++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql @@ -0,0 +1,14 @@ +/** + * @name API-graph use nodes + * @description The number of data-flow nodes corresponding to a use of an API-graph node. + * @kind metric + * @metricType project + * @metricAggregate sum + * @tags meta + * @id js/meta/api-graph-use-nodes + */ + +import javascript +import meta.MetaMetrics + +select projectRoot(), count(any(API::Node nd).getAUse()) From 32b0f1b480a279573687ac277023b727a6c657f6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 22 Sep 2020 10:28:43 +0200 Subject: [PATCH 058/185] add code example to isDecoratedCall --- .../ql/src/semmle/javascript/frameworks/HTTP.qll | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll index bf13d8243c0..0f224ff060f 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll @@ -237,6 +237,18 @@ module HTTP { * Holds if `call` decorates the function `pred`. * This means that `call` returns a function that forwards its arguments to `pred`. * Only holds when the decorator looks like it is decorating a route-handler. + * + * Below is a code example relating `call`, `decoratee`, `outer`, `inner`. + * ``` + * function outer(method) { + * return function inner(req, res) { + * return method.call(this, req, res); + * }; + * } + * var route = outer(function decoratee(req, res) { // <- call + * res.end("foo"); + * }); + * ``` */ private predicate isDecoratedCall(DataFlow::CallNode call, DataFlow::FunctionNode decoratee) { // indirect route-handler `result` is given to function `outer`, which returns function `inner` which calls the function `pred`. From 71da9045e5361c261c1acb5af5ddb7bdfbcbd828 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 22 Sep 2020 11:00:26 +0200 Subject: [PATCH 059/185] Java/Python: Reduce size of `blockPrecedesVar` --- csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll | 6 +++--- java/ql/src/semmle/code/java/dataflow/SSA.qll | 4 +++- java/ql/src/semmle/code/java/dataflow/internal/BaseSSA.qll | 4 +++- python/ql/src/semmle/python/essa/SsaCompute.qll | 4 +++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll index adcfdfaaca7..899165f5f34 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll @@ -706,9 +706,9 @@ module Ssa { /** * Holds if `def` is accessed in basic block `bb1` (either a read or a write), - * `bb2` is a transitive successor of `bb1`, and `def` is *maybe* read in `bb2` - * or one of its transitive successors, but not in any block on the path between - * `bb1` and `bb2`. + * `bb2` is a transitive successor of `bb1`, `def` is live at the end of `bb1`, + * and the underlying variable for `def` is neither read nor written in any block + * on the path between `bb1` and `bb2`. */ private predicate varBlockReaches(TrackedDefinition def, BasicBlock bb1, BasicBlock bb2) { varOccursInBlock(def, bb1, _) and diff --git a/java/ql/src/semmle/code/java/dataflow/SSA.qll b/java/ql/src/semmle/code/java/dataflow/SSA.qll index 84d6f179efb..851427c4b26 100644 --- a/java/ql/src/semmle/code/java/dataflow/SSA.qll +++ b/java/ql/src/semmle/code/java/dataflow/SSA.qll @@ -770,7 +770,9 @@ private module SsaImpl { /** Holds if `v` occurs in `b` or one of `b`'s transitive successors. */ private predicate blockPrecedesVar(TrackedVar v, BasicBlock b) { - varOccursInBlock(v, b.getABBSuccessor*()) + varOccursInBlock(v, b) + or + ssaDefReachesEndOfBlock(v, _, b) } /** diff --git a/java/ql/src/semmle/code/java/dataflow/internal/BaseSSA.qll b/java/ql/src/semmle/code/java/dataflow/internal/BaseSSA.qll index 9c911a2782c..58021120b5c 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/BaseSSA.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/BaseSSA.qll @@ -321,7 +321,9 @@ private module SsaImpl { /** Holds if `v` occurs in `b` or one of `b`'s transitive successors. */ private predicate blockPrecedesVar(BaseSsaSourceVariable v, BasicBlock b) { - varOccursInBlock(v, b.getABBSuccessor*()) + varOccursInBlock(v, b) + or + ssaDefReachesEndOfBlock(v, _, b) } /** diff --git a/python/ql/src/semmle/python/essa/SsaCompute.qll b/python/ql/src/semmle/python/essa/SsaCompute.qll index ad348675805..c9b4b28f96d 100644 --- a/python/ql/src/semmle/python/essa/SsaCompute.qll +++ b/python/ql/src/semmle/python/essa/SsaCompute.qll @@ -382,7 +382,9 @@ private module SsaComputeImpl { /** Holds if `v` occurs in `b` or one of `b`'s transitive successors. */ private predicate blockPrecedesVar(SsaSourceVariable v, BasicBlock b) { - varOccursInBlock(v, b.getASuccessor*()) + varOccursInBlock(v, b) + or + SsaDefinitionsImpl::reachesEndOfBlock(v, _, _, b) } /** From cc979d0b5f2d1467eaaad26493f0356af3177ce5 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 22 Sep 2020 09:36:33 +0200 Subject: [PATCH 060/185] C#: Add switch case expression type test --- .../csharp8/switchCaseExprTypes.expected | 16 ++++++++++++++++ .../library-tests/csharp8/switchCaseExprTypes.ql | 4 ++++ 2 files changed, 20 insertions(+) create mode 100644 csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected create mode 100644 csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.ql diff --git a/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected new file mode 100644 index 00000000000..80d73991ea2 --- /dev/null +++ b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected @@ -0,0 +1,16 @@ +| patterns.cs:101:34:101:40 | "large" | Int32 | +| patterns.cs:102:18:102:24 | "small" | Int32 | +| patterns.cs:110:22:110:26 | (..., ...) | (Int32,Int32) | +| patterns.cs:111:22:111:26 | (..., ...) | (Int32,Int32) | +| patterns.cs:117:27:117:33 | (..., ...) | (Int32,Int32) | +| patterns.cs:118:28:118:34 | (..., ...) | (Int32,Int32) | +| patterns.cs:119:33:119:38 | (..., ...) | (Int32,Int32) | +| patterns.cs:128:49:128:49 | 0 | MyStruct | +| patterns.cs:129:38:129:38 | 1 | MyStruct | +| patterns.cs:130:23:130:23 | 2 | MyStruct | +| patterns.cs:131:27:131:27 | 3 | MyStruct | +| patterns.cs:138:22:138:50 | throw ... | Object | +| patterns.cs:139:22:139:22 | 3 | Object | +| patterns.cs:140:42:140:42 | 4 | Object | +| patterns.cs:141:29:141:29 | 5 | Object | +| patterns.cs:142:41:142:41 | 6 | Object | diff --git a/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.ql b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.ql new file mode 100644 index 00000000000..a9ad01da2f1 --- /dev/null +++ b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.ql @@ -0,0 +1,4 @@ +import csharp + +from SwitchCaseExpr case +select case.getBody(), case.getType().toString() From c35a5d120a477e79fbbbd8c26e91fe21de7b4da1 Mon Sep 17 00:00:00 2001 From: Faten Healy <5361987+fatenhealy@users.noreply.github.com> Date: Thu, 10 Sep 2020 16:28:02 +1000 Subject: [PATCH 061/185] C#: Increasing required size of RSA key to 2048 --- .../ql/src/Security Features/InsufficientKeySize.cs | 8 ++++---- .../src/Security Features/InsufficientKeySize.qhelp | 2 +- .../ql/src/Security Features/InsufficientKeySize.ql | 8 ++++---- .../InsufficientKeySize/InsufficientKeySize.cs | 12 ++++++------ .../InsufficientKeySize/InsufficientKeySize.expected | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.cs b/csharp/ql/src/Security Features/InsufficientKeySize.cs index 5a12d01c1a1..9d12299dfb0 100644 --- a/csharp/ql/src/Security Features/InsufficientKeySize.cs +++ b/csharp/ql/src/Security Features/InsufficientKeySize.cs @@ -11,7 +11,7 @@ namespace InsufficientKeySize { try { - RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(512); // BAD + RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024); // BAD rsa.ImportParameters(key); return rsa.Encrypt(plaintext, true); } @@ -27,7 +27,7 @@ namespace InsufficientKeySize try { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); // BAD - rsa = new RSACryptoServiceProvider(1024); // GOOD + rsa = new RSACryptoServiceProvider(2048); // GOOD rsa.ImportParameters(key); return rsa.Encrypt(plaintext, true); } @@ -58,7 +58,7 @@ namespace InsufficientKeySize try { DSACryptoServiceProvider dsa = new DSACryptoServiceProvider(); // BAD - dsa = new DSACryptoServiceProvider(1024); // GOOD + dsa = new DSACryptoServiceProvider(2048); // GOOD dsa.ImportParameters(key); return dsa.SignData(plaintext); } @@ -121,7 +121,7 @@ namespace InsufficientKeySize try { // Create a new instance of DSACryptoServiceProvider. - using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider(1024)) // GOOD + using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider(2048)) // GOOD { // Import the key information. DSA.ImportParameters(DSAKeyInfo); diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.qhelp b/csharp/ql/src/Security Features/InsufficientKeySize.qhelp index 2b9ee39c610..906881cf0c2 100644 --- a/csharp/ql/src/Security Features/InsufficientKeySize.qhelp +++ b/csharp/ql/src/Security Features/InsufficientKeySize.qhelp @@ -8,7 +8,7 @@ are vulnerable to brute force attack when too small a key size is used.

-

The key should be at least 1024-bit long when using RSA encryption, and 128-bit long when using +

The key should be at least 2048-bit long when using RSA encryption, and 128-bit long when using symmetric encryption.

diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.ql b/csharp/ql/src/Security Features/InsufficientKeySize.ql index 08bdcfd6724..04623b1d4b0 100644 --- a/csharp/ql/src/Security Features/InsufficientKeySize.ql +++ b/csharp/ql/src/Security Features/InsufficientKeySize.ql @@ -29,8 +29,8 @@ predicate incorrectUseOfDSA(ObjectCreation e, string msg) { .getTarget() .getDeclaringType() .hasQualifiedName("System.Security.Cryptography", "DSACryptoServiceProvider") and - exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 1024) and - msg = "Key size should be at least 1024 bits for DSA encryption." + exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 2048) and + msg = "Key size should be at least 2048 bits for DSA encryption." } predicate incorrectUseOfRSA(ObjectCreation e, string msg) { @@ -38,8 +38,8 @@ predicate incorrectUseOfRSA(ObjectCreation e, string msg) { .getTarget() .getDeclaringType() .hasQualifiedName("System.Security.Cryptography", "RSACryptoServiceProvider") and - exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 1024) and - msg = "Key size should be at least 1024 bits for RSA encryption." + exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 2048) and + msg = "Key size should be at least 2048 bits for RSA encryption." } from Expr e, string msg diff --git a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs index 4ec825ddd63..90be31d0a07 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs +++ b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs @@ -13,18 +13,18 @@ public class InsufficientKeySize // GOOD: Key size is greater than 128 new RC2CryptoServiceProvider().EffectiveKeySize = 256; - // BAD: Key size is less than 1024. + // BAD: Key size is less than 2048. DSACryptoServiceProvider dsaBad = new DSACryptoServiceProvider(512); - // GOOD: Key size defaults to 1024. + // GOOD: Key size defaults to 2048. DSACryptoServiceProvider dsaGood1 = new DSACryptoServiceProvider(); - // GOOD: Key size is greater than 1024. + // GOOD: Key size is greater than 2048. DSACryptoServiceProvider dsaGood2 = new DSACryptoServiceProvider(2048); - // BAD: Key size is less than 1024. + // BAD: Key size is less than 2048. RSACryptoServiceProvider rsaBad = new RSACryptoServiceProvider(512); - // GOOD: Key size defaults to 1024. + // GOOD: Key size defaults to 2048. RSACryptoServiceProvider rsaGood1 = new RSACryptoServiceProvider(); - // GOOD: Key size is greater than 1024. + // GOOD: Key size is greater than 2048. RSACryptoServiceProvider rsaGood2 = new RSACryptoServiceProvider(2048); } } diff --git a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected index dc03302c7f3..feb87da77d2 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected @@ -1,3 +1,3 @@ | InsufficientKeySize.cs:10:9:10:60 | ... = ... | Key size should be at least 128 bits for RC2 encryption. | -| InsufficientKeySize.cs:17:43:17:75 | object creation of type DSACryptoServiceProvider | Key size should be at least 1024 bits for DSA encryption. | -| InsufficientKeySize.cs:24:43:24:75 | object creation of type RSACryptoServiceProvider | Key size should be at least 1024 bits for RSA encryption. | +| InsufficientKeySize.cs:17:43:17:75 | object creation of type DSACryptoServiceProvider | Key size should be at least 2048 bits for DSA encryption. | +| InsufficientKeySize.cs:24:43:24:75 | object creation of type RSACryptoServiceProvider | Key size should be at least 2048 bits for RSA encryption. | From bc09bc45bc79e3c1759a9e1598eb4f4a2fe2fa71 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 22 Sep 2020 10:17:30 +0100 Subject: [PATCH 062/185] JS: Concatenate paths properly --- javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index d97dda5daf3..72933e1a7ad 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -1249,7 +1249,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set cmd.addAll(xmlExtensions); } else { String command = Env.getOS() == OS.WINDOWS ? "codeql.cmd" : "codeql"; - cmd.add(EnvironmentVariables.getCodeQLDist() + "/" + command); + cmd.add(Paths.get(EnvironmentVariables.getCodeQLDist(), command).toString()); cmd.add("database"); cmd.add("index-files"); cmd.add("--language"); From d34bd51f6167add2b022130280d8d27035c98f0d Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 22 Sep 2020 10:28:40 +0100 Subject: [PATCH 063/185] JS: Call codeql.exe instead of codeql.cmd --- javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 72933e1a7ad..edff3c8a857 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -1248,7 +1248,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set cmd.add("--extensions"); cmd.addAll(xmlExtensions); } else { - String command = Env.getOS() == OS.WINDOWS ? "codeql.cmd" : "codeql"; + String command = Env.getOS() == OS.WINDOWS ? "codeql.exe" : "codeql"; cmd.add(Paths.get(EnvironmentVariables.getCodeQLDist(), command).toString()); cmd.add("database"); cmd.add("index-files"); From 8de57c7d198b6b8b06b34adb1a23b9fcf7a55b64 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 4 Aug 2020 10:25:09 +0200 Subject: [PATCH 064/185] C#: Move ASP extraction from auto builder to `pre-finalize.{sh,cmd}` --- .../BuildScripts.cs | 69 +++++-------------- .../Semmle.Autobuild.CSharp/AspBuildRule.cs | 23 ------- .../CSharpAutobuilder.cs | 5 +- .../Semmle.Autobuild.CSharp/XmlBuildRule.cs | 21 ------ .../AutobuildOptions.cs | 3 - .../Semmle.Autobuild.Shared/Autobuilder.cs | 33 +++------ csharp/tools/autobuild.cmd | 3 - csharp/tools/autobuild.sh | 4 -- csharp/tools/pre-finalize.cmd | 3 + csharp/tools/pre-finalize.sh | 4 +- 10 files changed, 34 insertions(+), 134 deletions(-) delete mode 100644 csharp/autobuilder/Semmle.Autobuild.CSharp/AspBuildRule.cs delete mode 100644 csharp/autobuilder/Semmle.Autobuild.CSharp/XmlBuildRule.cs diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs index 51d56e6cda1..5934fb51c67 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs @@ -363,7 +363,6 @@ namespace Semmle.Autobuild.CSharp.Tests string cwd = @"C:\Project") { string codeqlUpperLanguage = Language.CSharp.UpperCaseName; - Actions.GetEnvironmentVariable[$"CODEQL_AUTOBUILDER_{codeqlUpperLanguage}_NO_INDEXING"] = "false"; Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_TRAP_DIR"] = ""; Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_SOURCE_ARCHIVE_DIR"] = ""; Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{codeqlUpperLanguage.ToLowerInvariant()}"; @@ -400,8 +399,6 @@ namespace Semmle.Autobuild.CSharp.Tests Actions.RunProcess[@"cmd.exe /C dotnet clean C:\Project\test.csproj"] = 0; Actions.RunProcess[@"cmd.exe /C dotnet restore C:\Project\test.csproj"] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental C:\Project\test.csproj"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"C:\Project\test.csproj"] = true; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -419,7 +416,7 @@ namespace Semmle.Autobuild.CSharp.Tests Actions.LoadXml[@"C:\Project\test.csproj"] = xml; var autobuilder = CreateAutoBuilder(true); - TestAutobuilderScript(autobuilder, 0, 6); + TestAutobuilderScript(autobuilder, 0, 4); } [Fact] @@ -432,8 +429,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0; Actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"C:\Project/test.csproj"] = true; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -451,7 +446,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.LoadXml[@"C:\Project/test.csproj"] = xml; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 0, 7); + TestAutobuilderScript(autobuilder, 0, 5); } [Fact] @@ -522,8 +517,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap public void TestLinuxBuildlessExtractionSuccess() { Actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone --references:."] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; @@ -531,7 +524,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateDirectories[@"C:\Project"] = ""; var autobuilder = CreateAutoBuilder(false, buildless: "true"); - TestAutobuilderScript(autobuilder, 0, 3); + TestAutobuilderScript(autobuilder, 0, 1); } [Fact] @@ -552,8 +545,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap public void TestLinuxBuildlessExtractionSolution() { Actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --references:."] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; @@ -561,7 +552,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateDirectories[@"C:\Project"] = ""; var autobuilder = CreateAutoBuilder(false, buildless: "true", solution: "foo.sln"); - TestAutobuilderScript(autobuilder, 0, 3); + TestAutobuilderScript(autobuilder, 0, 1); } void SkipVsWhere() @@ -598,8 +589,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess["dotnet --list-runtimes"] = 1; Actions.RunProcessOut["dotnet --list-runtimes"] = ""; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto ""./build.sh --skip-tests"""] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; @@ -609,7 +598,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap SkipVsWhere(); var autobuilder = CreateAutoBuilder(false, buildCommand: "./build.sh --skip-tests"); - TestAutobuilderScript(autobuilder, 0, 4); + TestAutobuilderScript(autobuilder, 0, 2); } [Fact] @@ -624,12 +613,10 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcessOut["dotnet --list-runtimes"] = ""; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = 0; Actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = @"C:\Project/build"; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 0, 5); + TestAutobuilderScript(autobuilder, 0, 3); } [Fact] @@ -679,12 +666,10 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = 0; Actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = @"C:\Project"; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; var autobuilder = CreateAutoBuilder(true); - TestAutobuilderScript(autobuilder, 0, 3); + TestAutobuilderScript(autobuilder, 0, 1); } [Fact] @@ -729,8 +714,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test2.sln"] = 0; Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; @@ -752,7 +735,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution1); autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution2); - TestAutobuilderScript(autobuilder, 0, 6); + TestAutobuilderScript(autobuilder, 0, 4); } [Fact] @@ -762,8 +745,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test2.csproj"] = 0; Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"C:\Project\test1.csproj"] = true; Actions.FileExists[@"C:\Project\test2.csproj"] = true; @@ -799,7 +780,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", vsToolsVersion: "12"); - TestAutobuilderScript(autobuilder, 0, 6); + TestAutobuilderScript(autobuilder, 0, 4); } [Fact] @@ -834,8 +815,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap { Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false; @@ -855,15 +834,13 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution1); autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution2); - TestAutobuilderScript(autobuilder, 0, 4); + TestAutobuilderScript(autobuilder, 0, 2); } [Fact] public void TestSkipNugetBuildless() { Actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --references:. --skip-nuget"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; @@ -871,7 +848,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.EnumerateDirectories[@"C:\Project"] = ""; var autobuilder = CreateAutoBuilder(false, buildless: "true", solution: "foo.sln", nugetRestore: "false"); - TestAutobuilderScript(autobuilder, 0, 3); + TestAutobuilderScript(autobuilder, 0, 1); } @@ -885,8 +862,6 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0; Actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false --no-restore C:\Project/test.csproj"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"C:\Project/test.csproj"] = true; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -904,7 +879,7 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.LoadXml[@"C:\Project/test.csproj"] = xml; var autobuilder = CreateAutoBuilder(false, dotnetArguments: "--no-restore"); // nugetRestore=false does not work for now. - TestAutobuilderScript(autobuilder, 0, 7); + TestAutobuilderScript(autobuilder, 0, 5); } [Fact] @@ -922,8 +897,6 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0; Actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental C:\Project/test.csproj"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -943,7 +916,7 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh")); var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 11); + TestAutobuilderScript(autobuilder, 0, 9); } [Fact] @@ -964,8 +937,6 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0; Actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -985,7 +956,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh")); var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 11); + TestAutobuilderScript(autobuilder, 0, 9); } [Fact] @@ -999,8 +970,6 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet clean C:\Project\test.csproj"] = 0; Actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet restore C:\Project\test.csproj"] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental C:\Project\test.csproj"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"C:\Project\test.csproj"] = true; Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -1019,7 +988,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.LoadXml[@"C:\Project\test.csproj"] = xml; var autobuilder = CreateAutoBuilder(true, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 9); + TestAutobuilderScript(autobuilder, 0, 7); } [Fact] @@ -1028,8 +997,6 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\dirs.proj"] = 1; Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\dirs.proj"] = 0; Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\dirs.proj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"C:\Project\a\test.csproj"] = true; Actions.FileExists[@"C:\Project\dirs.proj"] = true; @@ -1065,7 +1032,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug", vsToolsVersion: "12", allSolutions: "true"); - TestAutobuilderScript(autobuilder, 0, 5); + TestAutobuilderScript(autobuilder, 0, 3); } [Fact] @@ -1074,8 +1041,6 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.RunProcess[@"nuget restore C:\Project/dirs.proj"] = 1; Actions.RunProcess[@"mono C:\Project/.nuget/nuget.exe restore C:\Project/dirs.proj"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto msbuild C:\Project/dirs.proj /p:UseSharedCompilation=false /t:rebuild /p:MvcBuildViews=true"] = 0; - Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"C:\Project/a/test.csproj"] = true; Actions.FileExists[@"C:\Project/dirs.proj"] = true; @@ -1104,7 +1069,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap Actions.LoadXml[@"C:\Project/dirs.proj"] = dirsproj; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 0, 5); + TestAutobuilderScript(autobuilder, 0, 3); } [Fact] diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/AspBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/AspBuildRule.cs deleted file mode 100644 index 2f69faeafde..00000000000 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/AspBuildRule.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Semmle.Autobuild.Shared; - -namespace Semmle.Autobuild.CSharp -{ - /// - /// ASP extraction. - /// - class AspBuildRule : IBuildRule - { - public BuildScript Analyse(Autobuilder builder, bool auto) - { - var javaHome = builder.JavaHome; - var dist = builder.Distribution; - - var command = new CommandBuilder(builder.Actions). - RunCommand(builder.Actions.PathCombine(javaHome, "bin", "java")). - Argument("-jar"). - QuoteArgument(builder.Actions.PathCombine(dist, "tools", "extractor-asp.jar")). - Argument("."); - return command.Script; - } - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs index 647a3ad2b4d..cf52b77b239 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs @@ -85,10 +85,7 @@ namespace Semmle.Autobuild.CSharp break; } - return - attempt & - (() => new AspBuildRule().Analyse(this, false)) & - (() => new XmlBuildRule().Analyse(this, false)); + return attempt; } /// diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/XmlBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/XmlBuildRule.cs deleted file mode 100644 index d262ec1f20b..00000000000 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/XmlBuildRule.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Semmle.Autobuild.Shared; - -namespace Semmle.Autobuild.CSharp -{ - /// - /// XML extraction. - /// - class XmlBuildRule : IBuildRule - { - public BuildScript Analyse(Autobuilder builder, bool auto) - { - if (!builder.Options.Indexing || builder.Odasa is null) - return BuildScript.Success; - - var command = new CommandBuilder(builder.Actions). - RunCommand(builder.Odasa). - Argument("index --xml --extensions config csproj props xml"); - return command.Script; - } - } -} diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs index 28c14be13ff..92bad4d6c7f 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs @@ -29,7 +29,6 @@ namespace Semmle.Autobuild.Shared public readonly bool NugetRestore; public readonly Language Language; - public readonly bool Indexing; /// /// Reads options from environment variables. @@ -54,8 +53,6 @@ namespace Semmle.Autobuild.Shared NugetRestore = actions.GetEnvironmentVariable(prefix + "NUGET_RESTORE").AsBool("nuget_restore", true); Language = language; - - Indexing = !actions.GetEnvironmentVariable($"CODEQL_AUTOBUILDER_{Language.UpperCaseName}_NO_INDEXING").AsBool("no_indexing", false); } } diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs index 16ba41bfe34..1e8f58c45b7 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs @@ -1,4 +1,4 @@ -using Semmle.Util.Logging; +using Semmle.Util.Logging; using System; using System.Collections.Generic; using System.IO; @@ -190,21 +190,10 @@ namespace Semmle.Autobuild.Shared }); CodeQLExtractorLangRoot = Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_ROOT"); - SemmleDist = Actions.GetEnvironmentVariable("SEMMLE_DIST"); SemmlePlatformTools = Actions.GetEnvironmentVariable("SEMMLE_PLATFORM_TOOLS"); CodeQlPlatform = Actions.GetEnvironmentVariable("CODEQL_PLATFORM"); - JavaHome = - Actions.GetEnvironmentVariable("CODEQL_JAVA_HOME") ?? - Actions.GetEnvironmentVariable("SEMMLE_JAVA_HOME") ?? - throw new InvalidEnvironmentException("The environment variable CODEQL_JAVA_HOME or SEMMLE_JAVA_HOME has not been set."); - - Distribution = - CodeQLExtractorLangRoot ?? - SemmleDist ?? - throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_ROOT or SEMMLE_DIST has not been set."); - TrapDir = Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR") ?? Actions.GetEnvironmentVariable("TRAP_FOLDER") ?? @@ -275,15 +264,6 @@ namespace Semmle.Autobuild.Shared /// public string? CodeQLExtractorLangRoot { get; } - /// - /// Value of SEMMLE_DIST environment variable. - /// - private string? SemmleDist { get; } - - public string Distribution { get; } - - public string JavaHome { get; } - /// /// Value of SEMMLE_PLATFORM_TOOLS environment variable. /// @@ -298,13 +278,20 @@ namespace Semmle.Autobuild.Shared /// The absolute path of the odasa executable. /// null if we are running in CodeQL. /// - public string? Odasa => SemmleDist is null ? null : Actions.PathCombine(SemmleDist, "tools", "odasa"); + public string? Odasa + { + get + { + var semmleDist = Actions.GetEnvironmentVariable("SEMMLE_DIST"); + return semmleDist is null ? null : Actions.PathCombine(semmleDist, "tools", "odasa"); + } + } /// /// Construct a command that executed the given wrapped in /// an odasa --index, unless indexing has been disabled, in which case /// is run directly. /// - public CommandBuilder MaybeIndex(CommandBuilder builder, string cmd) => Options.Indexing && !(Odasa is null) ? builder.IndexCommand(Odasa, cmd) : builder.RunCommand(cmd); + public CommandBuilder MaybeIndex(CommandBuilder builder, string cmd) => Odasa is null ? builder.RunCommand(cmd) : builder.IndexCommand(Odasa, cmd); } } diff --git a/csharp/tools/autobuild.cmd b/csharp/tools/autobuild.cmd index 418ba37a972..7d24060ff46 100644 --- a/csharp/tools/autobuild.cmd +++ b/csharp/tools/autobuild.cmd @@ -1,7 +1,4 @@ @echo off -rem The autobuilder is already being traced -set CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING=true - type NUL && "%CODEQL_EXTRACTOR_CSHARP_ROOT%/tools/%CODEQL_PLATFORM%/Semmle.Autobuild.CSharp.exe" exit /b %ERRORLEVEL% diff --git a/csharp/tools/autobuild.sh b/csharp/tools/autobuild.sh index 341f64af50b..e8e007a10d7 100755 --- a/csharp/tools/autobuild.sh +++ b/csharp/tools/autobuild.sh @@ -7,8 +7,4 @@ if [ "$CODEQL_PLATFORM" != "linux64" ] && [ "$CODEQL_PLATFORM" != "osx64" ] ; th exit 1 fi -# The autobuilder is already being traced -CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING="true" -export CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING - "$CODEQL_EXTRACTOR_CSHARP_ROOT/tools/$CODEQL_PLATFORM/Semmle.Autobuild.CSharp" || exit $? diff --git a/csharp/tools/pre-finalize.cmd b/csharp/tools/pre-finalize.cmd index a8a5a6bc68a..b6e7abab41b 100644 --- a/csharp/tools/pre-finalize.cmd +++ b/csharp/tools/pre-finalize.cmd @@ -9,4 +9,7 @@ type NUL && "%CODEQL_DIST%\codeql" database index-files ^ --language xml ^ -- ^ "%CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE%" +IF %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% + +type NUL && "%CODEQL_JAVA_HOME%\bin\java.exe" -jar "%CODEQL_EXTRACTOR_CSHARP_ROOT%\tools\extractor-asp.jar" . exit /b %ERRORLEVEL% diff --git a/csharp/tools/pre-finalize.sh b/csharp/tools/pre-finalize.sh index ed12b3b1d72..0e8ab78c9fd 100755 --- a/csharp/tools/pre-finalize.sh +++ b/csharp/tools/pre-finalize.sh @@ -10,4 +10,6 @@ set -eu --size-limit 10m \ --language xml \ -- \ - "$CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE" \ No newline at end of file + "$CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE" + +"$CODEQL_JAVA_HOME/bin/java" -jar "$CODEQL_EXTRACTOR_CSHARP_ROOT/tools/extractor-asp.jar" . From a89d13a5eef8f33d79161987639bf4b8d4c460f6 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 22 Sep 2020 11:20:22 +0200 Subject: [PATCH 065/185] C#: Add change notes for increased required key size in 'cs/insufficient-key-size' --- change-notes/1.26/analysis-csharp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change-notes/1.26/analysis-csharp.md b/change-notes/1.26/analysis-csharp.md index 5b65481c925..3d17e00ab70 100644 --- a/change-notes/1.26/analysis-csharp.md +++ b/change-notes/1.26/analysis-csharp.md @@ -12,7 +12,7 @@ The following changes in version 1.26 affect C# analysis in all applications. | **Query** | **Expected impact** | **Change** | |------------------------------|------------------------|-----------------------------------| - +| Weak encryption: Insufficient key size (`cs/insufficient-key-size`) | More results | The required key size has been increased from 1024 to 2048. | ## Removal of old queries From 5f96c37b283a956e94ddf0960f564800ada9f5d9 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 22 Sep 2020 09:40:07 +0200 Subject: [PATCH 066/185] C#: Fix switch case expression types --- .../Entities/Expressions/Switch.cs | 9 ++++---- .../controlflow/guards/Implications.expected | 6 +++++ .../csharp8/switchCaseExprTypes.expected | 22 +++++++++---------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs index 5316998a38f..0dfd33751fc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs @@ -18,10 +18,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions protected override void PopulateExpression(TextWriter trapFile) { SwitchedExpr = Expression.Create(cx, Syntax.GoverningExpression, this, -1); - int child = 0; - foreach (var arm in Syntax.Arms) + for (var i = 0; i < Syntax.Arms.Count; i++) { - new SwitchCase(cx, arm, this, child++); + new SwitchCase(cx, Syntax.Arms[i], this, i); } } } @@ -29,7 +28,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions class SwitchCase : Expression { internal SwitchCase(Context cx, SwitchExpressionArmSyntax arm, Switch parent, int child) : - base(new ExpressionInfo(cx, parent.SwitchedExpr.Type, cx.Create(arm.GetLocation()), ExprKind.SWITCH_CASE, parent, child, false, null)) + base(new ExpressionInfo( + cx, Entities.Type.Create(cx, cx.GetType(arm.Expression)), cx.Create(arm.GetLocation()), + ExprKind.SWITCH_CASE, parent, child, false, null)) { cx.CreatePattern(arm.Pattern, this, 0); if (arm.WhenClause is WhenClauseSyntax when) diff --git a/csharp/ql/test/library-tests/controlflow/guards/Implications.expected b/csharp/ql/test/library-tests/controlflow/guards/Implications.expected index 59f89c30800..ec6c60ff9e8 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/Implications.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/Implications.expected @@ -397,8 +397,14 @@ | Guards.cs:276:16:276:16 | access to parameter o | match access to type Action | Guards.cs:276:16:276:16 | access to parameter o | non-null | | Guards.cs:276:16:276:16 | access to parameter o | match null | Guards.cs:276:16:276:16 | access to parameter o | null | | Guards.cs:276:16:276:16 | access to parameter o | non-match null | Guards.cs:276:16:276:16 | access to parameter o | non-null | +| Guards.cs:278:13:279:28 | ... => ... | true | Guards.cs:276:16:276:16 | access to parameter o | non-null | +| Guards.cs:280:13:281:28 | ... => ... | true | Guards.cs:276:16:276:16 | access to parameter o | non-null | | Guards.cs:281:17:281:17 | access to local variable a | non-null | Guards.cs:276:16:276:16 | access to parameter o | non-null | | Guards.cs:281:17:281:17 | access to local variable a | null | Guards.cs:276:16:276:16 | access to parameter o | null | +| Guards.cs:282:13:283:28 | ... => ... | true | Guards.cs:276:16:276:16 | access to parameter o | non-null | +| Guards.cs:284:13:285:28 | ... => ... | false | Guards.cs:276:16:276:16 | access to parameter o | non-null | +| Guards.cs:284:13:285:28 | ... => ... | true | Guards.cs:276:16:276:16 | access to parameter o | null | +| Guards.cs:286:13:287:28 | ... => ... | true | Guards.cs:276:16:276:16 | access to parameter o | non-null | | Guards.cs:296:16:296:17 | access to local variable b2 | match true | Guards.cs:294:13:294:14 | access to parameter b1 | false | | Guards.cs:296:16:296:17 | access to local variable b2 | match true | Guards.cs:296:16:296:17 | access to local variable b2 | true | | Guards.cs:308:16:308:17 | access to local variable b2 | match true | Guards.cs:306:13:306:14 | access to parameter b1 | true | diff --git a/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected index 80d73991ea2..06e2fd994c5 100644 --- a/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected +++ b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected @@ -1,16 +1,16 @@ -| patterns.cs:101:34:101:40 | "large" | Int32 | -| patterns.cs:102:18:102:24 | "small" | Int32 | +| patterns.cs:101:34:101:40 | "large" | String | +| patterns.cs:102:18:102:24 | "small" | String | | patterns.cs:110:22:110:26 | (..., ...) | (Int32,Int32) | | patterns.cs:111:22:111:26 | (..., ...) | (Int32,Int32) | | patterns.cs:117:27:117:33 | (..., ...) | (Int32,Int32) | | patterns.cs:118:28:118:34 | (..., ...) | (Int32,Int32) | | patterns.cs:119:33:119:38 | (..., ...) | (Int32,Int32) | -| patterns.cs:128:49:128:49 | 0 | MyStruct | -| patterns.cs:129:38:129:38 | 1 | MyStruct | -| patterns.cs:130:23:130:23 | 2 | MyStruct | -| patterns.cs:131:27:131:27 | 3 | MyStruct | -| patterns.cs:138:22:138:50 | throw ... | Object | -| patterns.cs:139:22:139:22 | 3 | Object | -| patterns.cs:140:42:140:42 | 4 | Object | -| patterns.cs:141:29:141:29 | 5 | Object | -| patterns.cs:142:41:142:41 | 6 | Object | +| patterns.cs:128:49:128:49 | 0 | Int32 | +| patterns.cs:129:38:129:38 | 1 | Int32 | +| patterns.cs:130:23:130:23 | 2 | Int32 | +| patterns.cs:131:27:131:27 | 3 | Int32 | +| patterns.cs:138:22:138:50 | throw ... | null | +| patterns.cs:139:22:139:22 | 3 | Int32 | +| patterns.cs:140:42:140:42 | 4 | Int32 | +| patterns.cs:141:29:141:29 | 5 | Int32 | +| patterns.cs:142:41:142:41 | 6 | Int32 | From b065d8724e75630271556beadb9c46fb28a0ee8a Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 22 Sep 2020 13:52:30 +0200 Subject: [PATCH 067/185] Python: Fixup comments after merge --- .../dataflow/internal/DataFlowPrivate.qll | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 33fdd89d424..cf58b2935b1 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -225,8 +225,12 @@ private Node update(Node node) { //-------- // Global flow //-------- +// /** - * A DataFlowCallable is any callable value. + * IPA type for DataFlowCallable. + * + * A callable is either a callable value or a module (for enclosing `ModuleVariable`s). + * A module has no calls. */ newtype TDataFlowCallable = TCallableValue(CallableValue callable) or @@ -284,11 +288,24 @@ class DataFlowModuleScope extends DataFlowCallable, TModule { override string getName() { result = mod.getName() } } +/** + * IPA type for DataFlowCall. + * + * Calls corresponding to `CallNode`s are either to callable values or to classes. + * The latter is directed to the callable corresponding to the calss' `__init__`-method. + * + * An `__init__`-method can also be called directly, so that callable can be targetted by + * different types of calls. In that case, the parameter mappings will be different, + * as the class call will synthesise an argument node to be mapped to the `self` parameter. + * + * A calls corresponding to a special method call is handled by the corresponding `SpecialMethodCallNode`. + */ newtype TDataFlowCall = TCallNode(CallNode call) { call = any(CallableValue c).getACall() } or TClassCall(CallNode call) { call = any(ClassValue c).getACall() } or TSpecialCall(SpecialMethodCallNode special) +/** Represents a call. */ abstract class DataFlowCall extends TDataFlowCall { /** Gets a textual representation of this element. */ abstract string toString(); @@ -306,7 +323,7 @@ abstract class DataFlowCall extends TDataFlowCall { abstract DataFlowCallable getEnclosingCallable(); } -/** Represents a call to a callable. */ +/** Represents a call to a callable (currently only callable values). */ class CallNodeCall extends DataFlowCall, TCallNode { CallNode call; DataFlowCallable callable; From 131cf8d2ecc8ab772d35fde72916cb1f4b31860a Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 22 Sep 2020 15:02:31 +0200 Subject: [PATCH 068/185] Python: Fix compilation error --- .../src/experimental/dataflow/internal/TaintTrackingPrivate.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll b/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll index 64bdd3bb3ab..68e1e9fc283 100644 --- a/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll @@ -182,7 +182,7 @@ predicate containerStep(DataFlow::CfgNode nodeFrom, DataFlow::Node nodeTo) { exists(CallNode call, string name | name in ["append", "add"] and call.getFunction().(AttrNode).getObject(name) = - nodeTo.(PostUpdateNode).getPreUpdateNode().asCfgNode() and + nodeTo.(DataFlow::PostUpdateNode).getPreUpdateNode().asCfgNode() and call.getArg(0) = nodeFrom.getNode() ) } From 7dce4d0a6e5944cf349277c6da6f8d4311df4159 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Tue, 22 Sep 2020 13:39:22 +0200 Subject: [PATCH 069/185] C++: Rename: name the file the same as the class --- ...leRangeAnalysisDef.qll => SimpleRangeAnalysisDefinition.qll} | 0 .../src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/{SimpleRangeAnalysisDef.qll => SimpleRangeAnalysisDefinition.qll} (100%) diff --git a/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDef.qll b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll similarity index 100% rename from cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDef.qll rename to cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll index 05c2e519b46..d6e7d62435c 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll @@ -45,7 +45,7 @@ import cpp private import RangeAnalysisUtils private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr -private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDef +private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDefinition import RangeSSA import SimpleRangeAnalysisCached private import NanAnalysis From 8065bf15ad865d50ebed795dd12eb6f6079a8921 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Tue, 22 Sep 2020 13:37:24 +0200 Subject: [PATCH 070/185] C++: Per-variable overrides Without these changes, there was no way to tell which variables were overridden by a given instance of `SimpleRangeAnalysisDefinition`. All four overrides are needed because they fit into different mutual recursions of the `SimpleRangeAnalysis` implementation. --- .../SimpleRangeAnalysisDefinition.qll | 53 ++++++++++++++----- .../cpp/rangeanalysis/SimpleRangeAnalysis.qll | 11 ++-- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll index c2647e2bebc..e1d64dfcf5a 100644 --- a/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll +++ b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll @@ -10,25 +10,54 @@ import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis /** * EXPERIMENTAL: The API of this class may change without notice. * - * An SSA definition for which a range can be deduced. Extend this class to add - * functionality to the range analysis library. + * An SSA definition for which a range can be deduced. As with + * `RangeSsaDefinition` and `SsaDefinition`, instances of this class + * correspond to points in the program where one or more variables are defined + * or have their value constrained in some way. + * + * Extend this class to add functionality to the range analysis library. */ abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition { - /** - * Gets the lower bound of the defomotopn. + /** + * Holds if this `SimpleRangeAnalysisDefinition` adds range information for + * `v`. Because a `SimpleRangeAnalysisDefinition` is just a point in the + * program, it's possible that more than one variable might be defined at + * this point. This predicate clarifies which variable(s) should get range + * information from `this`. * - * Implementations of this predicate should use - * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for - * recursive calls to get the bounds of their children. + * This predicate **must be overridden** to hold for any `v` that can show + * up in the other members of `SimpleRangeAnalysisDefinition`. Conversely, + * the other members **must be accurate** for any `v` in this predicate. */ - abstract float getLowerBounds(); + abstract predicate hasRangeInformationFor(StackVariable v); /** - * Gets the upper bound of the definition. + * Holds if `(this, v)` depends on the range of the unconverted expression + * `e`. This information is used to inform the range analysis about cyclic + * dependencies. Without this information, range analysis might work for + * simple cases but will go into infinite loops on complex code. + * + * For example, when modelling the definition by reference in a call to an + * overloaded `operator=`, written as `v = e`, the definition of `(this, v)` + * depends on `e`. + */ + abstract predicate dependsOnExpr(StackVariable v, Expr e); + + /** + * Gets the lower bound of the variable `v` defined by this definition. * * Implementations of this predicate should use * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for - * recursive calls to get the bounds of their children. + * recursive calls to get the bounds of their dependencies. */ - abstract float getUpperBounds(); -} \ No newline at end of file + abstract float getLowerBounds(StackVariable v); + + /** + * Gets the upper bound of the variable `v` defined by this definition. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their dependencies. + */ + abstract float getUpperBounds(StackVariable v); +} diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll index d6e7d62435c..886c6347622 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll @@ -336,6 +336,11 @@ private predicate defDependsOnDef( or // Phi nodes. phiDependsOnDef(def, v, srcDef, srcVar) + or + // Extensions + exists(Expr expr | def.(SimpleRangeAnalysisDefinition).dependsOnExpr(v, expr) | + exprDependsOnDef(expr, srcDef, srcVar) + ) } /** @@ -495,7 +500,7 @@ private predicate analyzableDef(RangeSsaDefinition def, StackVariable v) { phiDependsOnDef(def, v, _, _) or // A modeled def for range analysis - def.(SimpleRangeAnalysisDefinition).getAVariable() =v + def.(SimpleRangeAnalysisDefinition).hasRangeInformationFor(v) } /** @@ -1220,7 +1225,7 @@ private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) { result = getPhiLowerBounds(v, def) or // A modeled def for range analysis - result = def.(SimpleRangeAnalysisDefinition).getLowerBounds() + result = def.(SimpleRangeAnalysisDefinition).getLowerBounds(v) or // Unanalyzable definitions. unanalyzableDefBounds(def, v, result, _) @@ -1256,7 +1261,7 @@ private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) { result = getPhiUpperBounds(v, def) or // A modeled def for range analysis - result = def.(SimpleRangeAnalysisDefinition).getUpperBounds() + result = def.(SimpleRangeAnalysisDefinition).getUpperBounds(v) or // Unanalyzable definitions. unanalyzableDefBounds(def, v, _, result) From d1f453be368103541800393a669e6014edac1092 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Tue, 22 Sep 2020 13:40:27 +0200 Subject: [PATCH 071/185] C++: import SimpleRangeAnalysisInternal This ensures that `getFullyConverted{Lower,Upper}Bounds` are available where they need to be called. --- .../cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll index e1d64dfcf5a..0cf570a72b4 100644 --- a/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll +++ b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll @@ -61,3 +61,5 @@ abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition { */ abstract float getUpperBounds(StackVariable v); } + +import SimpleRangeAnalysisInternal From 826632d6a96b636f54ce674a3e0a1b8e755c71b8 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Tue, 22 Sep 2020 14:02:14 +0200 Subject: [PATCH 072/185] C++: Add a test of def overrides The def used in this test is not overridden yet. --- .../rangeanalysis/extensibility/extensibility.c | 7 ++++++- .../rangeanalysis/extensibility/extensibility.expected | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c index afdde0b615c..288b68a0bd7 100644 --- a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c @@ -11,4 +11,9 @@ int test_extensibility_add(int x) { int test_overridability_sub(int x) { int result = x - x; // Returns 0 due to custom modeling in QL return result; // 0 -} \ No newline at end of file +} + +void test_parameter_override(int magic_name_at_most_10, int magic_name_at_most_20) { + magic_name_at_most_10; + magic_name_at_most_20; +} diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected index ad97d9b2df5..4ae1961c247 100644 --- a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected @@ -5,3 +5,5 @@ | extensibility.c:12:16:12:16 | x | -2.147483648E9 | 2.147483647E9 | | extensibility.c:12:20:12:20 | x | -2.147483648E9 | 2.147483647E9 | | extensibility.c:13:10:13:15 | result | 0.0 | 0.0 | +| extensibility.c:17:3:17:23 | magic_name_at_most_10 | -2.147483648E9 | 2.147483647E9 | +| extensibility.c:18:3:18:23 | magic_name_at_most_20 | -2.147483648E9 | 2.147483647E9 | From 9fd8b0431ae8e58ddadce0b68cab4cd7c7befcad Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Tue, 22 Sep 2020 15:45:17 +0200 Subject: [PATCH 073/185] C++: Add a SimpleRangeAnalysisDefinition test def --- .../extensibility/extensibility.expected | 4 +-- .../extensibility/extensibility.ql | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected index 4ae1961c247..cc8508a7858 100644 --- a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected @@ -5,5 +5,5 @@ | extensibility.c:12:16:12:16 | x | -2.147483648E9 | 2.147483647E9 | | extensibility.c:12:20:12:20 | x | -2.147483648E9 | 2.147483647E9 | | extensibility.c:13:10:13:15 | result | 0.0 | 0.0 | -| extensibility.c:17:3:17:23 | magic_name_at_most_10 | -2.147483648E9 | 2.147483647E9 | -| extensibility.c:18:3:18:23 | magic_name_at_most_20 | -2.147483648E9 | 2.147483647E9 | +| extensibility.c:17:3:17:23 | magic_name_at_most_10 | -2.147483648E9 | 10.0 | +| extensibility.c:18:3:18:23 | magic_name_at_most_20 | -2.147483648E9 | 20.0 | diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql index a007a5d4dab..841fe2c8e6c 100644 --- a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql @@ -1,5 +1,7 @@ import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils // for typeLowerBound import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr +import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDefinition class CustomAddFunctionCall extends SimpleRangeAnalysisExpr, FunctionCall { CustomAddFunctionCall() { this.getTarget().hasGlobalName("custom_add_function") } @@ -37,6 +39,40 @@ class SelfSub extends SimpleRangeAnalysisExpr, SubExpr { override predicate dependsOnChild(Expr child) { child = this.getAnOperand() } } +/** + * A definition for test purposes of a parameter `p` that starts with a + * special prefix. This class is written to exploit how QL behaves when class + * fields are not functionally determined by `this`. When multiple parameters + * of the same function have the special prefix, there is still only one + * instance of this class. + */ +class MagicParameterName extends SimpleRangeAnalysisDefinition { + Parameter p; + float value; + + MagicParameterName() { + this.definedByParameter(p) and + value = p.getName().regexpCapture("magic_name_at_most_(\\d+)", 1).toFloat() + } + + override predicate hasRangeInformationFor(StackVariable v) { v = p } + + override predicate dependsOnExpr(StackVariable v, Expr e) { + // No dependencies. This sample class yields constant values. + none() + } + + override float getLowerBounds(StackVariable var) { + var = p and + result = typeLowerBound(p.getUnspecifiedType()) + } + + override float getUpperBounds(StackVariable var) { + var = p and + result = value + } +} + from VariableAccess expr, float lower, float upper where lower = lowerBound(expr) and From 2551173156b24cb01a3dab53344ea1d882fcd6ad Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 21 Sep 2020 13:55:48 +0200 Subject: [PATCH 074/185] Python: Update example in QLDoc for TypeTracker --- python/ql/src/experimental/dataflow/TypeTracker.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/dataflow/TypeTracker.qll b/python/ql/src/experimental/dataflow/TypeTracker.qll index 40b45359f97..4491279a971 100644 --- a/python/ql/src/experimental/dataflow/TypeTracker.qll +++ b/python/ql/src/experimental/dataflow/TypeTracker.qll @@ -154,7 +154,7 @@ private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalAttributeN * t.start() and * result = < source of myType > * or - * exists (TypeTracker t2 | + * exists (DataFlow::TypeTracker t2 | * result = myType(t2).track(t2, t) * ) * } From 0265f263018f58305966d37e690a991d04960939 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 21 Sep 2020 14:00:42 +0200 Subject: [PATCH 075/185] Python: Add importModule and importMember DataFlow helpers --- .../dataflow/internal/DataFlowUtil.qll | 49 +++++++++++++++++++ .../import-helper/ImportHelper.expected | 20 ++++++++ .../dataflow/import-helper/ImportHelper.ql | 8 +++ .../dataflow/import-helper/README.md | 1 + .../dataflow/import-helper/mypkg/__init__.py | 1 + .../dataflow/import-helper/mypkg/bar.py | 1 + .../dataflow/import-helper/mypkg/foo.py | 1 + .../dataflow/import-helper/test1.py | 6 +++ .../dataflow/import-helper/test2.py | 3 ++ .../dataflow/import-helper/test3.py | 4 ++ .../dataflow/import-helper/test4.py | 4 ++ .../dataflow/import-helper/test5.py | 10 ++++ .../dataflow/import-helper/test6.py | 6 +++ .../dataflow/import-helper/test7.py | 10 ++++ 14 files changed, 124 insertions(+) create mode 100644 python/ql/test/experimental/dataflow/import-helper/ImportHelper.expected create mode 100644 python/ql/test/experimental/dataflow/import-helper/ImportHelper.ql create mode 100644 python/ql/test/experimental/dataflow/import-helper/README.md create mode 100644 python/ql/test/experimental/dataflow/import-helper/mypkg/__init__.py create mode 100644 python/ql/test/experimental/dataflow/import-helper/mypkg/bar.py create mode 100644 python/ql/test/experimental/dataflow/import-helper/mypkg/foo.py create mode 100644 python/ql/test/experimental/dataflow/import-helper/test1.py create mode 100644 python/ql/test/experimental/dataflow/import-helper/test2.py create mode 100644 python/ql/test/experimental/dataflow/import-helper/test3.py create mode 100644 python/ql/test/experimental/dataflow/import-helper/test4.py create mode 100644 python/ql/test/experimental/dataflow/import-helper/test5.py create mode 100644 python/ql/test/experimental/dataflow/import-helper/test6.py create mode 100644 python/ql/test/experimental/dataflow/import-helper/test7.py diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll b/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll index c4acc112051..c868e1762ec 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll @@ -16,3 +16,52 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr * (intra-procedural) steps. */ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) } + +/** + * Gets an EssaNode that holds the module imported by `name`. + * Note that for the statement `import pkg.mod`, the new variable introduced is `pkg` that is a + * reference to the module `pkg`. + * + * This predicate handles (with optional `... as `): + * 1. `import ` + * 2. `from import ` when ` = + "." + ` + * 3. `from import ` when ` = + "." + ` + * + * Note: + * While it is technically possible that `import mypkg.foo` and `from mypkg import foo` can give different values, + * it's highly unlikely that this will be a problem in production level code. + * Example: If `mypkg/__init__.py` contains `foo = 42`, then `from mypkg import foo` will not import the module + * `mypkg/foo.py` but the variable `foo` containing `42` -- however, `import mypkg.foo` will always cause `mypkg.foo` + * to refer to the module. + * + * Also see `DataFlow::importMember` + */ +EssaNode importModule(string name) { + exists(Variable var, Import imp, Alias alias | + alias = imp.getAName() and + alias.getAsname() = var.getAStore() and + ( + name = alias.getValue().(ImportMember).getImportedModuleName() + or + name = alias.getValue().(ImportExpr).getImportedModuleName() + ) and + result.getVar().(AssignmentDefinition).getSourceVariable() = var + ) +} + +/** + * Gets a EssaNode that holds the value imported by using fully qualified name in + *`from import `. + * + * Also see `DataFlow::importModule`. + */ +EssaNode importMember(string moduleName, string memberName) { + exists(Variable var, Import imp, Alias alias, ImportMember member | + alias = imp.getAName() and + member = alias.getValue() and + moduleName = member.getModule().(ImportExpr).getImportedModuleName() and + memberName = member.getName() and + alias.getAsname() = var.getAStore() and + result.getVar().(AssignmentDefinition).getSourceVariable() = var + ) +} diff --git a/python/ql/test/experimental/dataflow/import-helper/ImportHelper.expected b/python/ql/test/experimental/dataflow/import-helper/ImportHelper.expected new file mode 100644 index 00000000000..521bb780a56 --- /dev/null +++ b/python/ql/test/experimental/dataflow/import-helper/ImportHelper.expected @@ -0,0 +1,20 @@ +importModule +| test1.py:1:8:1:12 | GSSA Variable mypkg | mypkg | +| test2.py:1:19:1:21 | GSSA Variable foo | mypkg.foo | +| test2.py:1:24:1:26 | GSSA Variable bar | mypkg.bar | +| test3.py:2:8:2:16 | GSSA Variable mypkg | mypkg | +| test4.py:1:21:1:24 | GSSA Variable _foo | mypkg.foo | +| test4.py:2:21:2:24 | GSSA Variable _bar | mypkg.bar | +| test5.py:1:8:1:12 | GSSA Variable mypkg | mypkg | +| test5.py:9:26:9:29 | GSSA Variable _bar | mypkg.bar | +| test6.py:1:8:1:12 | GSSA Variable mypkg | mypkg | +| test6.py:5:8:5:16 | GSSA Variable mypkg | mypkg | +| test7.py:1:19:1:21 | GSSA Variable foo | mypkg.foo | +| test7.py:5:8:5:16 | GSSA Variable mypkg | mypkg | +| test7.py:9:19:9:21 | GSSA Variable foo | mypkg.foo | +importMember +| test2.py:1:19:1:21 | GSSA Variable foo | mypkg | foo | +| test2.py:1:24:1:26 | GSSA Variable bar | mypkg | bar | +| test5.py:9:26:9:29 | GSSA Variable _bar | mypkg | bar | +| test7.py:1:19:1:21 | GSSA Variable foo | mypkg | foo | +| test7.py:9:19:9:21 | GSSA Variable foo | mypkg | foo | diff --git a/python/ql/test/experimental/dataflow/import-helper/ImportHelper.ql b/python/ql/test/experimental/dataflow/import-helper/ImportHelper.ql new file mode 100644 index 00000000000..68d794f1636 --- /dev/null +++ b/python/ql/test/experimental/dataflow/import-helper/ImportHelper.ql @@ -0,0 +1,8 @@ +import python +import experimental.dataflow.DataFlow + +query predicate importModule(DataFlow::Node res, string name) { res = DataFlow::importModule(name) } + +query predicate importMember(DataFlow::Node res, string moduleName, string memberName) { + res = DataFlow::importMember(moduleName, memberName) +} diff --git a/python/ql/test/experimental/dataflow/import-helper/README.md b/python/ql/test/experimental/dataflow/import-helper/README.md new file mode 100644 index 00000000000..bcee9b620db --- /dev/null +++ b/python/ql/test/experimental/dataflow/import-helper/README.md @@ -0,0 +1 @@ +Small tests that explore difference between `import mypkg.foo` and `from mypkg import foo`. diff --git a/python/ql/test/experimental/dataflow/import-helper/mypkg/__init__.py b/python/ql/test/experimental/dataflow/import-helper/mypkg/__init__.py new file mode 100644 index 00000000000..c84a9b135a3 --- /dev/null +++ b/python/ql/test/experimental/dataflow/import-helper/mypkg/__init__.py @@ -0,0 +1 @@ +foo = 42 diff --git a/python/ql/test/experimental/dataflow/import-helper/mypkg/bar.py b/python/ql/test/experimental/dataflow/import-helper/mypkg/bar.py new file mode 100644 index 00000000000..2ae28399f5f --- /dev/null +++ b/python/ql/test/experimental/dataflow/import-helper/mypkg/bar.py @@ -0,0 +1 @@ +pass diff --git a/python/ql/test/experimental/dataflow/import-helper/mypkg/foo.py b/python/ql/test/experimental/dataflow/import-helper/mypkg/foo.py new file mode 100644 index 00000000000..2ae28399f5f --- /dev/null +++ b/python/ql/test/experimental/dataflow/import-helper/mypkg/foo.py @@ -0,0 +1 @@ +pass diff --git a/python/ql/test/experimental/dataflow/import-helper/test1.py b/python/ql/test/experimental/dataflow/import-helper/test1.py new file mode 100644 index 00000000000..fa4d1464942 --- /dev/null +++ b/python/ql/test/experimental/dataflow/import-helper/test1.py @@ -0,0 +1,6 @@ +import mypkg +print(mypkg.foo) # 42 +try: + print(mypkg.bar) +except AttributeError as e: + print(e) # module 'mypkg' has no attribute 'bar' diff --git a/python/ql/test/experimental/dataflow/import-helper/test2.py b/python/ql/test/experimental/dataflow/import-helper/test2.py new file mode 100644 index 00000000000..a706d61fd0c --- /dev/null +++ b/python/ql/test/experimental/dataflow/import-helper/test2.py @@ -0,0 +1,3 @@ +from mypkg import foo, bar +print(foo) +print(bar) diff --git a/python/ql/test/experimental/dataflow/import-helper/test3.py b/python/ql/test/experimental/dataflow/import-helper/test3.py new file mode 100644 index 00000000000..4fc602432c2 --- /dev/null +++ b/python/ql/test/experimental/dataflow/import-helper/test3.py @@ -0,0 +1,4 @@ +import mypkg.foo +import mypkg.bar +print(mypkg.foo) # Date: Mon, 21 Sep 2020 14:38:14 +0200 Subject: [PATCH 076/185] Python: Model os.system and os.popen --- .../semmle/python/frameworks/Stdlib.qll | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 60efafbdfde..a39e82f0879 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -7,3 +7,70 @@ private import python private import experimental.dataflow.DataFlow private import experimental.dataflow.RemoteFlowSources private import experimental.semmle.python.Concepts + +private module Stdlib { + /** Gets a reference to the `os` module. */ + DataFlow::Node os(DataFlow::TypeTracker t) { + t.start() and + result = DataFlow::importModule("os") + or + exists(DataFlow::TypeTracker t2 | result = os(t2).track(t2, t)) + } + + /** Gets a reference to the `os` module. */ + DataFlow::Node os() { result = os(DataFlow::TypeTracker::end()) } + + module os { + /** Gets a reference to the `os.system` function. */ + DataFlow::Node system(DataFlow::TypeTracker t) { + t.start() and + result = DataFlow::importMember("os", "system") + or + t.startInAttr("system") and + result = os() + or + exists(DataFlow::TypeTracker t2 | result = os::system(t2).track(t2, t)) + } + + /** Gets a reference to the `os.system` function. */ + DataFlow::Node system() { result = os::system(DataFlow::TypeTracker::end()) } + + /** Gets a reference to the `os.popen` function. */ + DataFlow::Node popen(DataFlow::TypeTracker t) { + t.start() and + result = DataFlow::importMember("os", "system") + or + t.startInAttr("popen") and + result = os() + or + exists(DataFlow::TypeTracker t2 | result = os::popen(t2).track(t2, t)) + } + + /** Gets a reference to the `os.popen` function. */ + DataFlow::Node popen() { result = os::popen(DataFlow::TypeTracker::end()) } + } + + /** + * A call to `os.system`. + * See https://docs.python.org/3/library/os.html#os.system + */ + private class OsSystemCall extends SystemCommandExecution::Range { + OsSystemCall() { this.asCfgNode().(CallNode).getFunction() = os::system().asCfgNode() } + + override DataFlow::Node getCommand() { + result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0) + } + } + + /** + * A call to `os.popen` + * See https://docs.python.org/3/library/os.html#os.popen + */ + private class OsPopenCall extends SystemCommandExecution::Range { + OsPopenCall() { this.asCfgNode().(CallNode).getFunction() = os::popen().asCfgNode() } + + override DataFlow::Node getCommand() { + result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0) + } + } +} From 7c205dd3fc25add98d9c8e679b66a8671043b21d Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 21 Sep 2020 14:39:15 +0200 Subject: [PATCH 077/185] Python: First attempt at modeling Flask --- .../semmle/python/frameworks/Flask.qll | 113 ++++++++- .../frameworks/flask/TestTaint.expected | 84 +++++++ .../frameworks/flask/TestTaint.ql | 6 + .../library-tests/frameworks/flask/test.py | 233 ++++++++++++++++++ .../test/experimental/library-tests/options | 1 + 5 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected create mode 100644 python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.ql create mode 100644 python/ql/test/experimental/library-tests/frameworks/flask/test.py create mode 100644 python/ql/test/experimental/library-tests/options diff --git a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll index 1639ceeebfa..f8dcd170255 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll @@ -7,4 +7,115 @@ private import experimental.dataflow.DataFlow private import experimental.dataflow.RemoteFlowSources private import experimental.semmle.python.Concepts -private module Flask { } +private module Flask { + /** Gets a reference to the `flask` module. */ + DataFlow::Node flask(DataFlow::TypeTracker t) { + t.start() and + result = DataFlow::importModule("flask") + or + exists(DataFlow::TypeTracker t2 | result = flask(t2).track(t2, t)) + } + + /** Gets a reference to the `flask` module. */ + DataFlow::Node flask() { result = flask(DataFlow::TypeTracker::end()) } + + module flask { + /** Gets a reference to the `flask.request` object. */ + DataFlow::Node request(DataFlow::TypeTracker t) { + t.start() and + result = DataFlow::importMember("flask", "request") + or + t.startInAttr("request") and + result = flask() + or + exists(DataFlow::TypeTracker t2 | result = flask::request(t2).track(t2, t)) + } + + /** Gets a reference to the `flask.request` object. */ + DataFlow::Node request() { result = flask::request(DataFlow::TypeTracker::end()) } + } + + // TODO: Do we even need this class then? :| + private class RequestSource extends RemoteFlowSource::Range { + RequestSource() { this = flask::request() } + + override string getSourceType() { result = "flask.request" } + } + + // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict + /** Gets a reference to the MultiDict attributes of `flask.request`. */ + DataFlow::Node requestMultiDictAttribute(DataFlow::TypeTracker t) { + t.start() and + result.asCfgNode().(AttrNode).getObject(["args", "values", "form"]) = + flask::request().asCfgNode() + or + exists(DataFlow::TypeTracker t2 | result = requestMultiDictAttribute(t2).track(t2, t)) + } + + /** Gets a reference to the MultiDict attributes of `flask.request`. */ + DataFlow::Node requestMultiDictAttribute() { + result = requestMultiDictAttribute(DataFlow::TypeTracker::end()) + } + + private class RequestInputAccess extends RemoteFlowSource::Range { + RequestInputAccess() { + // attributes + exists(AttrNode attr, string name | + this.asCfgNode() = attr and attr.getObject(name) = flask::request().asCfgNode() + | + name in ["path", + // string + "full_path", "base_url", "url", "access_control_request_method", "content_encoding", + "content_md5", "content_type", "data", "method", "mimetype", "origin", "query_string", + "referrer", "remote_addr", "remote_user", "user_agent", + // dict + "environ", "cookies", "mimetype_params", "view_args", + // + "args", "values", "form", + // json + "json", + // List[str] + "access_route", + // file-like + "stream", "input_stream", + // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.HeaderSet + "access_control_request_headers", "pragma", + // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Accept + // TODO: Kinda badly modeled for now -- has type List[Tuple[value, quality]], and some extra methods + "accept_charsets", "accept_encodings", "accept_languages", "accept_mimetypes", + // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Authorization + // TODO: dict subclass with extra attributes like `username` and `password` + "authorization", + // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.RequestCacheControl + // TODO: has attributes like `no_cache`, and `to_header` method (actually, many of these models do) + "cache_control", + // TODO: MultiDict[FileStorage] + // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage + "files", + // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers + // TODO: dict-like with wsgiref.headers.Header compatibility methods + "headers"] + ) + or + // methods + exists(CallNode call, string name | this.asCfgNode() = call | + // NOTE: will not track bound method, `f = func; f()` + name in ["get_data", "get_json"] and + call.getFunction().(AttrNode).getObject(name) = flask::request().asCfgNode() + ) + or + // multi dict special handling + ( + this = requestMultiDictAttribute() + or + exists(CallNode call | this.asCfgNode() = call | + // NOTE: will not track bound method, `f = func; f()` + call.getFunction().(AttrNode).getObject("getlist") = + requestMultiDictAttribute().asCfgNode() + ) + ) + } + + override string getSourceType() { result = "flask.request input" } + } +} diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected new file mode 100644 index 00000000000..a3a7df7fbdb --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected @@ -0,0 +1,84 @@ +| test.py:6 | fail | test_taint | name | +| test.py:6 | fail | test_taint | number | +| test.py:7 | ok | test_taint | foo | +| test.py:14 | ok | test_taint | request.environ | +| test.py:15 | ok | test_taint | request.environ.get(..) | +| test.py:17 | ok | test_taint | request.path | +| test.py:18 | ok | test_taint | request.full_path | +| test.py:19 | ok | test_taint | request.base_url | +| test.py:20 | ok | test_taint | request.url | +| test.py:23 | fail | test_taint | request.accept_charsets.best | +| test.py:24 | fail | test_taint | request.accept_charsets.best_match(..) | +| test.py:25 | ok | test_taint | request.accept_charsets[0] | +| test.py:26 | ok | test_taint | request.accept_encodings | +| test.py:27 | ok | test_taint | request.accept_languages | +| test.py:28 | ok | test_taint | request.accept_mimetypes | +| test.py:31 | ok | test_taint | request.access_control_request_headers | +| test.py:33 | ok | test_taint | request.access_control_request_method | +| test.py:35 | ok | test_taint | request.access_route | +| test.py:36 | ok | test_taint | request.access_route[0] | +| test.py:39 | ok | test_taint | request.args | +| test.py:40 | ok | test_taint | request.args['key'] | +| test.py:41 | ok | test_taint | request.args.getlist(..) | +| test.py:44 | ok | test_taint | request.authorization | +| test.py:45 | ok | test_taint | request.authorization['username'] | +| test.py:46 | fail | test_taint | request.authorization.username | +| test.py:49 | ok | test_taint | request.cache_control | +| test.py:51 | fail | test_taint | request.cache_control.max_age | +| test.py:52 | fail | test_taint | request.cache_control.max_stale | +| test.py:53 | fail | test_taint | request.cache_control.min_fresh | +| test.py:55 | ok | test_taint | request.content_encoding | +| test.py:57 | ok | test_taint | request.content_md5 | +| test.py:59 | ok | test_taint | request.content_type | +| test.py:62 | ok | test_taint | request.cookies | +| test.py:63 | ok | test_taint | request.cookies['key'] | +| test.py:65 | ok | test_taint | request.data | +| test.py:68 | ok | test_taint | request.files | +| test.py:69 | ok | test_taint | request.files['key'] | +| test.py:70 | fail | test_taint | request.files['key'].filename | +| test.py:71 | fail | test_taint | request.files['key'].stream | +| test.py:72 | fail | test_taint | request.files.getlist(..) | +| test.py:75 | ok | test_taint | request.form | +| test.py:76 | ok | test_taint | request.form['key'] | +| test.py:77 | ok | test_taint | request.form.getlist(..) | +| test.py:79 | ok | test_taint | request.get_data() | +| test.py:81 | ok | test_taint | request.get_json() | +| test.py:82 | ok | test_taint | request.get_json()['foo'] | +| test.py:83 | ok | test_taint | request.get_json()['foo']['bar'] | +| test.py:87 | ok | test_taint | request.headers | +| test.py:88 | ok | test_taint | request.headers['key'] | +| test.py:89 | fail | test_taint | request.headers.get_all(..) | +| test.py:90 | fail | test_taint | request.headers.getlist(..) | +| test.py:91 | ok | test_taint | list(..) | +| test.py:92 | fail | test_taint | request.headers.to_wsgi_list() | +| test.py:94 | ok | test_taint | request.json | +| test.py:95 | ok | test_taint | request.json['foo'] | +| test.py:96 | ok | test_taint | request.json['foo']['bar'] | +| test.py:98 | ok | test_taint | request.method | +| test.py:100 | ok | test_taint | request.mimetype | +| test.py:102 | ok | test_taint | request.mimetype_params | +| test.py:104 | ok | test_taint | request.origin | +| test.py:107 | ok | test_taint | request.pragma | +| test.py:109 | ok | test_taint | request.query_string | +| test.py:111 | ok | test_taint | request.referrer | +| test.py:113 | ok | test_taint | request.remote_addr | +| test.py:115 | ok | test_taint | request.remote_user | +| test.py:118 | ok | test_taint | request.stream | +| test.py:119 | ok | test_taint | request.input_stream | +| test.py:121 | ok | test_taint | request.url | +| test.py:123 | ok | test_taint | request.user_agent | +| test.py:126 | ok | test_taint | request.values | +| test.py:127 | ok | test_taint | request.values['key'] | +| test.py:128 | ok | test_taint | request.values.getlist(..) | +| test.py:131 | ok | test_taint | request.view_args | +| test.py:132 | ok | test_taint | request.view_args['key'] | +| test.py:136 | ok | test_taint | request.script_root | +| test.py:137 | ok | test_taint | request.url_root | +| test.py:141 | ok | test_taint | request.charset | +| test.py:142 | ok | test_taint | request.url_charset | +| test.py:146 | ok | test_taint | request.date | +| test.py:149 | ok | test_taint | request.endpoint | +| test.py:154 | ok | test_taint | request.host | +| test.py:155 | ok | test_taint | request.host_url | +| test.py:157 | ok | test_taint | request.scheme | +| test.py:159 | ok | test_taint | request.script_root | diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.ql b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.ql new file mode 100644 index 00000000000..fe81ffc5339 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.ql @@ -0,0 +1,6 @@ +import experimental.dataflow.tainttracking.TestTaintLib +import experimental.dataflow.RemoteFlowSources + +class RemoteFlowTestTaintConfiguration extends TestTaintTrackingConfiguration { + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } +} diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/test.py b/python/ql/test/experimental/library-tests/frameworks/flask/test.py new file mode 100644 index 00000000000..90dded6b9fd --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/flask/test.py @@ -0,0 +1,233 @@ +from flask import Flask, request +app = Flask(__name__) + +@app.route('/test_taint//') +def test_taint(name = "World!", number="0", foo="foo"): + ensure_tainted(name, number) + ensure_not_tainted(foo) + + # Manually inspected all fields of the Request object + # https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request + + ensure_tainted( + + request.environ, + request.environ.get('HTTP_AUTHORIZATION'), + + request.path, + request.full_path, + request.base_url, + request.url, + + # These request.accept_* properties are instances of subclasses of werkzeug.datastructures.Accept + request.accept_charsets.best, + request.accept_charsets.best_match(["utf-8", "utf-16"]), + request.accept_charsets[0], + request.accept_encodings, + request.accept_languages, + request.accept_mimetypes, + + # werkzeug.datastructures.HeaderSet (subclass of collections_abc.MutableSet) + request.access_control_request_headers, + + request.access_control_request_method, + + request.access_route, + request.access_route[0], + + # By default werkzeug.datastructures.ImmutableMultiDict -- although can be changed :\ + request.args, + request.args['key'], + request.args.getlist('key'), + + # werkzeug.datastructures.Authorization (a dict, with some properties) + request.authorization, + request.authorization['username'], + request.authorization.username, + + # werkzeug.datastructures.RequestCacheControl + request.cache_control, + # These should be `int`s, but can be strings... see debug method below + request.cache_control.max_age, + request.cache_control.max_stale, + request.cache_control.min_fresh, + + request.content_encoding, + + request.content_md5, + + request.content_type, + + # werkzeug.datastructures.ImmutableTypeConversionDict (which is basically just a dict) + request.cookies, + request.cookies['key'], + + request.data, + + # a werkzeug.datastructures.MultiDict, mapping [str, werkzeug.datastructures.FileStorage] + request.files, + request.files['key'], + request.files['key'].filename, + request.files['key'].stream, + request.files.getlist('key'), + + # By default werkzeug.datastructures.ImmutableMultiDict -- although can be changed :\ + request.form, + request.form['key'], + request.form.getlist('key'), + + request.get_data(), + + request.get_json(), + request.get_json()['foo'], + request.get_json()['foo']['bar'], + + # werkzeug.datastructures.EnvironHeaders, + # which has same interface as werkzeug.datastructures.Headers + request.headers, + request.headers['key'], + request.headers.get_all('key'), + request.headers.getlist('key'), + list(request.headers), # (k, v) list + request.headers.to_wsgi_list(), # (k, v) list + + request.json, + request.json['foo'], + request.json['foo']['bar'], + + request.method, + + request.mimetype, + + request.mimetype_params, + + request.origin, + + # werkzeug.datastructures.HeaderSet (subclass of collections_abc.MutableSet) + request.pragma, + + request.query_string, + + request.referrer, + + request.remote_addr, + + request.remote_user, + + # file-like object + request.stream, + request.input_stream, + + request.url, + + request.user_agent, + + # werkzeug.datastructures.CombinedMultiDict, which is basically just a werkzeug.datastructures.MultiDict + request.values, + request.values['key'], + request.values.getlist('key'), + + # dict + request.view_args, + request.view_args['key'], + ) + + ensure_not_tainted( + request.script_root, + request.url_root, + + # The expected charset for parsing request data / urls. Can not be changed by client. + # https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/wrappers/base_request.py#L71-L72 + request.charset, + request.url_charset, + + # request.date is a parsed `datetime` + # https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/wrappers/common_descriptors.py#L76-L83 + request.date, + + # Assuming that endpoints are not created by user-input seems fair + request.endpoint, + + # In some rare circumstances a client could spoof the host, but by default they + # should not be able to. See + # https://werkzeug.palletsprojects.com/en/1.0.x/wrappers/#werkzeug.wrappers.BaseRequest.trusted_hosts + request.host, + request.host_url, + + request.scheme, + + request.script_root, + ) + + +@app.route('/debug//', methods=['GET']) +def debug(foo, bar): + print("request.view_args", request.view_args) + + print("request.headers {!r}".format(request.headers)) + print("request.headers['accept'] {!r}".format(request.headers['accept'])) + + print("request.pragma {!r}".format(request.pragma)) + + return 'ok' + +@app.route('/stream', methods=['POST']) +def stream(): + print(request.path) + s = request.stream + print(s) + # just works :) + print(s.read()) + + return 'ok' + +@app.route('/input_stream', methods=['POST']) +def input_stream(): + print(request.path) + s = request.input_stream + print(s) + # hangs until client stops connection, since max number of bytes to read must + # be handled manually + print(s.read()) + + return 'ok' + +@app.route('/form', methods=['POST']) +def form(): + print(request.path) + print("request.form", request.form) + + return 'ok' + +@app.route('/cache_control', methods=['POST']) +def cache_control(): + print(request.path) + print("request.cache_control.max_age", request.cache_control.max_age, type(request.cache_control.max_age)) + print("request.cache_control.max_stale", request.cache_control.max_stale, type(request.cache_control.max_stale)) + print("request.cache_control.min_fresh", request.cache_control.min_fresh, type(request.cache_control.min_fresh)) + + return 'ok' + +@app.route('/file_upload', methods=['POST']) +def file_upload(): + print(request.path) + for k,v in request.files.items(): + print(k, v, v.name, v.filename, v.stream) + + return 'ok' + +# curl --header "My-Header: some-value" http://localhost:5000/debug/fooval/barval +# curl --header "Pragma: foo, bar" --header "Pragma: stuff, foo" http://localhost:5000/debug/fooval/barval + +# curl -X POST --data 'wat' http://localhost:5000/stream +# curl -X POST --data 'wat' http://localhost:5000/input_stream + +# curl --form foo=foo --form foo=123 http://localhost:5000/form + +# curl --header "Cache-Control: max-age=foo, max-stale=bar, min-fresh=baz" http://localhost:5000/cache_control +# curl --header "Cache-Control: max-age=1, max-stale=2, min-fresh=3" http://localhost:5000/cache_control + +# curl -F myfile=@ localhost:5000/file_upload + +if __name__ == "__main__": + app.run(debug=True) diff --git a/python/ql/test/experimental/library-tests/options b/python/ql/test/experimental/library-tests/options new file mode 100644 index 00000000000..eb214fc2931 --- /dev/null +++ b/python/ql/test/experimental/library-tests/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=1 From 2bdd0284dc0a152a677fc72ec79532bf7c6a5ab7 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 21 Sep 2020 14:44:09 +0200 Subject: [PATCH 078/185] Python: Port py-command-line-injection with new dataflow --- .../CWE-078/CommandInjection.ql | 37 +++++++++++++++++++ .../CWE-078/CommandInjection.expected | 11 ++++++ .../CWE-078/CommandInjection.qlref | 1 + .../CWE-078/command_injection.py | 35 ++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100755 python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql create mode 100644 python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected create mode 100644 python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.qlref create mode 100644 python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py diff --git a/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql new file mode 100755 index 00000000000..3f200b77516 --- /dev/null +++ b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql @@ -0,0 +1,37 @@ +/** + * @name Uncontrolled command line + * @description Using externally controlled strings in a command line may allow a malicious + * user to change the meaning of the command. + * @kind path-problem + * @problem.severity error + * @sub-severity high + * @precision high + * @id py/command-line-injection + * @tags correctness + * security + * external/owasp/owasp-a1 + * external/cwe/cwe-078 + * external/cwe/cwe-088 + */ + +import python +import experimental.dataflow.DataFlow +import experimental.dataflow.TaintTracking +import experimental.semmle.python.Concepts +import experimental.dataflow.RemoteFlowSources +import DataFlow::PathGraph + +class CommandInjectionConfiguration extends TaintTracking::Configuration { + CommandInjectionConfiguration() { this = "CommandInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { + sink = any(SystemCommandExecution e).getCommand() + } +} + +from CommandInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "This command depends on $@.", source.getNode(), + "a user-provided value" diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected new file mode 100644 index 00000000000..7f71e143109 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected @@ -0,0 +1,11 @@ +edges +| command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | +| command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | +nodes +| command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +#select +| command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | a user-provided value | diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.qlref b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.qlref new file mode 100644 index 00000000000..2d70a75c372 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.qlref @@ -0,0 +1 @@ +experimental/Security-new-dataflow/CWE-078/CommandInjection.ql diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py new file mode 100644 index 00000000000..d50baf6563b --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py @@ -0,0 +1,35 @@ + +import os +import subprocess + +from flask import Flask, request +app = Flask(__name__) + +@app.route("/command1") +def command_injection1(): + files = request.args.get('files', '') + # Don't let files be `; rm -rf /` + os.system("ls " + files) + + +@app.route("/command2") +def command_injection2(): + files = request.args.get('files', '') + # Don't let files be `; rm -rf /` + subprocess.Popen(["ls", files], shell = True) + + +@app.route("/command3") +def first_arg_injection(): + cmd = request.args.get('cmd', '') + subprocess.Popen([cmd, "param1"]) + + +@app.route("/other_cases") +def others(): + files = request.args.get('files', '') + # Don't let files be `; rm -rf /` + os.popen("ls " + files) + +# TODO: popen2 module for Python 2 only https://devdocs.io/python~2.7/library/popen2 +# (deprecated since Python 2.6, but still functional in Python 2.7.17) From 3c08590ee4e13c9b40b3c60750ef013e500a5606 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 21 Sep 2020 20:52:36 +0200 Subject: [PATCH 079/185] Python: Expand flask tests a bit --- .../frameworks/flask/TestTaint.expected | 100 ++++++++++-------- .../library-tests/frameworks/flask/test.py | 32 ++++++ 2 files changed, 88 insertions(+), 44 deletions(-) diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected index a3a7df7fbdb..bf58180bd75 100644 --- a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected +++ b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected @@ -38,47 +38,59 @@ | test.py:70 | fail | test_taint | request.files['key'].filename | | test.py:71 | fail | test_taint | request.files['key'].stream | | test.py:72 | fail | test_taint | request.files.getlist(..) | -| test.py:75 | ok | test_taint | request.form | -| test.py:76 | ok | test_taint | request.form['key'] | -| test.py:77 | ok | test_taint | request.form.getlist(..) | -| test.py:79 | ok | test_taint | request.get_data() | -| test.py:81 | ok | test_taint | request.get_json() | -| test.py:82 | ok | test_taint | request.get_json()['foo'] | -| test.py:83 | ok | test_taint | request.get_json()['foo']['bar'] | -| test.py:87 | ok | test_taint | request.headers | -| test.py:88 | ok | test_taint | request.headers['key'] | -| test.py:89 | fail | test_taint | request.headers.get_all(..) | -| test.py:90 | fail | test_taint | request.headers.getlist(..) | -| test.py:91 | ok | test_taint | list(..) | -| test.py:92 | fail | test_taint | request.headers.to_wsgi_list() | -| test.py:94 | ok | test_taint | request.json | -| test.py:95 | ok | test_taint | request.json['foo'] | -| test.py:96 | ok | test_taint | request.json['foo']['bar'] | -| test.py:98 | ok | test_taint | request.method | -| test.py:100 | ok | test_taint | request.mimetype | -| test.py:102 | ok | test_taint | request.mimetype_params | -| test.py:104 | ok | test_taint | request.origin | -| test.py:107 | ok | test_taint | request.pragma | -| test.py:109 | ok | test_taint | request.query_string | -| test.py:111 | ok | test_taint | request.referrer | -| test.py:113 | ok | test_taint | request.remote_addr | -| test.py:115 | ok | test_taint | request.remote_user | -| test.py:118 | ok | test_taint | request.stream | -| test.py:119 | ok | test_taint | request.input_stream | -| test.py:121 | ok | test_taint | request.url | -| test.py:123 | ok | test_taint | request.user_agent | -| test.py:126 | ok | test_taint | request.values | -| test.py:127 | ok | test_taint | request.values['key'] | -| test.py:128 | ok | test_taint | request.values.getlist(..) | -| test.py:131 | ok | test_taint | request.view_args | -| test.py:132 | ok | test_taint | request.view_args['key'] | -| test.py:136 | ok | test_taint | request.script_root | -| test.py:137 | ok | test_taint | request.url_root | -| test.py:141 | ok | test_taint | request.charset | -| test.py:142 | ok | test_taint | request.url_charset | -| test.py:146 | ok | test_taint | request.date | -| test.py:149 | ok | test_taint | request.endpoint | -| test.py:154 | ok | test_taint | request.host | -| test.py:155 | ok | test_taint | request.host_url | -| test.py:157 | ok | test_taint | request.scheme | -| test.py:159 | ok | test_taint | request.script_root | +| test.py:73 | fail | test_taint | request.files.getlist(..)[0].filename | +| test.py:74 | fail | test_taint | request.files.getlist(..)[0].stream | +| test.py:77 | ok | test_taint | request.form | +| test.py:78 | ok | test_taint | request.form['key'] | +| test.py:79 | ok | test_taint | request.form.getlist(..) | +| test.py:81 | ok | test_taint | request.get_data() | +| test.py:83 | ok | test_taint | request.get_json() | +| test.py:84 | ok | test_taint | request.get_json()['foo'] | +| test.py:85 | ok | test_taint | request.get_json()['foo']['bar'] | +| test.py:89 | ok | test_taint | request.headers | +| test.py:90 | ok | test_taint | request.headers['key'] | +| test.py:91 | fail | test_taint | request.headers.get_all(..) | +| test.py:92 | fail | test_taint | request.headers.getlist(..) | +| test.py:93 | ok | test_taint | list(..) | +| test.py:94 | fail | test_taint | request.headers.to_wsgi_list() | +| test.py:96 | ok | test_taint | request.json | +| test.py:97 | ok | test_taint | request.json['foo'] | +| test.py:98 | ok | test_taint | request.json['foo']['bar'] | +| test.py:100 | ok | test_taint | request.method | +| test.py:102 | ok | test_taint | request.mimetype | +| test.py:104 | ok | test_taint | request.mimetype_params | +| test.py:106 | ok | test_taint | request.origin | +| test.py:109 | ok | test_taint | request.pragma | +| test.py:111 | ok | test_taint | request.query_string | +| test.py:113 | ok | test_taint | request.referrer | +| test.py:115 | ok | test_taint | request.remote_addr | +| test.py:117 | ok | test_taint | request.remote_user | +| test.py:120 | ok | test_taint | request.stream | +| test.py:121 | ok | test_taint | request.input_stream | +| test.py:123 | ok | test_taint | request.url | +| test.py:125 | ok | test_taint | request.user_agent | +| test.py:128 | ok | test_taint | request.values | +| test.py:129 | ok | test_taint | request.values['key'] | +| test.py:130 | ok | test_taint | request.values.getlist(..) | +| test.py:133 | ok | test_taint | request.view_args | +| test.py:134 | ok | test_taint | request.view_args['key'] | +| test.py:138 | ok | test_taint | request.script_root | +| test.py:139 | ok | test_taint | request.url_root | +| test.py:143 | ok | test_taint | request.charset | +| test.py:144 | ok | test_taint | request.url_charset | +| test.py:148 | ok | test_taint | request.date | +| test.py:151 | ok | test_taint | request.endpoint | +| test.py:156 | ok | test_taint | request.host | +| test.py:157 | ok | test_taint | request.host_url | +| test.py:159 | ok | test_taint | request.scheme | +| test.py:161 | ok | test_taint | request.script_root | +| test.py:169 | ok | test_taint | request.args | +| test.py:170 | ok | test_taint | a | +| test.py:171 | ok | test_taint | b | +| test.py:173 | ok | test_taint | request.args['key'] | +| test.py:174 | ok | test_taint | a['key'] | +| test.py:175 | ok | test_taint | b['key'] | +| test.py:177 | ok | test_taint | request.args.getlist(..) | +| test.py:178 | ok | test_taint | a.getlist(..) | +| test.py:179 | ok | test_taint | b.getlist(..) | +| test.py:180 | fail | test_taint | gl(..) | diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/test.py b/python/ql/test/experimental/library-tests/frameworks/flask/test.py index 90dded6b9fd..4d89603ee74 100644 --- a/python/ql/test/experimental/library-tests/frameworks/flask/test.py +++ b/python/ql/test/experimental/library-tests/frameworks/flask/test.py @@ -70,6 +70,8 @@ def test_taint(name = "World!", number="0", foo="foo"): request.files['key'].filename, request.files['key'].stream, request.files.getlist('key'), + request.files.getlist('key')[0].filename, + request.files.getlist('key')[0].stream, # By default werkzeug.datastructures.ImmutableMultiDict -- although can be changed :\ request.form, @@ -159,6 +161,27 @@ def test_taint(name = "World!", number="0", foo="foo"): request.script_root, ) + # Testing some more tricky data-flow still works + a = request.args + b = a + gl = b.getlist + ensure_tainted( + request.args, + a, + b, + + request.args['key'], + a['key'], + b['key'], + + request.args.getlist('key'), + a.getlist('key'), + b.getlist('key'), + gl('key'), + ) + + + @app.route('/debug//', methods=['GET']) def debug(foo, bar): @@ -216,6 +239,13 @@ def file_upload(): return 'ok' +@app.route('/args', methods=['GET']) +def args(): + print(request.path) + print("request.args", request.args) + + return 'ok' + # curl --header "My-Header: some-value" http://localhost:5000/debug/fooval/barval # curl --header "Pragma: foo, bar" --header "Pragma: stuff, foo" http://localhost:5000/debug/fooval/barval @@ -229,5 +259,7 @@ def file_upload(): # curl -F myfile=@ localhost:5000/file_upload +# curl http://localhost:5000/args?foo=42&bar=bar + if __name__ == "__main__": app.run(debug=True) From 00ea0cebc344284360a26d489fd359b29c5383f4 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 21 Sep 2020 20:55:53 +0200 Subject: [PATCH 080/185] Python: More Flask modeling kinda works It "kinda" works now, but it really is not a pretty solution. Adding all these "tracked" objects is SUPER annoying... it _would_ be possible to skip them, but that seems like it will give the wrong edges for dataflow/taintflow queries :| A good chunk of it should be able to be removed with access-paths like C# does for library modeling. Some of it could be solved by better type-tracking API like API Graphs... but it seems like we generally are just lacking the nice-to-have features like `.getAMemberCall` and the like. See https://github.com/github/codeql/pull/4082/files#diff-9aa94c4d713ef9d8da73918ff53db774L33 --- .../dataflow/internal/DataFlowUtil.qll | 25 ++++ .../semmle/python/frameworks/Flask.qll | 80 +++++----- .../semmle/python/frameworks/Werkzeug.qll | 140 ++++++++++++++++++ .../frameworks/flask/TestTaint.expected | 12 +- 4 files changed, 212 insertions(+), 45 deletions(-) create mode 100644 python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll b/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll index c868e1762ec..c407a94becb 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll @@ -65,3 +65,28 @@ EssaNode importMember(string moduleName, string memberName) { result.getVar().(AssignmentDefinition).getSourceVariable() = var ) } + +abstract class ListLike extends Node { + /** Gets a Node that is an access of an element of this list-like object */ + Node getElementAccess() { + // subscript + result.asCfgNode().(SubscriptNode).getObject() = this.asCfgNode() + or + // get + // NOTE: will not track bound method, `f = obj.func; f()` + result.asCfgNode().(CallNode).getFunction().(AttrNode).getObject("pop") = this.asCfgNode() + } +} + +/** Class of dictionary-like objects */ +abstract class DictLike extends Node { + /** Gets a Node that is an access of an element of this dictionary-like object */ + Node getElementAccess() { + // subscript + result.asCfgNode().(SubscriptNode).getObject() = this.asCfgNode() + or + // get + // NOTE: will not track bound method, `f = obj.func; f()` + result.asCfgNode().(CallNode).getFunction().(AttrNode).getObject("get") = this.asCfgNode() + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll index f8dcd170255..9c3703d3029 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll @@ -6,6 +6,7 @@ private import python private import experimental.dataflow.DataFlow private import experimental.dataflow.RemoteFlowSources private import experimental.semmle.python.Concepts +private import experimental.semmle.python.frameworks.Werkzeug private module Flask { /** Gets a reference to the `flask` module. */ @@ -42,42 +43,39 @@ private module Flask { override string getSourceType() { result = "flask.request" } } - // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict - /** Gets a reference to the MultiDict attributes of `flask.request`. */ - DataFlow::Node requestMultiDictAttribute(DataFlow::TypeTracker t) { - t.start() and - result.asCfgNode().(AttrNode).getObject(["args", "values", "form"]) = - flask::request().asCfgNode() - or - exists(DataFlow::TypeTracker t2 | result = requestMultiDictAttribute(t2).track(t2, t)) - } - - /** Gets a reference to the MultiDict attributes of `flask.request`. */ - DataFlow::Node requestMultiDictAttribute() { - result = requestMultiDictAttribute(DataFlow::TypeTracker::end()) - } - + /** + * A source of remote flow from attributes from a flask request. + * + * See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request + */ private class RequestInputAccess extends RemoteFlowSource::Range { + string attr_name; + RequestInputAccess() { // attributes - exists(AttrNode attr, string name | - this.asCfgNode() = attr and attr.getObject(name) = flask::request().asCfgNode() + exists(AttrNode attr | + this.asCfgNode() = attr and attr.getObject(attr_name) = flask::request().asCfgNode() | - name in ["path", - // string + attr_name in ["path", + // str "full_path", "base_url", "url", "access_control_request_method", "content_encoding", "content_md5", "content_type", "data", "method", "mimetype", "origin", "query_string", "referrer", "remote_addr", "remote_user", "user_agent", // dict "environ", "cookies", "mimetype_params", "view_args", - // - "args", "values", "form", // json "json", // List[str] "access_route", // file-like "stream", "input_stream", + // MultiDict[str, str] + // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict + "args", "values", "form", + // MultiDict[str, FileStorage] + // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage + // TODO: FileStorage needs extra taint steps + "files", // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.HeaderSet "access_control_request_headers", "pragma", // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Accept @@ -89,33 +87,37 @@ private module Flask { // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.RequestCacheControl // TODO: has attributes like `no_cache`, and `to_header` method (actually, many of these models do) "cache_control", - // TODO: MultiDict[FileStorage] - // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage - "files", // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers // TODO: dict-like with wsgiref.headers.Header compatibility methods "headers"] ) or // methods - exists(CallNode call, string name | this.asCfgNode() = call | - // NOTE: will not track bound method, `f = func; f()` - name in ["get_data", "get_json"] and - call.getFunction().(AttrNode).getObject(name) = flask::request().asCfgNode() - ) - or - // multi dict special handling - ( - this = requestMultiDictAttribute() - or - exists(CallNode call | this.asCfgNode() = call | - // NOTE: will not track bound method, `f = func; f()` - call.getFunction().(AttrNode).getObject("getlist") = - requestMultiDictAttribute().asCfgNode() - ) + exists(CallNode call | this.asCfgNode() = call | + // NOTE: will not track bound method, `f = obj.func; f()` + attr_name in ["get_data", "get_json"] and + call.getFunction().(AttrNode).getObject(attr_name) = flask::request().asCfgNode() ) } override string getSourceType() { result = "flask.request input" } } + + private class RequestInputMultiDict extends RequestInputAccess, + Werkzeug::Datastructures::MultiDict { + RequestInputMultiDict() { attr_name in ["args", "values", "form", "files"] } + } + + private class RequestInputFiles extends RequestInputMultiDict { + RequestInputFiles() { attr_name = "files" } + } + + private class RequestInputFileStorage extends Werkzeug::Datastructures::FileStorage { + RequestInputFileStorage() { + exists(RequestInputFiles files, Werkzeug::Datastructures::MultiDictTracked filesTracked | + filesTracked.getMultiDict() = files and + this = filesTracked.getElementAccess() + ) + } + } } diff --git a/python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll b/python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll new file mode 100644 index 00000000000..16cdf7c3f72 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll @@ -0,0 +1,140 @@ +/** + * Provides classes modeling security-relevant aspects of the `flask` package. + */ + +private import python +private import experimental.dataflow.DataFlow +private import experimental.dataflow.TaintTracking + +module Werkzeug { + module Datastructures { + // ---------------------------------------------------------------------- // + // MultiDict // + // ---------------------------------------------------------------------- // + /** + * A Node representing an instance of a werkzeug.datastructures.MultiDict + * + * See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict + */ + abstract class MultiDict extends DataFlow::Node { } + + private DataFlow::Node multiDictTrack(MultiDict multiDict, DataFlow::TypeTracker t) { + t.start() and + result instanceof MultiDict + or + exists(DataFlow::TypeTracker t2 | result = multiDictTrack(multiDict, t2).track(t2, t)) + } + + /** Gets a reference to the MultiDict attributes of `flask.request`. */ + private DataFlow::Node multiDictTrack(MultiDict multiDict) { + result = multiDictTrack(multiDict, DataFlow::TypeTracker::end()) + } + + class MultiDictTracked extends DataFlow::Node, DataFlow::DictLike { + MultiDict multiDict; + + MultiDictTracked() { this = multiDictTrack(multiDict) } + + MultiDict getMultiDict() { result = multiDict } + + override DataFlow::Node getElementAccess() { + result = DataFlow::DictLike.super.getElementAccess() + or + exists(MultiDictGetListCallResultTracked tracked_call_result | + tracked_call_result.getCall().getMultiDict() = this and + result = tracked_call_result.getElementAccess() + ) + } + } + + private DataFlow::Node multiDictGetListTrack(MultiDictTracked multiDict, DataFlow::TypeTracker t) { + /* + * using t.startInAttr("getlist") was not good solution + * ```py + * a = request.args + * b = a + * a.getlist("key") + * b.getlist("key") + * ``` + * would give `request.args` -> `b.getlist` -- this is correct, but not helpful in a taint-path explanation, + * we REALLY WANT it to be `request.args -> a -> b -> b.getlist` + * This requirement means that we do need the predicate `multiDictTrack`, which could be spared otherwise. + */ + + t.start() and + result.asCfgNode().(AttrNode).getObject("getlist") = multiDict.asCfgNode() + or + exists(DataFlow::TypeTracker t2 | result = multiDictGetListTrack(multiDict, t2).track(t2, t)) + } + + private DataFlow::Node multiDictGetListTrack(MultiDictTracked multiDict) { + result = multiDictGetListTrack(multiDict, DataFlow::TypeTracker::end()) + } + + private class MultiDictGetListCall extends DataFlow::Node { + MultiDictTracked multiDict; + + MultiDictGetListCall() { + this.asCfgNode().(CallNode).getFunction() = multiDictGetListTrack(multiDict).asCfgNode() + } + + MultiDictTracked getMultiDict() { result = multiDict } + } + + private DataFlow::Node multiDictGetListCallTrack( + MultiDictGetListCall call, DataFlow::TypeTracker t + ) { + t.start() and + result = call + or + exists(DataFlow::TypeTracker t2 | result = multiDictGetListCallTrack(call, t2).track(t2, t)) + } + + /** Gets a reference to the MultiDict attributes of `flask.request`. */ + private DataFlow::Node multiDictGetListCallTrack(MultiDictGetListCall call) { + result = multiDictGetListCallTrack(call, DataFlow::TypeTracker::end()) + } + + private class MultiDictGetListCallResultTracked extends DataFlow::Node, DataFlow::ListLike { + MultiDictGetListCall call; + + MultiDictGetListCallResultTracked() { this = multiDictGetListCallTrack(call) } + + MultiDictGetListCall getCall() { result = call } + } + + private class MultiDictAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { + override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + nodeTo.(MultiDictGetListCall).getMultiDict() = nodeFrom.(MultiDictTracked) + } + } + + // ---------------------------------------------------------------------- // + // FileStorage // + // ---------------------------------------------------------------------- // + /** + * A Node representing an instance of a werkzeug.datastructures.FileStorage + * + * See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage + */ + abstract class FileStorage extends DataFlow::Node { } + + private class FileStorageAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { + override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + nodeFrom instanceof FileStorage and + exists(string name | + name in ["filename", + // str + "name", "content_type", "mimetype", + // file-like + "stream", + // TODO: werkzeug.datastructures.Headers + "headers", + // dict[str, str] + "mimetype_params"] and + nodeTo.asCfgNode().(AttrNode).getObject(name) = nodeFrom.asCfgNode() + ) + } + } + } +} diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected index bf58180bd75..0e35defeca2 100644 --- a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected +++ b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected @@ -35,11 +35,11 @@ | test.py:65 | ok | test_taint | request.data | | test.py:68 | ok | test_taint | request.files | | test.py:69 | ok | test_taint | request.files['key'] | -| test.py:70 | fail | test_taint | request.files['key'].filename | -| test.py:71 | fail | test_taint | request.files['key'].stream | -| test.py:72 | fail | test_taint | request.files.getlist(..) | -| test.py:73 | fail | test_taint | request.files.getlist(..)[0].filename | -| test.py:74 | fail | test_taint | request.files.getlist(..)[0].stream | +| test.py:70 | ok | test_taint | request.files['key'].filename | +| test.py:71 | ok | test_taint | request.files['key'].stream | +| test.py:72 | ok | test_taint | request.files.getlist(..) | +| test.py:73 | ok | test_taint | request.files.getlist(..)[0].filename | +| test.py:74 | ok | test_taint | request.files.getlist(..)[0].stream | | test.py:77 | ok | test_taint | request.form | | test.py:78 | ok | test_taint | request.form['key'] | | test.py:79 | ok | test_taint | request.form.getlist(..) | @@ -93,4 +93,4 @@ | test.py:177 | ok | test_taint | request.args.getlist(..) | | test.py:178 | ok | test_taint | a.getlist(..) | | test.py:179 | ok | test_taint | b.getlist(..) | -| test.py:180 | fail | test_taint | gl(..) | +| test.py:180 | ok | test_taint | gl(..) | From a82fa04d8a49fa8147d484a6798c6c397216d90e Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 22 Sep 2020 14:41:07 +0200 Subject: [PATCH 081/185] Python: Add worked example of taint step modeling of external libs This can't be seen on the example, but I went through quite a lot of iterations before arriving at this fairly simple solution. --- .../modeling-example/NaiveModel.expected | 35 ++++++ .../frameworks/modeling-example/NaiveModel.ql | 24 ++++ .../modeling-example/ProperModel.expected | 67 +++++++++++ .../modeling-example/ProperModel.ql | 26 +++++ .../frameworks/modeling-example/README.md | 29 +++++ .../modeling-example/SharedCode.qll | 36 ++++++ .../frameworks/modeling-example/test.py | 108 ++++++++++++++++++ 7 files changed, 325 insertions(+) create mode 100644 python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.expected create mode 100644 python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.ql create mode 100644 python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.expected create mode 100644 python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.ql create mode 100644 python/ql/test/experimental/library-tests/frameworks/modeling-example/README.md create mode 100644 python/ql/test/experimental/library-tests/frameworks/modeling-example/SharedCode.qll create mode 100644 python/ql/test/experimental/library-tests/frameworks/modeling-example/test.py diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.expected b/python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.expected new file mode 100644 index 00000000000..159c2f3f256 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.expected @@ -0,0 +1,35 @@ +edges +| test.py:21:11:21:18 | ControlFlowNode for source() | test.py:22:10:22:24 | ControlFlowNode for Attribute() | +| test.py:29:11:29:18 | ControlFlowNode for source() | test.py:33:10:33:12 | ControlFlowNode for val | +| test.py:40:11:40:25 | ControlFlowNode for Attribute() | test.py:41:10:41:12 | ControlFlowNode for val | +| test.py:45:11:45:18 | ControlFlowNode for source() | test.py:40:11:40:25 | ControlFlowNode for Attribute() | +| test.py:53:11:53:25 | ControlFlowNode for Attribute() | test.py:54:10:54:12 | ControlFlowNode for val | +| test.py:70:11:70:18 | ControlFlowNode for source() | test.py:53:11:53:25 | ControlFlowNode for Attribute() | +| test.py:78:11:78:14 | ControlFlowNode for bm() | test.py:79:10:79:12 | ControlFlowNode for val | +| test.py:83:11:83:18 | ControlFlowNode for source() | test.py:78:11:78:14 | ControlFlowNode for bm() | +| test.py:90:11:90:14 | ControlFlowNode for bm() | test.py:91:10:91:12 | ControlFlowNode for val | +| test.py:107:11:107:18 | ControlFlowNode for source() | test.py:90:11:90:14 | ControlFlowNode for bm() | +nodes +| test.py:21:11:21:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +| test.py:22:10:22:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| test.py:29:11:29:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +| test.py:33:10:33:12 | ControlFlowNode for val | semmle.label | ControlFlowNode for val | +| test.py:40:11:40:25 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| test.py:41:10:41:12 | ControlFlowNode for val | semmle.label | ControlFlowNode for val | +| test.py:45:11:45:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +| test.py:53:11:53:25 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| test.py:54:10:54:12 | ControlFlowNode for val | semmle.label | ControlFlowNode for val | +| test.py:70:11:70:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +| test.py:78:11:78:14 | ControlFlowNode for bm() | semmle.label | ControlFlowNode for bm() | +| test.py:79:10:79:12 | ControlFlowNode for val | semmle.label | ControlFlowNode for val | +| test.py:83:11:83:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +| test.py:90:11:90:14 | ControlFlowNode for bm() | semmle.label | ControlFlowNode for bm() | +| test.py:91:10:91:12 | ControlFlowNode for val | semmle.label | ControlFlowNode for val | +| test.py:107:11:107:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +#select +| test.py:22:10:22:24 | ControlFlowNode for Attribute() | test.py:21:11:21:18 | ControlFlowNode for source() | test.py:22:10:22:24 | ControlFlowNode for Attribute() | test flow (naive): test_simple | +| test.py:33:10:33:12 | ControlFlowNode for val | test.py:29:11:29:18 | ControlFlowNode for source() | test.py:33:10:33:12 | ControlFlowNode for val | test flow (naive): test_alias | +| test.py:41:10:41:12 | ControlFlowNode for val | test.py:45:11:45:18 | ControlFlowNode for source() | test.py:41:10:41:12 | ControlFlowNode for val | test flow (naive): test_accross_functions | +| test.py:54:10:54:12 | ControlFlowNode for val | test.py:70:11:70:18 | ControlFlowNode for source() | test.py:54:10:54:12 | ControlFlowNode for val | test flow (naive): test_deeply_nested | +| test.py:79:10:79:12 | ControlFlowNode for val | test.py:83:11:83:18 | ControlFlowNode for source() | test.py:79:10:79:12 | ControlFlowNode for val | test flow (naive): test_pass_bound_method | +| test.py:91:10:91:12 | ControlFlowNode for val | test.py:107:11:107:18 | ControlFlowNode for source() | test.py:91:10:91:12 | ControlFlowNode for val | test flow (naive): test_deeply_nested_bound_method | diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.ql b/python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.ql new file mode 100644 index 00000000000..6203923378a --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/modeling-example/NaiveModel.ql @@ -0,0 +1,24 @@ +/** + * @kind path-problem + */ + +private import python +private import experimental.dataflow.DataFlow +private import experimental.dataflow.TaintTracking +import DataFlow::PathGraph +import SharedCode + +class MyClassGetValueAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { + override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // obj -> obj.get_value() + exists(DataFlow::Node bound_method | + bound_method = myClassGetValue(nodeFrom) and + nodeTo.asCfgNode().(CallNode).getFunction() = bound_method.asCfgNode() + ) + } +} + +from SharedConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, + "test flow (naive): " + source.getNode().asCfgNode().getScope().getName() diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.expected b/python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.expected new file mode 100644 index 00000000000..9c6f6ad30c1 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.expected @@ -0,0 +1,67 @@ +edges +| test.py:21:11:21:18 | ControlFlowNode for source() | test.py:22:10:22:24 | ControlFlowNode for Attribute() | +| test.py:29:11:29:18 | ControlFlowNode for source() | test.py:33:10:33:12 | ControlFlowNode for val | +| test.py:39:15:39:17 | SSA variable arg | test.py:41:10:41:12 | ControlFlowNode for val | +| test.py:45:11:45:18 | ControlFlowNode for source() | test.py:46:15:46:17 | ControlFlowNode for src | +| test.py:46:15:46:17 | ControlFlowNode for src | test.py:39:15:39:17 | SSA variable arg | +| test.py:52:24:52:26 | SSA variable arg | test.py:54:10:54:12 | ControlFlowNode for val | +| test.py:57:33:57:35 | SSA variable arg | test.py:58:24:58:26 | ControlFlowNode for arg | +| test.py:58:24:58:26 | ControlFlowNode for arg | test.py:52:24:52:26 | SSA variable arg | +| test.py:61:33:61:35 | SSA variable arg | test.py:62:33:62:35 | ControlFlowNode for arg | +| test.py:62:33:62:35 | ControlFlowNode for arg | test.py:57:33:57:35 | SSA variable arg | +| test.py:65:33:65:35 | SSA variable arg | test.py:66:33:66:35 | ControlFlowNode for arg | +| test.py:66:33:66:35 | ControlFlowNode for arg | test.py:61:33:61:35 | SSA variable arg | +| test.py:70:11:70:18 | ControlFlowNode for source() | test.py:71:33:71:35 | ControlFlowNode for src | +| test.py:71:33:71:35 | ControlFlowNode for src | test.py:65:33:65:35 | SSA variable arg | +| test.py:77:23:77:24 | SSA variable bm | test.py:79:10:79:12 | ControlFlowNode for val | +| test.py:83:11:83:18 | ControlFlowNode for source() | test.py:84:23:84:35 | ControlFlowNode for Attribute | +| test.py:84:23:84:35 | ControlFlowNode for Attribute | test.py:77:23:77:24 | SSA variable bm | +| test.py:89:37:89:38 | SSA variable bm | test.py:91:10:91:12 | ControlFlowNode for val | +| test.py:94:46:94:47 | SSA variable bm | test.py:95:37:95:38 | ControlFlowNode for bm | +| test.py:95:37:95:38 | ControlFlowNode for bm | test.py:89:37:89:38 | SSA variable bm | +| test.py:98:46:98:47 | SSA variable bm | test.py:99:46:99:47 | ControlFlowNode for bm | +| test.py:99:46:99:47 | ControlFlowNode for bm | test.py:94:46:94:47 | SSA variable bm | +| test.py:102:46:102:47 | SSA variable bm | test.py:103:46:103:47 | ControlFlowNode for bm | +| test.py:103:46:103:47 | ControlFlowNode for bm | test.py:98:46:98:47 | SSA variable bm | +| test.py:107:11:107:18 | ControlFlowNode for source() | test.py:108:46:108:58 | ControlFlowNode for Attribute | +| test.py:108:46:108:58 | ControlFlowNode for Attribute | test.py:102:46:102:47 | SSA variable bm | +nodes +| test.py:21:11:21:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +| test.py:22:10:22:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| test.py:29:11:29:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +| test.py:33:10:33:12 | ControlFlowNode for val | semmle.label | ControlFlowNode for val | +| test.py:39:15:39:17 | SSA variable arg | semmle.label | SSA variable arg | +| test.py:41:10:41:12 | ControlFlowNode for val | semmle.label | ControlFlowNode for val | +| test.py:45:11:45:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +| test.py:46:15:46:17 | ControlFlowNode for src | semmle.label | ControlFlowNode for src | +| test.py:52:24:52:26 | SSA variable arg | semmle.label | SSA variable arg | +| test.py:54:10:54:12 | ControlFlowNode for val | semmle.label | ControlFlowNode for val | +| test.py:57:33:57:35 | SSA variable arg | semmle.label | SSA variable arg | +| test.py:58:24:58:26 | ControlFlowNode for arg | semmle.label | ControlFlowNode for arg | +| test.py:61:33:61:35 | SSA variable arg | semmle.label | SSA variable arg | +| test.py:62:33:62:35 | ControlFlowNode for arg | semmle.label | ControlFlowNode for arg | +| test.py:65:33:65:35 | SSA variable arg | semmle.label | SSA variable arg | +| test.py:66:33:66:35 | ControlFlowNode for arg | semmle.label | ControlFlowNode for arg | +| test.py:70:11:70:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +| test.py:71:33:71:35 | ControlFlowNode for src | semmle.label | ControlFlowNode for src | +| test.py:77:23:77:24 | SSA variable bm | semmle.label | SSA variable bm | +| test.py:79:10:79:12 | ControlFlowNode for val | semmle.label | ControlFlowNode for val | +| test.py:83:11:83:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +| test.py:84:23:84:35 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:89:37:89:38 | SSA variable bm | semmle.label | SSA variable bm | +| test.py:91:10:91:12 | ControlFlowNode for val | semmle.label | ControlFlowNode for val | +| test.py:94:46:94:47 | SSA variable bm | semmle.label | SSA variable bm | +| test.py:95:37:95:38 | ControlFlowNode for bm | semmle.label | ControlFlowNode for bm | +| test.py:98:46:98:47 | SSA variable bm | semmle.label | SSA variable bm | +| test.py:99:46:99:47 | ControlFlowNode for bm | semmle.label | ControlFlowNode for bm | +| test.py:102:46:102:47 | SSA variable bm | semmle.label | SSA variable bm | +| test.py:103:46:103:47 | ControlFlowNode for bm | semmle.label | ControlFlowNode for bm | +| test.py:107:11:107:18 | ControlFlowNode for source() | semmle.label | ControlFlowNode for source() | +| test.py:108:46:108:58 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +#select +| test.py:22:10:22:24 | ControlFlowNode for Attribute() | test.py:21:11:21:18 | ControlFlowNode for source() | test.py:22:10:22:24 | ControlFlowNode for Attribute() | test flow (proper): test_simple | +| test.py:33:10:33:12 | ControlFlowNode for val | test.py:29:11:29:18 | ControlFlowNode for source() | test.py:33:10:33:12 | ControlFlowNode for val | test flow (proper): test_alias | +| test.py:41:10:41:12 | ControlFlowNode for val | test.py:45:11:45:18 | ControlFlowNode for source() | test.py:41:10:41:12 | ControlFlowNode for val | test flow (proper): test_accross_functions | +| test.py:54:10:54:12 | ControlFlowNode for val | test.py:70:11:70:18 | ControlFlowNode for source() | test.py:54:10:54:12 | ControlFlowNode for val | test flow (proper): test_deeply_nested | +| test.py:79:10:79:12 | ControlFlowNode for val | test.py:83:11:83:18 | ControlFlowNode for source() | test.py:79:10:79:12 | ControlFlowNode for val | test flow (proper): test_pass_bound_method | +| test.py:91:10:91:12 | ControlFlowNode for val | test.py:107:11:107:18 | ControlFlowNode for source() | test.py:91:10:91:12 | ControlFlowNode for val | test flow (proper): test_deeply_nested_bound_method | diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.ql b/python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.ql new file mode 100644 index 00000000000..235276e630f --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/modeling-example/ProperModel.ql @@ -0,0 +1,26 @@ +/** + * @kind path-problem + */ + +private import python +private import experimental.dataflow.DataFlow +private import experimental.dataflow.TaintTracking +import DataFlow::PathGraph +import SharedCode + +class MyClassGetValueAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { + override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // obj -> obj.get_value + nodeTo.asCfgNode().(AttrNode).getObject("get_value") = nodeFrom.asCfgNode() and + nodeTo = myClassGetValue(_) + or + // get_value -> get_value() + nodeFrom = myClassGetValue(_) and + nodeTo.asCfgNode().(CallNode).getFunction() = nodeFrom.asCfgNode() + } +} + +from SharedConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, + "test flow (proper): " + source.getNode().asCfgNode().getScope().getName() diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/README.md b/python/ql/test/experimental/library-tests/frameworks/modeling-example/README.md new file mode 100644 index 00000000000..7ab6f366264 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/modeling-example/README.md @@ -0,0 +1,29 @@ +This test illustrates that you need to be very careful when adding additional taint-steps or dataflow steps using `TypeTracker`. + +The basic setup is that we're modeling the behavior of a (fictitious) external library class `MyClass`, and (fictitious) source of such an instance (the `source` function). + +```py3 +class MyClass: + def __init__(self, value): + self.value = value + + def get_value(self): + return self.value +``` + +We want to extend our analysis to `obj.get_value()` is also tainted if `obj` is a tainted instance of `MyClass`. + +The actual type-tracking is done in `SharedCode.qll`, but it's the _way_ we use it that matters. + +In `NaiveModel.ql` we add an additional taint step from an instance of `MyClass` to calls of the bound method `get_value` (that we have tracked). It provides us with the correct results, but the path explanations are not very useful, since we are now able to cross functions in _one step_. + +In `ProperModel.ql` we split the additional taint step in two: + +1. from tracked `obj` that is instance of `MyClass`, to `obj.get_value` **but only** exactly where the attribute is accessed (by an `AttrNode`). This is important, since if we allowed `.get_value` we would again be able to cross functions in one step. +2. from tracked `get_value` bound method to calls of it, **but only** exactly where the call is (by an `CallNode`). for same reason as above. + +**Try running the queries in VS Code to see the difference** + +### Possible improvements + +Using `AttrNode` directly in the code here means there is no easy way to add `getattr` support too all such predicates. Not really sure how to handle this in a generalized way though :| diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/SharedCode.qll b/python/ql/test/experimental/library-tests/frameworks/modeling-example/SharedCode.qll new file mode 100644 index 00000000000..1bf6e3930ab --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/modeling-example/SharedCode.qll @@ -0,0 +1,36 @@ +private import python +private import experimental.dataflow.DataFlow +private import experimental.dataflow.TaintTracking + +// Helpers modeling MyClass +/** A data-flow Node representing an instance of MyClass. */ +abstract class MyClass extends DataFlow::Node { } + +private DataFlow::Node myClassGetValue(MyClass qualifier, DataFlow::TypeTracker t) { + t.startInAttr("get_value") and + result = qualifier + or + exists(DataFlow::TypeTracker t2 | result = myClassGetValue(qualifier, t2).track(t2, t)) +} + +DataFlow::Node myClassGetValue(MyClass qualifier) { + result = myClassGetValue(qualifier, DataFlow::TypeTracker::end()) +} + +// Config +class SourceCall extends DataFlow::Node, MyClass { + SourceCall() { this.asCfgNode().(CallNode).getFunction().(NameNode).getId() = "source" } +} + +class SharedConfig extends TaintTracking::Configuration { + SharedConfig() { this = "SharedConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof SourceCall } + + override predicate isSink(DataFlow::Node sink) { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "sink" and + call.getArg(0) = sink.asCfgNode() + ) + } +} diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/test.py b/python/ql/test/experimental/library-tests/frameworks/modeling-example/test.py new file mode 100644 index 00000000000..6fe7bb3fcad --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/modeling-example/test.py @@ -0,0 +1,108 @@ +class MyClass: + def __init__(self, value): + self.value = value + + def get_value(self): + return self.value + + +def source(): + return MyClass("tainted") + + +def sink(obj): + print("sink", obj) + + +################################################################################ + + +def test_simple(): + src = source() + sink(src.get_value()) + + +################################################################################ + + +def test_alias(): + src = source() + foo = src + bound_method = foo.get_value + val = bound_method() + sink(val) + + +################################################################################ + + +def sink_func(arg): + val = arg.get_value() + sink(val) + + +def test_accross_functions(): + src = source() + sink_func(src) + + +################################################################################ + + +def deeply_nested_sink(arg): + val = arg.get_value() + sink(val) + + +def deeply_nested_passthrough_1(arg): + deeply_nested_sink(arg) + + +def deeply_nested_passthrough_2(arg): + deeply_nested_passthrough_1(arg) + + +def deeply_nested_passthrough_3(arg): + deeply_nested_passthrough_2(arg) + + +def test_deeply_nested(): + src = source() + deeply_nested_passthrough_3(src) + + +################################################################################ + + +def recv_bound_method(bm): + val = bm() + sink(val) + + +def test_pass_bound_method(): + src = source() + recv_bound_method(src.get_value) + + +################################################################################ + +def deeply_nested_bound_method_sink(bm): + val = bm() + sink(val) + + +def deeply_nested_bound_method_passthrough_1(bm): + deeply_nested_bound_method_sink(bm) + + +def deeply_nested_bound_method_passthrough_2(bm): + deeply_nested_bound_method_passthrough_1(bm) + + +def deeply_nested_bound_method_passthrough_3(bm): + deeply_nested_bound_method_passthrough_2(bm) + + +def test_deeply_nested_bound_method(): + src = source() + deeply_nested_bound_method_passthrough_3(src.get_value) From e614365963086bfabc373a7e40fe3ff0750e1fb7 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 22 Sep 2020 16:09:22 +0200 Subject: [PATCH 082/185] Python: Adopt new approach in flask modeling Removed all the dict-like stuff, not sure that is how we should do things. --- .../dataflow/internal/DataFlowUtil.qll | 25 ----- .../semmle/python/frameworks/Flask.qll | 22 +++-- .../semmle/python/frameworks/Werkzeug.qll | 97 +++---------------- .../frameworks/flask/TestTaint.expected | 8 +- 4 files changed, 32 insertions(+), 120 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll b/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll index c407a94becb..c868e1762ec 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowUtil.qll @@ -65,28 +65,3 @@ EssaNode importMember(string moduleName, string memberName) { result.getVar().(AssignmentDefinition).getSourceVariable() = var ) } - -abstract class ListLike extends Node { - /** Gets a Node that is an access of an element of this list-like object */ - Node getElementAccess() { - // subscript - result.asCfgNode().(SubscriptNode).getObject() = this.asCfgNode() - or - // get - // NOTE: will not track bound method, `f = obj.func; f()` - result.asCfgNode().(CallNode).getFunction().(AttrNode).getObject("pop") = this.asCfgNode() - } -} - -/** Class of dictionary-like objects */ -abstract class DictLike extends Node { - /** Gets a Node that is an access of an element of this dictionary-like object */ - Node getElementAccess() { - // subscript - result.asCfgNode().(SubscriptNode).getObject() = this.asCfgNode() - or - // get - // NOTE: will not track bound method, `f = obj.func; f()` - result.asCfgNode().(CallNode).getFunction().(AttrNode).getObject("get") = this.asCfgNode() - } -} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll index 9c3703d3029..bbbe726b88f 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll @@ -8,6 +8,8 @@ private import experimental.dataflow.RemoteFlowSources private import experimental.semmle.python.Concepts private import experimental.semmle.python.frameworks.Werkzeug +// for old improved impl see +// https://github.com/github/codeql/blob/9f95212e103c68d0c1dfa4b6f30fb5d53954ccef/python/ql/src/semmle/python/web/flask/Request.qll private module Flask { /** Gets a reference to the `flask` module. */ DataFlow::Node flask(DataFlow::TypeTracker t) { @@ -36,7 +38,12 @@ private module Flask { DataFlow::Node request() { result = flask::request(DataFlow::TypeTracker::end()) } } - // TODO: Do we even need this class then? :| + // TODO: Do we even need this class? :| + /** + * A source of remote flow from a flask request. + * + * See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request + */ private class RequestSource extends RemoteFlowSource::Range { RequestSource() { this = flask::request() } @@ -111,13 +118,8 @@ private module Flask { private class RequestInputFiles extends RequestInputMultiDict { RequestInputFiles() { attr_name = "files" } } - - private class RequestInputFileStorage extends Werkzeug::Datastructures::FileStorage { - RequestInputFileStorage() { - exists(RequestInputFiles files, Werkzeug::Datastructures::MultiDictTracked filesTracked | - filesTracked.getMultiDict() = files and - this = filesTracked.getElementAccess() - ) - } - } + // TODO: Somehow specify that elements of `RequestInputFiles` are + // Werkzeug::Datastructures::FileStorage and should have those additional taint steps + // AND that the 0-indexed argument to its' save method is a sink for path-injection. + // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.save } diff --git a/python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll b/python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll index 16cdf7c3f72..0c682b71464 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll @@ -6,6 +6,8 @@ private import python private import experimental.dataflow.DataFlow private import experimental.dataflow.TaintTracking +// for old impl see +// https://github.com/github/codeql/blob/9f95212e103c68d0c1dfa4b6f30fb5d53954ccef/python/ql/src/semmle/python/libraries/Werkzeug.qll module Werkzeug { module Datastructures { // ---------------------------------------------------------------------- // @@ -18,94 +20,26 @@ module Werkzeug { */ abstract class MultiDict extends DataFlow::Node { } - private DataFlow::Node multiDictTrack(MultiDict multiDict, DataFlow::TypeTracker t) { - t.start() and - result instanceof MultiDict - or - exists(DataFlow::TypeTracker t2 | result = multiDictTrack(multiDict, t2).track(t2, t)) - } - - /** Gets a reference to the MultiDict attributes of `flask.request`. */ - private DataFlow::Node multiDictTrack(MultiDict multiDict) { - result = multiDictTrack(multiDict, DataFlow::TypeTracker::end()) - } - - class MultiDictTracked extends DataFlow::Node, DataFlow::DictLike { - MultiDict multiDict; - - MultiDictTracked() { this = multiDictTrack(multiDict) } - - MultiDict getMultiDict() { result = multiDict } - - override DataFlow::Node getElementAccess() { - result = DataFlow::DictLike.super.getElementAccess() + private module MultiDictTracking { + private DataFlow::Node getlist(DataFlow::TypeTracker t) { + t.startInAttr("getlist") and + result instanceof MultiDict or - exists(MultiDictGetListCallResultTracked tracked_call_result | - tracked_call_result.getCall().getMultiDict() = this and - result = tracked_call_result.getElementAccess() - ) - } - } - - private DataFlow::Node multiDictGetListTrack(MultiDictTracked multiDict, DataFlow::TypeTracker t) { - /* - * using t.startInAttr("getlist") was not good solution - * ```py - * a = request.args - * b = a - * a.getlist("key") - * b.getlist("key") - * ``` - * would give `request.args` -> `b.getlist` -- this is correct, but not helpful in a taint-path explanation, - * we REALLY WANT it to be `request.args -> a -> b -> b.getlist` - * This requirement means that we do need the predicate `multiDictTrack`, which could be spared otherwise. - */ - - t.start() and - result.asCfgNode().(AttrNode).getObject("getlist") = multiDict.asCfgNode() - or - exists(DataFlow::TypeTracker t2 | result = multiDictGetListTrack(multiDict, t2).track(t2, t)) - } - - private DataFlow::Node multiDictGetListTrack(MultiDictTracked multiDict) { - result = multiDictGetListTrack(multiDict, DataFlow::TypeTracker::end()) - } - - private class MultiDictGetListCall extends DataFlow::Node { - MultiDictTracked multiDict; - - MultiDictGetListCall() { - this.asCfgNode().(CallNode).getFunction() = multiDictGetListTrack(multiDict).asCfgNode() + exists(DataFlow::TypeTracker t2 | result = getlist(t2).track(t2, t)) } - MultiDictTracked getMultiDict() { result = multiDict } - } - - private DataFlow::Node multiDictGetListCallTrack( - MultiDictGetListCall call, DataFlow::TypeTracker t - ) { - t.start() and - result = call - or - exists(DataFlow::TypeTracker t2 | result = multiDictGetListCallTrack(call, t2).track(t2, t)) - } - - /** Gets a reference to the MultiDict attributes of `flask.request`. */ - private DataFlow::Node multiDictGetListCallTrack(MultiDictGetListCall call) { - result = multiDictGetListCallTrack(call, DataFlow::TypeTracker::end()) - } - - private class MultiDictGetListCallResultTracked extends DataFlow::Node, DataFlow::ListLike { - MultiDictGetListCall call; - - MultiDictGetListCallResultTracked() { this = multiDictGetListCallTrack(call) } - - MultiDictGetListCall getCall() { result = call } + DataFlow::Node getlist() { result = getlist(DataFlow::TypeTracker::end()) } } private class MultiDictAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - nodeTo.(MultiDictGetListCall).getMultiDict() = nodeFrom.(MultiDictTracked) + // obj -> obj.getlist + nodeTo.asCfgNode().(AttrNode).getObject("getlist") = nodeFrom.asCfgNode() and + nodeTo = MultiDictTracking::getlist() + or + // getlist -> getlist() + nodeFrom = MultiDictTracking::getlist() and + nodeTo.asCfgNode().(CallNode).getFunction() = nodeFrom.asCfgNode() } } @@ -121,6 +55,7 @@ module Werkzeug { private class FileStorageAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // TODO: should be `nodeFrom = tracked(any(FileStorage fs))` nodeFrom instanceof FileStorage and exists(string name | name in ["filename", diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected index 0e35defeca2..bb929ee98f4 100644 --- a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected +++ b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected @@ -35,11 +35,11 @@ | test.py:65 | ok | test_taint | request.data | | test.py:68 | ok | test_taint | request.files | | test.py:69 | ok | test_taint | request.files['key'] | -| test.py:70 | ok | test_taint | request.files['key'].filename | -| test.py:71 | ok | test_taint | request.files['key'].stream | +| test.py:70 | fail | test_taint | request.files['key'].filename | +| test.py:71 | fail | test_taint | request.files['key'].stream | | test.py:72 | ok | test_taint | request.files.getlist(..) | -| test.py:73 | ok | test_taint | request.files.getlist(..)[0].filename | -| test.py:74 | ok | test_taint | request.files.getlist(..)[0].stream | +| test.py:73 | fail | test_taint | request.files.getlist(..)[0].filename | +| test.py:74 | fail | test_taint | request.files.getlist(..)[0].stream | | test.py:77 | ok | test_taint | request.form | | test.py:78 | ok | test_taint | request.form['key'] | | test.py:79 | ok | test_taint | request.form.getlist(..) | From 5709189c2a70e0ad832d8e438586e0034093c355 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 22 Sep 2020 16:10:50 +0200 Subject: [PATCH 083/185] Python: Expand flask test --- .../library-tests/frameworks/flask/TestTaint.expected | 2 ++ .../experimental/library-tests/frameworks/flask/test.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected index bb929ee98f4..c1fed711aa7 100644 --- a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected +++ b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected @@ -94,3 +94,5 @@ | test.py:178 | ok | test_taint | a.getlist(..) | | test.py:179 | ok | test_taint | b.getlist(..) | | test.py:180 | ok | test_taint | gl(..) | +| test.py:187 | ok | test_taint | req.path | +| test.py:188 | fail | test_taint | gd() | diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/test.py b/python/ql/test/experimental/library-tests/frameworks/flask/test.py index 4d89603ee74..9cf9e7a2027 100644 --- a/python/ql/test/experimental/library-tests/frameworks/flask/test.py +++ b/python/ql/test/experimental/library-tests/frameworks/flask/test.py @@ -180,6 +180,14 @@ def test_taint(name = "World!", number="0", foo="foo"): gl('key'), ) + # aliasing tests + req = request + gd = request.get_data + ensure_tainted( + req.path, + gd(), + ) + From 71a75ce596ec78f8072ed3d2499242d5af487b7d Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 22 Sep 2020 16:24:30 +0200 Subject: [PATCH 084/185] Python: Handle bound methods in flask modeling --- .../semmle/python/frameworks/Flask.qll | 32 +++++++++++++++---- .../frameworks/flask/TestTaint.expected | 2 +- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll index bbbe726b88f..de72eb9128f 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll @@ -5,6 +5,7 @@ private import python private import experimental.dataflow.DataFlow private import experimental.dataflow.RemoteFlowSources +private import experimental.dataflow.TaintTracking private import experimental.semmle.python.Concepts private import experimental.semmle.python.frameworks.Werkzeug @@ -50,6 +51,20 @@ private module Flask { override string getSourceType() { result = "flask.request" } } + private module FlaskRequestTracking { + private DataFlow::Node tainted_methods(string attr_name, DataFlow::TypeTracker t) { + attr_name in ["get_data", "get_json"] and + t.startInAttr(attr_name) and + result = flask::request() + or + exists(DataFlow::TypeTracker t2 | result = tainted_methods(attr_name, t2).track(t2, t)) + } + + DataFlow::Node tainted_methods(string attr_name) { + result = tainted_methods(attr_name, DataFlow::TypeTracker::end()) + } + } + /** * A source of remote flow from attributes from a flask request. * @@ -99,17 +114,22 @@ private module Flask { "headers"] ) or - // methods - exists(CallNode call | this.asCfgNode() = call | - // NOTE: will not track bound method, `f = obj.func; f()` - attr_name in ["get_data", "get_json"] and - call.getFunction().(AttrNode).getObject(attr_name) = flask::request().asCfgNode() - ) + // methods (needs special handling to track bound-methods -- see `FlaskRequestMethodCallsAdditionalTaintStep` below) + this = FlaskRequestTracking::tainted_methods(attr_name) } override string getSourceType() { result = "flask.request input" } } + private class FlaskRequestMethodCallsAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { + override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // NOTE: `request -> request.tainted_method` part is handled as part of RequestInputAccess + // tainted_method -> tainted_method() + nodeFrom = FlaskRequestTracking::tainted_methods(_) and + nodeTo.asCfgNode().(CallNode).getFunction() = nodeFrom.asCfgNode() + } + } + private class RequestInputMultiDict extends RequestInputAccess, Werkzeug::Datastructures::MultiDict { RequestInputMultiDict() { attr_name in ["args", "values", "form", "files"] } diff --git a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected index c1fed711aa7..9a5e8e26009 100644 --- a/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected +++ b/python/ql/test/experimental/library-tests/frameworks/flask/TestTaint.expected @@ -95,4 +95,4 @@ | test.py:179 | ok | test_taint | b.getlist(..) | | test.py:180 | ok | test_taint | gl(..) | | test.py:187 | ok | test_taint | req.path | -| test.py:188 | fail | test_taint | gd() | +| test.py:188 | ok | test_taint | gd() | From 4faeede5cd60ea2ec114b5865ca1f62798400801 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Tue, 22 Sep 2020 16:55:25 +0200 Subject: [PATCH 085/185] C++: Remove unnecessary comment on import --- .../library-tests/rangeanalysis/extensibility/extensibility.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql index 841fe2c8e6c..4fbfed1706d 100644 --- a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql @@ -1,5 +1,5 @@ import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis -import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils // for typeLowerBound +import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDefinition From b382711f14d19a4455b4d46c003db399709576c9 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 22 Sep 2020 18:55:07 +0200 Subject: [PATCH 086/185] Java: change note for Hiberate ORM improvements --- java/change-notes/2020-09-22-hibernate-sql-sinks.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 java/change-notes/2020-09-22-hibernate-sql-sinks.md diff --git a/java/change-notes/2020-09-22-hibernate-sql-sinks.md b/java/change-notes/2020-09-22-hibernate-sql-sinks.md new file mode 100644 index 00000000000..3a565a351c3 --- /dev/null +++ b/java/change-notes/2020-09-22-hibernate-sql-sinks.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Support for the [Hibernate ORM](https://hibernate.org/orm/) ORM library (specifically, its Query + creation methods) has been improved, which may lead to more results from the security queries. From ec2b3f0b6ca6d07613b0b68cb13d89fc80e6f9f4 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 22 Sep 2020 21:02:26 +0200 Subject: [PATCH 087/185] better join-order fix in HTTP --- .../ql/src/semmle/javascript/frameworks/HTTP.qll | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll index 0f224ff060f..b3faa55501a 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll @@ -253,21 +253,20 @@ module HTTP { private predicate isDecoratedCall(DataFlow::CallNode call, DataFlow::FunctionNode decoratee) { // indirect route-handler `result` is given to function `outer`, which returns function `inner` which calls the function `pred`. exists(int i, DataFlow::FunctionNode outer, HTTP::RouteHandlerCandidate inner | + inner = outer.getAReturn().getALocalSource() and decoratee = call.getArgument(i).getALocalSource() and outer.getFunction() = call.getACallee() and - returnsRouteHandler(outer, inner) and - isAForwardingRouteHandlerCall(outer.getParameter(i), inner) + hasForwardingHandlerParameter(i, outer, inner) ) } /** - * Holds if `fun` returns the route-handler-candidate `routeHandler`. + * Holds if the `i`th parameter of `outer` has a call that `inner` forwards its parameters to. */ - pragma[noinline] - private predicate returnsRouteHandler( - DataFlow::FunctionNode fun, HTTP::RouteHandlerCandidate routeHandler + private predicate hasForwardingHandlerParameter( + int i, DataFlow::FunctionNode outer, HTTP::RouteHandlerCandidate inner ) { - routeHandler = fun.getAReturn().getALocalSource() + isAForwardingRouteHandlerCall(outer.getParameter(i), inner) } /** From aece0ff65271219717e7dda416813d6352a88eeb Mon Sep 17 00:00:00 2001 From: yoff Date: Tue, 22 Sep 2020 22:33:46 +0200 Subject: [PATCH 088/185] Apply suggestions from code review Co-authored-by: Taus --- .../dataflow/internal/DataFlowPrivate.qll | 16 ++++++++-------- .../dataflow/internal/DataFlowPublic.qll | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index cf58b2935b1..7684b77bd74 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -229,7 +229,7 @@ private Node update(Node node) { /** * IPA type for DataFlowCallable. * - * A callable is either a callable value or a module (for enclosing `ModuleVariable`s). + * A callable is either a callable value or a module (for enclosing `ModuleVariableNode`s). * A module has no calls. */ newtype TDataFlowCallable = @@ -292,13 +292,13 @@ class DataFlowModuleScope extends DataFlowCallable, TModule { * IPA type for DataFlowCall. * * Calls corresponding to `CallNode`s are either to callable values or to classes. - * The latter is directed to the callable corresponding to the calss' `__init__`-method. + * The latter is directed to the callable corresponding to the `__init__` method of the class. * - * An `__init__`-method can also be called directly, so that callable can be targetted by + * An `__init__` method can also be called directly, so that the callable can be targeted by * different types of calls. In that case, the parameter mappings will be different, * as the class call will synthesise an argument node to be mapped to the `self` parameter. * - * A calls corresponding to a special method call is handled by the corresponding `SpecialMethodCallNode`. + * A call corresponding to a special method call is handled by the corresponding `SpecialMethodCallNode`. */ newtype TDataFlowCall = TCallNode(CallNode call) { call = any(CallableValue c).getACall() } or @@ -406,12 +406,12 @@ class ArgumentNode extends Node { final DataFlowCall getCall() { this.argumentOf(result, _) } predicate isNotPostUpdate() { + this = any(CallNodeCall c).getArg(_) + or + this = any(SpecialCall c).getArg(_) + or // Avoid argument 0 of class calls as those have non-synthetic post-update nodes. - exists(CallNodeCall c | this = c.getArg(_)) - or exists(ClassCall c, int n | n > 0 | this = c.getArg(n)) - or - exists(SpecialCall c | this = c.getArg(_)) } } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index f95e07ddaea..fb68fe3b1f1 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -163,7 +163,7 @@ class ParameterNode extends EssaNode { * * Nodes corresponding to AST elements, for example `ExprNode`s, usually refer * to the value before the update with the exception of `ObjectCreationNode`s, - * which represents the value after the constructor has run. + * which represents the value _after_ the constructor has run. */ abstract class PostUpdateNode extends Node { /** Gets the node before the state update. */ From ef4461ce544b3c28c20e6546b1368fc46e4afed9 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 22 Sep 2020 23:48:28 +0200 Subject: [PATCH 089/185] Python: Address review comments --- .../dataflow/internal/DataFlowPrivate.qll | 49 ++++++++----------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 7684b77bd74..b8d30ae7203 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -18,6 +18,7 @@ class DataFlowCfgNode extends ControlFlowNode { /** A data flow node for which we should synthesise an associated pre-update node. */ abstract class NeedsSyntheticPreUpdateNode extends Node { + /** This will figure in the texttual representation of the synthesised pre-update node. */ abstract string label(); } @@ -38,6 +39,7 @@ class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode { /** A data flow node for which we should synthesise an associated post-update node. */ abstract class NeedsSyntheticPostUpdateNode extends Node { + /** This will figure in the texttual representation of the synthesised post-update node. */ abstract string label(); } @@ -45,7 +47,14 @@ abstract class NeedsSyntheticPostUpdateNode extends Node { class ArgumentPreUpdateNode extends NeedsSyntheticPostUpdateNode, ArgumentNode { // Certain arguments, such as implicit self arguments are already post-update nodes // and should not have an extra node synthesised. - ArgumentPreUpdateNode() { this.isNotPostUpdate() } + ArgumentPreUpdateNode() { + this = any(CallNodeCall c).getArg(_) + or + this = any(SpecialCall c).getArg(_) + or + // Avoid argument 0 of class calls as those have non-synthetic post-update nodes. + exists(ClassCall c, int n | n > 0 | this = c.getArg(n)) + } override string label() { result = "arg" } } @@ -95,7 +104,7 @@ class SyntheticPostUpdateNode extends PostUpdateNode, TSyntheticPostUpdateNode { * object after the constructor (currently only `__init__`) has run. */ class ObjectCreationNode extends PostUpdateNode, NeedsSyntheticPreUpdateNode, CfgNode { - ObjectCreationNode() { node.(CallNode) = any(ClassValue c).getACall() } + ObjectCreationNode() { node.(CallNode) = any(ClassCall c).getNode() } override Node getPreUpdateNode() { result.(SyntheticPreUpdateNode).getPostUpdateNode() = this } @@ -371,7 +380,7 @@ class ClassCall extends DataFlowCall, TClassCall { ) } - override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() } + override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() } } /** Represents a call to a special method. */ @@ -404,15 +413,6 @@ class ArgumentNode extends Node { /** Gets the call in which this node is an argument. */ final DataFlowCall getCall() { this.argumentOf(result, _) } - - predicate isNotPostUpdate() { - this = any(CallNodeCall c).getArg(_) - or - this = any(SpecialCall c).getArg(_) - or - // Avoid argument 0 of class calls as those have non-synthetic post-update nodes. - exists(ClassCall c, int n | n > 0 | this = c.getArg(n)) - } } /** Gets a viable run-time target for the call `call`. */ @@ -469,7 +469,7 @@ class DataFlowType extends TDataFlowType { } /** A node that performs a type cast. */ -class CastNode extends CfgNode { +class CastNode extends Node { CastNode() { none() } } @@ -605,19 +605,10 @@ predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) { * data flows from `x` to (the post-update node for) `obj` via assignment to `foo`. */ predicate attributeStoreStep(CfgNode nodeFrom, Content c, PostUpdateNode nodeTo) { - exists(AssignStmt a, Attribute attr | - a.getValue().getAFlowNode() = nodeFrom.getNode() and - a.getATarget().(Attribute) = attr and + exists(AttrNode attr | + nodeFrom.asCfgNode() = attr.(DefinitionNode).getValue() and attr.getName() = c.(AttributeContent).getAttribute() and - attr.getObject().getAFlowNode() = nodeTo.getPreUpdateNode().(CfgNode).getNode() and - attr.getCtx() instanceof Store - ) - or - exists(AssignExpr ae | - ae.getValue().getAFlowNode() = nodeFrom.getNode() and - ae.getTarget().(Attribute).getName() = c.(AttributeContent).getAttribute() and - ae.getTarget().(Attribute).getObject().getAFlowNode() = - nodeTo.getPreUpdateNode().(CfgNode).getNode() + attr.getObject() = nodeTo.getPreUpdateNode().(CfgNode).getNode() ) } @@ -726,11 +717,11 @@ predicate comprehensionReadStep(CfgNode nodeFrom, Content c, EssaNode nodeTo) { * data flows from `obj` to `obj.foo` via a read from `foo`. */ predicate attributeReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) { - exists(Attribute attr | - nodeTo.asCfgNode().(AttrNode).getNode() = attr and - nodeFrom.asCfgNode() = attr.getObject().getAFlowNode() and + exists(AttrNode attr | + nodeTo.asCfgNode() = attr and + nodeFrom.asCfgNode() = attr.getObject() and attr.getName() = c.(AttributeContent).getAttribute() and - attr.getCtx() instanceof Load + attr.isLoad() ) } From 624cdd339aab6411a9c0889a8de47e5e206e39e0 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 23 Sep 2020 11:18:12 +0200 Subject: [PATCH 090/185] Python: Fix grammar Co-authored-by: yoff --- .../library-tests/frameworks/modeling-example/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/test/experimental/library-tests/frameworks/modeling-example/README.md b/python/ql/test/experimental/library-tests/frameworks/modeling-example/README.md index 7ab6f366264..09a01d043f4 100644 --- a/python/ql/test/experimental/library-tests/frameworks/modeling-example/README.md +++ b/python/ql/test/experimental/library-tests/frameworks/modeling-example/README.md @@ -20,7 +20,7 @@ In `NaiveModel.ql` we add an additional taint step from an instance of `MyClass` In `ProperModel.ql` we split the additional taint step in two: 1. from tracked `obj` that is instance of `MyClass`, to `obj.get_value` **but only** exactly where the attribute is accessed (by an `AttrNode`). This is important, since if we allowed `.get_value` we would again be able to cross functions in one step. -2. from tracked `get_value` bound method to calls of it, **but only** exactly where the call is (by an `CallNode`). for same reason as above. +2. from tracked `get_value` bound method to calls of it, **but only** exactly where the call is (by a `CallNode`). for same reason as above. **Try running the queries in VS Code to see the difference** From 6aec2ec67359bc18e4ef859dbed1d08918331b46 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 23 Sep 2020 11:18:32 +0200 Subject: [PATCH 091/185] Python: Fix os.popen modeling Co-authored-by: yoff --- python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index a39e82f0879..7587e9187b2 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -38,7 +38,7 @@ private module Stdlib { /** Gets a reference to the `os.popen` function. */ DataFlow::Node popen(DataFlow::TypeTracker t) { t.start() and - result = DataFlow::importMember("os", "system") + result = DataFlow::importMember("os", "popen") or t.startInAttr("popen") and result = os() From 2868d5bf34648dddcf29ffa3f0d4107e08013740 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 22 Sep 2020 10:54:25 +0200 Subject: [PATCH 092/185] C#: Add pointer cast test cases --- .../conversion/pointer/Pointer.cs | 28 +++++++++++++++++++ .../conversion/pointer/Pointer.expected | 11 ++++++++ .../conversion/pointer/Pointer.ql | 5 ++++ 3 files changed, 44 insertions(+) create mode 100644 csharp/ql/test/library-tests/conversion/pointer/Pointer.cs create mode 100644 csharp/ql/test/library-tests/conversion/pointer/Pointer.expected create mode 100644 csharp/ql/test/library-tests/conversion/pointer/Pointer.ql diff --git a/csharp/ql/test/library-tests/conversion/pointer/Pointer.cs b/csharp/ql/test/library-tests/conversion/pointer/Pointer.cs new file mode 100644 index 00000000000..c7e12ac39b4 --- /dev/null +++ b/csharp/ql/test/library-tests/conversion/pointer/Pointer.cs @@ -0,0 +1,28 @@ +using System; + +class C +{ + unsafe static void M1(int[] arr) + { + fixed (int* i1 = arr) + { + } + + fixed (int* i2 = &arr[0]) + { + int* i3 = i2; + i3 = i3 + 1; + *i2 = *i2 + 1; + void* v2 = i2; + } + + int* i4 = null; + + int number = 1024; + byte* p = (byte*)&number; + + var s = "some string"; + fixed (char* c1 = s) + { } + } +} diff --git a/csharp/ql/test/library-tests/conversion/pointer/Pointer.expected b/csharp/ql/test/library-tests/conversion/pointer/Pointer.expected new file mode 100644 index 00000000000..7db63e5ef9f --- /dev/null +++ b/csharp/ql/test/library-tests/conversion/pointer/Pointer.expected @@ -0,0 +1,11 @@ +| Pointer.cs:7:21:7:28 | Pointer.cs:7:21:7:28 | Int32* | Int32[] | access to parameter arr | +| Pointer.cs:11:21:11:32 | Pointer.cs:11:21:11:32 | Int32* | Int32* | &... | +| Pointer.cs:13:18:13:24 | Pointer.cs:13:18:13:24 | Int32* | Int32* | access to local variable i2 | +| Pointer.cs:14:13:14:23 | Pointer.cs:14:13:14:23 | Int32* | Int32* | ... + ... | +| Pointer.cs:15:13:15:25 | Pointer.cs:15:13:15:25 | Int32 | Int32 | ... + ... | +| Pointer.cs:16:19:16:25 | Pointer.cs:16:19:16:25 | Void* | Void* | (...) ... | +| Pointer.cs:19:14:19:22 | Pointer.cs:19:14:19:22 | Int32* | null | null | +| Pointer.cs:21:13:21:25 | Pointer.cs:21:13:21:25 | Int32 | Int32 | 1024 | +| Pointer.cs:22:15:22:32 | Pointer.cs:22:15:22:32 | Byte* | Byte* | (...) ... | +| Pointer.cs:24:13:24:29 | Pointer.cs:24:13:24:29 | String | String | "some string" | +| Pointer.cs:25:22:25:27 | Pointer.cs:25:22:25:27 | Char* | String | access to local variable s | diff --git a/csharp/ql/test/library-tests/conversion/pointer/Pointer.ql b/csharp/ql/test/library-tests/conversion/pointer/Pointer.ql new file mode 100644 index 00000000000..69e7db8c1cf --- /dev/null +++ b/csharp/ql/test/library-tests/conversion/pointer/Pointer.ql @@ -0,0 +1,5 @@ +import csharp + +from Assignment a +select a.getLocation(), a.getLValue().getType().toString(), a.getRValue().getType().toString(), + a.getRValue().toString() From 66815c9d3d6a9075032b6c98d0d9e696d6f4acc2 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 23 Sep 2020 14:30:48 +0200 Subject: [PATCH 093/185] Python: Suppress unused variable warnings in DataFlowPrivate --- .../dataflow/internal/DataFlowPrivate.qll | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index bc904274524..448bedbb84b 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -411,7 +411,11 @@ predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() } /** * Gets the type of `node`. */ -DataFlowType getNodeType(Node node) { result = TAnyFlow() } +DataFlowType getNodeType(Node node) { + result = TAnyFlow() and + // Suppress unused variable warning + node = node +} /** Gets a string representation of a type returned by `getErasedRepr`. */ string ppReprType(DataFlowType t) { none() } @@ -458,7 +462,9 @@ predicate listStoreStep(CfgNode nodeFrom, ListElementContent c, CfgNode nodeTo) // nodeFrom is `42`, cfg node // nodeTo is the list, `[..., 42, ...]`, cfg node // c denotes element of list - nodeTo.getNode().(ListNode).getAnElement() = nodeFrom.getNode() + nodeTo.getNode().(ListNode).getAnElement() = nodeFrom.getNode() and + // Suppress unused variable warning + c = c } /** Data flows from an element of a set to the set. */ @@ -468,7 +474,9 @@ predicate setStoreStep(CfgNode nodeFrom, ListElementContent c, CfgNode nodeTo) { // nodeFrom is `42`, cfg node // nodeTo is the set, `{..., 42, ...}`, cfg node // c denotes element of list - nodeTo.getNode().(SetNode).getAnElement() = nodeFrom.getNode() + nodeTo.getNode().(SetNode).getAnElement() = nodeFrom.getNode() and + // Suppress unused variable warning + c = c } /** Data flows from an element of a tuple to the tuple at a specific index. */ From 825fc2228ba10ab1fb7e62ebf303f488947f4931 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Wed, 23 Sep 2020 09:45:30 +0100 Subject: [PATCH 094/185] JavaScript: Add two new command-injection tests. --- .../Security/CWE-078/Consistency.expected | 2 ++ .../Security/CWE-078/UselessUseOfCat.expected | 2 ++ .../query-tests/Security/CWE-078/exec-sh.js | 21 +++++++++++++++++++ .../query-tests/Security/CWE-078/exec-sh2.js | 16 ++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-078/exec-sh.js create mode 100644 javascript/ql/test/query-tests/Security/CWE-078/exec-sh2.js diff --git a/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected index e69de29bb2d..738c3367f7f 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected @@ -0,0 +1,2 @@ +| query-tests/Security/CWE-078/exec-sh2.js:10 | expected an alert, but found none | BAD | ComandInjection | +| query-tests/Security/CWE-078/exec-sh.js:15 | expected an alert, but found none | BAD | ComandInjection | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UselessUseOfCat.expected b/javascript/ql/test/query-tests/Security/CWE-078/UselessUseOfCat.expected index 720292c10c2..0480391fd82 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UselessUseOfCat.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/UselessUseOfCat.expected @@ -94,6 +94,8 @@ options | child_process-test.js:56:5:56:59 | cp.spaw ... cmd])) | child_process-test.js:56:25:56:58 | ['/C', ... , cmd]) | | child_process-test.js:57:5:57:50 | cp.spaw ... t(cmd)) | child_process-test.js:57:25:57:49 | ['/C', ... at(cmd) | | child_process-test.js:67:3:67:21 | cp.spawn(cmd, args) | child_process-test.js:67:17:67:20 | args | +| exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:10:50:10:56 | options | +| exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:15:54:15:60 | options | | lib/lib.js:152:2:152:23 | cp.spaw ... gs, cb) | lib/lib.js:152:21:152:22 | cb | | lib/lib.js:159:2:159:23 | cp.spaw ... gs, cb) | lib/lib.js:159:21:159:22 | cb | | lib/lib.js:163:2:167:2 | cp.spaw ... t' }\\n\\t) | lib/lib.js:166:3:166:22 | { stdio: 'inherit' } | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/exec-sh.js b/javascript/ql/test/query-tests/Security/CWE-078/exec-sh.js new file mode 100644 index 00000000000..b5b8fc602bd --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-078/exec-sh.js @@ -0,0 +1,21 @@ +const cp = require('child_process'), + http = require('http'), + url = require('url'); + +function getShell() { + if (process.platform === 'win32') { + return { cmd: 'cmd', arg: '/C' } + } else { + return { cmd: 'sh', arg: '-c' } + } +} + +function execSh(command, options) { + var shell = getShell() + return cp.spawn(shell.cmd, [shell.arg, command], options) // BAD +} + +http.createServer(function (req, res) { + let cmd = url.parse(req.url, true).query.path; + execSh(cmd); +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-078/exec-sh2.js b/javascript/ql/test/query-tests/Security/CWE-078/exec-sh2.js new file mode 100644 index 00000000000..ad91b66f534 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-078/exec-sh2.js @@ -0,0 +1,16 @@ +const cp = require('child_process'), + http = require('http'), + url = require('url'); + +function getShell() { + return "sh"; +} + +function execSh(command, options) { + return cp.spawn(getShell(), ["-c", command], options) // BAD +}; + +http.createServer(function (req, res) { + let cmd = url.parse(req.url, true).query.path; + execSh(cmd); +}); From ef18b39124ee97439f02db83e150ffa88d52d313 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Wed, 23 Sep 2020 09:52:50 +0100 Subject: [PATCH 095/185] JavaScript: Fix use of type backtracker in `IndirectCommandArgument.qll`. --- .../dataflow/IndirectCommandArgument.qll | 2 +- .../CWE-078/CommandInjection.expected | 25 +++++++++++++++++++ .../Security/CWE-078/Consistency.expected | 1 - 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll b/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll index 519d0938a27..0ed2e890e7e 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll @@ -30,7 +30,7 @@ private DataFlow::Node commandArgument(SystemCommandExecution sys, DataFlow::Typ t.start() and result = sys.getACommandArgument() or - exists(DataFlow::TypeBackTracker t2 | t = t2.smallstep(result, commandArgument(sys, t2))) + exists(DataFlow::TypeBackTracker t2 | t2 = t.smallstep(result, commandArgument(sys, t2))) } /** diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected index 2ebaeec1c24..20bb093c5eb 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected @@ -55,6 +55,18 @@ nodes | child_process-test.js:83:19:83:36 | req.query.fileName | | child_process-test.js:85:37:85:54 | req.query.fileName | | child_process-test.js:85:37:85:54 | req.query.fileName | +| exec-sh2.js:9:17:9:23 | command | +| exec-sh2.js:10:33:10:47 | ["-c", command] | +| exec-sh2.js:10:33:10:47 | ["-c", command] | +| exec-sh2.js:10:40:10:46 | command | +| exec-sh2.js:10:40:10:46 | command | +| exec-sh2.js:14:9:14:49 | cmd | +| exec-sh2.js:14:15:14:38 | url.par ... , true) | +| exec-sh2.js:14:15:14:44 | url.par ... ).query | +| exec-sh2.js:14:15:14:49 | url.par ... ry.path | +| exec-sh2.js:14:25:14:31 | req.url | +| exec-sh2.js:14:25:14:31 | req.url | +| exec-sh2.js:15:12:15:14 | cmd | | execSeries.js:3:20:3:22 | arr | | execSeries.js:6:14:6:16 | arr | | execSeries.js:6:14:6:21 | arr[i++] | @@ -174,6 +186,17 @@ edges | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | | child_process-test.js:85:37:85:54 | req.query.fileName | lib/subLib/index.js:7:32:7:35 | name | | child_process-test.js:85:37:85:54 | req.query.fileName | lib/subLib/index.js:7:32:7:35 | name | +| exec-sh2.js:9:17:9:23 | command | exec-sh2.js:10:40:10:46 | command | +| exec-sh2.js:9:17:9:23 | command | exec-sh2.js:10:40:10:46 | command | +| exec-sh2.js:10:40:10:46 | command | exec-sh2.js:10:33:10:47 | ["-c", command] | +| exec-sh2.js:10:40:10:46 | command | exec-sh2.js:10:33:10:47 | ["-c", command] | +| exec-sh2.js:14:9:14:49 | cmd | exec-sh2.js:15:12:15:14 | cmd | +| exec-sh2.js:14:15:14:38 | url.par ... , true) | exec-sh2.js:14:15:14:44 | url.par ... ).query | +| exec-sh2.js:14:15:14:44 | url.par ... ).query | exec-sh2.js:14:15:14:49 | url.par ... ry.path | +| exec-sh2.js:14:15:14:49 | url.par ... ry.path | exec-sh2.js:14:9:14:49 | cmd | +| exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:14:15:14:38 | url.par ... , true) | +| exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:14:15:14:38 | url.par ... , true) | +| exec-sh2.js:15:12:15:14 | cmd | exec-sh2.js:9:17:9:23 | command | | execSeries.js:3:20:3:22 | arr | execSeries.js:6:14:6:16 | arr | | execSeries.js:6:14:6:16 | arr | execSeries.js:6:14:6:21 | arr[i++] | | execSeries.js:6:14:6:21 | arr[i++] | execSeries.js:14:24:14:30 | command | @@ -260,6 +283,8 @@ edges | child_process-test.js:67:3:67:21 | cp.spawn(cmd, args) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:48:15:48:17 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:75:29:75:31 | cmd | child_process-test.js:73:25:73:31 | req.url | child_process-test.js:75:29:75:31 | cmd | This command depends on $@. | child_process-test.js:73:25:73:31 | req.url | a user-provided value | | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | This command depends on $@. | child_process-test.js:83:19:83:36 | req.query.fileName | a user-provided value | +| exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:33:10:47 | ["-c", command] | This command depends on $@. | exec-sh2.js:14:25:14:31 | req.url | a user-provided value | +| exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:40:10:46 | command | This command depends on $@. | exec-sh2.js:14:25:14:31 | req.url | a user-provided value | | execSeries.js:14:41:14:47 | command | execSeries.js:18:34:18:40 | req.url | execSeries.js:14:41:14:47 | command | This command depends on $@. | execSeries.js:18:34:18:40 | req.url | a user-provided value | | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | child_process-test.js:85:37:85:54 | req.query.fileName | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | This command depends on $@. | child_process-test.js:85:37:85:54 | req.query.fileName | a user-provided value | | other.js:7:33:7:35 | cmd | other.js:5:25:5:31 | req.url | other.js:7:33:7:35 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected index 738c3367f7f..bf9aaa6b4dd 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected @@ -1,2 +1 @@ -| query-tests/Security/CWE-078/exec-sh2.js:10 | expected an alert, but found none | BAD | ComandInjection | | query-tests/Security/CWE-078/exec-sh.js:15 | expected an alert, but found none | BAD | ComandInjection | From 439aadf0b614a639330104ec63f0d7360e63b166 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Wed, 23 Sep 2020 09:57:20 +0100 Subject: [PATCH 096/185] JavaScript: Do even more type tracking in command injection. --- .../dataflow/IndirectCommandArgument.qll | 22 +++++++++++++++- .../CWE-078/CommandInjection.expected | 25 +++++++++++++++++++ .../Security/CWE-078/Consistency.expected | 1 - 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll b/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll index 0ed2e890e7e..33402cfe199 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll @@ -63,6 +63,26 @@ private DataFlow::SourceNode argumentList(SystemCommandExecution sys) { result = argumentList(sys, DataFlow::TypeBackTracker::end()) } +/** + * Gets a data-flow node whose value ends up being interpreted as an element of the argument list + * `args` after a flow summarised by `t`. + */ +private DataFlow::Node argumentListElement(DataFlow::SourceNode args, DataFlow::TypeBackTracker t) { + t.start() and + args = argumentList(_) and + result = args.getAPropertyWrite().getRhs() + or + exists(DataFlow::TypeBackTracker t2 | t2 = t.smallstep(result, argumentListElement(args, t2))) +} + +/** + * Gets a data-flow node whose value ends up being interpreted as an element of the argument list + * `args`. + */ +private DataFlow::Node argumentListElement(DataFlow::SourceNode args) { + result = argumentListElement(args, DataFlow::TypeBackTracker::end()) +} + /** * Holds if `source` contributes to the arguments of an indirect command execution `sys`. * @@ -86,8 +106,8 @@ predicate isIndirectCommandArgument(DataFlow::Node source, SystemCommandExecutio exists(DataFlow::ArrayCreationNode args, DataFlow::Node shell, string dashC | shellCmd(shell.asExpr(), dashC) and shell = commandArgument(sys) and - args.getAPropertyWrite().getRhs().mayHaveStringValue(dashC) and args = argumentList(sys) and + argumentListElement(args).mayHaveStringValue(dashC) and ( source = argumentList(sys) or diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected index 20bb093c5eb..1a9b11674b2 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected @@ -67,6 +67,18 @@ nodes | exec-sh2.js:14:25:14:31 | req.url | | exec-sh2.js:14:25:14:31 | req.url | | exec-sh2.js:15:12:15:14 | cmd | +| exec-sh.js:13:17:13:23 | command | +| exec-sh.js:15:32:15:51 | [shell.arg, command] | +| exec-sh.js:15:32:15:51 | [shell.arg, command] | +| exec-sh.js:15:44:15:50 | command | +| exec-sh.js:15:44:15:50 | command | +| exec-sh.js:19:9:19:49 | cmd | +| exec-sh.js:19:15:19:38 | url.par ... , true) | +| exec-sh.js:19:15:19:44 | url.par ... ).query | +| exec-sh.js:19:15:19:49 | url.par ... ry.path | +| exec-sh.js:19:25:19:31 | req.url | +| exec-sh.js:19:25:19:31 | req.url | +| exec-sh.js:20:12:20:14 | cmd | | execSeries.js:3:20:3:22 | arr | | execSeries.js:6:14:6:16 | arr | | execSeries.js:6:14:6:21 | arr[i++] | @@ -197,6 +209,17 @@ edges | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:14:15:14:38 | url.par ... , true) | | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:14:15:14:38 | url.par ... , true) | | exec-sh2.js:15:12:15:14 | cmd | exec-sh2.js:9:17:9:23 | command | +| exec-sh.js:13:17:13:23 | command | exec-sh.js:15:44:15:50 | command | +| exec-sh.js:13:17:13:23 | command | exec-sh.js:15:44:15:50 | command | +| exec-sh.js:15:44:15:50 | command | exec-sh.js:15:32:15:51 | [shell.arg, command] | +| exec-sh.js:15:44:15:50 | command | exec-sh.js:15:32:15:51 | [shell.arg, command] | +| exec-sh.js:19:9:19:49 | cmd | exec-sh.js:20:12:20:14 | cmd | +| exec-sh.js:19:15:19:38 | url.par ... , true) | exec-sh.js:19:15:19:44 | url.par ... ).query | +| exec-sh.js:19:15:19:44 | url.par ... ).query | exec-sh.js:19:15:19:49 | url.par ... ry.path | +| exec-sh.js:19:15:19:49 | url.par ... ry.path | exec-sh.js:19:9:19:49 | cmd | +| exec-sh.js:19:25:19:31 | req.url | exec-sh.js:19:15:19:38 | url.par ... , true) | +| exec-sh.js:19:25:19:31 | req.url | exec-sh.js:19:15:19:38 | url.par ... , true) | +| exec-sh.js:20:12:20:14 | cmd | exec-sh.js:13:17:13:23 | command | | execSeries.js:3:20:3:22 | arr | execSeries.js:6:14:6:16 | arr | | execSeries.js:6:14:6:16 | arr | execSeries.js:6:14:6:21 | arr[i++] | | execSeries.js:6:14:6:21 | arr[i++] | execSeries.js:14:24:14:30 | command | @@ -285,6 +308,8 @@ edges | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | This command depends on $@. | child_process-test.js:83:19:83:36 | req.query.fileName | a user-provided value | | exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:33:10:47 | ["-c", command] | This command depends on $@. | exec-sh2.js:14:25:14:31 | req.url | a user-provided value | | exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:40:10:46 | command | This command depends on $@. | exec-sh2.js:14:25:14:31 | req.url | a user-provided value | +| exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:19:25:19:31 | req.url | exec-sh.js:15:32:15:51 | [shell.arg, command] | This command depends on $@. | exec-sh.js:19:25:19:31 | req.url | a user-provided value | +| exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:19:25:19:31 | req.url | exec-sh.js:15:44:15:50 | command | This command depends on $@. | exec-sh.js:19:25:19:31 | req.url | a user-provided value | | execSeries.js:14:41:14:47 | command | execSeries.js:18:34:18:40 | req.url | execSeries.js:14:41:14:47 | command | This command depends on $@. | execSeries.js:18:34:18:40 | req.url | a user-provided value | | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | child_process-test.js:85:37:85:54 | req.query.fileName | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | This command depends on $@. | child_process-test.js:85:37:85:54 | req.query.fileName | a user-provided value | | other.js:7:33:7:35 | cmd | other.js:5:25:5:31 | req.url | other.js:7:33:7:35 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected index bf9aaa6b4dd..e69de29bb2d 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected @@ -1 +0,0 @@ -| query-tests/Security/CWE-078/exec-sh.js:15 | expected an alert, but found none | BAD | ComandInjection | From dc7b447895deae5925bf25389eb81c6e3709b607 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Wed, 23 Sep 2020 10:24:49 +0100 Subject: [PATCH 097/185] JavaScript: Make alert locations for command injection more precise. --- .../dataflow/IndirectCommandArgument.qll | 8 ++++---- .../Security/CWE-078/CommandInjection.expected | 18 ------------------ 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll b/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll index 33402cfe199..a84b9701ed0 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll @@ -108,10 +108,10 @@ predicate isIndirectCommandArgument(DataFlow::Node source, SystemCommandExecutio shell = commandArgument(sys) and args = argumentList(sys) and argumentListElement(args).mayHaveStringValue(dashC) and - ( - source = argumentList(sys) - or - source = argumentList(sys).getAPropertyWrite().getRhs() + exists(DataFlow::SourceNode argsSource | argsSource = argumentList(sys) | + if exists(argsSource.getAPropertyWrite()) + then source = argsSource.getAPropertyWrite().getRhs() + else source = argsSource ) ) } diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected index 1a9b11674b2..5d9c4fcdb61 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected @@ -23,8 +23,6 @@ nodes | child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" | | child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" | | child_process-test.js:25:21:25:23 | cmd | -| child_process-test.js:39:18:39:30 | [ flag, cmd ] | -| child_process-test.js:39:18:39:30 | [ flag, cmd ] | | child_process-test.js:39:26:39:28 | cmd | | child_process-test.js:39:26:39:28 | cmd | | child_process-test.js:43:15:43:17 | cmd | @@ -36,7 +34,6 @@ nodes | child_process-test.js:56:25:56:58 | ['/C', ... , cmd]) | | child_process-test.js:56:25:56:58 | ['/C', ... , cmd]) | | child_process-test.js:56:46:56:57 | ["bar", cmd] | -| child_process-test.js:56:46:56:57 | ["bar", cmd] | | child_process-test.js:56:54:56:56 | cmd | | child_process-test.js:56:54:56:56 | cmd | | child_process-test.js:57:25:57:49 | ['/C', ... at(cmd) | @@ -56,8 +53,6 @@ nodes | child_process-test.js:85:37:85:54 | req.query.fileName | | child_process-test.js:85:37:85:54 | req.query.fileName | | exec-sh2.js:9:17:9:23 | command | -| exec-sh2.js:10:33:10:47 | ["-c", command] | -| exec-sh2.js:10:33:10:47 | ["-c", command] | | exec-sh2.js:10:40:10:46 | command | | exec-sh2.js:10:40:10:46 | command | | exec-sh2.js:14:9:14:49 | cmd | @@ -68,8 +63,6 @@ nodes | exec-sh2.js:14:25:14:31 | req.url | | exec-sh2.js:15:12:15:14 | cmd | | exec-sh.js:13:17:13:23 | command | -| exec-sh.js:15:32:15:51 | [shell.arg, command] | -| exec-sh.js:15:32:15:51 | [shell.arg, command] | | exec-sh.js:15:44:15:50 | command | | exec-sh.js:15:44:15:50 | command | | exec-sh.js:19:9:19:49 | cmd | @@ -180,12 +173,9 @@ edges | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:6:15:6:38 | url.par ... , true) | | child_process-test.js:25:21:25:23 | cmd | child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" | | child_process-test.js:25:21:25:23 | cmd | child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" | -| child_process-test.js:39:26:39:28 | cmd | child_process-test.js:39:18:39:30 | [ flag, cmd ] | -| child_process-test.js:39:26:39:28 | cmd | child_process-test.js:39:18:39:30 | [ flag, cmd ] | | child_process-test.js:56:46:56:57 | ["bar", cmd] | child_process-test.js:56:25:56:58 | ['/C', ... , cmd]) | | child_process-test.js:56:46:56:57 | ["bar", cmd] | child_process-test.js:56:25:56:58 | ['/C', ... , cmd]) | | child_process-test.js:56:54:56:56 | cmd | child_process-test.js:56:46:56:57 | ["bar", cmd] | -| child_process-test.js:56:54:56:56 | cmd | child_process-test.js:56:46:56:57 | ["bar", cmd] | | child_process-test.js:57:46:57:48 | cmd | child_process-test.js:57:25:57:49 | ['/C', ... at(cmd) | | child_process-test.js:57:46:57:48 | cmd | child_process-test.js:57:25:57:49 | ['/C', ... at(cmd) | | child_process-test.js:73:9:73:49 | cmd | child_process-test.js:75:29:75:31 | cmd | @@ -200,8 +190,6 @@ edges | child_process-test.js:85:37:85:54 | req.query.fileName | lib/subLib/index.js:7:32:7:35 | name | | exec-sh2.js:9:17:9:23 | command | exec-sh2.js:10:40:10:46 | command | | exec-sh2.js:9:17:9:23 | command | exec-sh2.js:10:40:10:46 | command | -| exec-sh2.js:10:40:10:46 | command | exec-sh2.js:10:33:10:47 | ["-c", command] | -| exec-sh2.js:10:40:10:46 | command | exec-sh2.js:10:33:10:47 | ["-c", command] | | exec-sh2.js:14:9:14:49 | cmd | exec-sh2.js:15:12:15:14 | cmd | | exec-sh2.js:14:15:14:38 | url.par ... , true) | exec-sh2.js:14:15:14:44 | url.par ... ).query | | exec-sh2.js:14:15:14:44 | url.par ... ).query | exec-sh2.js:14:15:14:49 | url.par ... ry.path | @@ -211,8 +199,6 @@ edges | exec-sh2.js:15:12:15:14 | cmd | exec-sh2.js:9:17:9:23 | command | | exec-sh.js:13:17:13:23 | command | exec-sh.js:15:44:15:50 | command | | exec-sh.js:13:17:13:23 | command | exec-sh.js:15:44:15:50 | command | -| exec-sh.js:15:44:15:50 | command | exec-sh.js:15:32:15:51 | [shell.arg, command] | -| exec-sh.js:15:44:15:50 | command | exec-sh.js:15:32:15:51 | [shell.arg, command] | | exec-sh.js:19:9:19:49 | cmd | exec-sh.js:20:12:20:14 | cmd | | exec-sh.js:19:15:19:38 | url.par ... , true) | exec-sh.js:19:15:19:44 | url.par ... ).query | | exec-sh.js:19:15:19:44 | url.par ... ).query | exec-sh.js:19:15:19:49 | url.par ... ry.path | @@ -293,12 +279,10 @@ edges | child_process-test.js:22:18:22:20 | cmd | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:22:18:22:20 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:23:13:23:15 | cmd | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:23:13:23:15 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | -| child_process-test.js:39:5:39:31 | cp.spaw ... cmd ]) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:39:18:39:30 | [ flag, cmd ] | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:39:5:39:31 | cp.spaw ... cmd ]) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:39:26:39:28 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:44:5:44:34 | cp.exec ... , args) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:43:15:43:17 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:54:5:54:39 | cp.exec ... , args) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:53:15:53:17 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:56:5:56:59 | cp.spaw ... cmd])) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:56:25:56:58 | ['/C', ... , cmd]) | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | -| child_process-test.js:56:5:56:59 | cp.spaw ... cmd])) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:56:46:56:57 | ["bar", cmd] | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:56:5:56:59 | cp.spaw ... cmd])) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:56:54:56:56 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:57:5:57:50 | cp.spaw ... t(cmd)) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:6:15:6:49 | url.par ... ry.path | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:57:5:57:50 | cp.spaw ... t(cmd)) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:57:25:57:49 | ['/C', ... at(cmd) | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | @@ -306,9 +290,7 @@ edges | child_process-test.js:67:3:67:21 | cp.spawn(cmd, args) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:48:15:48:17 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value | | child_process-test.js:75:29:75:31 | cmd | child_process-test.js:73:25:73:31 | req.url | child_process-test.js:75:29:75:31 | cmd | This command depends on $@. | child_process-test.js:73:25:73:31 | req.url | a user-provided value | | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | This command depends on $@. | child_process-test.js:83:19:83:36 | req.query.fileName | a user-provided value | -| exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:33:10:47 | ["-c", command] | This command depends on $@. | exec-sh2.js:14:25:14:31 | req.url | a user-provided value | | exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:40:10:46 | command | This command depends on $@. | exec-sh2.js:14:25:14:31 | req.url | a user-provided value | -| exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:19:25:19:31 | req.url | exec-sh.js:15:32:15:51 | [shell.arg, command] | This command depends on $@. | exec-sh.js:19:25:19:31 | req.url | a user-provided value | | exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:19:25:19:31 | req.url | exec-sh.js:15:44:15:50 | command | This command depends on $@. | exec-sh.js:19:25:19:31 | req.url | a user-provided value | | execSeries.js:14:41:14:47 | command | execSeries.js:18:34:18:40 | req.url | execSeries.js:14:41:14:47 | command | This command depends on $@. | execSeries.js:18:34:18:40 | req.url | a user-provided value | | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | child_process-test.js:85:37:85:54 | req.query.fileName | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | This command depends on $@. | child_process-test.js:85:37:85:54 | req.query.fileName | a user-provided value | From 589426367106d302490fbe3ef74a4713cc6a60da Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Wed, 23 Sep 2020 15:37:55 +0200 Subject: [PATCH 098/185] Java: improve change note Co-authored-by: Anders Schack-Mulligen --- java/change-notes/2020-09-22-hibernate-sql-sinks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/change-notes/2020-09-22-hibernate-sql-sinks.md b/java/change-notes/2020-09-22-hibernate-sql-sinks.md index 3a565a351c3..1d469825f8e 100644 --- a/java/change-notes/2020-09-22-hibernate-sql-sinks.md +++ b/java/change-notes/2020-09-22-hibernate-sql-sinks.md @@ -1,3 +1,3 @@ lgtm,codescanning -* Support for the [Hibernate ORM](https://hibernate.org/orm/) ORM library (specifically, its Query +* Support for the [Hibernate ORM](https://hibernate.org/orm/) library (specifically, its Query creation methods) has been improved, which may lead to more results from the security queries. From b7d0939f4a0991815d5077a33f86aa6b0407fc20 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Wed, 23 Sep 2020 15:50:07 +0200 Subject: [PATCH 099/185] C++: ExtendedRangeAnalysis stub implementation Just to demonstrate how things fit together, I've created `SubtractSelf.qll` that adds a (hopefully sound) version of the test extension that was already used in `extensibility.ql`. --- .../cpp/rangeanalysis/ExtendedRangeAnalysis.qll | 4 ++++ .../cpp/rangeanalysis/extensions/SubtractSelf.qll | 15 +++++++++++++++ .../rangeanalysis/extended/extended.cpp | 9 +++++++++ .../rangeanalysis/extended/extended.expected | 6 ++++++ .../rangeanalysis/extended/extended.ql | 7 +++++++ .../rangeanalysis/extensibility/extensibility.c | 2 +- .../extensibility/extensibility.expected | 2 +- 7 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ExtendedRangeAnalysis.qll create mode 100644 cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/SubtractSelf.qll create mode 100644 cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.cpp create mode 100644 cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.expected create mode 100644 cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.ql diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ExtendedRangeAnalysis.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ExtendedRangeAnalysis.qll new file mode 100644 index 00000000000..4c9b0c738f4 --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ExtendedRangeAnalysis.qll @@ -0,0 +1,4 @@ +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +// +// Import each extension we want to enable +import extensions.SubtractSelf diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/SubtractSelf.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/SubtractSelf.qll new file mode 100644 index 00000000000..ff716d02d6f --- /dev/null +++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/SubtractSelf.qll @@ -0,0 +1,15 @@ +import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr + +private class SelfSub extends SimpleRangeAnalysisExpr, SubExpr { + SelfSub() { + // Match `x - x` but not `myInt - (unsigned char)myInt`. + getLeftOperand().getExplicitlyConverted().(VariableAccess).getTarget() = + getRightOperand().getExplicitlyConverted().(VariableAccess).getTarget() + } + + override float getLowerBounds() { result = 0 } + + override float getUpperBounds() { result = 0 } + + override predicate dependsOnChild(Expr child) { none() } +} diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.cpp new file mode 100644 index 00000000000..0fed35bc9af --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.cpp @@ -0,0 +1,9 @@ + + +void test_overridability_sub(int x) { + int zero = x - x; + zero; // 0 + + int nonzero = x - (unsigned char)x; + nonzero; // full range +} diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.expected new file mode 100644 index 00000000000..b43601c8088 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.expected @@ -0,0 +1,6 @@ +| extended.cpp:4:14:4:14 | x | -2.147483648E9 | 2.147483647E9 | +| extended.cpp:4:18:4:18 | x | -2.147483648E9 | 2.147483647E9 | +| extended.cpp:5:3:5:6 | zero | 0.0 | 0.0 | +| extended.cpp:7:17:7:17 | x | -2.147483648E9 | 2.147483647E9 | +| extended.cpp:7:36:7:36 | x | -2.147483648E9 | 2.147483647E9 | +| extended.cpp:8:3:8:9 | nonzero | -2.147483648E9 | 2.147483647E9 | diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.ql new file mode 100644 index 00000000000..d6344e5d062 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.ql @@ -0,0 +1,7 @@ +import experimental.semmle.code.cpp.rangeanalysis.ExtendedRangeAnalysis + +from VariableAccess expr, float lower, float upper +where + lower = lowerBound(expr) and + upper = upperBound(expr) +select expr, lower, upper diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c index afdde0b615c..8cc4e42cb11 100644 --- a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c @@ -9,6 +9,6 @@ int test_extensibility_add(int x) { } int test_overridability_sub(int x) { - int result = x - x; // Returns 0 due to custom modeling in QL + int result = x - (unsigned char)x; // Returns 0 due to custom modeling for this test being deliberately wrong return result; // 0 } \ No newline at end of file diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected index ad97d9b2df5..6f62b05f06a 100644 --- a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected +++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected @@ -3,5 +3,5 @@ | extensibility.c:6:38:6:38 | x | -10.0 | 10.0 | | extensibility.c:7:12:7:17 | result | 90.0 | 110.0 | | extensibility.c:12:16:12:16 | x | -2.147483648E9 | 2.147483647E9 | -| extensibility.c:12:20:12:20 | x | -2.147483648E9 | 2.147483647E9 | +| extensibility.c:12:35:12:35 | x | -2.147483648E9 | 2.147483647E9 | | extensibility.c:13:10:13:15 | result | 0.0 | 0.0 | From 83f0514475e0a8a7054e403e3fef699db3cc409b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 23 Sep 2020 15:50:14 +0200 Subject: [PATCH 100/185] add req.files as a RequestInputAccess in the Express model --- .../semmle/javascript/frameworks/Express.qll | 4 ++++ .../query-tests/Security/CWE-611/Xxe.expected | 22 +++++++++++++++++++ .../Security/CWE-611/libxml.noent.js | 10 +++++++++ 3 files changed, 36 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index ce47919793b..5a5af64aefc 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -505,6 +505,10 @@ module Express { // `req.cookies` kind = "cookie" and this = request.getAPropertyRead("cookies") + or + // `req.files`, treated the same as `req.body`. + kind = "body" and + this = request.getAPropertyRead("files") ) or kind = "body" and diff --git a/javascript/ql/test/query-tests/Security/CWE-611/Xxe.expected b/javascript/ql/test/query-tests/Security/CWE-611/Xxe.expected index fd44e6cc756..f7c8c7c1878 100644 --- a/javascript/ql/test/query-tests/Security/CWE-611/Xxe.expected +++ b/javascript/ql/test/query-tests/Security/CWE-611/Xxe.expected @@ -10,6 +10,18 @@ nodes | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | +| libxml.noent.js:11:21:11:41 | req.par ... e-xml") | +| libxml.noent.js:11:21:11:41 | req.par ... e-xml") | +| libxml.noent.js:11:21:11:41 | req.par ... e-xml") | +| libxml.noent.js:14:27:14:47 | req.par ... e-xml") | +| libxml.noent.js:14:27:14:47 | req.par ... e-xml") | +| libxml.noent.js:14:27:14:47 | req.par ... e-xml") | +| libxml.noent.js:16:26:16:34 | req.files | +| libxml.noent.js:16:26:16:34 | req.files | +| libxml.noent.js:16:26:16:43 | req.files.products | +| libxml.noent.js:16:26:16:48 | req.fil ... ts.data | +| libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | +| libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | @@ -25,11 +37,21 @@ edges | domparser.js:2:13:2:29 | document.location | domparser.js:2:13:2:36 | documen ... .search | | domparser.js:2:13:2:36 | documen ... .search | domparser.js:2:7:2:36 | src | | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | +| libxml.noent.js:11:21:11:41 | req.par ... e-xml") | libxml.noent.js:11:21:11:41 | req.par ... e-xml") | +| libxml.noent.js:14:27:14:47 | req.par ... e-xml") | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | +| libxml.noent.js:16:26:16:34 | req.files | libxml.noent.js:16:26:16:43 | req.files.products | +| libxml.noent.js:16:26:16:34 | req.files | libxml.noent.js:16:26:16:43 | req.files.products | +| libxml.noent.js:16:26:16:43 | req.files.products | libxml.noent.js:16:26:16:48 | req.fil ... ts.data | +| libxml.noent.js:16:26:16:48 | req.fil ... ts.data | libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | +| libxml.noent.js:16:26:16:48 | req.fil ... ts.data | libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | #select | domparser.js:11:55:11:57 | src | domparser.js:2:13:2:29 | document.location | domparser.js:11:55:11:57 | src | A $@ is parsed as XML without guarding against external entity expansion. | domparser.js:2:13:2:29 | document.location | user-provided value | | domparser.js:14:57:14:59 | src | domparser.js:2:13:2:29 | document.location | domparser.js:14:57:14:59 | src | A $@ is parsed as XML without guarding against external entity expansion. | domparser.js:2:13:2:29 | document.location | user-provided value | | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | A $@ is parsed as XML without guarding against external entity expansion. | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | user-provided value | +| libxml.noent.js:11:21:11:41 | req.par ... e-xml") | libxml.noent.js:11:21:11:41 | req.par ... e-xml") | libxml.noent.js:11:21:11:41 | req.par ... e-xml") | A $@ is parsed as XML without guarding against external entity expansion. | libxml.noent.js:11:21:11:41 | req.par ... e-xml") | user-provided value | +| libxml.noent.js:14:27:14:47 | req.par ... e-xml") | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | A $@ is parsed as XML without guarding against external entity expansion. | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | user-provided value | +| libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | libxml.noent.js:16:26:16:34 | req.files | libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | A $@ is parsed as XML without guarding against external entity expansion. | libxml.noent.js:16:26:16:34 | req.files | user-provided value | | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | A $@ is parsed as XML without guarding against external entity expansion. | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | user-provided value | | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | A $@ is parsed as XML without guarding against external entity expansion. | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-611/libxml.noent.js b/javascript/ql/test/query-tests/Security/CWE-611/libxml.noent.js index b55e8c9c413..b0f0d6168e1 100644 --- a/javascript/ql/test/query-tests/Security/CWE-611/libxml.noent.js +++ b/javascript/ql/test/query-tests/Security/CWE-611/libxml.noent.js @@ -5,3 +5,13 @@ express().get('/some/path', function(req) { // NOT OK: unguarded entity expansion libxmljs.parseXml(req.param("some-xml"), { noent: true }); }); + +express().post('/some/path', function(req, res) { + // NOT OK: unguarded entity expansion + libxmljs.parseXml(req.param("some-xml"), { noent: true }); + + // NOT OK: unguarded entity expansion + libxmljs.parseXmlString(req.param("some-xml"), {noent:true,noblanks:true}) + // NOT OK: unguarded entity expansion + libxmljs.parseXmlString(req.files.products.data.toString('utf8'), {noent:true,noblanks:true}) +}); From f794eaa67020fd988523fbc5f8c51a9acb9c313c Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 23 Sep 2020 16:26:40 +0200 Subject: [PATCH 101/185] C++: Manual recursion in skipCopyValueInstructions instead of transitive closure --- .../code/cpp/ir/dataflow/internal/DataFlowPrivate.qll | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 9d188351272..fb9b39c0fda 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -355,14 +355,16 @@ private class ArrayToPointerConvertInstruction extends ConvertInstruction { } } -private Instruction skipOneCopyValueInstruction(Instruction instr) { - not instr instanceof CopyValueInstruction and result = instr +private Instruction skipOneCopyValueInstructionRec(CopyValueInstruction copy) { + copy.getUnary() = result and not result instanceof CopyValueInstruction or - result = instr.(CopyValueInstruction).getUnary() + result = skipOneCopyValueInstructionRec(copy.getUnary()) } private Instruction skipCopyValueInstructions(Instruction instr) { - result = skipOneCopyValueInstruction*(instr) and not result instanceof CopyValueInstruction + not result instanceof CopyValueInstruction and result = instr + or + result = skipOneCopyValueInstructionRec(instr) } private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) { From 48bf6d55aa3cb2807ea7776101e9e175588c936a Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Wed, 23 Sep 2020 12:19:42 +0200 Subject: [PATCH 102/185] C#: Add implicit cast from array to pointer --- .../Entities/Expressions/ImplicitCast.cs | 9 +++++++++ csharp/ql/test/experimental/ir/ir/PrintAst.expected | 3 ++- csharp/ql/test/experimental/ir/ir/raw_ir.expected | 4 ++-- .../controlflow/graph/BasicBlock.expected | 2 +- .../controlflow/graph/Dominance.expected | 12 ++++++++---- .../controlflow/graph/EnclosingCallable.expected | 2 ++ .../controlflow/graph/EntryElement.expected | 2 ++ .../controlflow/graph/ExitElement.expected | 2 ++ .../controlflow/graph/NodeGraph.expected | 6 ++++-- .../conversion/pointer/Pointer.expected | 4 ++-- .../dataflow/signanalysis/MissingSign.expected | 3 --- .../ql/test/library-tests/unsafe/PrintAst.expected | 3 ++- 12 files changed, 36 insertions(+), 16 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs index d8d8c80cf75..299c3aa8f72 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs @@ -86,6 +86,15 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions return new ImplicitCast(info); } + if (conversion.IsIdentity && conversion.IsImplicit && + convertedType.Symbol is IPointerTypeSymbol && + !(resolvedType.Symbol is IPointerTypeSymbol)) + { + // int[] -> int* + // string -> char* + return new ImplicitCast(info); + } + // Default: Just create the expression without a conversion. return Factory.Create(info); } diff --git a/csharp/ql/test/experimental/ir/ir/PrintAst.expected b/csharp/ql/test/experimental/ir/ir/PrintAst.expected index 0ba7be8a584..25ea10bc476 100644 --- a/csharp/ql/test/experimental/ir/ir/PrintAst.expected +++ b/csharp/ql/test/experimental/ir/ir/PrintAst.expected @@ -762,7 +762,8 @@ pointers.cs: # 5| 1: [LocalVariableAccess] access to local variable length # 6| 1: [FixedStmt] fixed(...) { ... } # 6| -1: [LocalVariableDeclAndInitExpr] Int32* b = ... -# 6| 0: [ParameterAccess] access to parameter arr +# 6| 0: [CastExpr] (...) ... +# 6| 0: [ParameterAccess] access to parameter arr # 6| 1: [LocalVariableAccess] access to local variable b # 7| 0: [BlockStmt] {...} # 8| 0: [LocalVariableDeclStmt] ... ...; diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir.expected b/csharp/ql/test/experimental/ir/ir/raw_ir.expected index 785f3872787..e426955f9da 100644 --- a/csharp/ql/test/experimental/ir/ir/raw_ir.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir.expected @@ -1327,8 +1327,8 @@ pointers.cs: # 6| r6_1(glval) = VariableAddress[b] : # 6| r6_2(glval) = VariableAddress[arr] : # 6| r6_3(Int32[]) = Load : &:r6_2, ~m? -# 6| r6_4(Int32*) = Convert : r6_3 -# 6| mu6_5(Int32*) = Store : &:r6_1, r6_3 +# 6| r6_4(Int32*) = CheckedConvertOrThrow : r6_3 +# 6| mu6_5(Int32*) = Store : &:r6_1, r6_4 # 8| r8_1(glval) = VariableAddress[p] : # 8| r8_2(glval) = VariableAddress[b] : # 8| r8_3(Int32*) = Load : &:r8_2, ~m? diff --git a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected index cf6a34b5ba7..d7ca5bdd073 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected @@ -725,7 +725,7 @@ | TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:7:13:7:22 | ... is ... | 14 | | TypeAccesses.cs:7:25:7:25 | ; | TypeAccesses.cs:7:25:7:25 | ; | 1 | | TypeAccesses.cs:8:9:8:28 | ... ...; | TypeAccesses.cs:3:10:3:10 | exit M | 4 | -| VarDecls.cs:5:18:5:19 | enter M1 | VarDecls.cs:5:18:5:19 | exit M1 | 16 | +| VarDecls.cs:5:18:5:19 | enter M1 | VarDecls.cs:5:18:5:19 | exit M1 | 18 | | VarDecls.cs:13:12:13:13 | enter M2 | VarDecls.cs:13:12:13:13 | exit M2 | 12 | | VarDecls.cs:19:7:19:8 | enter M3 | VarDecls.cs:25:20:25:20 | access to parameter b | 12 | | VarDecls.cs:25:13:25:29 | return ...; | VarDecls.cs:19:7:19:8 | exit M3 | 2 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected index 9faece057e1..773e3cd0204 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected @@ -2568,11 +2568,13 @@ dominance | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:7:27:7:33 | access to parameter strings | | VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:44:7:50 | access to parameter strings | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:7:35:7:35 | 0 | -| VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:22:7:36 | Char* c1 = ... | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:7:22:7:36 | Char* c1 = ... | +| VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:27:7:36 | (...) ... | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:7:27:7:36 | access to array element | | VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:8:9:10:9 | {...} | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:7:52:7:52 | 1 | -| VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:39:7:53 | Char* c2 = ... | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:7:39:7:53 | Char* c2 = ... | +| VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:44:7:53 | (...) ... | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:7:44:7:53 | access to array element | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:9:27:9:28 | access to local variable c1 | | VarDecls.cs:9:13:9:29 | return ...; | VarDecls.cs:5:18:5:19 | exit M1 | @@ -5703,12 +5705,14 @@ postDominance | VarDecls.cs:5:18:5:19 | exit M1 | VarDecls.cs:9:13:9:29 | return ...; | | VarDecls.cs:6:5:11:5 | {...} | VarDecls.cs:5:18:5:19 | enter M1 | | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:6:5:11:5 | {...} | -| VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:27:7:36 | access to array element | +| VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:27:7:36 | (...) ... | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:7:27:7:36 | access to array element | | VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:35:7:35 | 0 | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:7:27:7:33 | access to parameter strings | -| VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:7:44:7:53 | access to array element | +| VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:7:44:7:53 | (...) ... | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:7:22:7:36 | Char* c1 = ... | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:7:44:7:53 | access to array element | | VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:52:7:52 | 1 | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:7:44:7:50 | access to parameter strings | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:7:39:7:53 | Char* c2 = ... | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected index 6dbf1e568b0..10afd142066 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected @@ -2931,10 +2931,12 @@ nodeEnclosing | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:5:18:5:19 | M1 | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:5:18:5:19 | M1 | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:5:18:5:19 | M1 | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:5:18:5:19 | M1 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected index 8751b4d35f7..cf3a8c054eb 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected @@ -2064,10 +2064,12 @@ | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | | VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:27:7:33 | access to parameter strings | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:7:27:7:33 | access to parameter strings | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:7:27:7:33 | access to parameter strings | | VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:27:7:33 | access to parameter strings | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:7:35:7:35 | 0 | | VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:7:44:7:50 | access to parameter strings | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:7:44:7:50 | access to parameter strings | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:7:44:7:50 | access to parameter strings | | VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:44:7:50 | access to parameter strings | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:7:52:7:52 | 1 | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:8:9:10:9 | {...} | diff --git a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected index 96165e8fe19..428ddfeca24 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected @@ -2770,10 +2770,12 @@ | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:9:13:9:29 | return ...; | return | | VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:22:7:36 | Char* c1 = ... | normal | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:7:27:7:33 | access to parameter strings | normal | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:7:27:7:36 | (...) ... | normal | | VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:27:7:36 | access to array element | normal | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:7:35:7:35 | 0 | normal | | VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:7:39:7:53 | Char* c2 = ... | normal | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:7:44:7:50 | access to parameter strings | normal | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:7:44:7:53 | (...) ... | normal | | VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:44:7:53 | access to array element | normal | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:7:52:7:52 | 1 | normal | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:9:13:9:29 | return ...; | return | diff --git a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected index 03cbc257d26..7feee30c18b 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected @@ -2937,11 +2937,13 @@ | VarDecls.cs:7:9:10:9 | fixed(...) { ... } | VarDecls.cs:7:27:7:33 | access to parameter strings | semmle.label | successor | | VarDecls.cs:7:22:7:36 | Char* c1 = ... | VarDecls.cs:7:44:7:50 | access to parameter strings | semmle.label | successor | | VarDecls.cs:7:27:7:33 | access to parameter strings | VarDecls.cs:7:35:7:35 | 0 | semmle.label | successor | -| VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:22:7:36 | Char* c1 = ... | semmle.label | successor | +| VarDecls.cs:7:27:7:36 | (...) ... | VarDecls.cs:7:22:7:36 | Char* c1 = ... | semmle.label | successor | +| VarDecls.cs:7:27:7:36 | access to array element | VarDecls.cs:7:27:7:36 | (...) ... | semmle.label | successor | | VarDecls.cs:7:35:7:35 | 0 | VarDecls.cs:7:27:7:36 | access to array element | semmle.label | successor | | VarDecls.cs:7:39:7:53 | Char* c2 = ... | VarDecls.cs:8:9:10:9 | {...} | semmle.label | successor | | VarDecls.cs:7:44:7:50 | access to parameter strings | VarDecls.cs:7:52:7:52 | 1 | semmle.label | successor | -| VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:39:7:53 | Char* c2 = ... | semmle.label | successor | +| VarDecls.cs:7:44:7:53 | (...) ... | VarDecls.cs:7:39:7:53 | Char* c2 = ... | semmle.label | successor | +| VarDecls.cs:7:44:7:53 | access to array element | VarDecls.cs:7:44:7:53 | (...) ... | semmle.label | successor | | VarDecls.cs:7:52:7:52 | 1 | VarDecls.cs:7:44:7:53 | access to array element | semmle.label | successor | | VarDecls.cs:8:9:10:9 | {...} | VarDecls.cs:9:27:9:28 | access to local variable c1 | semmle.label | successor | | VarDecls.cs:9:13:9:29 | return ...; | VarDecls.cs:5:18:5:19 | exit M1 | semmle.label | return | diff --git a/csharp/ql/test/library-tests/conversion/pointer/Pointer.expected b/csharp/ql/test/library-tests/conversion/pointer/Pointer.expected index 7db63e5ef9f..5aca5582d14 100644 --- a/csharp/ql/test/library-tests/conversion/pointer/Pointer.expected +++ b/csharp/ql/test/library-tests/conversion/pointer/Pointer.expected @@ -1,4 +1,4 @@ -| Pointer.cs:7:21:7:28 | Pointer.cs:7:21:7:28 | Int32* | Int32[] | access to parameter arr | +| Pointer.cs:7:21:7:28 | Pointer.cs:7:21:7:28 | Int32* | Int32* | (...) ... | | Pointer.cs:11:21:11:32 | Pointer.cs:11:21:11:32 | Int32* | Int32* | &... | | Pointer.cs:13:18:13:24 | Pointer.cs:13:18:13:24 | Int32* | Int32* | access to local variable i2 | | Pointer.cs:14:13:14:23 | Pointer.cs:14:13:14:23 | Int32* | Int32* | ... + ... | @@ -8,4 +8,4 @@ | Pointer.cs:21:13:21:25 | Pointer.cs:21:13:21:25 | Int32 | Int32 | 1024 | | Pointer.cs:22:15:22:32 | Pointer.cs:22:15:22:32 | Byte* | Byte* | (...) ... | | Pointer.cs:24:13:24:29 | Pointer.cs:24:13:24:29 | String | String | "some string" | -| Pointer.cs:25:22:25:27 | Pointer.cs:25:22:25:27 | Char* | String | access to local variable s | +| Pointer.cs:25:22:25:27 | Pointer.cs:25:22:25:27 | Char* | Char* | (...) ... | diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected index 72f1bdadeda..2fd751f77e0 100644 --- a/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected @@ -1,4 +1 @@ | SignAnalysis.cs:428:23:428:24 | access to constant A | -| SignAnalysis.cs:448:22:448:29 | Byte* to = ... | -| SignAnalysis.cs:450:38:450:44 | (...) ... | -| SignAnalysis.cs:450:43:450:44 | access to local variable to | diff --git a/csharp/ql/test/library-tests/unsafe/PrintAst.expected b/csharp/ql/test/library-tests/unsafe/PrintAst.expected index b4e9ce56c64..079719907c7 100644 --- a/csharp/ql/test/library-tests/unsafe/PrintAst.expected +++ b/csharp/ql/test/library-tests/unsafe/PrintAst.expected @@ -109,7 +109,8 @@ unsafe.cs: # 36| 1: [LocalVariableAccess] access to local variable data # 37| 1: [FixedStmt] fixed(...) { ... } # 37| -1: [LocalVariableDeclAndInitExpr] Int32* p = ... -# 37| 0: [LocalVariableAccess] access to local variable data +# 37| 0: [CastExpr] (...) ... +# 37| 0: [LocalVariableAccess] access to local variable data # 37| 1: [LocalVariableAccess] access to local variable p # 38| 0: [BlockStmt] {...} # 44| 2: [Class] SafeClass From 774dcc7c5292696d69e3b5a1726b7aa164028450 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 23 Sep 2020 15:29:37 -0700 Subject: [PATCH 103/185] C++: New model class for iterator op* and op[] --- .../dataflow/internal/TaintTrackingUtil.qll | 10 ++-------- .../cpp/models/implementations/Iterator.qll | 14 +++++++++++--- .../code/cpp/models/interfaces/Iterator.qll | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll index 17cb9b88104..1ef340c4f21 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -10,7 +10,7 @@ private import semmle.code.cpp.models.interfaces.DataFlow private import semmle.code.cpp.models.interfaces.Taint -private import semmle.code.cpp.models.implementations.Iterator +private import semmle.code.cpp.models.interfaces.Iterator private module DataFlow { import semmle.code.cpp.dataflow.internal.DataFlowUtil @@ -264,10 +264,4 @@ private predicate exprToPartialDefinitionStep(Expr exprIn, Expr exprOut) { ) } -private predicate iteratorDereference(Call c) { - c.getTarget() instanceof IteratorArrayMemberOperator - or - c.getTarget() instanceof IteratorPointerDereferenceMemberOperator - or - c.getTarget() instanceof IteratorPointerDereferenceOperator -} +private predicate iteratorDereference(Call c) { c.getTarget() instanceof IteratorReferenceFunction } diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll index 93a04d5ef90..c1b8e24e7db 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll @@ -8,6 +8,7 @@ import cpp import semmle.code.cpp.models.interfaces.Taint import semmle.code.cpp.models.interfaces.DataFlow +import semmle.code.cpp.models.interfaces.Iterator /** * An instantiation of the `std::iterator_traits` template. @@ -80,7 +81,7 @@ private FunctionInput getIteratorArgumentInput(Operator op, int index) { /** * A non-member prefix `operator*` function for an iterator type. */ -class IteratorPointerDereferenceOperator extends Operator, TaintFunction { +class IteratorPointerDereferenceOperator extends Operator, TaintFunction, IteratorReferenceFunction { FunctionInput iteratorInput; IteratorPointerDereferenceOperator() { @@ -92,6 +93,8 @@ class IteratorPointerDereferenceOperator extends Operator, TaintFunction { input = iteratorInput and output.isReturnValue() } + + override FunctionInput getIteratorInput() { result = iteratorInput } } /** @@ -169,12 +172,15 @@ class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, Taint /** * A prefix `operator*` member function for an iterator type. */ -class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction { +class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction, + IteratorReferenceFunction { IteratorPointerDereferenceMemberOperator() { this.hasName("operator*") and this.getDeclaringType() instanceof Iterator } + override FunctionInput getIteratorInput() { result.isQualifierObject() } + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { input.isQualifierObject() and output.isReturnValue() @@ -260,7 +266,7 @@ class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFun /** * An `operator[]` member function of an iterator class. */ -class IteratorArrayMemberOperator extends MemberFunction, TaintFunction { +class IteratorArrayMemberOperator extends MemberFunction, TaintFunction, IteratorReferenceFunction { IteratorArrayMemberOperator() { this.hasName("operator[]") and this.getDeclaringType() instanceof Iterator @@ -270,6 +276,8 @@ class IteratorArrayMemberOperator extends MemberFunction, TaintFunction { input.isQualifierObject() and output.isReturnValue() } + + override FunctionInput getIteratorInput() { result.isQualifierObject() } } /** diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll new file mode 100644 index 00000000000..ea9ce04e530 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll @@ -0,0 +1,19 @@ +/** + * Provides an abstract class for accurate modeling of flow through output + * iterators. To use this QL library, create a QL class extending + * `IteratorReferenceFunction` with a characteristic predicate that selects the + * function or set of functions you are modeling. Within that class, override + * the predicates provided by `AliasFunction` to match the flow within that + * function. + */ + +import cpp +import semmle.code.cpp.models.Models + +/** + * A function which takes an iterator argument and returns a reference that + * can be used to write to the iterator's underlying collection. + */ +abstract class IteratorReferenceFunction extends Function { + abstract FunctionInput getIteratorInput(); +} From 89332ca3037ac3b8c0c664cfe0ad90f9e44a421c Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 23 Sep 2020 15:29:51 -0700 Subject: [PATCH 104/185] C++: autoformat --- cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll index e737f8b2d94..4562eb3fd19 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -3,7 +3,6 @@ private import DataFlowUtil private import DataFlowDispatch private import FlowVar - /** Gets the instance argument of a non-static call. */ private Node getInstanceArgument(Call call) { result.asExpr() = call.getQualifier() From 6163e6cf5f5dcb58e8fd7ab7d7e13c341cf987d2 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 24 Sep 2020 09:53:06 +0200 Subject: [PATCH 105/185] adjust test case for XML entity expansion --- .../query-tests/Security/CWE-611/Xxe.expected | 24 +++++++++---------- .../Security/CWE-611/libxml.noent.js | 7 ++++-- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-611/Xxe.expected b/javascript/ql/test/query-tests/Security/CWE-611/Xxe.expected index f7c8c7c1878..31bfc72e291 100644 --- a/javascript/ql/test/query-tests/Security/CWE-611/Xxe.expected +++ b/javascript/ql/test/query-tests/Security/CWE-611/Xxe.expected @@ -16,12 +16,12 @@ nodes | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | -| libxml.noent.js:16:26:16:34 | req.files | -| libxml.noent.js:16:26:16:34 | req.files | -| libxml.noent.js:16:26:16:43 | req.files.products | -| libxml.noent.js:16:26:16:48 | req.fil ... ts.data | -| libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | -| libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | +| libxml.noent.js:16:27:16:35 | req.files | +| libxml.noent.js:16:27:16:35 | req.files | +| libxml.noent.js:16:27:16:44 | req.files.products | +| libxml.noent.js:16:27:16:49 | req.fil ... ts.data | +| libxml.noent.js:16:27:16:66 | req.fil ... 'utf8') | +| libxml.noent.js:16:27:16:66 | req.fil ... 'utf8') | | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | @@ -39,11 +39,11 @@ edges | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | | libxml.noent.js:11:21:11:41 | req.par ... e-xml") | libxml.noent.js:11:21:11:41 | req.par ... e-xml") | | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | -| libxml.noent.js:16:26:16:34 | req.files | libxml.noent.js:16:26:16:43 | req.files.products | -| libxml.noent.js:16:26:16:34 | req.files | libxml.noent.js:16:26:16:43 | req.files.products | -| libxml.noent.js:16:26:16:43 | req.files.products | libxml.noent.js:16:26:16:48 | req.fil ... ts.data | -| libxml.noent.js:16:26:16:48 | req.fil ... ts.data | libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | -| libxml.noent.js:16:26:16:48 | req.fil ... ts.data | libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | +| libxml.noent.js:16:27:16:35 | req.files | libxml.noent.js:16:27:16:44 | req.files.products | +| libxml.noent.js:16:27:16:35 | req.files | libxml.noent.js:16:27:16:44 | req.files.products | +| libxml.noent.js:16:27:16:44 | req.files.products | libxml.noent.js:16:27:16:49 | req.fil ... ts.data | +| libxml.noent.js:16:27:16:49 | req.fil ... ts.data | libxml.noent.js:16:27:16:66 | req.fil ... 'utf8') | +| libxml.noent.js:16:27:16:49 | req.fil ... ts.data | libxml.noent.js:16:27:16:66 | req.fil ... 'utf8') | | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | #select @@ -52,6 +52,6 @@ edges | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | A $@ is parsed as XML without guarding against external entity expansion. | libxml.noent.js:6:21:6:41 | req.par ... e-xml") | user-provided value | | libxml.noent.js:11:21:11:41 | req.par ... e-xml") | libxml.noent.js:11:21:11:41 | req.par ... e-xml") | libxml.noent.js:11:21:11:41 | req.par ... e-xml") | A $@ is parsed as XML without guarding against external entity expansion. | libxml.noent.js:11:21:11:41 | req.par ... e-xml") | user-provided value | | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | A $@ is parsed as XML without guarding against external entity expansion. | libxml.noent.js:14:27:14:47 | req.par ... e-xml") | user-provided value | -| libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | libxml.noent.js:16:26:16:34 | req.files | libxml.noent.js:16:26:16:65 | req.fil ... 'utf8') | A $@ is parsed as XML without guarding against external entity expansion. | libxml.noent.js:16:26:16:34 | req.files | user-provided value | +| libxml.noent.js:16:27:16:66 | req.fil ... 'utf8') | libxml.noent.js:16:27:16:35 | req.files | libxml.noent.js:16:27:16:66 | req.fil ... 'utf8') | A $@ is parsed as XML without guarding against external entity expansion. | libxml.noent.js:16:27:16:35 | req.files | user-provided value | | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | A $@ is parsed as XML without guarding against external entity expansion. | libxml.sax.js:6:22:6:42 | req.par ... e-xml") | user-provided value | | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | A $@ is parsed as XML without guarding against external entity expansion. | libxml.saxpush.js:6:15:6:35 | req.par ... e-xml") | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-611/libxml.noent.js b/javascript/ql/test/query-tests/Security/CWE-611/libxml.noent.js index b0f0d6168e1..47df4223e0e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-611/libxml.noent.js +++ b/javascript/ql/test/query-tests/Security/CWE-611/libxml.noent.js @@ -11,7 +11,10 @@ express().post('/some/path', function(req, res) { libxmljs.parseXml(req.param("some-xml"), { noent: true }); // NOT OK: unguarded entity expansion - libxmljs.parseXmlString(req.param("some-xml"), {noent:true,noblanks:true}) + libxmljs.parseXmlString(req.param("some-xml"), {noent:true}) // NOT OK: unguarded entity expansion - libxmljs.parseXmlString(req.files.products.data.toString('utf8'), {noent:true,noblanks:true}) + libxmljs.parseXmlString(req.files.products.data.toString('utf8'), {noent:true}) + + // OK - no entity expansion + libxmljs.parseXmlString(req.files.products.data.toString('utf8'), {noent:false}) }); From 45651cf1236a6aa2c4aeb723c7b49a664d63baa4 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 23 Sep 2020 16:58:35 +0100 Subject: [PATCH 106/185] Java: PrintAst: Add a synthetic node for the initialisers of for statements --- java/ql/src/semmle/code/java/PrintAst.qll | 31 ++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/PrintAst.qll b/java/ql/src/semmle/code/java/PrintAst.qll index a3f4807661d..b26db2704ee 100644 --- a/java/ql/src/semmle/code/java/PrintAst.qll +++ b/java/ql/src/semmle/code/java/PrintAst.qll @@ -113,6 +113,7 @@ private predicate locationSortKeys(Element ast, string file, int line, int colum */ private newtype TPrintAstNode = TElementNode(Element el) { shouldPrint(el, _) } or + TForInitNode(ForStmt fs) { shouldPrint(fs, _) and exists(fs.getAnInit()) } or TAnnotationsNode(Annotatable ann) { shouldPrint(ann, _) and ann.hasAnnotation() and not partOfAnnotation(ann) } or @@ -248,7 +249,11 @@ final class ExprStmtNode extends ElementNode { override PrintAstNode getChild(int childIndex) { exists(Element el | result.(ElementNode).getElement() = el | el.(Expr).isNthChildOf(element, childIndex) and - not partOfAnnotation(element) + not partOfAnnotation(element) and + not ( + element instanceof ForStmt and + childIndex <= 0 + ) or el.(Stmt).isNthChildOf(element, childIndex) or @@ -271,6 +276,9 @@ final class ExprStmtNode extends ElementNode { childIndex = -2 and el = element.(LocalVariableDeclExpr).getVariable() ) + or + childIndex = 0 and + result.(ForInitNode).getForStmt() = element } } @@ -432,6 +440,27 @@ final class TypeVariableNode extends ElementNode { } } +/** + * A node representing the initializers of a `ForStmt`. + */ +final class ForInitNode extends PrintAstNode, TForInitNode { + ForStmt fs; + + ForInitNode() { this = TForInitNode(fs) } + + override string toString() { result = "(For Initializers) "} + + override ElementNode getChild(int childIndex) { + childIndex >= 0 and + result.getElement().(Expr).isNthChildOf(fs, -childIndex) + } + + /** + * Gets the underlying `ForStmt`. + */ + ForStmt getForStmt() {result = fs} +} + /** * A node representing the annotations of an `Annotatable`. * Only rendered if there is at least one annotation. From 1f9960762425b676b64a9a150ded4015a6ff9606 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 23 Sep 2020 17:11:20 +0100 Subject: [PATCH 107/185] Java: PrintAst: Improve test --- java/ql/test/library-tests/printAst/A.java | 6 ++ .../library-tests/printAst/PrintAst.expected | 77 +++++++++++++------ 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/java/ql/test/library-tests/printAst/A.java b/java/ql/test/library-tests/printAst/A.java index b0eeb9276a1..c3176cc14f3 100644 --- a/java/ql/test/library-tests/printAst/A.java +++ b/java/ql/test/library-tests/printAst/A.java @@ -18,6 +18,12 @@ class A { /** Does something */ @Deprecated static int doSomething(@SuppressWarnings("all") String text) { + int i=0, j=1; + + for(i=0, j=1; i<3; i++) {} + + for(int m=0, n=1; m<3; m++) {} + return 0; } diff --git a/java/ql/test/library-tests/printAst/PrintAst.expected b/java/ql/test/library-tests/printAst/PrintAst.expected index b23ed34ea82..c8f22de6950 100644 --- a/java/ql/test/library-tests/printAst/PrintAst.expected +++ b/java/ql/test/library-tests/printAst/PrintAst.expected @@ -31,26 +31,59 @@ A.java: # 20| 1: [StringLiteral] "all" # 20| 0: [TypeAccess] String # 20| 5: [BlockStmt] stmt -# 21| 0: [ReturnStmt] stmt -# 21| 0: [IntegerLiteral] 0 -# 24| 6: [FieldDeclaration] int counter, ...; -# 24| -1: [TypeAccess] int -# 24| 0: [IntegerLiteral] 1 -# 26| 7: [BlockStmt] stmt -# 27| 0: [ExprStmt] stmt -# 27| 0: [AssignExpr] ...=... -# 27| 0: [VarAccess] counter -# 27| 1: [MethodAccess] doSomething(...) -# 27| 0: [StringLiteral] "hi" -# 36| 8: [Method] doSomethingElse +# 21| 0: [LocalVariableDeclStmt] stmt +# 21| 0: [TypeAccess] int +# 21| 1: [LocalVariableDeclExpr] i +# 21| 0: [IntegerLiteral] 0 +# 21| 2: [LocalVariableDeclExpr] j +# 21| 0: [IntegerLiteral] 1 +# 23| 1: [ForStmt] stmt +#-----| 0: (For Initializers) +# 23| 1: [AssignExpr] ...=... +# 23| 0: [VarAccess] i +# 23| 1: [IntegerLiteral] 0 +# 23| 2: [AssignExpr] ...=... +# 23| 0: [VarAccess] j +# 23| 1: [IntegerLiteral] 1 +# 23| 1: [LTExpr] ... < ... +# 23| 0: [VarAccess] i +# 23| 1: [IntegerLiteral] 3 +# 23| 2: [BlockStmt] stmt +# 23| 3: [PostIncExpr] ...++ +# 23| 0: [VarAccess] i +# 25| 2: [ForStmt] stmt +#-----| 0: (For Initializers) +# 25| 0: [TypeAccess] int +# 25| 1: [LocalVariableDeclExpr] m +# 25| 0: [IntegerLiteral] 0 +# 25| 2: [LocalVariableDeclExpr] n +# 25| 0: [IntegerLiteral] 1 +# 25| 1: [LTExpr] ... < ... +# 25| 0: [VarAccess] m +# 25| 1: [IntegerLiteral] 3 +# 25| 2: [BlockStmt] stmt +# 25| 3: [PostIncExpr] ...++ +# 25| 0: [VarAccess] m +# 27| 3: [ReturnStmt] stmt +# 27| 0: [IntegerLiteral] 0 +# 30| 6: [FieldDeclaration] int counter, ...; +# 30| -1: [TypeAccess] int +# 30| 0: [IntegerLiteral] 1 +# 32| 7: [BlockStmt] stmt +# 33| 0: [ExprStmt] stmt +# 33| 0: [AssignExpr] ...=... +# 33| 0: [VarAccess] counter +# 33| 1: [MethodAccess] doSomething(...) +# 33| 0: [StringLiteral] "hi" +# 42| 8: [Method] doSomethingElse #-----| 1: (Annotations) -# 30| 1: [Annotation] Ann1 -# 31| 1: [StringLiteral] "a" -# 32| 2: [ArrayInit] {...} -# 33| 1: [Annotation] Ann2 -# 34| 2: [Annotation] Ann2 -# 34| 1: [IntegerLiteral] 7 -# 36| 3: [TypeAccess] String -# 36| 5: [BlockStmt] stmt -# 36| 0: [ReturnStmt] stmt -# 36| 0: [StringLiteral] "c" +# 36| 1: [Annotation] Ann1 +# 37| 1: [StringLiteral] "a" +# 38| 2: [ArrayInit] {...} +# 39| 1: [Annotation] Ann2 +# 40| 2: [Annotation] Ann2 +# 40| 1: [IntegerLiteral] 7 +# 42| 3: [TypeAccess] String +# 42| 5: [BlockStmt] stmt +# 42| 0: [ReturnStmt] stmt +# 42| 0: [StringLiteral] "c" From 3e960c1e0bdac2062fbe047152acc9d73f75f30c Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 23 Sep 2020 17:53:30 +0100 Subject: [PATCH 108/185] Java: PrintAst: Refactor exceptions to the usual AST of expressions and statements using dispatch --- java/ql/src/semmle/code/java/PrintAst.qll | 132 +++++++++++++++------- 1 file changed, 90 insertions(+), 42 deletions(-) diff --git a/java/ql/src/semmle/code/java/PrintAst.qll b/java/ql/src/semmle/code/java/PrintAst.qll index b26db2704ee..08b108c8e47 100644 --- a/java/ql/src/semmle/code/java/PrintAst.qll +++ b/java/ql/src/semmle/code/java/PrintAst.qll @@ -222,6 +222,24 @@ abstract class ElementNode extends PrintAstNode, TElementNode { final Element getElement() { result = element } } +/** + * A node representing an `Expr` or a `Stmt`. + */ +class ExprStmtNode extends ElementNode { + ExprStmtNode() { element instanceof ExprOrStmt } + + override PrintAstNode getChild(int childIndex) { + exists(Element el | result.(ElementNode).getElement() = el | + el.(Expr).isNthChildOf(element, childIndex) + or + el.(Stmt).isNthChildOf(element, childIndex) + ) + } +} + +/** + * Holds if the given expression is part of an annotation. + */ private predicate partOfAnnotation(Expr e) { e instanceof Annotation or @@ -229,53 +247,83 @@ private predicate partOfAnnotation(Expr e) { partOfAnnotation(e.getParent()) } -private Expr getAnAnnotationChild(Expr e) { - partOfAnnotation(e) and - ( - result = e.(Annotation).getValue(_) - or - result = e.(ArrayInit).getAnInit() - or - result = e.(ArrayInit).(Annotatable).getAnAnnotation() - ) +/** + * A node representing an `Expr` that is part of an annotation. + */ +final class AnnotationPartNode extends ExprStmtNode { + AnnotationPartNode() { partOfAnnotation(element) } + + override ElementNode getChild(int childIndex) { + result.getElement() = + rank[childIndex](Element ch, string file, int line, int column | + ch = getAnAnnotationChild() and locationSortKeys(ch, file, line, column) + | + ch order by file, line, column + ) + } + + private Expr getAnAnnotationChild() { + ( + result = element.(Annotation).getValue(_) + or + result = element.(ArrayInit).getAnInit() + or + result = element.(ArrayInit).(Annotatable).getAnAnnotation() + ) + } } /** - * An node representing an `Expr` or a `Stmt`. + * A node representing a `LocalVariableDeclExpr`. */ -final class ExprStmtNode extends ElementNode { - ExprStmtNode() { element instanceof ExprOrStmt } +final class LocalVarDeclExprNode extends ExprStmtNode { + LocalVarDeclExprNode() { element instanceof LocalVariableDeclExpr } override PrintAstNode getChild(int childIndex) { - exists(Element el | result.(ElementNode).getElement() = el | - el.(Expr).isNthChildOf(element, childIndex) and - not partOfAnnotation(element) and - not ( - element instanceof ForStmt and - childIndex <= 0 - ) - or - el.(Stmt).isNthChildOf(element, childIndex) - or - childIndex = -4 and - el = element.(ClassInstanceExpr).getAnonymousClass() - or - childIndex = 0 and - el = element.(LocalClassDeclStmt).getLocalClass() - or - partOfAnnotation(element) and - el = - rank[childIndex](Element ch, string file, int line, int column | - ch = getAnAnnotationChild(element) and locationSortKeys(ch, file, line, column) - | - ch order by file, line, column - ) - ) + result = super.getChild(childIndex) or - exists(Element el | result.(AnnotationsNode).getAnnotated() = el | - childIndex = -2 and - el = element.(LocalVariableDeclExpr).getVariable() - ) + childIndex = -2 and + result.(AnnotationsNode).getAnnotated() = element.(LocalVariableDeclExpr).getVariable() + } +} + +/** + * A node representing a `ClassInstanceExpr`. + */ +final class ClassInstanceExprNode extends ExprStmtNode { + ClassInstanceExprNode() { element instanceof ClassInstanceExpr } + + override ElementNode getChild(int childIndex) { + result = super.getChild(childIndex) + or + childIndex = -4 and + result.getElement() = element.(ClassInstanceExpr).getAnonymousClass() + } +} + +/** + * A node representing a `LocalClassDeclStmt`. + */ +final class LocalClassDeclStmtNode extends ExprStmtNode { + LocalClassDeclStmtNode() { element instanceof LocalClassDeclStmt } + + override ElementNode getChild(int childIndex) { + result = super.getChild(childIndex) + or + childIndex = 0 and + result.getElement() = element.(LocalClassDeclStmt).getLocalClass() + } +} + +/** + * A node representing a `ForStmt`. + */ +final class ForStmtNode extends ExprStmtNode { + ForStmtNode() { element instanceof ForStmt } + + override PrintAstNode getChild(int childIndex) { + childIndex >= 1 and + result = super.getChild(childIndex) or childIndex = 0 and result.(ForInitNode).getForStmt() = element @@ -448,7 +496,7 @@ final class ForInitNode extends PrintAstNode, TForInitNode { ForInitNode() { this = TForInitNode(fs) } - override string toString() { result = "(For Initializers) "} + override string toString() { result = "(For Initializers) " } override ElementNode getChild(int childIndex) { childIndex >= 0 and @@ -458,7 +506,7 @@ final class ForInitNode extends PrintAstNode, TForInitNode { /** * Gets the underlying `ForStmt`. */ - ForStmt getForStmt() {result = fs} + ForStmt getForStmt() { result = fs } } /** From 9c8a4682377ea2a65985552531754e27ebfda030 Mon Sep 17 00:00:00 2001 From: Joe Date: Thu, 24 Sep 2020 12:12:28 +0100 Subject: [PATCH 109/185] Java: PrintAst: Add synthetic nodes for other declarations --- java/ql/src/semmle/code/java/PrintAst.qll | 67 +++++++++++++++++++ .../java7/MultiCatch/PrintAst.expected | 23 ++++--- 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/java/ql/src/semmle/code/java/PrintAst.qll b/java/ql/src/semmle/code/java/PrintAst.qll index 08b108c8e47..ca1daf6c2a4 100644 --- a/java/ql/src/semmle/code/java/PrintAst.qll +++ b/java/ql/src/semmle/code/java/PrintAst.qll @@ -114,6 +114,9 @@ private predicate locationSortKeys(Element ast, string file, int line, int colum private newtype TPrintAstNode = TElementNode(Element el) { shouldPrint(el, _) } or TForInitNode(ForStmt fs) { shouldPrint(fs, _) and exists(fs.getAnInit()) } or + TLocalVarDeclNode(LocalVariableDeclExpr lvde) { + shouldPrint(lvde, _) and lvde.getParent() instanceof LocalVarDeclParent + } or TAnnotationsNode(Annotatable ann) { shouldPrint(ann, _) and ann.hasAnnotation() and not partOfAnnotation(ann) } or @@ -330,6 +333,46 @@ final class ForStmtNode extends ExprStmtNode { } } +/** + * An element that can be the parent of a `LocalVariableDeclExpr` for which we want + * to use a synthetic node to hold the variable declaration and its `TypeAccess`. + */ +private class LocalVarDeclParent extends ExprOrStmt { + LocalVarDeclParent() { + this instanceof EnhancedForStmt or + this instanceof CatchClause or + this.(InstanceOfExpr).isPattern() + } + + /** Gets the variable declaration that this element contains */ + LocalVariableDeclExpr getVariable() { result.getParent() = this } + + /** Gets the type access of the variable */ + Expr getTypeAccess() { result = getVariable().getTypeAccess() } +} + +/** + * A node representing an element that can be the parent of a `LocalVariableDeclExpr` for which we + * want to use a synthetic node to variable declaration and its type access. + * + * Excludes: + * - `LocalVariableDeclStmt` because a synthetic node isn't needed + * - `ForStmt` becasue a different synthetic node is already used + */ +final class LocalVarDeclParentNode extends ExprStmtNode { + LocalVarDeclParent lvdp; + + LocalVarDeclParentNode() { lvdp = element } + + override PrintAstNode getChild(int childIndex) { + result = super.getChild(childIndex) and + not result.(ElementNode).getElement() = [lvdp.getVariable(), lvdp.getTypeAccess()] + or + childIndex = lvdp.getVariable().getIndex() and + result.(LocalVarDeclSynthNode).getVariable() = lvdp.getVariable() + } +} + /** * A node representing a `Callable`, such as method declaration. */ @@ -509,6 +552,30 @@ final class ForInitNode extends PrintAstNode, TForInitNode { ForStmt getForStmt() { result = fs } } +/** + * A synthetic node holding a `LocalVariableDeclExpr` and its type access. + */ +final class LocalVarDeclSynthNode extends PrintAstNode, TLocalVarDeclNode { + LocalVariableDeclExpr lvde; + + LocalVarDeclSynthNode() { this = TLocalVarDeclNode(lvde) } + + override string toString() { result = "(Local Variable Declaration)" } + + override ElementNode getChild(int childIndex) { + childIndex = 0 and + result.getElement() = lvde.getTypeAccess() + or + childIndex = 1 and + result.getElement() = lvde + } + + /** + * Gets the underlying `LocalVariableDeclExpr` + */ + LocalVariableDeclExpr getVariable() { result = lvde } +} + /** * A node representing the annotations of an `Annotatable`. * Only rendered if there is at least one annotation. diff --git a/java/ql/test/library-tests/java7/MultiCatch/PrintAst.expected b/java/ql/test/library-tests/java7/MultiCatch/PrintAst.expected index 8b8fe959628..98172ceff75 100644 --- a/java/ql/test/library-tests/java7/MultiCatch/PrintAst.expected +++ b/java/ql/test/library-tests/java7/MultiCatch/PrintAst.expected @@ -21,10 +21,11 @@ MultiCatch.java: # 14| 0: [ClassInstanceExpr] new SQLException(...) # 14| -3: [TypeAccess] SQLException # 15| 0: [CatchClause] stmt -# 15| -1: [UnionTypeAccess] ...|... -# 15| 0: [TypeAccess] IOException -# 15| 1: [TypeAccess] SQLException -# 15| 0: [LocalVariableDeclExpr] e +#-----| 0: (Local Variable Declaration) +# 15| 0: [UnionTypeAccess] ...|... +# 15| 0: [TypeAccess] IOException +# 15| 1: [TypeAccess] SQLException +# 15| 1: [LocalVariableDeclExpr] e # 16| 1: [BlockStmt] stmt # 17| 0: [ExprStmt] stmt # 17| 0: [MethodAccess] printStackTrace(...) @@ -55,10 +56,11 @@ MultiCatch.java: # 30| 0: [ClassInstanceExpr] new Exception(...) # 30| -3: [TypeAccess] Exception # 31| 0: [CatchClause] stmt -# 31| -1: [UnionTypeAccess] ...|... -# 31| 0: [TypeAccess] IOException -# 31| 1: [TypeAccess] SQLException -# 31| 0: [LocalVariableDeclExpr] e +#-----| 0: (Local Variable Declaration) +# 31| 0: [UnionTypeAccess] ...|... +# 31| 0: [TypeAccess] IOException +# 31| 1: [TypeAccess] SQLException +# 31| 1: [LocalVariableDeclExpr] e # 32| 1: [BlockStmt] stmt # 35| 4: [Method] ordinaryCatch # 35| 3: [TypeAccess] void @@ -69,6 +71,7 @@ MultiCatch.java: # 39| 0: [ClassInstanceExpr] new IOException(...) # 39| -3: [TypeAccess] IOException # 40| 0: [CatchClause] stmt -# 40| -1: [TypeAccess] Exception -# 40| 0: [LocalVariableDeclExpr] e +#-----| 0: (Local Variable Declaration) +# 40| 0: [TypeAccess] Exception +# 40| 1: [LocalVariableDeclExpr] e # 41| 1: [BlockStmt] stmt From 094b06ec2accc225ba57df0a1686e972d700d252 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 24 Sep 2020 10:37:38 -0700 Subject: [PATCH 110/185] C++: remove unneeded predicate --- .../src/semmle/code/cpp/models/implementations/Iterator.qll | 6 ------ cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll | 1 - 2 files changed, 7 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll index c1b8e24e7db..ded937d5312 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll @@ -93,8 +93,6 @@ class IteratorPointerDereferenceOperator extends Operator, TaintFunction, Iterat input = iteratorInput and output.isReturnValue() } - - override FunctionInput getIteratorInput() { result = iteratorInput } } /** @@ -179,8 +177,6 @@ class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunc this.getDeclaringType() instanceof Iterator } - override FunctionInput getIteratorInput() { result.isQualifierObject() } - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { input.isQualifierObject() and output.isReturnValue() @@ -276,8 +272,6 @@ class IteratorArrayMemberOperator extends MemberFunction, TaintFunction, Iterato input.isQualifierObject() and output.isReturnValue() } - - override FunctionInput getIteratorInput() { result.isQualifierObject() } } /** diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll index ea9ce04e530..1086e088979 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll @@ -15,5 +15,4 @@ import semmle.code.cpp.models.Models * can be used to write to the iterator's underlying collection. */ abstract class IteratorReferenceFunction extends Function { - abstract FunctionInput getIteratorInput(); } From ca06637de0d4adaeb27286e8385ad42073dcec4f Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 24 Sep 2020 10:40:45 -0700 Subject: [PATCH 111/185] C++: add qldoc comment --- .../semmle/code/cpp/dataflow/internal/DataFlowUtil.qll | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 25a37f9db0f..9bbedfc16a8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -182,6 +182,13 @@ class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode { override predicate isParameterOf(Function fun, int i) { f = fun and i = -1 } } +/** + * INTERNAL: do not use. + * + * A node that represents the value of a variable after a function call that + * may have changed the variable because it's passed by reference or because an + * iterator for it was passed by value or by reference. + */ class DefinitionByReferenceOrIteratorNode extends PartialDefinitionNode { Expr inner; Expr argument; @@ -215,7 +222,6 @@ class DefinitionByReferenceOrIteratorNode extends PartialDefinitionNode { } } - /** * A node that represents the value of a variable after a function call that * may have changed the variable because it's passed by reference. From b8154d41b17995c0248dcbafee307c93356c67d7 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 24 Sep 2020 20:55:25 +0200 Subject: [PATCH 112/185] type-track objects where the "$where" property has been written --- .../ql/src/semmle/javascript/frameworks/NoSQL.qll | 14 +++++++++++++- .../CWE-094/CodeInjection/CodeInjection.expected | 10 ++++++++++ .../HeuristicSourceCodeInjection.expected | 9 +++++++++ .../CWE-094/CodeInjection/NoSQLCodeInjection.js | 6 ++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/NoSQL.qll b/javascript/ql/src/semmle/javascript/frameworks/NoSQL.qll index 13385057b5d..2854cdaa8f4 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/NoSQL.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/NoSQL.qll @@ -12,11 +12,23 @@ module NoSQL { } } +/** + * Gets a reference to an object where the "$where" property has been assigned to`rhs`. + */ +DataFlow::SourceNode getADollarWherePropertyValueSource(DataFlow::TypeTracker t, DataFlow::Node rhs) { + t.start() and + rhs = result.getAPropertyWrite("$where").getRhs() + or + exists(DataFlow::TypeTracker t2 | + result = getADollarWherePropertyValueSource(t2, rhs).track(t2, t) + ) +} + /** * Gets the value of a `$where` property of an object that flows to `n`. */ private DataFlow::Node getADollarWherePropertyValue(DataFlow::Node n) { - result = n.getALocalSource().getAPropertyWrite("$where").getRhs() + getADollarWherePropertyValueSource(DataFlow::TypeTracker::end(), result).flowsTo(n) } /** diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected index 5b23332d205..dec023d72f3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected @@ -8,6 +8,11 @@ nodes | NoSQLCodeInjection.js:19:36:19:43 | req.body | | NoSQLCodeInjection.js:19:36:19:43 | req.body | | NoSQLCodeInjection.js:19:36:19:48 | req.body.name | +| NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | +| NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | +| NoSQLCodeInjection.js:22:36:22:43 | req.body | +| NoSQLCodeInjection.js:22:36:22:43 | req.body | +| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | | angularjs.js:10:22:10:29 | location | | angularjs.js:10:22:10:29 | location | | angularjs.js:10:22:10:36 | location.search | @@ -152,6 +157,10 @@ edges | NoSQLCodeInjection.js:19:36:19:43 | req.body | NoSQLCodeInjection.js:19:36:19:48 | req.body.name | | NoSQLCodeInjection.js:19:36:19:48 | req.body.name | NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | | NoSQLCodeInjection.js:19:36:19:48 | req.body.name | NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | +| NoSQLCodeInjection.js:22:36:22:43 | req.body | NoSQLCodeInjection.js:22:36:22:48 | req.body.name | +| NoSQLCodeInjection.js:22:36:22:43 | req.body | NoSQLCodeInjection.js:22:36:22:48 | req.body.name | +| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | +| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | | angularjs.js:10:22:10:29 | location | angularjs.js:10:22:10:36 | location.search | | angularjs.js:10:22:10:29 | location | angularjs.js:10:22:10:36 | location.search | | angularjs.js:10:22:10:29 | location | angularjs.js:10:22:10:36 | location.search | @@ -275,6 +284,7 @@ edges #select | NoSQLCodeInjection.js:18:24:18:37 | req.body.query | NoSQLCodeInjection.js:18:24:18:31 | req.body | NoSQLCodeInjection.js:18:24:18:37 | req.body.query | $@ flows to here and is interpreted as code. | NoSQLCodeInjection.js:18:24:18:31 | req.body | User-provided value | | NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | NoSQLCodeInjection.js:19:36:19:43 | req.body | NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | $@ flows to here and is interpreted as code. | NoSQLCodeInjection.js:19:36:19:43 | req.body | User-provided value | +| NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | NoSQLCodeInjection.js:22:36:22:43 | req.body | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | $@ flows to here and is interpreted as code. | NoSQLCodeInjection.js:22:36:22:43 | req.body | User-provided value | | angularjs.js:10:22:10:36 | location.search | angularjs.js:10:22:10:29 | location | angularjs.js:10:22:10:36 | location.search | $@ flows to here and is interpreted as code. | angularjs.js:10:22:10:29 | location | User-provided value | | angularjs.js:13:23:13:37 | location.search | angularjs.js:13:23:13:30 | location | angularjs.js:13:23:13:37 | location.search | $@ flows to here and is interpreted as code. | angularjs.js:13:23:13:30 | location | User-provided value | | angularjs.js:16:28:16:42 | location.search | angularjs.js:16:28:16:35 | location | angularjs.js:16:28:16:42 | location.search | $@ flows to here and is interpreted as code. | angularjs.js:16:28:16:35 | location | User-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected index 50bf4e455a2..fa34f829b92 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected @@ -8,6 +8,11 @@ nodes | NoSQLCodeInjection.js:19:36:19:43 | req.body | | NoSQLCodeInjection.js:19:36:19:43 | req.body | | NoSQLCodeInjection.js:19:36:19:48 | req.body.name | +| NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | +| NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | +| NoSQLCodeInjection.js:22:36:22:43 | req.body | +| NoSQLCodeInjection.js:22:36:22:43 | req.body | +| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | | angularjs.js:10:22:10:29 | location | | angularjs.js:10:22:10:29 | location | | angularjs.js:10:22:10:36 | location.search | @@ -156,6 +161,10 @@ edges | NoSQLCodeInjection.js:19:36:19:43 | req.body | NoSQLCodeInjection.js:19:36:19:48 | req.body.name | | NoSQLCodeInjection.js:19:36:19:48 | req.body.name | NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | | NoSQLCodeInjection.js:19:36:19:48 | req.body.name | NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | +| NoSQLCodeInjection.js:22:36:22:43 | req.body | NoSQLCodeInjection.js:22:36:22:48 | req.body.name | +| NoSQLCodeInjection.js:22:36:22:43 | req.body | NoSQLCodeInjection.js:22:36:22:48 | req.body.name | +| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | +| NoSQLCodeInjection.js:22:36:22:48 | req.body.name | NoSQLCodeInjection.js:22:24:22:48 | "name = ... dy.name | | angularjs.js:10:22:10:29 | location | angularjs.js:10:22:10:36 | location.search | | angularjs.js:10:22:10:29 | location | angularjs.js:10:22:10:36 | location.search | | angularjs.js:10:22:10:29 | location | angularjs.js:10:22:10:36 | location.search | diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/NoSQLCodeInjection.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/NoSQLCodeInjection.js index 3e7025a8e6c..6facf5ec75a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/NoSQLCodeInjection.js +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/NoSQLCodeInjection.js @@ -17,5 +17,11 @@ app.post("/documents/find", (req, res) => { doc.find(query); // NOT OK, but that is flagged by js/sql-injection [INCONSISTENCY] doc.find({ $where: req.body.query }); // NOT OK doc.find({ $where: "name = " + req.body.name }); // NOT OK + + function mkWhereObj() { + return { $where: "name = " + req.body.name }; // NOT OK + } + + doc.find(mkWhereObj()); // the alert location is in mkWhereObj. }); }); From 46ff4d524fe3ff18b76a006e099fab12072a903e Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 24 Sep 2020 14:54:31 -0700 Subject: [PATCH 113/185] C++: autoformat --- cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll index 1086e088979..de697776525 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll @@ -14,5 +14,4 @@ import semmle.code.cpp.models.Models * A function which takes an iterator argument and returns a reference that * can be used to write to the iterator's underlying collection. */ -abstract class IteratorReferenceFunction extends Function { -} +abstract class IteratorReferenceFunction extends Function { } From e9b1d817c757ee96747cdbba3c935e05a7a9bf92 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 24 Sep 2020 15:55:57 -0700 Subject: [PATCH 114/185] C++: QLDoc for VirtualVariable in IR construction --- .../ir/implementation/aliased_ssa/internal/AliasedSSA.qll | 6 ++++++ .../ir/implementation/unaliased_ssa/internal/SimpleSSA.qll | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll index 8124fa438d4..1e034051f05 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll @@ -133,6 +133,12 @@ abstract class MemoryLocation extends TMemoryLocation { predicate isAlwaysAllocatedOnStack() { none() } } +/** + * Represents a set of `MemoryLocation`s that cannot overlap with + * `MemoryLocation`s outside of the set. The `VirtualVariable` will be + * represented by a `MemoryLocation` that totally overlaps all other + * `MemoryLocations` in the set. + */ abstract class VirtualVariable extends MemoryLocation { } abstract class AllocationMemoryLocation extends MemoryLocation { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index 3de8f5259f8..a7b9160bdc7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -59,6 +59,12 @@ class MemoryLocation extends TMemoryLocation { final string getUniqueId() { result = var.getUniqueId() } } +/** + * Represents a set of `MemoryLocation`s that cannot overlap with + * `MemoryLocation`s outside of the set. The `VirtualVariable` will be + * represented by a `MemoryLocation` that totally overlaps all other + * `MemoryLocations` in the set. + */ class VirtualVariable extends MemoryLocation { } /** A virtual variable that groups all escaped memory within a function. */ From e51b9215e4b3c391d7227ca163b298baa316fec6 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 24 Sep 2020 15:56:29 -0700 Subject: [PATCH 115/185] C++: QLDoc for Overlap in IR construction --- .../src/semmle/code/cpp/ir/internal/Overlap.qll | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll index 8ce0549b2b4..f9a0c574f8c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll @@ -3,18 +3,33 @@ private newtype TOverlap = TMustTotallyOverlap() or TMustExactlyOverlap() +/** + * Represents a possible overlap between two memory ranges. + */ abstract class Overlap extends TOverlap { abstract string toString(); } +/** + * Represents a partial overlap between two memory ranges, which may or may not + * actually occur in practice. + */ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { final override string toString() { result = "MayPartiallyOverlap" } } +/** + * Represents an overlap in which the first memory range is known to include all + * bits of the second memory range, but may be larger or have a different type. + */ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { final override string toString() { result = "MustTotallyOverlap" } } +/** + * Represents an overlap between two memory ranges that have the same extent and + * the same type. + */ class MustExactlyOverlap extends Overlap, TMustExactlyOverlap { final override string toString() { result = "MustExactlyOverlap" } } From 1445b3186464a33e04d67b1e1bb019f790f901d7 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 24 Sep 2020 16:34:16 -0700 Subject: [PATCH 116/185] C++: QLDoc for Operand --- .../code/cpp/ir/implementation/unaliased_ssa/Operand.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index 14f62da51cd..e476aec60af 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -79,7 +79,8 @@ private PhiOperandBase phiOperand( } /** - * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. + * An operand of an `Instruction`. The operand represents a use of the result of one instruction + * (the defining instruction) in another instruction (the use instruction) */ class Operand extends TOperand { /** Gets a textual representation of this element. */ From 120a569c6f1dec110b3434b259a54384502e9918 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 25 Sep 2020 11:40:49 +0200 Subject: [PATCH 117/185] Python: Explain how CallGraph test.py even works Also remove options file, since it did nothing at all (and blocked experimental/library-tests/options from taking effect) --- python/ql/test/experimental/library-tests/CallGraph/options | 1 - python/ql/test/experimental/library-tests/CallGraph/test.py | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 python/ql/test/experimental/library-tests/CallGraph/options diff --git a/python/ql/test/experimental/library-tests/CallGraph/options b/python/ql/test/experimental/library-tests/CallGraph/options deleted file mode 100644 index 86f027c42dd..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/options +++ /dev/null @@ -1 +0,0 @@ -semmle-extractor-options: diff --git a/python/ql/test/experimental/library-tests/CallGraph/test.py b/python/ql/test/experimental/library-tests/CallGraph/test.py index 04176e98a74..87c585c5cf8 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/test.py +++ b/python/ql/test/experimental/library-tests/CallGraph/test.py @@ -1 +1,3 @@ +# This import will only import code/__init__.py -- BUT we still analyse the other python +# files inside code/ which is what we want. from code import * From 3d5511221e3fdeb53d4a91d6eb3d133d25190649 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 25 Sep 2020 11:44:54 +0200 Subject: [PATCH 118/185] Python: Add test for implicit __init__.py files --- .../CallGraph-implicit-init/PointsTo.expected | 6 ++++++ .../CallGraph-implicit-init/PointsTo.qlref | 1 + .../CallGraph-implicit-init/Relative.expected | 5 +++++ .../CallGraph-implicit-init/Relative.qlref | 1 + .../TypeTracker.expected | 6 ++++++ .../CallGraph-implicit-init/TypeTracker.qlref | 1 + .../CallGraph-implicit-init/example.py | 18 ++++++++++++++++++ .../CallGraph-implicit-init/foo/bar/a.py | 4 ++++ .../CallGraph-implicit-init/options | 1 + 9 files changed, 43 insertions(+) create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.qlref create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.qlref create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.qlref create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/options diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected new file mode 100644 index 00000000000..e1f0f883631 --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected @@ -0,0 +1,6 @@ +debug_missingAnnotationForCallable +debug_nonUniqueAnnotationForCallable +debug_missingAnnotationForCall +expectedCallEdgeNotFound +| example.py:12:1:12:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | +unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.qlref b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.qlref new file mode 100644 index 00000000000..da8a0d1631a --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.qlref @@ -0,0 +1 @@ +../CallGraph/PointsTo.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected new file mode 100644 index 00000000000..c25b2538ffa --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected @@ -0,0 +1,5 @@ +debug_missingAnnotationForCallable +debug_nonUniqueAnnotationForCallable +debug_missingAnnotationForCall +pointsTo_found_typeTracker_notFound +pointsTo_notFound_typeTracker_found diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.qlref b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.qlref new file mode 100644 index 00000000000..2ffa6c10d51 --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.qlref @@ -0,0 +1 @@ +../CallGraph/Relative.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected new file mode 100644 index 00000000000..e1f0f883631 --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected @@ -0,0 +1,6 @@ +debug_missingAnnotationForCallable +debug_nonUniqueAnnotationForCallable +debug_missingAnnotationForCall +expectedCallEdgeNotFound +| example.py:12:1:12:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | +unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.qlref b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.qlref new file mode 100644 index 00000000000..60c029a5510 --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.qlref @@ -0,0 +1 @@ +../CallGraph/TypeTracker.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py new file mode 100644 index 00000000000..67bbf5c707c --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py @@ -0,0 +1,18 @@ +""" +Test that we can resolve callables correctly without using explicit __init__.py files + +This is not included in the standard `CallGraph/code` folder, since we're testing our +understanding import work properly, so it's better to have a clean test setup that is +obviously correct (the other one isn't in regards to imports). + +Technically this is part of PEP 420 -- Implicit Namespace Packages, but does use the +*real* namespace package feature of allowing source code for a single package to reside +in multiple places. + +Since PEP 420 was accepted in Python 3, this test is Python 3 only. +""" + +from foo.bar.a import afunc + +# calls:afunc +afunc() diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py new file mode 100644 index 00000000000..a294b33191e --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py @@ -0,0 +1,4 @@ +# name:afunc +def afunc(): + print("afunc called") + return 1 diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/options b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/options new file mode 100644 index 00000000000..cfef58cf2b2 --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=1 --lang=3 From 85607fe2d574bc6c123c62fa21b8af8fd55aab09 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 25 Sep 2020 11:56:45 +0200 Subject: [PATCH 119/185] Python: Adjust location for .expected output --- .../library-tests/CallGraph-implicit-init/PointsTo.expected | 2 +- .../library-tests/CallGraph-implicit-init/TypeTracker.expected | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected index e1f0f883631..d4362c9edf5 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected @@ -2,5 +2,5 @@ debug_missingAnnotationForCallable debug_nonUniqueAnnotationForCallable debug_missingAnnotationForCall expectedCallEdgeNotFound -| example.py:12:1:12:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | +| example.py:18:1:18:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected index e1f0f883631..d4362c9edf5 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected @@ -2,5 +2,5 @@ debug_missingAnnotationForCallable debug_nonUniqueAnnotationForCallable debug_missingAnnotationForCall expectedCallEdgeNotFound -| example.py:12:1:12:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | +| example.py:18:1:18:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | unexpectedCallEdgeFound From c56ff986d4808e69a20db3ab0100a97a41956da9 Mon Sep 17 00:00:00 2001 From: yoff Date: Fri, 25 Sep 2020 11:56:50 +0200 Subject: [PATCH 120/185] Apply suggestions from code review Co-authored-by: Taus --- .../dataflow/internal/DataFlowPrivate.qll | 28 +++++++++++-------- .../dataflow/coverage/argumentRouting1.ql | 2 +- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index b8d30ae7203..7a9967a7619 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -18,7 +18,7 @@ class DataFlowCfgNode extends ControlFlowNode { /** A data flow node for which we should synthesise an associated pre-update node. */ abstract class NeedsSyntheticPreUpdateNode extends Node { - /** This will figure in the texttual representation of the synthesised pre-update node. */ + /** A label for this kind of node. This will figure in the textual representation of the synthesized pre-update node. */ abstract string label(); } @@ -39,7 +39,7 @@ class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode { /** A data flow node for which we should synthesise an associated post-update node. */ abstract class NeedsSyntheticPostUpdateNode extends Node { - /** This will figure in the texttual representation of the synthesised post-update node. */ + /** A label for this kind of node. This will figure in the textual representation of the synthesized post-update node. */ abstract string label(); } @@ -83,7 +83,7 @@ class ReadPreUpdateNode extends NeedsSyntheticPostUpdateNode, CfgNode { override string label() { result = "read" } } -/** A post-update node is synthesised for all nodes which satisfy `NeedsSyntheticPostUpdateNode`. */ +/** A post-update node is synthesized for all nodes which satisfy `NeedsSyntheticPostUpdateNode`. */ class SyntheticPostUpdateNode extends PostUpdateNode, TSyntheticPostUpdateNode { NeedsSyntheticPostUpdateNode pre; @@ -99,7 +99,7 @@ class SyntheticPostUpdateNode extends PostUpdateNode, TSyntheticPostUpdateNode { } /** - * Calls to constructors are treated as post-update nodes for the synthesised argument + * Calls to constructors are treated as post-update nodes for the synthesized argument * that is mapped to the `self` parameter. That way, constructor calls represent the value of the * object after the constructor (currently only `__init__`) has run. */ @@ -305,7 +305,7 @@ class DataFlowModuleScope extends DataFlowCallable, TModule { * * An `__init__` method can also be called directly, so that the callable can be targeted by * different types of calls. In that case, the parameter mappings will be different, - * as the class call will synthesise an argument node to be mapped to the `self` parameter. + * as the class call will synthesize an argument node to be mapped to the `self` parameter. * * A call corresponding to a special method call is handled by the corresponding `SpecialMethodCallNode`. */ @@ -598,16 +598,18 @@ predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) { } /** - * In + * Holds if `nodeFrom` flows into an attribute (corresponding to `c`) of `nodeTo` via an attribute assignment. + * + * For example, in * ```python * obj.foo = x * ``` * data flows from `x` to (the post-update node for) `obj` via assignment to `foo`. */ -predicate attributeStoreStep(CfgNode nodeFrom, Content c, PostUpdateNode nodeTo) { +predicate attributeStoreStep(CfgNode nodeFrom, AttributeContent c, PostUpdateNode nodeTo) { exists(AttrNode attr | nodeFrom.asCfgNode() = attr.(DefinitionNode).getValue() and - attr.getName() = c.(AttributeContent).getAttribute() and + attr.getName() = c.getAttribute() and attr.getObject() = nodeTo.getPreUpdateNode().(CfgNode).getNode() ) } @@ -710,17 +712,19 @@ predicate comprehensionReadStep(CfgNode nodeFrom, Content c, EssaNode nodeTo) { } /** - * In + * Holds if `nodeTo` is a read of an attribute (corresponding to `c`) of the object in `nodeFrom`. + * + * For example, in * ```python * obj.foo * ``` * data flows from `obj` to `obj.foo` via a read from `foo`. */ -predicate attributeReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) { +predicate attributeReadStep(CfgNode nodeFrom, AttributeContent c, CfgNode nodeTo) { exists(AttrNode attr | - nodeTo.asCfgNode() = attr and nodeFrom.asCfgNode() = attr.getObject() and - attr.getName() = c.(AttributeContent).getAttribute() and + nodeTo.asCfgNode() = attr and + attr.getName() = c.getAttribute() and attr.isLoad() ) } diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql index 78640c5722a..32df6d3788e 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql @@ -12,7 +12,7 @@ class ArgumentRoutingConfig extends DataFlow::Configuration { exists(AssignmentDefinition def, DataFlowPrivate::DataFlowCall call | def.getVariable() = node.(DataFlow::EssaNode).getVar() and def.getValue() = call.getNode() and - call.getNode().(CallNode).getNode().(Call).toString().matches("With\\_%") // TODO: Do not rely on toString + call.getNode().(CallNode).getFunction().(NameNode).getId().matches("With\\_%") ) and node.(DataFlow::EssaNode).getVar().getName().matches("with\\_%") } From 5256c0ba39caba0b2939c5626e2edc3f7e9dc92b Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 25 Sep 2020 11:19:13 +0100 Subject: [PATCH 121/185] Java: Improve PrintAst tests and rename things Add tests for `EnhcancedForStmt`s and `InstanceOfExpr`s. Rename LocalVarDeclParent to SingleLocalVarDeclParent --- java/ql/src/semmle/code/java/PrintAst.qll | 35 ++++++++--------- .../java7/MultiCatch/PrintAst.expected | 6 +-- java/ql/test/library-tests/printAst/A.java | 16 ++++++++ .../library-tests/printAst/PrintAst.expected | 38 +++++++++++++++++++ java/ql/test/library-tests/printAst/options | 1 + 5 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 java/ql/test/library-tests/printAst/options diff --git a/java/ql/src/semmle/code/java/PrintAst.qll b/java/ql/src/semmle/code/java/PrintAst.qll index ca1daf6c2a4..4e219603024 100644 --- a/java/ql/src/semmle/code/java/PrintAst.qll +++ b/java/ql/src/semmle/code/java/PrintAst.qll @@ -115,7 +115,7 @@ private newtype TPrintAstNode = TElementNode(Element el) { shouldPrint(el, _) } or TForInitNode(ForStmt fs) { shouldPrint(fs, _) and exists(fs.getAnInit()) } or TLocalVarDeclNode(LocalVariableDeclExpr lvde) { - shouldPrint(lvde, _) and lvde.getParent() instanceof LocalVarDeclParent + shouldPrint(lvde, _) and lvde.getParent() instanceof SingleLocalVarDeclParent } or TAnnotationsNode(Annotatable ann) { shouldPrint(ann, _) and ann.hasAnnotation() and not partOfAnnotation(ann) @@ -266,13 +266,11 @@ final class AnnotationPartNode extends ExprStmtNode { } private Expr getAnAnnotationChild() { - ( - result = element.(Annotation).getValue(_) - or - result = element.(ArrayInit).getAnInit() - or - result = element.(ArrayInit).(Annotatable).getAnAnnotation() - ) + result = element.(Annotation).getValue(_) + or + result = element.(ArrayInit).getAnInit() + or + result = element.(ArrayInit).(Annotatable).getAnAnnotation() } } @@ -334,11 +332,11 @@ final class ForStmtNode extends ExprStmtNode { } /** - * An element that can be the parent of a `LocalVariableDeclExpr` for which we want + * An element that can be the parent of up to one `LocalVariableDeclExpr` for which we want * to use a synthetic node to hold the variable declaration and its `TypeAccess`. */ -private class LocalVarDeclParent extends ExprOrStmt { - LocalVarDeclParent() { +private class SingleLocalVarDeclParent extends ExprOrStmt { + SingleLocalVarDeclParent() { this instanceof EnhancedForStmt or this instanceof CatchClause or this.(InstanceOfExpr).isPattern() @@ -352,17 +350,16 @@ private class LocalVarDeclParent extends ExprOrStmt { } /** - * A node representing an element that can be the parent of a `LocalVariableDeclExpr` for which we + * A node representing an element that can be the parent of up to one `LocalVariableDeclExpr` for which we * want to use a synthetic node to variable declaration and its type access. * - * Excludes: - * - `LocalVariableDeclStmt` because a synthetic node isn't needed - * - `ForStmt` becasue a different synthetic node is already used + * Excludes `LocalVariableDeclStmt` and `ForStmt`, as they can hold multiple declarations. + * For these cases, either a synthetic node is not necassary or a different synthetic node is used. */ -final class LocalVarDeclParentNode extends ExprStmtNode { - LocalVarDeclParent lvdp; +final class SingleLocalVarDeclParentNode extends ExprStmtNode { + SingleLocalVarDeclParent lvdp; - LocalVarDeclParentNode() { lvdp = element } + SingleLocalVarDeclParentNode() { lvdp = element } override PrintAstNode getChild(int childIndex) { result = super.getChild(childIndex) and @@ -560,7 +557,7 @@ final class LocalVarDeclSynthNode extends PrintAstNode, TLocalVarDeclNode { LocalVarDeclSynthNode() { this = TLocalVarDeclNode(lvde) } - override string toString() { result = "(Local Variable Declaration)" } + override string toString() { result = "(Single Local Variable Declaration)" } override ElementNode getChild(int childIndex) { childIndex = 0 and diff --git a/java/ql/test/library-tests/java7/MultiCatch/PrintAst.expected b/java/ql/test/library-tests/java7/MultiCatch/PrintAst.expected index 98172ceff75..bba3b450e3e 100644 --- a/java/ql/test/library-tests/java7/MultiCatch/PrintAst.expected +++ b/java/ql/test/library-tests/java7/MultiCatch/PrintAst.expected @@ -21,7 +21,7 @@ MultiCatch.java: # 14| 0: [ClassInstanceExpr] new SQLException(...) # 14| -3: [TypeAccess] SQLException # 15| 0: [CatchClause] stmt -#-----| 0: (Local Variable Declaration) +#-----| 0: (Single Local Variable Declaration) # 15| 0: [UnionTypeAccess] ...|... # 15| 0: [TypeAccess] IOException # 15| 1: [TypeAccess] SQLException @@ -56,7 +56,7 @@ MultiCatch.java: # 30| 0: [ClassInstanceExpr] new Exception(...) # 30| -3: [TypeAccess] Exception # 31| 0: [CatchClause] stmt -#-----| 0: (Local Variable Declaration) +#-----| 0: (Single Local Variable Declaration) # 31| 0: [UnionTypeAccess] ...|... # 31| 0: [TypeAccess] IOException # 31| 1: [TypeAccess] SQLException @@ -71,7 +71,7 @@ MultiCatch.java: # 39| 0: [ClassInstanceExpr] new IOException(...) # 39| -3: [TypeAccess] IOException # 40| 0: [CatchClause] stmt -#-----| 0: (Local Variable Declaration) +#-----| 0: (Single Local Variable Declaration) # 40| 0: [TypeAccess] Exception # 40| 1: [LocalVariableDeclExpr] e # 41| 1: [BlockStmt] stmt diff --git a/java/ql/test/library-tests/printAst/A.java b/java/ql/test/library-tests/printAst/A.java index c3176cc14f3..38319c64706 100644 --- a/java/ql/test/library-tests/printAst/A.java +++ b/java/ql/test/library-tests/printAst/A.java @@ -40,4 +40,20 @@ class A { @Ann2(7) }) String doSomethingElse() { return "c"; } + + void varDecls(Object[] things) { + try { + for(Object thing : things) { + if (thing instanceof Integer) { + return; + } + if (thing instanceof String s) { + throw new RuntimeException(s); + } + } + } + catch (RuntimeException rte) { + return; + } + } } \ No newline at end of file diff --git a/java/ql/test/library-tests/printAst/PrintAst.expected b/java/ql/test/library-tests/printAst/PrintAst.expected index c8f22de6950..922acc4011a 100644 --- a/java/ql/test/library-tests/printAst/PrintAst.expected +++ b/java/ql/test/library-tests/printAst/PrintAst.expected @@ -87,3 +87,41 @@ A.java: # 42| 5: [BlockStmt] stmt # 42| 0: [ReturnStmt] stmt # 42| 0: [StringLiteral] "c" +# 44| 9: [Method] varDecls +# 44| 3: [TypeAccess] void +#-----| 4: (Parameters) +# 44| 0: [Parameter] things +# 44| 0: [ArrayTypeAccess] ...[] +# 44| 0: [TypeAccess] Object +# 44| 5: [BlockStmt] stmt +# 45| 0: [TryStmt] stmt +# 45| -1: [BlockStmt] stmt +# 46| 0: [EnhancedForStmt] stmt +#-----| 0: (Single Local Variable Declaration) +# 46| 0: [TypeAccess] Object +# 46| 1: [LocalVariableDeclExpr] thing +# 46| 1: [VarAccess] things +# 46| 2: [BlockStmt] stmt +# 47| 0: [IfStmt] stmt +# 47| 0: [InstanceOfExpr] ...instanceof... +# 47| 0: [VarAccess] thing +# 47| 1: [TypeAccess] Integer +# 47| 1: [BlockStmt] stmt +# 48| 0: [ReturnStmt] stmt +# 50| 1: [IfStmt] stmt +# 50| 0: [InstanceOfExpr] ...instanceof... +#-----| 0: (Single Local Variable Declaration) +# 50| 0: [TypeAccess] String +# 50| 1: [LocalVariableDeclExpr] s +# 50| 0: [VarAccess] thing +# 50| 1: [BlockStmt] stmt +# 51| 0: [ThrowStmt] stmt +# 51| 0: [ClassInstanceExpr] new RuntimeException(...) +# 51| -3: [TypeAccess] RuntimeException +# 51| 0: [VarAccess] s +# 55| 0: [CatchClause] stmt +#-----| 0: (Single Local Variable Declaration) +# 55| 0: [TypeAccess] RuntimeException +# 55| 1: [LocalVariableDeclExpr] rte +# 55| 1: [BlockStmt] stmt +# 56| 0: [ReturnStmt] stmt diff --git a/java/ql/test/library-tests/printAst/options b/java/ql/test/library-tests/printAst/options new file mode 100644 index 00000000000..266b0eadc5e --- /dev/null +++ b/java/ql/test/library-tests/printAst/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args --enable-preview -source 14 -target 14 From 0ccbaf9e88f9053edf990a0aedfc035bc606ade9 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Fri, 25 Sep 2020 12:12:39 +0100 Subject: [PATCH 122/185] JavaScript: Handle empty `package.json` files gracefully. --- .../semmle/js/extractor/ScriptExtractor.java | 6 +++- .../tests/moduleTypes3/input/package.json | 0 .../extractor/tests/moduleTypes3/input/tst.js | 0 .../output/trap/package.json.trap | 15 ++++++++++ .../moduleTypes3/output/trap/tst.js.trap | 28 +++++++++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 javascript/extractor/tests/moduleTypes3/input/package.json create mode 100644 javascript/extractor/tests/moduleTypes3/input/tst.js create mode 100644 javascript/extractor/tests/moduleTypes3/output/trap/package.json.trap create mode 100644 javascript/extractor/tests/moduleTypes3/output/trap/tst.js.trap diff --git a/javascript/extractor/src/com/semmle/js/extractor/ScriptExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/ScriptExtractor.java index b0ce54d02aa..7d8ccc6b3b1 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/ScriptExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/ScriptExtractor.java @@ -126,7 +126,11 @@ public class ScriptExtractor implements IExtractor { } try { BufferedReader reader = new BufferedReader(new FileReader(file)); - String result = new Gson().fromJson(reader, PackageJSON.class).type; + PackageJSON pkgjson = new Gson().fromJson(reader, PackageJSON.class); + if (pkgjson == null) { + return null; + } + String result = pkgjson.type; packageTypeCache.put(folder, Optional.ofNullable(result)); return result; } catch (IOException | JsonSyntaxException e) { diff --git a/javascript/extractor/tests/moduleTypes3/input/package.json b/javascript/extractor/tests/moduleTypes3/input/package.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/extractor/tests/moduleTypes3/input/tst.js b/javascript/extractor/tests/moduleTypes3/input/tst.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/extractor/tests/moduleTypes3/output/trap/package.json.trap b/javascript/extractor/tests/moduleTypes3/output/trap/package.json.trap new file mode 100644 index 00000000000..eed0d0523d5 --- /dev/null +++ b/javascript/extractor/tests/moduleTypes3/output/trap/package.json.trap @@ -0,0 +1,15 @@ +#10000=@"/package.json;sourcefile" +files(#10000,"/package.json","package","json",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=* +json_errors(#20000,"Error: Unexpected token") +#20001=@"loc,{#10000},1,1,1,1" +locations_default(#20001,#10000,1,1,1,1) +hasLocation(#20000,#20001) +numlines(#10000,0,0,0) +filetype(#10000,"json") diff --git a/javascript/extractor/tests/moduleTypes3/output/trap/tst.js.trap b/javascript/extractor/tests/moduleTypes3/output/trap/tst.js.trap new file mode 100644 index 00000000000..cedeb436b92 --- /dev/null +++ b/javascript/extractor/tests/moduleTypes3/output/trap/tst.js.trap @@ -0,0 +1,28 @@ +#10000=@"/tst.js;sourcefile" +files(#10000,"/tst.js","tst","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +numlines(#20001,0,0,0) +#20002=* +tokeninfo(#20002,0,#20001,0,"") +#20003=@"loc,{#10000},1,1,1,0" +locations_default(#20003,#10000,1,1,1,0) +hasLocation(#20002,#20003) +toplevels(#20001,0) +hasLocation(#20001,#20003) +#20004=* +entry_cfg_node(#20004,#20001) +hasLocation(#20004,#20003) +#20005=* +exit_cfg_node(#20005,#20001) +hasLocation(#20005,#20003) +successor(#20004,#20005) +numlines(#10000,0,0,0) +filetype(#10000,"javascript") From 88bba466986728a89faf7721278d6da5b86e326f Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 25 Sep 2020 13:35:30 +0200 Subject: [PATCH 123/185] Python: Modify tests based on review The extra hist in `test.py` seen in `globalStep.expected` are due to the removal of manual filtering code. (That code was from when dataflow had many strange things in it.) --- .../dataflow/coverage/dataflow.expected | 152 +++--- .../dataflow/coverage/dataflow.ql | 2 +- .../dataflow/fieldflow/allLocalFlow.expected | 76 +++ .../dataflow/fieldflow/allLocalFlow.ql | 2 +- .../dataflow/fieldflow/dataflow.expected | 28 +- .../dataflow/fieldflow/dataflow.ql | 2 +- .../fieldflow/dataflowExplore.expected | 74 --- .../dataflow/fieldflow/dataflowExplore.ql | 13 - .../dataflow/fieldflow/globalStep.expected | 473 ++++++++++++++++++ .../dataflow/fieldflow/globalStep.ql | 4 +- 10 files changed, 643 insertions(+), 183 deletions(-) delete mode 100644 python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected delete mode 100644 python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index 0d8bf3687fb..379b44a3090 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -300,79 +300,79 @@ nodes | test.py:506:10:506:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | | test.py:511:10:511:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | #select -| datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:38:6:38:17 | ControlFlowNode for f() | | -| datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | | -| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | | -| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | | -| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | | -| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | | -| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | | -| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | | -| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | | -| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | -| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | -| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | -| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | -| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:73:18:73:23 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | -| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | -| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | | -| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | | -| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | | -| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | | -| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:73:18:73:23 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | | -| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | | -| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | | -| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | | -| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | | -| test.py:38:10:38:10 | ControlFlowNode for y | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:38:10:38:10 | ControlFlowNode for y | | -| test.py:38:10:38:10 | ControlFlowNode for y | test.py:36:21:36:26 | ControlFlowNode for SOURCE | test.py:38:10:38:10 | ControlFlowNode for y | | -| test.py:51:10:51:10 | ControlFlowNode for x | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:51:10:51:10 | ControlFlowNode for x | | -| test.py:51:10:51:10 | ControlFlowNode for x | test.py:50:9:50:14 | ControlFlowNode for SOURCE | test.py:51:10:51:10 | ControlFlowNode for x | | -| test.py:58:10:58:10 | ControlFlowNode for x | test.py:57:9:57:16 | ControlFlowNode for Str | test.py:58:10:58:10 | ControlFlowNode for x | | -| test.py:63:10:63:10 | ControlFlowNode for x | test.py:62:9:62:17 | ControlFlowNode for Str | test.py:63:10:63:10 | ControlFlowNode for x | | -| test.py:68:10:68:10 | ControlFlowNode for x | test.py:67:9:67:10 | ControlFlowNode for IntegerLiteral | test.py:68:10:68:10 | ControlFlowNode for x | | -| test.py:73:10:73:10 | ControlFlowNode for x | test.py:72:9:72:12 | ControlFlowNode for FloatLiteral | test.py:73:10:73:10 | ControlFlowNode for x | | -| test.py:85:10:85:10 | ControlFlowNode for x | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:85:10:85:10 | ControlFlowNode for x | | -| test.py:85:10:85:10 | ControlFlowNode for x | test.py:84:10:84:15 | ControlFlowNode for SOURCE | test.py:85:10:85:10 | ControlFlowNode for x | | -| test.py:92:10:92:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:92:10:92:13 | ControlFlowNode for Subscript | | -| test.py:92:10:92:13 | ControlFlowNode for Subscript | test.py:91:10:91:15 | ControlFlowNode for SOURCE | test.py:92:10:92:13 | ControlFlowNode for Subscript | | -| test.py:102:10:102:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:102:10:102:13 | ControlFlowNode for Subscript | | -| test.py:102:10:102:13 | ControlFlowNode for Subscript | test.py:101:10:101:15 | ControlFlowNode for SOURCE | test.py:102:10:102:13 | ControlFlowNode for Subscript | | -| test.py:107:10:107:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:107:10:107:13 | ControlFlowNode for Subscript | | -| test.py:107:10:107:13 | ControlFlowNode for Subscript | test.py:106:22:106:27 | ControlFlowNode for SOURCE | test.py:107:10:107:13 | ControlFlowNode for Subscript | | -| test.py:113:10:113:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:113:10:113:13 | ControlFlowNode for Subscript | | -| test.py:113:10:113:13 | ControlFlowNode for Subscript | test.py:111:10:111:15 | ControlFlowNode for SOURCE | test.py:113:10:113:13 | ControlFlowNode for Subscript | | -| test.py:125:10:125:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:125:10:125:16 | ControlFlowNode for Attribute() | | -| test.py:125:10:125:16 | ControlFlowNode for Attribute() | test.py:124:10:124:15 | ControlFlowNode for SOURCE | test.py:125:10:125:16 | ControlFlowNode for Attribute() | | -| test.py:130:10:130:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:130:10:130:16 | ControlFlowNode for Attribute() | | -| test.py:130:10:130:16 | ControlFlowNode for Attribute() | test.py:129:10:129:15 | ControlFlowNode for SOURCE | test.py:130:10:130:16 | ControlFlowNode for Attribute() | | -| test.py:135:10:135:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:135:10:135:16 | ControlFlowNode for Attribute() | | -| test.py:135:10:135:16 | ControlFlowNode for Attribute() | test.py:134:22:134:27 | ControlFlowNode for SOURCE | test.py:135:10:135:16 | ControlFlowNode for Attribute() | | -| test.py:141:10:141:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:141:10:141:16 | ControlFlowNode for Attribute() | | -| test.py:141:10:141:16 | ControlFlowNode for Attribute() | test.py:139:10:139:15 | ControlFlowNode for SOURCE | test.py:141:10:141:16 | ControlFlowNode for Attribute() | | -| test.py:153:10:153:15 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:153:10:153:15 | ControlFlowNode for Subscript | | -| test.py:153:10:153:15 | ControlFlowNode for Subscript | test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:153:10:153:15 | ControlFlowNode for Subscript | | -| test.py:158:10:158:19 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:158:10:158:19 | ControlFlowNode for Attribute() | | -| test.py:158:10:158:19 | ControlFlowNode for Attribute() | test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:158:10:158:19 | ControlFlowNode for Attribute() | | -| test.py:185:10:185:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:185:10:185:13 | ControlFlowNode for Subscript | | -| test.py:185:10:185:13 | ControlFlowNode for Subscript | test.py:184:23:184:28 | ControlFlowNode for SOURCE | test.py:185:10:185:13 | ControlFlowNode for Subscript | | -| test.py:190:10:190:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:190:10:190:13 | ControlFlowNode for Subscript | | -| test.py:190:10:190:13 | ControlFlowNode for Subscript | test.py:189:25:189:30 | ControlFlowNode for SOURCE | test.py:190:10:190:13 | ControlFlowNode for Subscript | | -| test.py:201:10:201:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:201:10:201:13 | ControlFlowNode for Subscript | | -| test.py:201:10:201:13 | ControlFlowNode for Subscript | test.py:200:34:200:39 | ControlFlowNode for SOURCE | test.py:201:10:201:13 | ControlFlowNode for Subscript | | -| test.py:344:10:344:21 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:344:10:344:21 | ControlFlowNode for Subscript | | -| test.py:344:10:344:21 | ControlFlowNode for Subscript | test.py:344:11:344:16 | ControlFlowNode for SOURCE | test.py:344:10:344:21 | ControlFlowNode for Subscript | | -| test.py:348:10:348:20 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:348:10:348:20 | ControlFlowNode for Subscript | | -| test.py:348:10:348:20 | ControlFlowNode for Subscript | test.py:348:11:348:16 | ControlFlowNode for SOURCE | test.py:348:10:348:20 | ControlFlowNode for Subscript | | -| test.py:352:10:352:27 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:352:10:352:27 | ControlFlowNode for Subscript | | -| test.py:352:10:352:27 | ControlFlowNode for Subscript | test.py:352:16:352:21 | ControlFlowNode for SOURCE | test.py:352:10:352:27 | ControlFlowNode for Subscript | | -| test.py:375:10:375:34 | ControlFlowNode for second() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:375:10:375:34 | ControlFlowNode for second() | | -| test.py:375:10:375:34 | ControlFlowNode for second() | test.py:375:28:375:33 | ControlFlowNode for SOURCE | test.py:375:10:375:34 | ControlFlowNode for second() | | -| test.py:457:10:457:18 | ControlFlowNode for f() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:457:10:457:18 | ControlFlowNode for f() | | -| test.py:457:10:457:18 | ControlFlowNode for f() | test.py:457:12:457:17 | ControlFlowNode for SOURCE | test.py:457:10:457:18 | ControlFlowNode for f() | | -| test.py:462:10:462:34 | ControlFlowNode for second() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:462:10:462:34 | ControlFlowNode for second() | | -| test.py:462:10:462:34 | ControlFlowNode for second() | test.py:462:28:462:33 | ControlFlowNode for SOURCE | test.py:462:10:462:34 | ControlFlowNode for second() | | -| test.py:506:10:506:10 | ControlFlowNode for a | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:506:10:506:10 | ControlFlowNode for a | | -| test.py:506:10:506:10 | ControlFlowNode for a | test.py:504:9:504:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for a | | -| test.py:511:10:511:10 | ControlFlowNode for b | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:511:10:511:10 | ControlFlowNode for b | | -| test.py:511:10:511:10 | ControlFlowNode for b | test.py:504:9:504:14 | ControlFlowNode for SOURCE | test.py:511:10:511:10 | ControlFlowNode for b | | +| datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:38:6:38:17 | ControlFlowNode for f() | Flow found | +| datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | Flow found | +| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:73:18:73:23 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:73:18:73:23 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found | +| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | Flow found | +| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | Flow found | +| test.py:38:10:38:10 | ControlFlowNode for y | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:38:10:38:10 | ControlFlowNode for y | Flow found | +| test.py:38:10:38:10 | ControlFlowNode for y | test.py:36:21:36:26 | ControlFlowNode for SOURCE | test.py:38:10:38:10 | ControlFlowNode for y | Flow found | +| test.py:51:10:51:10 | ControlFlowNode for x | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:51:10:51:10 | ControlFlowNode for x | Flow found | +| test.py:51:10:51:10 | ControlFlowNode for x | test.py:50:9:50:14 | ControlFlowNode for SOURCE | test.py:51:10:51:10 | ControlFlowNode for x | Flow found | +| test.py:58:10:58:10 | ControlFlowNode for x | test.py:57:9:57:16 | ControlFlowNode for Str | test.py:58:10:58:10 | ControlFlowNode for x | Flow found | +| test.py:63:10:63:10 | ControlFlowNode for x | test.py:62:9:62:17 | ControlFlowNode for Str | test.py:63:10:63:10 | ControlFlowNode for x | Flow found | +| test.py:68:10:68:10 | ControlFlowNode for x | test.py:67:9:67:10 | ControlFlowNode for IntegerLiteral | test.py:68:10:68:10 | ControlFlowNode for x | Flow found | +| test.py:73:10:73:10 | ControlFlowNode for x | test.py:72:9:72:12 | ControlFlowNode for FloatLiteral | test.py:73:10:73:10 | ControlFlowNode for x | Flow found | +| test.py:85:10:85:10 | ControlFlowNode for x | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:85:10:85:10 | ControlFlowNode for x | Flow found | +| test.py:85:10:85:10 | ControlFlowNode for x | test.py:84:10:84:15 | ControlFlowNode for SOURCE | test.py:85:10:85:10 | ControlFlowNode for x | Flow found | +| test.py:92:10:92:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:92:10:92:13 | ControlFlowNode for Subscript | Flow found | +| test.py:92:10:92:13 | ControlFlowNode for Subscript | test.py:91:10:91:15 | ControlFlowNode for SOURCE | test.py:92:10:92:13 | ControlFlowNode for Subscript | Flow found | +| test.py:102:10:102:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:102:10:102:13 | ControlFlowNode for Subscript | Flow found | +| test.py:102:10:102:13 | ControlFlowNode for Subscript | test.py:101:10:101:15 | ControlFlowNode for SOURCE | test.py:102:10:102:13 | ControlFlowNode for Subscript | Flow found | +| test.py:107:10:107:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:107:10:107:13 | ControlFlowNode for Subscript | Flow found | +| test.py:107:10:107:13 | ControlFlowNode for Subscript | test.py:106:22:106:27 | ControlFlowNode for SOURCE | test.py:107:10:107:13 | ControlFlowNode for Subscript | Flow found | +| test.py:113:10:113:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:113:10:113:13 | ControlFlowNode for Subscript | Flow found | +| test.py:113:10:113:13 | ControlFlowNode for Subscript | test.py:111:10:111:15 | ControlFlowNode for SOURCE | test.py:113:10:113:13 | ControlFlowNode for Subscript | Flow found | +| test.py:125:10:125:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:125:10:125:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:125:10:125:16 | ControlFlowNode for Attribute() | test.py:124:10:124:15 | ControlFlowNode for SOURCE | test.py:125:10:125:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:130:10:130:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:130:10:130:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:130:10:130:16 | ControlFlowNode for Attribute() | test.py:129:10:129:15 | ControlFlowNode for SOURCE | test.py:130:10:130:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:135:10:135:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:135:10:135:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:135:10:135:16 | ControlFlowNode for Attribute() | test.py:134:22:134:27 | ControlFlowNode for SOURCE | test.py:135:10:135:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:141:10:141:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:141:10:141:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:141:10:141:16 | ControlFlowNode for Attribute() | test.py:139:10:139:15 | ControlFlowNode for SOURCE | test.py:141:10:141:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:153:10:153:15 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:153:10:153:15 | ControlFlowNode for Subscript | Flow found | +| test.py:153:10:153:15 | ControlFlowNode for Subscript | test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:153:10:153:15 | ControlFlowNode for Subscript | Flow found | +| test.py:158:10:158:19 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:158:10:158:19 | ControlFlowNode for Attribute() | Flow found | +| test.py:158:10:158:19 | ControlFlowNode for Attribute() | test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:158:10:158:19 | ControlFlowNode for Attribute() | Flow found | +| test.py:185:10:185:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:185:10:185:13 | ControlFlowNode for Subscript | Flow found | +| test.py:185:10:185:13 | ControlFlowNode for Subscript | test.py:184:23:184:28 | ControlFlowNode for SOURCE | test.py:185:10:185:13 | ControlFlowNode for Subscript | Flow found | +| test.py:190:10:190:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:190:10:190:13 | ControlFlowNode for Subscript | Flow found | +| test.py:190:10:190:13 | ControlFlowNode for Subscript | test.py:189:25:189:30 | ControlFlowNode for SOURCE | test.py:190:10:190:13 | ControlFlowNode for Subscript | Flow found | +| test.py:201:10:201:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:201:10:201:13 | ControlFlowNode for Subscript | Flow found | +| test.py:201:10:201:13 | ControlFlowNode for Subscript | test.py:200:34:200:39 | ControlFlowNode for SOURCE | test.py:201:10:201:13 | ControlFlowNode for Subscript | Flow found | +| test.py:344:10:344:21 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:344:10:344:21 | ControlFlowNode for Subscript | Flow found | +| test.py:344:10:344:21 | ControlFlowNode for Subscript | test.py:344:11:344:16 | ControlFlowNode for SOURCE | test.py:344:10:344:21 | ControlFlowNode for Subscript | Flow found | +| test.py:348:10:348:20 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:348:10:348:20 | ControlFlowNode for Subscript | Flow found | +| test.py:348:10:348:20 | ControlFlowNode for Subscript | test.py:348:11:348:16 | ControlFlowNode for SOURCE | test.py:348:10:348:20 | ControlFlowNode for Subscript | Flow found | +| test.py:352:10:352:27 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:352:10:352:27 | ControlFlowNode for Subscript | Flow found | +| test.py:352:10:352:27 | ControlFlowNode for Subscript | test.py:352:16:352:21 | ControlFlowNode for SOURCE | test.py:352:10:352:27 | ControlFlowNode for Subscript | Flow found | +| test.py:375:10:375:34 | ControlFlowNode for second() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:375:10:375:34 | ControlFlowNode for second() | Flow found | +| test.py:375:10:375:34 | ControlFlowNode for second() | test.py:375:28:375:33 | ControlFlowNode for SOURCE | test.py:375:10:375:34 | ControlFlowNode for second() | Flow found | +| test.py:457:10:457:18 | ControlFlowNode for f() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:457:10:457:18 | ControlFlowNode for f() | Flow found | +| test.py:457:10:457:18 | ControlFlowNode for f() | test.py:457:12:457:17 | ControlFlowNode for SOURCE | test.py:457:10:457:18 | ControlFlowNode for f() | Flow found | +| test.py:462:10:462:34 | ControlFlowNode for second() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:462:10:462:34 | ControlFlowNode for second() | Flow found | +| test.py:462:10:462:34 | ControlFlowNode for second() | test.py:462:28:462:33 | ControlFlowNode for SOURCE | test.py:462:10:462:34 | ControlFlowNode for second() | Flow found | +| test.py:506:10:506:10 | ControlFlowNode for a | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:506:10:506:10 | ControlFlowNode for a | Flow found | +| test.py:506:10:506:10 | ControlFlowNode for a | test.py:504:9:504:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for a | Flow found | +| test.py:511:10:511:10 | ControlFlowNode for b | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:511:10:511:10 | ControlFlowNode for b | Flow found | +| test.py:511:10:511:10 | ControlFlowNode for b | test.py:504:9:504:14 | ControlFlowNode for SOURCE | test.py:511:10:511:10 | ControlFlowNode for b | Flow found | diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.ql b/python/ql/test/experimental/dataflow/coverage/dataflow.ql index 18b21324e47..868f24a598f 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.ql +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.ql @@ -8,4 +8,4 @@ import DataFlow::PathGraph from TestConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "" +select sink.getNode(), source, sink, "Flow found" diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected index c17e4d8e673..0f33cbc6bda 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected @@ -1,3 +1,79 @@ +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:1:1:1:66 | GSSA Variable SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:28:1:28:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK_F | examples.py:1:1:1:66 | GSSA Variable SINK_F | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:1:1:1:66 | GSSA Variable SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable __name__ | examples.py:1:1:1:66 | GSSA Variable __name__ | +| examples.py:0:0:0:0 | GSSA Variable __package__ | examples.py:1:1:1:66 | GSSA Variable __package__ | +| examples.py:0:0:0:0 | GSSA Variable a | examples.py:1:1:1:66 | GSSA Variable a | +| examples.py:0:0:0:0 | GSSA Variable fields_with_local_flow | examples.py:1:1:1:66 | GSSA Variable fields_with_local_flow | +| examples.py:0:0:0:0 | GSSA Variable myobj | examples.py:1:1:1:66 | GSSA Variable myobj | +| examples.py:0:0:0:0 | GSSA Variable obj | examples.py:1:1:1:66 | GSSA Variable obj | +| examples.py:0:0:0:0 | GSSA Variable object | examples.py:1:1:1:66 | GSSA Variable object | +| examples.py:0:0:0:0 | GSSA Variable object | examples.py:5:13:5:18 | ControlFlowNode for object | +| examples.py:0:0:0:0 | GSSA Variable x | examples.py:1:1:1:66 | GSSA Variable x | +| examples.py:0:0:0:0 | SSA variable $ | examples.py:1:1:1:66 | SSA variable $ | +| examples.py:0:0:0:0 | SSA variable * | examples.py:1:1:1:66 | SSA variable * | +| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:41:7:41:19 | GSSA Variable SOURCE | +| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:5:7:5:11 | GSSA Variable MyObj | +| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:25:9:25:13 | ControlFlowNode for MyObj | +| examples.py:5:13:5:18 | ControlFlowNode for object | examples.py:11:17:11:22 | ControlFlowNode for object | +| examples.py:7:5:7:28 | ControlFlowNode for FunctionExpr | examples.py:7:9:7:16 | SSA variable __init__ | +| examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:12 | ControlFlowNode for self | +| examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:16 | SSA variable self | +| examples.py:7:24:7:26 | SSA variable foo | examples.py:8:20:8:22 | ControlFlowNode for foo | +| examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:11:7:11:15 | GSSA Variable NestedObj | +| examples.py:11:7:11:15 | GSSA Variable NestedObj | examples.py:33:5:33:13 | ControlFlowNode for NestedObj | +| examples.py:13:5:13:23 | ControlFlowNode for FunctionExpr | examples.py:13:9:13:16 | SSA variable __init__ | +| examples.py:13:5:13:23 | GSSA Variable MyObj | examples.py:14:20:14:24 | ControlFlowNode for MyObj | +| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:12 | ControlFlowNode for self | +| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:16 | SSA variable self | +| examples.py:16:5:16:21 | ControlFlowNode for FunctionExpr | examples.py:16:9:16:14 | SSA variable getObj | +| examples.py:16:16:16:19 | SSA variable self | examples.py:17:16:17:19 | ControlFlowNode for self | +| examples.py:21:1:21:19 | ControlFlowNode for FunctionExpr | examples.py:21:5:21:10 | GSSA Variable setFoo | +| examples.py:21:1:21:19 | GSSA Variable SINK_F | examples.py:22:5:22:10 | ControlFlowNode for SINK_F | +| examples.py:21:5:21:10 | GSSA Variable setFoo | examples.py:27:1:27:6 | ControlFlowNode for setFoo | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:22:12:22:14 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:11 | SSA variable obj | +| examples.py:21:17:21:17 | SSA variable x | examples.py:23:15:23:15 | ControlFlowNode for x | +| examples.py:22:12:22:14 | ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:27:1:27:21 | GSSA Variable myobj | +| examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj | +| examples.py:25:9:25:13 | ControlFlowNode for MyObj | examples.py:41:7:41:11 | ControlFlowNode for MyObj | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:25:1:25:5 | GSSA Variable myobj | +| examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:38:1:38:4 | ControlFlowNode for SINK | +| examples.py:31:1:31:1 | GSSA Variable x | examples.py:35:13:35:13 | ControlFlowNode for x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:35:1:35:1 | ControlFlowNode for a | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:36:1:36:10 | GSSA Variable a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:33:1:33:1 | GSSA Variable a | +| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:35:13:35:13 | ControlFlowNode for x | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:36:1:36:1 | ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:38:1:38:4 | ControlFlowNode for SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | +| examples.py:41:1:41:3 | GSSA Variable obj | examples.py:42:6:42:8 | ControlFlowNode for obj | +| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:41:1:41:3 | GSSA Variable obj | +| examples.py:41:7:41:19 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | +| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:42:1:42:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:45:1:45:30 | ControlFlowNode for FunctionExpr | examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | +| examples.py:45:1:45:30 | GSSA Variable MyObj | examples.py:46:9:46:13 | ControlFlowNode for MyObj | +| examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | examples.py:50:6:50:27 | ControlFlowNode for fields_with_local_flow | +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | +| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | +| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | +| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | | test.py:0:0:0:0 | GSSA Variable SINK | test.py:1:1:1:66 | GSSA Variable SINK | | test.py:0:0:0:0 | GSSA Variable SINK_F | test.py:1:1:1:66 | GSSA Variable SINK_F | | test.py:0:0:0:0 | GSSA Variable SOURCE | test.py:1:1:1:66 | GSSA Variable SOURCE | diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql index 0da43687495..4df2ae7fdfd 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql +++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql @@ -4,5 +4,5 @@ import experimental.dataflow.DataFlow from DataFlow::Node nodeFrom, DataFlow::Node nodeTo where DataFlow::localFlowStep(nodeFrom, nodeTo) and - nodeFrom.getLocation().getFile().getBaseName() = "test.py" + nodeFrom.getLocation().getFile().getParent().getBaseName() = "fieldflow" select nodeFrom, nodeTo diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected index dd6f8ab0138..76e77ef58b4 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected @@ -67,17 +67,17 @@ nodes | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | | test.py:56:33:56:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | #select -| examples.py:28:6:28:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:28:6:28:14 | ControlFlowNode for Attribute | | -| examples.py:38:6:38:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:38:6:38:14 | ControlFlowNode for Attribute | | -| examples.py:38:6:38:14 | ControlFlowNode for Attribute | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:38:6:38:14 | ControlFlowNode for Attribute | | -| examples.py:42:6:42:12 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:42:6:42:12 | ControlFlowNode for Attribute | | -| examples.py:42:6:42:12 | ControlFlowNode for Attribute | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:42:6:42:12 | ControlFlowNode for Attribute | | -| examples.py:42:6:42:12 | ControlFlowNode for Attribute | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:42:6:42:12 | ControlFlowNode for Attribute | | -| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | | -| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | | -| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | | -| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | | -| test.py:30:10:30:18 | ControlFlowNode for Attribute | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:30:10:30:18 | ControlFlowNode for Attribute | | -| test.py:41:10:41:18 | ControlFlowNode for Attribute | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:41:10:41:18 | ControlFlowNode for Attribute | | -| test.py:46:10:46:16 | ControlFlowNode for Attribute | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:46:10:46:16 | ControlFlowNode for Attribute | | -| test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | | +| examples.py:28:6:28:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:28:6:28:14 | ControlFlowNode for Attribute | Flow found | +| examples.py:38:6:38:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:38:6:38:14 | ControlFlowNode for Attribute | Flow found | +| examples.py:38:6:38:14 | ControlFlowNode for Attribute | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:38:6:38:14 | ControlFlowNode for Attribute | Flow found | +| examples.py:42:6:42:12 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:42:6:42:12 | ControlFlowNode for Attribute | Flow found | +| examples.py:42:6:42:12 | ControlFlowNode for Attribute | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:42:6:42:12 | ControlFlowNode for Attribute | Flow found | +| examples.py:42:6:42:12 | ControlFlowNode for Attribute | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:42:6:42:12 | ControlFlowNode for Attribute | Flow found | +| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | Flow found | +| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | Flow found | +| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | Flow found | +| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | Flow found | +| test.py:30:10:30:18 | ControlFlowNode for Attribute | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:30:10:30:18 | ControlFlowNode for Attribute | Flow found | +| test.py:41:10:41:18 | ControlFlowNode for Attribute | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:41:10:41:18 | ControlFlowNode for Attribute | Flow found | +| test.py:46:10:46:16 | ControlFlowNode for Attribute | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:46:10:46:16 | ControlFlowNode for Attribute | Flow found | +| test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | Flow found | diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.ql b/python/ql/test/experimental/dataflow/fieldflow/dataflow.ql index 1347c440496..807f2e91931 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.ql +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.ql @@ -7,4 +7,4 @@ import DataFlow::PathGraph from TestConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "" +select sink.getNode(), source, sink, "Flow found" diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected deleted file mode 100644 index b702c8f1384..00000000000 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected +++ /dev/null @@ -1,74 +0,0 @@ -edges -| examples.py:7:24:7:26 | SSA variable foo | examples.py:8:20:8:22 | ControlFlowNode for foo | -| examples.py:8:20:8:22 | ControlFlowNode for foo | examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | -| examples.py:21:17:21:17 | SSA variable x | examples.py:23:15:23:15 | ControlFlowNode for x | -| examples.py:23:15:23:15 | ControlFlowNode for x | examples.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | -| examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:21:17:21:17 | SSA variable x | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | -| examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:14 | ControlFlowNode for Attribute | -| examples.py:31:1:31:1 | GSSA Variable x | examples.py:35:13:35:13 | ControlFlowNode for x | -| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | -| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, ... (2)] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, ... (2)] | -| examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute [Attribute foo] | examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, ... (2)] | -| examples.py:35:13:35:13 | ControlFlowNode for x | examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute [Attribute foo] | -| examples.py:35:13:35:13 | ControlFlowNode for x | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, ... (2)] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, ... (2)] | -| examples.py:36:18:36:18 | ControlFlowNode for x | examples.py:36:1:36:10 | [post store] ControlFlowNode for Attribute() [Attribute foo] | -| examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, ... (2)] | examples.py:38:6:38:10 | ControlFlowNode for Attribute [Attribute foo] | -| examples.py:38:6:38:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:38:6:38:14 | ControlFlowNode for Attribute | -| examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] | examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | -| examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:7:24:7:26 | SSA variable foo | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | examples.py:42:6:42:12 | ControlFlowNode for Attribute | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | -| examples.py:46:3:46:5 | SSA variable obj [Attribute foo] | examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:46:3:46:5 | SSA variable obj [Attribute foo] | -| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | -| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | -| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | -| examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | examples.py:47:7:47:13 | ControlFlowNode for Attribute | -| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | -| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:45:28:45:28 | SSA variable x | -| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | -| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | -| test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | -| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | -| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | -| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x | -| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | -| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | -| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x | -| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, ... (2)] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, ... (2)] | -| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, ... (2)] | -| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | -| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | -| test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, ... (2)] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, ... (2)] | -| test.py:39:22:39:22 | ControlFlowNode for x | test.py:39:5:39:14 | [post store] ControlFlowNode for Attribute() [Attribute foo] | -| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, ... (2)] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | -| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | -| test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | -| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | -| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | -| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | -| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | -| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | -| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x | -| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | -#select -| test.py:46:10:46:16 | ControlFlowNode for Attribute | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:46:10:46:16 | ControlFlowNode for Attribute | | diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql deleted file mode 100644 index 393e14a0b3c..00000000000 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @kind path-problem - */ - -import experimental.dataflow.testConfig -import DataFlow::PartialPathGraph - -from TestConfiguration config, DataFlow::PartialPathNode source, DataFlow::PartialPathNode sink -where - config.hasPartialFlow(source, sink, _) and - source.hasLocationInfo(_, 45, _, _, _) and - config.isSink(sink.getNode()) -select sink.getNode(), source, sink, "" diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected index 6954f8154d6..ec077fa4b64 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected @@ -1,3 +1,476 @@ +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:1:1:1:66 | GSSA Variable SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:1:1:1:66 | GSSA Variable SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:28:1:28:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:28:1:28:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:38:1:38:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:38:1:38:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK_F | examples.py:1:1:1:66 | GSSA Variable SINK_F | +| examples.py:0:0:0:0 | GSSA Variable SINK_F | examples.py:1:1:1:66 | GSSA Variable SINK_F | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:1:1:1:66 | GSSA Variable SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:1:1:1:66 | GSSA Variable SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:31:1:31:1 | GSSA Variable x | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:31:1:31:1 | GSSA Variable x | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:41:7:41:19 | GSSA Variable SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:41:7:41:19 | GSSA Variable SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable __name__ | examples.py:1:1:1:66 | GSSA Variable __name__ | +| examples.py:0:0:0:0 | GSSA Variable __name__ | examples.py:1:1:1:66 | GSSA Variable __name__ | +| examples.py:0:0:0:0 | GSSA Variable __package__ | examples.py:1:1:1:66 | GSSA Variable __package__ | +| examples.py:0:0:0:0 | GSSA Variable __package__ | examples.py:1:1:1:66 | GSSA Variable __package__ | +| examples.py:0:0:0:0 | GSSA Variable a | examples.py:1:1:1:66 | GSSA Variable a | +| examples.py:0:0:0:0 | GSSA Variable a | examples.py:1:1:1:66 | GSSA Variable a | +| examples.py:0:0:0:0 | GSSA Variable fields_with_local_flow | examples.py:1:1:1:66 | GSSA Variable fields_with_local_flow | +| examples.py:0:0:0:0 | GSSA Variable fields_with_local_flow | examples.py:1:1:1:66 | GSSA Variable fields_with_local_flow | +| examples.py:0:0:0:0 | GSSA Variable myobj | examples.py:1:1:1:66 | GSSA Variable myobj | +| examples.py:0:0:0:0 | GSSA Variable myobj | examples.py:1:1:1:66 | GSSA Variable myobj | +| examples.py:0:0:0:0 | GSSA Variable obj | examples.py:1:1:1:66 | GSSA Variable obj | +| examples.py:0:0:0:0 | GSSA Variable obj | examples.py:1:1:1:66 | GSSA Variable obj | +| examples.py:0:0:0:0 | GSSA Variable object | examples.py:1:1:1:66 | GSSA Variable object | +| examples.py:0:0:0:0 | GSSA Variable object | examples.py:1:1:1:66 | GSSA Variable object | +| examples.py:0:0:0:0 | GSSA Variable object | examples.py:5:13:5:18 | ControlFlowNode for object | +| examples.py:0:0:0:0 | GSSA Variable object | examples.py:5:13:5:18 | ControlFlowNode for object | +| examples.py:0:0:0:0 | GSSA Variable object | examples.py:11:17:11:22 | ControlFlowNode for object | +| examples.py:0:0:0:0 | GSSA Variable object | examples.py:11:17:11:22 | ControlFlowNode for object | +| examples.py:0:0:0:0 | GSSA Variable x | examples.py:1:1:1:66 | GSSA Variable x | +| examples.py:0:0:0:0 | GSSA Variable x | examples.py:1:1:1:66 | GSSA Variable x | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:14:20:14:24 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:14:20:14:24 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:46:9:46:13 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:46:9:46:13 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module examples | examples.py:22:5:22:10 | ControlFlowNode for SINK_F | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module examples | examples.py:22:5:22:10 | ControlFlowNode for SINK_F | +| examples.py:0:0:0:0 | SSA variable $ | examples.py:1:1:1:66 | SSA variable $ | +| examples.py:0:0:0:0 | SSA variable $ | examples.py:1:1:1:66 | SSA variable $ | +| examples.py:0:0:0:0 | SSA variable * | examples.py:1:1:1:66 | SSA variable * | +| examples.py:0:0:0:0 | SSA variable * | examples.py:1:1:1:66 | SSA variable * | +| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:41:7:41:19 | GSSA Variable SOURCE | +| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:41:7:41:19 | GSSA Variable SOURCE | +| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | +| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | +| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:5:7:5:11 | GSSA Variable MyObj | +| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:5:7:5:11 | GSSA Variable MyObj | +| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:25:9:25:13 | ControlFlowNode for MyObj | +| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:25:9:25:13 | ControlFlowNode for MyObj | +| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:41:7:41:11 | ControlFlowNode for MyObj | +| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:41:7:41:11 | ControlFlowNode for MyObj | +| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | +| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | +| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:25:9:25:13 | ControlFlowNode for MyObj | +| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:25:9:25:13 | ControlFlowNode for MyObj | +| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:41:7:41:11 | ControlFlowNode for MyObj | +| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:41:7:41:11 | ControlFlowNode for MyObj | +| examples.py:5:13:5:18 | ControlFlowNode for object | examples.py:11:17:11:22 | ControlFlowNode for object | +| examples.py:5:13:5:18 | ControlFlowNode for object | examples.py:11:17:11:22 | ControlFlowNode for object | +| examples.py:7:5:7:28 | ControlFlowNode for FunctionExpr | examples.py:7:9:7:16 | SSA variable __init__ | +| examples.py:7:5:7:28 | ControlFlowNode for FunctionExpr | examples.py:7:9:7:16 | SSA variable __init__ | +| examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:12 | ControlFlowNode for self | +| examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:12 | ControlFlowNode for self | +| examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:12 | ControlFlowNode for self | +| examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:12 | ControlFlowNode for self | +| examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:16 | SSA variable self | +| examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:16 | SSA variable self | +| examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:16 | SSA variable self | +| examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:16 | SSA variable self | +| examples.py:7:24:7:26 | SSA variable foo | examples.py:8:20:8:22 | ControlFlowNode for foo | +| examples.py:7:24:7:26 | SSA variable foo | examples.py:8:20:8:22 | ControlFlowNode for foo | +| examples.py:7:24:7:26 | SSA variable foo | examples.py:8:20:8:22 | ControlFlowNode for foo | +| examples.py:7:24:7:26 | SSA variable foo | examples.py:8:20:8:22 | ControlFlowNode for foo | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:14:20:14:30 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:14:20:14:30 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:25:9:25:19 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:25:9:25:19 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:41:7:41:19 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:41:7:41:19 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:46:9:46:16 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:46:9:46:16 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:14:20:14:30 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:25:9:25:19 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:8:20:8:22 | ControlFlowNode for foo | examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | +| examples.py:8:20:8:22 | ControlFlowNode for foo | examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | +| examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:11:7:11:15 | GSSA Variable NestedObj | +| examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:11:7:11:15 | GSSA Variable NestedObj | +| examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:33:5:33:13 | ControlFlowNode for NestedObj | +| examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:33:5:33:13 | ControlFlowNode for NestedObj | +| examples.py:11:7:11:15 | GSSA Variable NestedObj | examples.py:33:5:33:13 | ControlFlowNode for NestedObj | +| examples.py:11:7:11:15 | GSSA Variable NestedObj | examples.py:33:5:33:13 | ControlFlowNode for NestedObj | +| examples.py:13:5:13:23 | ControlFlowNode for FunctionExpr | examples.py:13:9:13:16 | SSA variable __init__ | +| examples.py:13:5:13:23 | ControlFlowNode for FunctionExpr | examples.py:13:9:13:16 | SSA variable __init__ | +| examples.py:13:5:13:23 | GSSA Variable MyObj | examples.py:14:20:14:24 | ControlFlowNode for MyObj | +| examples.py:13:5:13:23 | GSSA Variable MyObj | examples.py:14:20:14:24 | ControlFlowNode for MyObj | +| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:12 | ControlFlowNode for self | +| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:12 | ControlFlowNode for self | +| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:12 | ControlFlowNode for self | +| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:12 | ControlFlowNode for self | +| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:16 | SSA variable self | +| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:16 | SSA variable self | +| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:16 | SSA variable self | +| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:16 | SSA variable self | +| examples.py:14:9:14:12 | [post store] ControlFlowNode for self | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | +| examples.py:14:9:14:12 | [post store] ControlFlowNode for self | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | +| examples.py:14:9:14:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | +| examples.py:14:9:14:12 | [post store] ControlFlowNode for self [Attribute obj] | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | +| examples.py:14:20:14:30 | ControlFlowNode for MyObj() | examples.py:14:9:14:12 | [post store] ControlFlowNode for self [Attribute obj] | +| examples.py:14:20:14:30 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:14:9:14:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | +| examples.py:14:20:14:30 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:14:20:14:30 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:14:26:14:29 | ControlFlowNode for Str | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:14:26:14:29 | ControlFlowNode for Str | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:14:26:14:29 | ControlFlowNode for Str | examples.py:14:20:14:30 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:16:5:16:21 | ControlFlowNode for FunctionExpr | examples.py:16:9:16:14 | SSA variable getObj | +| examples.py:16:5:16:21 | ControlFlowNode for FunctionExpr | examples.py:16:9:16:14 | SSA variable getObj | +| examples.py:16:16:16:19 | SSA variable self | examples.py:17:16:17:19 | ControlFlowNode for self | +| examples.py:16:16:16:19 | SSA variable self | examples.py:17:16:17:19 | ControlFlowNode for self | +| examples.py:21:1:21:19 | ControlFlowNode for FunctionExpr | examples.py:21:5:21:10 | GSSA Variable setFoo | +| examples.py:21:1:21:19 | ControlFlowNode for FunctionExpr | examples.py:21:5:21:10 | GSSA Variable setFoo | +| examples.py:21:1:21:19 | ControlFlowNode for FunctionExpr | examples.py:27:1:27:6 | ControlFlowNode for setFoo | +| examples.py:21:1:21:19 | ControlFlowNode for FunctionExpr | examples.py:27:1:27:6 | ControlFlowNode for setFoo | +| examples.py:21:1:21:19 | GSSA Variable SINK_F | examples.py:22:5:22:10 | ControlFlowNode for SINK_F | +| examples.py:21:1:21:19 | GSSA Variable SINK_F | examples.py:22:5:22:10 | ControlFlowNode for SINK_F | +| examples.py:21:5:21:10 | GSSA Variable setFoo | examples.py:27:1:27:6 | ControlFlowNode for setFoo | +| examples.py:21:5:21:10 | GSSA Variable setFoo | examples.py:27:1:27:6 | ControlFlowNode for setFoo | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:22:12:22:14 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:22:12:22:14 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:22:12:22:14 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:22:12:22:14 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:11 | SSA variable obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:11 | SSA variable obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:11 | SSA variable obj | +| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:11 | SSA variable obj | +| examples.py:21:12:21:14 | SSA variable obj [Attribute foo] | examples.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | +| examples.py:21:17:21:17 | SSA variable x | examples.py:23:15:23:15 | ControlFlowNode for x | +| examples.py:21:17:21:17 | SSA variable x | examples.py:23:15:23:15 | ControlFlowNode for x | +| examples.py:21:17:21:17 | SSA variable x | examples.py:23:15:23:15 | ControlFlowNode for x | +| examples.py:21:17:21:17 | SSA variable x | examples.py:23:15:23:15 | ControlFlowNode for x | +| examples.py:22:12:22:14 | ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:22:12:22:14 | ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:22:12:22:14 | ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:22:12:22:14 | ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | examples.py:22:12:22:18 | ControlFlowNode for Attribute | +| examples.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | examples.py:22:12:22:18 | ControlFlowNode for Attribute | +| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | +| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | +| examples.py:23:5:23:7 | [post store] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | +| examples.py:23:5:23:7 | [post store] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | +| examples.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| examples.py:23:15:23:15 | ControlFlowNode for x | examples.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | +| examples.py:23:15:23:15 | ControlFlowNode for x | examples.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | +| examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:27:1:27:21 | GSSA Variable myobj | +| examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:27:1:27:21 | GSSA Variable myobj | +| examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj | +| examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj | +| examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:25:1:25:5 | GSSA Variable myobj [Attribute foo] | examples.py:27:8:27:12 | ControlFlowNode for myobj [Attribute foo] | +| examples.py:25:1:25:5 | GSSA Variable myobj [Attribute foo] | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | +| examples.py:25:9:25:13 | ControlFlowNode for MyObj | examples.py:41:7:41:11 | ControlFlowNode for MyObj | +| examples.py:25:9:25:13 | ControlFlowNode for MyObj | examples.py:41:7:41:11 | ControlFlowNode for MyObj | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:25:1:25:5 | GSSA Variable myobj | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:25:1:25:5 | GSSA Variable myobj | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:27:1:27:21 | GSSA Variable myobj | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:27:1:27:21 | GSSA Variable myobj | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:27:8:27:12 | ControlFlowNode for myobj | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:27:8:27:12 | ControlFlowNode for myobj | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:25:1:25:5 | GSSA Variable myobj [Attribute foo] | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:27:8:27:12 | ControlFlowNode for myobj [Attribute foo] | +| examples.py:25:9:25:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | +| examples.py:25:9:25:19 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:25:9:25:19 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:25:15:25:18 | ControlFlowNode for Str | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:25:15:25:18 | ControlFlowNode for Str | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:25:15:25:18 | ControlFlowNode for Str | examples.py:25:9:25:19 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:21:12:21:14 | SSA variable obj | +| examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:21:12:21:14 | SSA variable obj | +| examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:27:8:27:12 | ControlFlowNode for myobj [Attribute foo] | examples.py:21:12:21:14 | SSA variable obj [Attribute foo] | +| examples.py:27:8:27:12 | ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | +| examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | +| examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:21:17:21:17 | SSA variable x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:21:17:21:17 | SSA variable x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:38:1:38:4 | ControlFlowNode for SINK | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:38:1:38:4 | ControlFlowNode for SINK | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:14 | ControlFlowNode for Attribute | +| examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:14 | ControlFlowNode for Attribute | +| examples.py:31:1:31:1 | GSSA Variable x | examples.py:35:13:35:13 | ControlFlowNode for x | +| examples.py:31:1:31:1 | GSSA Variable x | examples.py:35:13:35:13 | ControlFlowNode for x | +| examples.py:31:1:31:1 | GSSA Variable x | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:31:1:31:1 | GSSA Variable x | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:35:1:35:1 | ControlFlowNode for a | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:35:1:35:1 | ControlFlowNode for a | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:36:1:36:10 | GSSA Variable a | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:36:1:36:10 | GSSA Variable a | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj, Attribute foo] | examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj, Attribute foo] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj, Attribute foo] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj] | examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | +| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj] | +| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:33:1:33:1 | GSSA Variable a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:33:1:33:1 | GSSA Variable a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:35:1:35:1 | ControlFlowNode for a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:35:1:35:1 | ControlFlowNode for a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:36:1:36:10 | GSSA Variable a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:36:1:36:10 | GSSA Variable a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:33:1:33:1 | GSSA Variable a [Attribute obj, Attribute foo] | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:33:1:33:1 | GSSA Variable a [Attribute obj] | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj] | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:33:5:33:15 | [pre objCreate] ControlFlowNode for NestedObj() | examples.py:13:18:13:21 | SSA variable self | +| examples.py:33:5:33:15 | [pre objCreate] ControlFlowNode for NestedObj() | examples.py:13:18:13:21 | SSA variable self | +| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | examples.py:35:1:35:5 | ControlFlowNode for Attribute | +| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | examples.py:35:1:35:5 | ControlFlowNode for Attribute | +| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj] | +| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj] | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute | examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj] | +| examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute [Attribute foo] | examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:35:13:35:13 | ControlFlowNode for x | examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute [Attribute foo] | +| examples.py:35:13:35:13 | ControlFlowNode for x | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:35:13:35:13 | ControlFlowNode for x | examples.py:36:18:36:18 | ControlFlowNode for x | +| examples.py:36:1:36:1 | ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:36:1:36:1 | ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:38:1:38:4 | ControlFlowNode for SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | +| examples.py:38:1:38:4 | ControlFlowNode for SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | +| examples.py:38:1:38:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:38:1:38:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:38:6:38:10 | ControlFlowNode for Attribute [Attribute foo] | +| examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | examples.py:38:6:38:10 | ControlFlowNode for Attribute | +| examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | examples.py:38:6:38:10 | ControlFlowNode for Attribute | +| examples.py:38:6:38:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:38:6:38:14 | ControlFlowNode for Attribute | +| examples.py:38:6:38:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:38:6:38:14 | ControlFlowNode for Attribute | +| examples.py:41:1:41:3 | GSSA Variable obj | examples.py:42:6:42:8 | ControlFlowNode for obj | +| examples.py:41:1:41:3 | GSSA Variable obj | examples.py:42:6:42:8 | ControlFlowNode for obj | +| examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] | examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | +| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:41:1:41:3 | GSSA Variable obj | +| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:41:1:41:3 | GSSA Variable obj | +| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:42:6:42:8 | ControlFlowNode for obj | +| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:42:6:42:8 | ControlFlowNode for obj | +| examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] | +| examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | +| examples.py:41:7:41:19 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | +| examples.py:41:7:41:19 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | +| examples.py:41:7:41:19 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:41:7:41:19 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:42:1:42:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:42:1:42:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | examples.py:42:6:42:12 | ControlFlowNode for Attribute | +| examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | examples.py:42:6:42:12 | ControlFlowNode for Attribute | +| examples.py:45:1:45:30 | ControlFlowNode for FunctionExpr | examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | +| examples.py:45:1:45:30 | ControlFlowNode for FunctionExpr | examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | +| examples.py:45:1:45:30 | ControlFlowNode for FunctionExpr | examples.py:50:6:50:27 | ControlFlowNode for fields_with_local_flow | +| examples.py:45:1:45:30 | ControlFlowNode for FunctionExpr | examples.py:50:6:50:27 | ControlFlowNode for fields_with_local_flow | +| examples.py:45:1:45:30 | GSSA Variable MyObj | examples.py:46:9:46:13 | ControlFlowNode for MyObj | +| examples.py:45:1:45:30 | GSSA Variable MyObj | examples.py:46:9:46:13 | ControlFlowNode for MyObj | +| examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | examples.py:50:6:50:27 | ControlFlowNode for fields_with_local_flow | +| examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | examples.py:50:6:50:27 | ControlFlowNode for fields_with_local_flow | +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | +| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | +| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj | +| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj | +| examples.py:46:3:46:5 | SSA variable obj [Attribute foo] | examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | +| examples.py:46:3:46:5 | SSA variable obj [Attribute foo] | examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:47:7:47:9 | ControlFlowNode for obj | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:47:7:47:9 | ControlFlowNode for obj | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:46:3:46:5 | SSA variable obj [Attribute foo] | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:46:3:46:5 | SSA variable obj [Attribute foo] | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | +| examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | +| examples.py:46:9:46:16 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:46:9:46:16 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | +| examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | +| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | +| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | +| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | +| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | +| examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | examples.py:47:7:47:13 | ControlFlowNode for Attribute | +| examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | examples.py:47:7:47:13 | ControlFlowNode for Attribute | +| examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | examples.py:47:7:47:13 | ControlFlowNode for Attribute | +| examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | examples.py:47:7:47:13 | ControlFlowNode for Attribute | +| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | +| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | +| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | +| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | +| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:48:10:48:10 | ControlFlowNode for a | +| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:48:10:48:10 | ControlFlowNode for a | +| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:48:10:48:10 | ControlFlowNode for a | +| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:48:10:48:10 | ControlFlowNode for a | +| examples.py:48:10:48:10 | ControlFlowNode for a | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | +| examples.py:48:10:48:10 | ControlFlowNode for a | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | +| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:45:28:45:28 | SSA variable x | +| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:45:28:45:28 | SSA variable x | +| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | +| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | +| test.py:0:0:0:0 | GSSA Variable SINK | test.py:1:1:1:66 | GSSA Variable SINK | +| test.py:0:0:0:0 | GSSA Variable SINK | test.py:1:1:1:66 | GSSA Variable SINK | +| test.py:0:0:0:0 | GSSA Variable SINK_F | test.py:1:1:1:66 | GSSA Variable SINK_F | +| test.py:0:0:0:0 | GSSA Variable SINK_F | test.py:1:1:1:66 | GSSA Variable SINK_F | +| test.py:0:0:0:0 | GSSA Variable SOURCE | test.py:1:1:1:66 | GSSA Variable SOURCE | +| test.py:0:0:0:0 | GSSA Variable SOURCE | test.py:1:1:1:66 | GSSA Variable SOURCE | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:1:1:1:66 | GSSA Variable __name__ | +| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:1:1:1:66 | GSSA Variable __name__ | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:1:1:1:66 | GSSA Variable __package__ | +| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:1:1:1:66 | GSSA Variable __package__ | +| test.py:0:0:0:0 | GSSA Variable object | test.py:1:1:1:66 | GSSA Variable object | +| test.py:0:0:0:0 | GSSA Variable object | test.py:1:1:1:66 | GSSA Variable object | +| test.py:0:0:0:0 | GSSA Variable object | test.py:6:13:6:18 | ControlFlowNode for object | +| test.py:0:0:0:0 | GSSA Variable object | test.py:6:13:6:18 | ControlFlowNode for object | +| test.py:0:0:0:0 | GSSA Variable object | test.py:12:17:12:22 | ControlFlowNode for object | +| test.py:0:0:0:0 | GSSA Variable object | test.py:12:17:12:22 | ControlFlowNode for object | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:15:20:15:24 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:15:20:15:24 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:27:13:27:17 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:27:13:27:17 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:45:11:45:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:45:11:45:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:50:11:50:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:50:11:50:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:36:9:36:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:36:9:36:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:30:5:30:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:30:5:30:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:41:5:41:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:41:5:41:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:46:5:46:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:46:5:46:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:56:5:56:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:56:5:56:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:22:5:22:10 | ControlFlowNode for SINK_F | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:22:5:22:10 | ControlFlowNode for SINK_F | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:29:19:29:24 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:29:19:29:24 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:34:9:34:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:34:9:34:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:45:17:45:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:45:17:45:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:56:33:56:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:56:33:56:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:29:5:29:10 | ControlFlowNode for setFoo | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:29:5:29:10 | ControlFlowNode for setFoo | +| test.py:0:0:0:0 | SSA variable $ | test.py:1:1:1:66 | SSA variable $ | +| test.py:0:0:0:0 | SSA variable $ | test.py:1:1:1:66 | SSA variable $ | +| test.py:0:0:0:0 | SSA variable * | test.py:1:1:1:66 | SSA variable * | +| test.py:0:0:0:0 | SSA variable * | test.py:1:1:1:66 | SSA variable * | | test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj | | test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj | | test.py:6:7:6:11 | GSSA Variable MyObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.ql b/python/ql/test/experimental/dataflow/fieldflow/globalStep.ql index 141e0587156..64d565339b7 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.ql +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.ql @@ -3,7 +3,5 @@ import experimental.dataflow.basic.allFlowsConfig from DataFlow::PathNode fromNode, DataFlow::PathNode toNode where toNode = fromNode.getASuccessor() and - fromNode.getNode().getLocation().getFile().getBaseName() = "test.py" and - fromNode.getNode().getLocation().getStartLine() > 1 and - not toNode.getNode().(DataFlow::CfgNode).getNode().isNormalExit() + fromNode.getNode().getLocation().getFile().getParent().getBaseName() = "fieldflow" select fromNode, toNode From 4621e6d8c043b90154affc42642e44bbcacb4f00 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 25 Sep 2020 13:37:39 +0200 Subject: [PATCH 124/185] Python: fix QL format --- .../ql/test/experimental/dataflow/coverage/argumentRouting1.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql index 32df6d3788e..90878dc9052 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql @@ -12,7 +12,7 @@ class ArgumentRoutingConfig extends DataFlow::Configuration { exists(AssignmentDefinition def, DataFlowPrivate::DataFlowCall call | def.getVariable() = node.(DataFlow::EssaNode).getVar() and def.getValue() = call.getNode() and - call.getNode().(CallNode).getFunction().(NameNode).getId().matches("With\\_%") + call.getNode().(CallNode).getFunction().(NameNode).getId().matches("With\\_%") ) and node.(DataFlow::EssaNode).getVar().getName().matches("with\\_%") } From a22ddb145bd0124aea81ff0d3afc40005c1c73c0 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 25 Sep 2020 13:53:22 +0200 Subject: [PATCH 125/185] model calls to needle --- .../javascript/frameworks/ClientRequests.qll | 30 +++++++++++++++++++ .../ClientRequests/ClientRequests.expected | 5 ++++ .../frameworks/ClientRequests/tst.js | 6 ++++ 3 files changed, 41 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index 1c0ac8d6a16..11a60eabb3b 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -327,6 +327,36 @@ module ClientRequest { } } + /** + * Classes for modelling the url request library `needle`. + */ + private module Needle { + /** + * A model of a URL request made using `require("needle")(...)`. + */ + class PromisedNeedleRequest extends ClientRequest::Range { + DataFlow::Node url; + + PromisedNeedleRequest() { this = DataFlow::moduleImport("needle").getACall() } + + override DataFlow::Node getUrl() { result = getArgument(1) } + + override DataFlow::Node getHost() { none() } + + override DataFlow::Node getADataNode() { + result = getOptionArgument([2, 3], "headers") + or + result = getArgument(2) + } + + override DataFlow::Node getAResponseDataNode(string responseType, boolean promise) { + responseType = "fetch.response" and + promise = true and + result = this + } + } + } + /** * A model of a URL request made using the `got` library. */ diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected index 1c91826ac8d..3209b0e8be0 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected @@ -67,6 +67,7 @@ test_ClientRequest | tst.js:202:5:208:7 | $.ajax( ... }}) | | tst.js:210:2:210:21 | $.get("example.php") | | tst.js:219:5:219:41 | data.so ... Host"}) | +| tst.js:229:5:229:67 | needle( ... ptions) | test_getADataNode | tst.js:53:5:53:23 | axios({data: data}) | tst.js:53:18:53:21 | data | | tst.js:57:5:57:39 | axios.p ... data2}) | tst.js:57:19:57:23 | data1 | @@ -97,6 +98,8 @@ test_getADataNode | tst.js:183:2:183:60 | $.post( ... ) { }) | tst.js:183:28:183:37 | "PostData" | | tst.js:187:2:193:3 | $.ajax( ... on"\\n\\t}) | tst.js:190:11:190:20 | "AjaxData" | | tst.js:219:5:219:41 | data.so ... Host"}) | tst.js:223:23:223:30 | "foobar" | +| tst.js:229:5:229:67 | needle( ... ptions) | tst.js:228:32:228:70 | { 'X-Cu ... tuna' } | +| tst.js:229:5:229:67 | needle( ... ptions) | tst.js:229:50:229:57 | "MyData" | test_getHost | tst.js:87:5:87:39 | http.ge ... host}) | tst.js:87:34:87:37 | host | | tst.js:89:5:89:23 | axios({host: host}) | tst.js:89:18:89:21 | host | @@ -177,6 +180,7 @@ test_getUrl | tst.js:202:5:208:7 | $.ajax( ... }}) | tst.js:203:10:203:22 | "example.php" | | tst.js:210:2:210:21 | $.get("example.php") | tst.js:210:8:210:20 | "example.php" | | tst.js:219:5:219:41 | data.so ... Host"}) | tst.js:219:25:219:40 | {host: "myHost"} | +| tst.js:229:5:229:67 | needle( ... ptions) | tst.js:229:20:229:47 | "http:/ ... oo/bar" | test_getAResponseDataNode | tst.js:19:5:19:23 | requestPromise(url) | tst.js:19:5:19:23 | requestPromise(url) | text | true | | tst.js:21:5:21:23 | superagent.get(url) | tst.js:21:5:21:23 | superagent.get(url) | stream | true | @@ -238,3 +242,4 @@ test_getAResponseDataNode | tst.js:202:5:208:7 | $.ajax( ... }}) | tst.js:207:21:207:36 | err.responseText | json | false | | tst.js:210:2:210:21 | $.get("example.php") | tst.js:210:55:210:70 | xhr.responseText | | false | | tst.js:219:5:219:41 | data.so ... Host"}) | tst.js:221:29:221:32 | data | text | false | +| tst.js:229:5:229:67 | needle( ... ptions) | tst.js:229:5:229:67 | needle( ... ptions) | fetch.response | true | diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js index e828423f91c..8dd1c2d60f0 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js @@ -221,4 +221,10 @@ const net = require("net"); data.socket.on("data", (data) => {}); data.socket.write("foobar"); +})(); + +const needle = require("needle"); +(function () { + const options = { headers: { 'X-Custom-Header': 'Bumbaway atuna' } }; + needle("POST", "http://example.org/foo/bar", "MyData", options).then(function(resp) { console.log(resp.body) }); })(); \ No newline at end of file From 6b9aea82ca963f065e6ade87bada4693ec634944 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 25 Sep 2020 14:13:31 +0200 Subject: [PATCH 126/185] model method calls in the needle library --- .../javascript/frameworks/ClientRequests.qll | 49 +++++++++++++++++++ .../ClientRequests/ClientRequests.expected | 10 ++++ .../frameworks/ClientRequests/tst.js | 8 +++ 3 files changed, 67 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index 11a60eabb3b..fa9fb550330 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -355,6 +355,55 @@ module ClientRequest { result = this } } + + /** + * A model of a URL request made using `require("needle")[method](...)`. + * E.g. `needle.get("http://example.org", (err, resp, body) => {})`. + * + * As opposed to the calls modeled in `PromisedNeedleRequest` these calls do not return promises. + * Instead they take an optional callback as their last argument. + */ + class NeedleMethodRequest extends ClientRequest::Range { + boolean hasData; + + NeedleMethodRequest() { + exists(string method | + method = ["get", "head"] and hasData = false + or + method = ["post", "put", "patch", "delete"] and hasData = true + or + method = "request" and hasData = [true, false] + | + this = DataFlow::moduleMember("needle", method).getACall() + ) + } + + override DataFlow::Node getUrl() { result = getArgument(0) } + + override DataFlow::Node getHost() { none() } + + override DataFlow::Node getADataNode() { + hasData = true and + ( + result = getArgument(1) + or + result = getOptionArgument(2, "headers") + ) + or + hasData = false and + result = getOptionArgument(1, "headers") + } + + override DataFlow::Node getAResponseDataNode(string responseType, boolean promise) { + promise = false and + result = this.getCallback(this.getNumArgument() - 1).getParameter(1) and + responseType = "fetch.response" + or + promise = false and + result = this.getCallback(this.getNumArgument() - 1).getParameter(2) and + responseType = "json" + } + } } /** diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected index 3209b0e8be0..5ba426c968f 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected @@ -68,6 +68,8 @@ test_ClientRequest | tst.js:210:2:210:21 | $.get("example.php") | | tst.js:219:5:219:41 | data.so ... Host"}) | | tst.js:229:5:229:67 | needle( ... ptions) | +| tst.js:231:5:233:6 | needle. ... \\n }) | +| tst.js:235:5:237:6 | needle. ... \\n }) | test_getADataNode | tst.js:53:5:53:23 | axios({data: data}) | tst.js:53:18:53:21 | data | | tst.js:57:5:57:39 | axios.p ... data2}) | tst.js:57:19:57:23 | data1 | @@ -100,6 +102,8 @@ test_getADataNode | tst.js:219:5:219:41 | data.so ... Host"}) | tst.js:223:23:223:30 | "foobar" | | tst.js:229:5:229:67 | needle( ... ptions) | tst.js:228:32:228:70 | { 'X-Cu ... tuna' } | | tst.js:229:5:229:67 | needle( ... ptions) | tst.js:229:50:229:57 | "MyData" | +| tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:228:32:228:70 | { 'X-Cu ... tuna' } | +| tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:44:235:49 | "data" | test_getHost | tst.js:87:5:87:39 | http.ge ... host}) | tst.js:87:34:87:37 | host | | tst.js:89:5:89:23 | axios({host: host}) | tst.js:89:18:89:21 | host | @@ -181,6 +185,8 @@ test_getUrl | tst.js:210:2:210:21 | $.get("example.php") | tst.js:210:8:210:20 | "example.php" | | tst.js:219:5:219:41 | data.so ... Host"}) | tst.js:219:25:219:40 | {host: "myHost"} | | tst.js:229:5:229:67 | needle( ... ptions) | tst.js:229:20:229:47 | "http:/ ... oo/bar" | +| tst.js:231:5:233:6 | needle. ... \\n }) | tst.js:231:16:231:35 | "http://example.org" | +| tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:17:235:41 | "http:/ ... g/post" | test_getAResponseDataNode | tst.js:19:5:19:23 | requestPromise(url) | tst.js:19:5:19:23 | requestPromise(url) | text | true | | tst.js:21:5:21:23 | superagent.get(url) | tst.js:21:5:21:23 | superagent.get(url) | stream | true | @@ -243,3 +249,7 @@ test_getAResponseDataNode | tst.js:210:2:210:21 | $.get("example.php") | tst.js:210:55:210:70 | xhr.responseText | | false | | tst.js:219:5:219:41 | data.so ... Host"}) | tst.js:221:29:221:32 | data | text | false | | tst.js:229:5:229:67 | needle( ... ptions) | tst.js:229:5:229:67 | needle( ... ptions) | fetch.response | true | +| tst.js:231:5:233:6 | needle. ... \\n }) | tst.js:231:44:231:47 | resp | fetch.response | false | +| tst.js:231:5:233:6 | needle. ... \\n }) | tst.js:231:50:231:53 | body | json | false | +| tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:67:235:70 | resp | fetch.response | false | +| tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:73:235:76 | body | json | false | diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js index 8dd1c2d60f0..a24a0dca791 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js @@ -227,4 +227,12 @@ const needle = require("needle"); (function () { const options = { headers: { 'X-Custom-Header': 'Bumbaway atuna' } }; needle("POST", "http://example.org/foo/bar", "MyData", options).then(function(resp) { console.log(resp.body) }); + + needle.get("http://example.org", (err, resp, body) => { + + }); + + needle.post("http://example.org/post", "data", options, (err, resp, body) => { + + }); })(); \ No newline at end of file From 2acfd4cdb1775ab311e36d4147c229bb3c7cf3ce Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 25 Sep 2020 18:28:31 +0200 Subject: [PATCH 127/185] Python: Show we're able to handle example with __init__.py files --- .../library-tests/CallGraph-implicit-init/PointsTo.expected | 2 +- .../library-tests/CallGraph-implicit-init/Relative.expected | 1 + .../CallGraph-implicit-init/TypeTracker.expected | 3 ++- .../library-tests/CallGraph-implicit-init/example.py | 4 ++++ .../CallGraph-implicit-init/foo_explicit/__init__.py | 0 .../CallGraph-implicit-init/foo_explicit/bar/__init__.py | 0 .../CallGraph-implicit-init/foo_explicit/bar/a.py | 4 ++++ 7 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/__init__.py create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/__init__.py create mode 100644 python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected index d4362c9edf5..349d99b46d8 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected @@ -2,5 +2,5 @@ debug_missingAnnotationForCallable debug_nonUniqueAnnotationForCallable debug_missingAnnotationForCall expectedCallEdgeNotFound -| example.py:18:1:18:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | +| example.py:19:1:19:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected index c25b2538ffa..38aab0b5888 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected @@ -2,4 +2,5 @@ debug_missingAnnotationForCallable debug_nonUniqueAnnotationForCallable debug_missingAnnotationForCall pointsTo_found_typeTracker_notFound +| example.py:22:1:22:16 | explicit_afunc() | foo_explicit/bar/a.py:2:1:2:21 | Function explicit_afunc | pointsTo_notFound_typeTracker_found diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected index d4362c9edf5..5283683a6a5 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected @@ -2,5 +2,6 @@ debug_missingAnnotationForCallable debug_nonUniqueAnnotationForCallable debug_missingAnnotationForCall expectedCallEdgeNotFound -| example.py:18:1:18:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | +| example.py:19:1:19:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | +| example.py:22:1:22:16 | explicit_afunc() | foo_explicit/bar/a.py:2:1:2:21 | Function explicit_afunc | unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py index 67bbf5c707c..35d56620af7 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py @@ -13,6 +13,10 @@ Since PEP 420 was accepted in Python 3, this test is Python 3 only. """ from foo.bar.a import afunc +from foo_explicit.bar.a import explicit_afunc # calls:afunc afunc() + +# calls:explicit_afunc +explicit_afunc() diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/__init__.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/__init__.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py new file mode 100644 index 00000000000..616c3fddca1 --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py @@ -0,0 +1,4 @@ +# name:explicit_afunc +def explicit_afunc(): + print("explicit_afunc called") + return 1 From 9240256a9f0910b6aac8da05c087d6208f353f7f Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 25 Sep 2020 11:55:39 -0700 Subject: [PATCH 128/185] C++: fix QLDoc --- cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index 15c15595c4f..40a671429f3 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -244,7 +244,7 @@ private module PartialDefinitions { } /** - * A partial definition that's a definition by reference. + * A partial definition that's a definition via an output iterator. */ class DefinitionByIterator extends IteratorPartialDefinition { DefinitionByIterator() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) } From 713bdae77adc8f91fb7f43ac69823d93d4088b2c Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 25 Sep 2020 13:54:58 -0700 Subject: [PATCH 129/185] C++: sync identical files --- .../cpp/ir/implementation/aliased_ssa/Operand.qll | 3 ++- .../code/cpp/ir/implementation/raw/Operand.qll | 3 ++- .../ir/implementation/raw/Operand.qll | 3 ++- .../ir/implementation/unaliased_ssa/Operand.qll | 3 ++- .../unaliased_ssa/internal/SimpleSSA.qll | 6 ++++++ .../ql/src/experimental/ir/internal/Overlap.qll | 15 +++++++++++++++ 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index 14f62da51cd..e476aec60af 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -79,7 +79,8 @@ private PhiOperandBase phiOperand( } /** - * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. + * An operand of an `Instruction`. The operand represents a use of the result of one instruction + * (the defining instruction) in another instruction (the use instruction) */ class Operand extends TOperand { /** Gets a textual representation of this element. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index 14f62da51cd..e476aec60af 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -79,7 +79,8 @@ private PhiOperandBase phiOperand( } /** - * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. + * An operand of an `Instruction`. The operand represents a use of the result of one instruction + * (the defining instruction) in another instruction (the use instruction) */ class Operand extends TOperand { /** Gets a textual representation of this element. */ diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll index 14f62da51cd..e476aec60af 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll @@ -79,7 +79,8 @@ private PhiOperandBase phiOperand( } /** - * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. + * An operand of an `Instruction`. The operand represents a use of the result of one instruction + * (the defining instruction) in another instruction (the use instruction) */ class Operand extends TOperand { /** Gets a textual representation of this element. */ diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll index 14f62da51cd..e476aec60af 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll @@ -79,7 +79,8 @@ private PhiOperandBase phiOperand( } /** - * A source operand of an `Instruction`. The operand represents a value consumed by the instruction. + * An operand of an `Instruction`. The operand represents a use of the result of one instruction + * (the defining instruction) in another instruction (the use instruction) */ class Operand extends TOperand { /** Gets a textual representation of this element. */ diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index 3de8f5259f8..a7b9160bdc7 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -59,6 +59,12 @@ class MemoryLocation extends TMemoryLocation { final string getUniqueId() { result = var.getUniqueId() } } +/** + * Represents a set of `MemoryLocation`s that cannot overlap with + * `MemoryLocation`s outside of the set. The `VirtualVariable` will be + * represented by a `MemoryLocation` that totally overlaps all other + * `MemoryLocations` in the set. + */ class VirtualVariable extends MemoryLocation { } /** A virtual variable that groups all escaped memory within a function. */ diff --git a/csharp/ql/src/experimental/ir/internal/Overlap.qll b/csharp/ql/src/experimental/ir/internal/Overlap.qll index 8ce0549b2b4..f9a0c574f8c 100644 --- a/csharp/ql/src/experimental/ir/internal/Overlap.qll +++ b/csharp/ql/src/experimental/ir/internal/Overlap.qll @@ -3,18 +3,33 @@ private newtype TOverlap = TMustTotallyOverlap() or TMustExactlyOverlap() +/** + * Represents a possible overlap between two memory ranges. + */ abstract class Overlap extends TOverlap { abstract string toString(); } +/** + * Represents a partial overlap between two memory ranges, which may or may not + * actually occur in practice. + */ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { final override string toString() { result = "MayPartiallyOverlap" } } +/** + * Represents an overlap in which the first memory range is known to include all + * bits of the second memory range, but may be larger or have a different type. + */ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { final override string toString() { result = "MustTotallyOverlap" } } +/** + * Represents an overlap between two memory ranges that have the same extent and + * the same type. + */ class MustExactlyOverlap extends Overlap, TMustExactlyOverlap { final override string toString() { result = "MustExactlyOverlap" } } From 27dc49ff7a6f0e1c0724367b628291b97f6b4407 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 25 Sep 2020 17:49:01 -0700 Subject: [PATCH 130/185] C++: Fix performance issue in PartialDefinition --- cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index 40a671429f3..50395dbaafc 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -112,8 +112,6 @@ private module PartialDefinitions { abstract class PartialDefinition extends Expr { ControlFlowNode node; - PartialDefinition() { not this instanceof Conversion } - abstract deprecated predicate partiallyDefines(Variable v); abstract deprecated predicate partiallyDefinesThis(ThisExpr e); @@ -161,6 +159,7 @@ private module PartialDefinitions { IteratorPartialDefinition() { exists(Expr convertedInner | + not this instanceof Conversion and valueToUpdate(convertedInner, this.getFullyConverted(), node) and innerDefinedExpr = convertedInner.getUnconverted() and ( @@ -212,6 +211,7 @@ private module PartialDefinitions { Expr innerDefinedExpr; VariablePartialDefinition() { + not this instanceof Conversion and exists(Expr convertedInner | valueToUpdate(convertedInner, this.getFullyConverted(), node) and innerDefinedExpr = convertedInner.getUnconverted() From 664342dd0f2f273d702c14a2c05d715700df5b85 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sat, 26 Sep 2020 21:31:06 +0200 Subject: [PATCH 131/185] change `SimpleParameter` to `Parameter` in the express model to support destructuring parameters --- .../ql/src/semmle/javascript/frameworks/Express.qll | 12 ++++++------ .../ServerSideUrlRedirect.expected | 10 ++++++++++ .../CWE-601/ServerSideUrlRedirect/express.js | 7 +++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index 5a5af64aefc..af6675d188e 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -195,7 +195,7 @@ module Express { PassportRouteHandler() { this = any(PassportRouteSetup setup).getARouteHandler() } - override SimpleParameter getRouteHandlerParameter(string kind) { + override Parameter getRouteHandlerParameter(string kind) { kind = "request" and result = astNode.getParameter(0) } @@ -329,17 +329,17 @@ module Express { * * `kind` is one of: "error", "request", "response", "next", or "parameter". */ - abstract SimpleParameter getRouteHandlerParameter(string kind); + abstract Parameter getRouteHandlerParameter(string kind); /** * Gets the parameter of the route handler that contains the request object. */ - SimpleParameter getRequestParameter() { result = getRouteHandlerParameter("request") } + Parameter getRequestParameter() { result = getRouteHandlerParameter("request") } /** * Gets the parameter of the route handler that contains the response object. */ - SimpleParameter getResponseParameter() { result = getRouteHandlerParameter("response") } + Parameter getResponseParameter() { result = getRouteHandlerParameter("response") } /** * Gets a request body access of this handler. @@ -357,7 +357,7 @@ module Express { StandardRouteHandler() { this = routeSetup.getARouteHandler() } - override SimpleParameter getRouteHandlerParameter(string kind) { + override Parameter getRouteHandlerParameter(string kind) { if routeSetup.isParameterHandler() then result = getRouteParameterHandlerParameter(astNode, kind) else result = getRouteHandlerParameter(astNode, kind) @@ -890,7 +890,7 @@ module Express { TrackedRouteHandlerCandidateWithSetup() { this = routeSetup.getARouteHandler() } - override SimpleParameter getRouteHandlerParameter(string kind) { + override Parameter getRouteHandlerParameter(string kind) { if routeSetup.isParameterHandler() then result = getRouteParameterHandlerParameter(astNode, kind) else result = getRouteHandlerParameter(astNode, kind) diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected index 299c434f329..5a8ed33b530 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected +++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected @@ -44,6 +44,12 @@ nodes | express.js:136:16:136:36 | 'u' + r ... ms.user | | express.js:136:22:136:36 | req.params.user | | express.js:136:22:136:36 | req.params.user | +| express.js:143:16:143:28 | req.query.foo | +| express.js:143:16:143:28 | req.query.foo | +| express.js:143:16:143:28 | req.query.foo | +| express.js:146:16:146:24 | query.foo | +| express.js:146:16:146:24 | query.foo | +| express.js:146:16:146:24 | query.foo | | koa.js:6:6:6:27 | url | | koa.js:6:12:6:27 | ctx.query.target | | koa.js:6:12:6:27 | ctx.query.target | @@ -128,6 +134,8 @@ edges | express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user | | express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user | | express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user | +| express.js:143:16:143:28 | req.query.foo | express.js:143:16:143:28 | req.query.foo | +| express.js:146:16:146:24 | query.foo | express.js:146:16:146:24 | query.foo | | koa.js:6:6:6:27 | url | koa.js:7:15:7:17 | url | | koa.js:6:6:6:27 | url | koa.js:7:15:7:17 | url | | koa.js:6:6:6:27 | url | koa.js:8:18:8:20 | url | @@ -181,6 +189,8 @@ edges | express.js:134:16:134:36 | '/' + r ... ms.user | express.js:134:22:134:36 | req.params.user | express.js:134:16:134:36 | '/' + r ... ms.user | Untrusted URL redirection due to $@. | express.js:134:22:134:36 | req.params.user | user-provided value | | express.js:135:16:135:37 | '//' + ... ms.user | express.js:135:23:135:37 | req.params.user | express.js:135:16:135:37 | '//' + ... ms.user | Untrusted URL redirection due to $@. | express.js:135:23:135:37 | req.params.user | user-provided value | | express.js:136:16:136:36 | 'u' + r ... ms.user | express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user | Untrusted URL redirection due to $@. | express.js:136:22:136:36 | req.params.user | user-provided value | +| express.js:143:16:143:28 | req.query.foo | express.js:143:16:143:28 | req.query.foo | express.js:143:16:143:28 | req.query.foo | Untrusted URL redirection due to $@. | express.js:143:16:143:28 | req.query.foo | user-provided value | +| express.js:146:16:146:24 | query.foo | express.js:146:16:146:24 | query.foo | express.js:146:16:146:24 | query.foo | Untrusted URL redirection due to $@. | express.js:146:16:146:24 | query.foo | user-provided value | | koa.js:7:15:7:17 | url | koa.js:6:12:6:27 | ctx.query.target | koa.js:7:15:7:17 | url | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value | | koa.js:8:15:8:26 | `${url}${x}` | koa.js:6:12:6:27 | ctx.query.target | koa.js:8:15:8:26 | `${url}${x}` | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value | | koa.js:14:16:14:18 | url | koa.js:6:12:6:27 | ctx.query.target | koa.js:14:16:14:18 | url | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js index 4c7c476a060..b319315b985 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js +++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js @@ -138,3 +138,10 @@ app.get('/redirect/:user', function(req, res) { res.redirect('/' + ('/u' + req.params.user)); // BAD - could go to //u.evil.com, but not flagged [INCONSISTENCY] res.redirect('/u' + req.params.user); // GOOD }); + +app.get("foo", (req, res) => { + res.redirect(req.query.foo); // NOT OK +}); +app.get("bar", ({query}, res) => { + res.redirect(query.foo); // NOT OK +}) \ No newline at end of file From a6b62a383835f9ec50dc44aac4709bf2ebded438 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 22 Sep 2020 08:42:06 +0200 Subject: [PATCH 132/185] C#: Add enum init value test --- csharp/ql/test/library-tests/enums/Enums11.expected | 0 csharp/ql/test/library-tests/enums/Enums11.ql | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 csharp/ql/test/library-tests/enums/Enums11.expected create mode 100644 csharp/ql/test/library-tests/enums/Enums11.ql diff --git a/csharp/ql/test/library-tests/enums/Enums11.expected b/csharp/ql/test/library-tests/enums/Enums11.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/csharp/ql/test/library-tests/enums/Enums11.ql b/csharp/ql/test/library-tests/enums/Enums11.ql new file mode 100644 index 00000000000..cf808bf4f29 --- /dev/null +++ b/csharp/ql/test/library-tests/enums/Enums11.ql @@ -0,0 +1,9 @@ +/** + * @name Test for enums + */ + +import csharp + +from FieldAccess fa +where fa.getParent().(Field).getDeclaringType() instanceof Enum +select fa, fa.getValue() From 77bb1b2cd9dbc80e174fad7307e3c79940ea0506 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Mon, 21 Sep 2020 17:43:38 +0200 Subject: [PATCH 133/185] C#: Extract constant value of enum member equal clauses --- .../Entities/Field.cs | 28 ++++++++++++++----- .../signanalysis/MissingSign.expected | 1 - .../signanalysis/SignAnalysis.expected | 3 ++ .../definitions/PrintAst.expected | 8 ++++-- .../test/library-tests/enums/Enums11.expected | 5 ++++ csharp/ql/test/library-tests/enums/Enums11.ql | 9 ++++-- .../library-tests/enums/PrintAst.expected | 27 ++++++++++++++---- csharp/ql/test/library-tests/enums/enums.cs | 2 +- 8 files changed, 64 insertions(+), 19 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs index 5f881338200..896614840ab 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs @@ -68,13 +68,12 @@ namespace Semmle.Extraction.CSharp.Entities Context.PopulateLater(() => { var loc = Context.Create(initializer.GetLocation()); - var simpleAssignExpr = new Expression(new ExpressionInfo(Context, Type, loc, ExprKind.SIMPLE_ASSIGN, this, child++, false, null)); - Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer.Initializer.Value, simpleAssignExpr, 0)); - var access = new Expression(new ExpressionInfo(Context, Type, Location, ExprKind.FIELD_ACCESS, simpleAssignExpr, 1, false, null)); - trapFile.expr_access(access, this); + + var fieldAccess = AddInitializerAssignment(trapFile, initializer.Initializer.Value, loc, null, ref child); + if (!symbol.IsStatic) { - This.CreateImplicit(Context, Entities.Type.Create(Context, symbol.ContainingType), Location, access, -1); + This.CreateImplicit(Context, Entities.Type.Create(Context, symbol.ContainingType), Location, fieldAccess, -1); } }); } @@ -85,8 +84,13 @@ namespace Semmle.Extraction.CSharp.Entities Where(n => n.EqualsValue != null)) { // Mark fields that have explicit initializers. - var expr = new Expression(new ExpressionInfo(Context, Type, Context.Create(initializer.EqualsValue.Value.FixedLocation()), Kinds.ExprKind.FIELD_ACCESS, this, child++, false, null)); - trapFile.expr_access(expr, this); + var constValue = symbol.HasConstantValue + ? Expression.ValueAsString(symbol.ConstantValue) + : null; + + var loc = Context.Create(initializer.GetLocation()); + + AddInitializerAssignment(trapFile, initializer.EqualsValue.Value, loc, constValue, ref child); } if (IsSourceDeclaration) @@ -96,6 +100,16 @@ namespace Semmle.Extraction.CSharp.Entities TypeMention.Create(Context, syntax.Type, this, Type); } + private Expression AddInitializerAssignment(TextWriter trapFile, ExpressionSyntax initializer, Extraction.Entities.Location loc, + string constValue, ref int child) + { + var simpleAssignExpr = new Expression(new ExpressionInfo(Context, Type, loc, ExprKind.SIMPLE_ASSIGN, this, child++, false, constValue)); + Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer, simpleAssignExpr, 0)); + var access = new Expression(new ExpressionInfo(Context, Type, Location, ExprKind.FIELD_ACCESS, simpleAssignExpr, 1, false, constValue)); + trapFile.expr_access(access, this); + return access; + } + readonly Lazy type; public AnnotatedType Type => type.Value; diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected index 2fd751f77e0..e69de29bb2d 100644 --- a/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected @@ -1 +0,0 @@ -| SignAnalysis.cs:428:23:428:24 | access to constant A | diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected index 31220df1a3c..ec33fec9374 100644 --- a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected @@ -211,6 +211,9 @@ | SignAnalysis.cs:414:13:414:13 | access to local variable i | strictlyPositive | | SignAnalysis.cs:415:31:415:31 | access to local variable i | strictlyPositive | | SignAnalysis.cs:424:31:424:31 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:428:19:428:19 | access to constant A | strictlyPositive | +| SignAnalysis.cs:428:19:428:24 | ... = ... | strictlyPositive | +| SignAnalysis.cs:428:23:428:24 | 12 | strictlyPositive | | SignAnalysis.cs:434:38:434:38 | access to local variable i | strictlyNegative | | SignAnalysis.cs:443:38:443:38 | access to local variable x | strictlyNegative | | SignAnalysis.cs:446:31:446:32 | 10 | strictlyPositive | diff --git a/csharp/ql/test/library-tests/definitions/PrintAst.expected b/csharp/ql/test/library-tests/definitions/PrintAst.expected index 18c13fe61bd..5543a3363d5 100644 --- a/csharp/ql/test/library-tests/definitions/PrintAst.expected +++ b/csharp/ql/test/library-tests/definitions/PrintAst.expected @@ -8,9 +8,13 @@ definitions.cs: # 9| 4: [BlockStmt] {...} # 13| 2: [Enum] Enumeration # 15| 5: [Field] e1 -# 15| 1: [MemberConstantAccess] access to constant e1 +# 15| 1: [AssignExpr] ... = ... +# 15| 0: [IntLiteral] 1 +# 15| 1: [MemberConstantAccess] access to constant e1 # 15| 6: [Field] e2 -# 15| 1: [MemberConstantAccess] access to constant e2 +# 15| 1: [AssignExpr] ... = ... +# 15| 0: [IntLiteral] 2 +# 15| 1: [MemberConstantAccess] access to constant e2 # 15| 7: [Field] e3 # 18| 3: [Class] C1 # 20| 4: [InstanceConstructor] C1 diff --git a/csharp/ql/test/library-tests/enums/Enums11.expected b/csharp/ql/test/library-tests/enums/Enums11.expected index e69de29bb2d..715beb400ff 100644 --- a/csharp/ql/test/library-tests/enums/Enums11.expected +++ b/csharp/ql/test/library-tests/enums/Enums11.expected @@ -0,0 +1,5 @@ +| enums.cs:28:18:28:18 | (...) ... | 1 | +| enums.cs:29:20:29:20 | (...) ... | 2 | +| enums.cs:30:20:30:20 | (...) ... | 4 | +| enums.cs:38:17:38:18 | 10 | 10 | +| enums.cs:40:23:40:32 | ... + ... | 11 | diff --git a/csharp/ql/test/library-tests/enums/Enums11.ql b/csharp/ql/test/library-tests/enums/Enums11.ql index cf808bf4f29..36b2c005a21 100644 --- a/csharp/ql/test/library-tests/enums/Enums11.ql +++ b/csharp/ql/test/library-tests/enums/Enums11.ql @@ -4,6 +4,9 @@ import csharp -from FieldAccess fa -where fa.getParent().(Field).getDeclaringType() instanceof Enum -select fa, fa.getValue() +from Expr e +where + exists(Assignment a | a.getRValue() = e | + a.getParent().(Field).getDeclaringType() instanceof Enum + ) +select e, e.getValue() diff --git a/csharp/ql/test/library-tests/enums/PrintAst.expected b/csharp/ql/test/library-tests/enums/PrintAst.expected index 05101b6ca19..446b4c01856 100644 --- a/csharp/ql/test/library-tests/enums/PrintAst.expected +++ b/csharp/ql/test/library-tests/enums/PrintAst.expected @@ -11,18 +11,35 @@ enums.cs: # 23| 3: [Enum] E # 25| 4: [Enum] ValueColor # 28| 5: [Field] OneRed -# 28| 1: [MemberConstantAccess] access to constant OneRed +# 28| 1: [AssignExpr] ... = ... +# 28| 0: [CastExpr] (...) ... +# 28| 0: [IntLiteral] 1 +# 28| 1: [MemberConstantAccess] access to constant OneRed # 29| 6: [Field] TwoGreen -# 29| 1: [MemberConstantAccess] access to constant TwoGreen +# 29| 1: [AssignExpr] ... = ... +# 29| 0: [CastExpr] (...) ... +# 29| 0: [IntLiteral] 2 +# 29| 1: [MemberConstantAccess] access to constant TwoGreen # 30| 7: [Field] FourBlue -# 30| 1: [MemberConstantAccess] access to constant FourBlue +# 30| 1: [AssignExpr] ... = ... +# 30| 0: [CastExpr] (...) ... +# 30| 0: [IntLiteral] 4 +# 30| 1: [MemberConstantAccess] access to constant FourBlue # 34| 5: [Enum] SparseColor # 37| 5: [Field] Red # 38| 6: [Field] Green -# 38| 1: [MemberConstantAccess] access to constant Green +# 38| 1: [AssignExpr] ... = ... +# 38| 0: [IntLiteral] 10 +# 38| 1: [MemberConstantAccess] access to constant Green # 39| 7: [Field] Blue # 40| 8: [Field] AnotherBlue -# 40| 1: [MemberConstantAccess] access to constant AnotherBlue +# 40| 1: [AssignExpr] ... = ... +# 40| 0: [AddExpr] ... + ... +# 40| 0: [CastExpr] (...) ... +# 40| 0: [MemberConstantAccess] access to constant Blue +# 40| 1: [CastExpr] (...) ... +# 40| 0: [MemberConstantAccess] access to constant Red +# 40| 1: [MemberConstantAccess] access to constant AnotherBlue # 44| 6: [Class] Test # 47| 5: [Method] Main # 48| 4: [BlockStmt] {...} diff --git a/csharp/ql/test/library-tests/enums/enums.cs b/csharp/ql/test/library-tests/enums/enums.cs index df7df79c066..784d4f64114 100644 --- a/csharp/ql/test/library-tests/enums/enums.cs +++ b/csharp/ql/test/library-tests/enums/enums.cs @@ -37,7 +37,7 @@ namespace Enums Red, Green = 10, Blue, - AnotherBlue = Blue + AnotherBlue = Blue + Red } From 3577b27f49ce6b38453d1a7413c70e87356aac57 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Fri, 25 Sep 2020 09:58:08 +0200 Subject: [PATCH 134/185] Fix to not report on enum member initialization --- csharp/ql/src/Likely Bugs/SelfAssignment.ql | 4 +++- .../query-tests/Likely Bugs/SelfAssignment/selfassigns.cs | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/csharp/ql/src/Likely Bugs/SelfAssignment.ql b/csharp/ql/src/Likely Bugs/SelfAssignment.ql index 0f0e23e8629..01c5d904cd4 100644 --- a/csharp/ql/src/Likely Bugs/SelfAssignment.ql +++ b/csharp/ql/src/Likely Bugs/SelfAssignment.ql @@ -20,7 +20,9 @@ class StructuralComparisonConfig extends StructuralComparisonConfiguration { exists(AssignExpr ae | // Member initializers are never self-assignments, in particular // not initializers such as `new C { F = F };` - not ae instanceof MemberInitializer + not ae instanceof MemberInitializer and + // Enum field initializers are never self assignments. `enum E { A = 42 }` + not ae.getParent().(Field).getDeclaringType() instanceof Enum | ae.getLValue() = x and ae.getRValue() = y diff --git a/csharp/ql/test/query-tests/Likely Bugs/SelfAssignment/selfassigns.cs b/csharp/ql/test/query-tests/Likely Bugs/SelfAssignment/selfassigns.cs index 3553606275f..6d1387a5e1d 100644 --- a/csharp/ql/test/query-tests/Likely Bugs/SelfAssignment/selfassigns.cs +++ b/csharp/ql/test/query-tests/Likely Bugs/SelfAssignment/selfassigns.cs @@ -82,4 +82,11 @@ class SelfAssigns : Super this.Self.Self.Self.StringProp = Self.Self.Self.StringProp; intArray[1] = this.intArray[1 + 0]; } + + enum Enum + { + X = 42, + Y = 100, + Z + } } From a635503be0987f4b3148f12f97cc093cd9596b54 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Fri, 25 Sep 2020 10:57:02 +0200 Subject: [PATCH 135/185] Add test cases to UselessCastToSelf --- .../UselessCastToSelf/UselessCastToSelf.cs | 9 +++++++++ .../UselessCastToSelf/UselessCastToSelf.expected | 2 ++ 2 files changed, 11 insertions(+) diff --git a/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.cs b/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.cs index 47df3468b4a..e36143d6b40 100644 --- a/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.cs +++ b/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.cs @@ -18,4 +18,13 @@ class Test var good6 = (Action)(delegate (int x) { }); var good7 = (Action)((int x) => { }); } + + enum Enum + { + A = 2, + B = 1 | A, + C = 1 | (int)A, // BAD + D = 9 | (32 << A), + E = 9 | (32 << (int)A) // BAD + } } diff --git a/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.expected b/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.expected index 28dcbbb9f6e..f5c4b708253 100644 --- a/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.expected +++ b/csharp/ql/test/query-tests/Language Abuse/UselessCastToSelf/UselessCastToSelf.expected @@ -1,3 +1,5 @@ | UselessCastToSelf.cs:8:20:8:25 | (...) ... | This cast is redundant because the expression already has type Int32. | | UselessCastToSelf.cs:9:20:9:29 | (...) ... | This cast is redundant because the expression already has type Test. | | UselessCastToSelf.cs:10:20:10:31 | ... as ... | This cast is redundant because the expression already has type Test. | +| UselessCastToSelf.cs:26:17:26:22 | (...) ... | This cast is redundant because the expression already has type Int32. | +| UselessCastToSelf.cs:28:24:28:29 | (...) ... | This cast is redundant because the expression already has type Int32. | From 060720aae7d460627378359763cde591b518c5e8 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 24 Sep 2020 18:16:49 +0200 Subject: [PATCH 136/185] Python: Add tests for all SystemCommandExecution from stdlib Overall idea is that `test/experimental/meta/ConceptsTest.qll` will set up inline expectation tests for all the classes defined in `Concepts.qll`, so any time you model a new instance of Concepts, you simply just import that file. That makes the tests a little verbose, but allows us to share test-setup between all the different frameworks we model. Note that since the definitions of SystemCommandExecution subclasses are scattered across multieple framework modeling qll files, it think it makes the most sense to have the tests for each framework in one location. I'm not 100% convinced about if this is the right choice or not (especially when we want to write tests for sanitizers), but for now I'm going to try it out at least. --- .../frameworks/stdlib/ConceptsTest.expected | 4 + .../frameworks/stdlib/ConceptsTest.ql | 2 + .../stdlib/SystemCommandExecution.py | 122 ++++++++++++++++++ .../test/experimental/meta/ConceptsTest.qll | 33 +++++ 4 files changed, 161 insertions(+) create mode 100644 python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.expected create mode 100644 python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.ql create mode 100644 python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py create mode 100644 python/ql/test/experimental/meta/ConceptsTest.qll diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.expected b/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.expected new file mode 100644 index 00000000000..a92521c154c --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.expected @@ -0,0 +1,4 @@ +| SystemCommandExecution.py:16:10:16:21 | ControlFlowNode for Str | Fixed false negative:SystemCommandExecution_getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:17:11:17:22 | ControlFlowNode for Str | Fixed false negative:SystemCommandExecution_getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:27:11:27:22 | ControlFlowNode for Str | Fixed false negative:SystemCommandExecution_getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:28:12:28:23 | ControlFlowNode for Str | Fixed false negative:SystemCommandExecution_getCommand="cmd1; cmd2" | diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.ql b/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.ql new file mode 100644 index 00000000000..b557a0bccb6 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.ql @@ -0,0 +1,2 @@ +import python +import experimental.meta.ConceptsTest diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py new file mode 100644 index 00000000000..03f520f2690 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py @@ -0,0 +1,122 @@ +# Note: Some of these commands will technically not allow an attacker to execute +# arbitrary system commands, but only specify the program to be executed. The general +# consensus was that even this is still a high security risk, so we also treat them as +# system command executions. +# +# As an example, executing `subprocess.Popen(["rm -rf /"])` will result in +# `FileNotFoundError: [Errno 2] No such file or directory: 'rm -rf /'` + +######################################## + + +import os + +# can't use a string literal with spaces in the tags of an InlineExpectationsTest, so using variables :| + +os.popen("cmd1; cmd2") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" +os.system("cmd1; cmd2") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" + + +def os_members(): + # hmm, it's kinda annoying to check that we handle this import correctly for + # everything. It's quite useful since I messed it up initially and didn't have a + # test for it, but in the long run it's just cumbersome to duplicate all the tests + # :| + from os import popen, system + + popen("cmd1; cmd2") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" + system("cmd1; cmd2") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" + + +######################################## +# https://docs.python.org/3.8/library/os.html#os.execv +# +# VS Code extension will ignore rest of program if encountering one of these, which we +# don't want. We could use `if False`, but just to be 100% sure we don't do anything too +# clever in our analysis that discards that code, I used `if UNKNOWN` instead +if UNKNOWN: + env = {"FOO": "foo"} + os.execl("executable", "", "arg0") # $f-:SystemCommandExecution_getCommand="executable" + os.execle("executable", "", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable" + os.execlp("executable", "", "arg0") # $f-:SystemCommandExecution_getCommand="executable" + os.execlpe("executable", "", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable" + os.execv("executable", ["", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" + os.execve("executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" + os.execvp("executable", ["", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" + os.execvpe("executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" + + +######################################## +# https://docs.python.org/3.8/library/os.html#os.spawnl +env = {"FOO": "foo"} +os.spawnl(os.P_WAIT, "executable", "", "arg0") # $f-:SystemCommandExecution_getCommand="executable" +os.spawnle(os.P_WAIT, "executable", "", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable" +os.spawnlp(os.P_WAIT, "executable", "", "arg0") # $f-:SystemCommandExecution_getCommand="executable" +os.spawnlpe(os.P_WAIT, "executable", "", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable" +os.spawnv(os.P_WAIT, "executable", ["", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" +os.spawnve(os.P_WAIT, "executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" +os.spawnvp(os.P_WAIT, "executable", ["", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" +os.spawnvpe(os.P_WAIT, "executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" + +# Added in Python 3.8 +os.posix_spawn("executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" +os.posix_spawnp("executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" + +######################################## + +import subprocess + +subprocess.Popen("cmd1; cmd2", shell=True) # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" +subprocess.Popen("cmd1; cmd2", shell="truthy string") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" +subprocess.Popen(["cmd1; cmd2", "shell-arg"], shell=True) # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" +subprocess.Popen("cmd1; cmd2", shell=True, executable="/bin/bash") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" + +subprocess.Popen("executable") # $f-:SystemCommandExecution_getCommand="executable" +subprocess.Popen(["executable", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" +subprocess.Popen("", executable="executable") # $f-:SystemCommandExecution_getCommand="executable" +subprocess.Popen(["", "arg0"], executable="executable") # $f-:SystemCommandExecution_getCommand="executable" + +# call/check_call/check_output/run all work like Popen from a command execution point of view +subprocess.call(["executable", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" +subprocess.check_call(["executable", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" +subprocess.check_output(["executable", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" +subprocess.run(["executable", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" + + +######################################## +# actively using known shell as the executable + +subprocess.Popen(["/bin/sh", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="/bin/sh",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["/bin/bash", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="/bin/bash",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["/bin/dash", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="/bin/dash",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["/bin/zsh", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="/bin/zsh",$f-:SystemCommandExecution_getCommand="vuln" + +subprocess.Popen(["sh", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="sh",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["bash", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="bash",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["dash", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="dash",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["zsh", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="zsh",$f-:SystemCommandExecution_getCommand="vuln" + +# Check that we don't consider ANY argument a command injection sink +subprocess.Popen(["sh", "/bin/python"]) # $f-:SystemCommandExecution_getCommand="sh" + +subprocess.Popen(["cmd.exe", "/c", "vuln"]) # $f-:SystemCommandExecution_getCommand="cmd.exe",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["cmd.exe", "/C", "vuln"]) # $f-:SystemCommandExecution_getCommand="cmd.exe",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["cmd", "/c", "vuln"]) # $f-:SystemCommandExecution_getCommand="cmd",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["cmd", "/C", "vuln"]) # $f-:SystemCommandExecution_getCommand="cmd",$f-:SystemCommandExecution_getCommand="vuln" + + +################################################################################ +# Taint related + +import shlex + +cmd = shlex.join(["echo", tainted]) +args = shlex.split(tainted) + +# will handle tainted = 'foo; rm -rf /' +safe_cmd = "ls {}".format(shlex.quote(tainted)) + +# not how you are supposed to use shlex.quote +wrong_use = shlex.quote("ls {}".format(tainted)) +# still dangerous, for example +cmd = "sh -c " + wrong_use diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll new file mode 100644 index 00000000000..89b490fb7d4 --- /dev/null +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -0,0 +1,33 @@ +import python +import experimental.dataflow.DataFlow +import experimental.semmle.python.Concepts +import TestUtilities.InlineExpectationsTest + +string value_from_expr(Expr e) { + // TODO: This one is starting to look like `repr` predicate from TestTaintLib + result = + e.(StrConst).getPrefix() + e.(StrConst).getText() + + e.(StrConst).getPrefix().regexpReplaceAll("[a-zA-Z]+", "") + or + result = e.(Name).getId() + or + not e instanceof StrConst and + not e instanceof Name and + result = e.toString() +} + +class SystemCommandExecutionTest extends InlineExpectationsTest { + SystemCommandExecutionTest() { this = "SystemCommandExecutionTest" } + + override string getARelevantTag() { result = "SystemCommandExecution_getCommand" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(SystemCommandExecution sce, DataFlow::Node command | + command = sce.getCommand() and + location = command.getLocation() and + element = command.toString() and + value = value_from_expr(command.asExpr()) and + tag = "SystemCommandExecution_getCommand" + ) + } +} From c440fd0c09eb43d80be492329bc81ae57c773804 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 24 Sep 2020 18:24:03 +0200 Subject: [PATCH 137/185] Python: Adjust expectations for system command executions I mostly did this to show my reviewers that the tests actually run and do something ;) --- .../library-tests/frameworks/stdlib/ConceptsTest.expected | 4 ---- .../frameworks/stdlib/SystemCommandExecution.py | 8 ++++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.expected b/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.expected index a92521c154c..e69de29bb2d 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.expected +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/ConceptsTest.expected @@ -1,4 +0,0 @@ -| SystemCommandExecution.py:16:10:16:21 | ControlFlowNode for Str | Fixed false negative:SystemCommandExecution_getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:17:11:17:22 | ControlFlowNode for Str | Fixed false negative:SystemCommandExecution_getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:27:11:27:22 | ControlFlowNode for Str | Fixed false negative:SystemCommandExecution_getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:28:12:28:23 | ControlFlowNode for Str | Fixed false negative:SystemCommandExecution_getCommand="cmd1; cmd2" | diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py index 03f520f2690..93f29787dd2 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py @@ -13,8 +13,8 @@ import os # can't use a string literal with spaces in the tags of an InlineExpectationsTest, so using variables :| -os.popen("cmd1; cmd2") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" -os.system("cmd1; cmd2") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" +os.popen("cmd1; cmd2") # $SystemCommandExecution_getCommand="cmd1; cmd2" +os.system("cmd1; cmd2") # $SystemCommandExecution_getCommand="cmd1; cmd2" def os_members(): @@ -24,8 +24,8 @@ def os_members(): # :| from os import popen, system - popen("cmd1; cmd2") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" - system("cmd1; cmd2") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" + popen("cmd1; cmd2") # $SystemCommandExecution_getCommand="cmd1; cmd2" + system("cmd1; cmd2") # $SystemCommandExecution_getCommand="cmd1; cmd2" ######################################## From 62dc0dd2632fad4c5f77225d6fa0e554d1b19073 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 24 Sep 2020 18:40:35 +0200 Subject: [PATCH 138/185] Python: Model os.exec* os.spawn* and os.posix_spawn* I also had to exclude the inline expectation tests from files outside the test repo. --- .../semmle/python/frameworks/Stdlib.qll | 103 +++++++++++++----- .../stdlib/SystemCommandExecution.py | 38 +++---- .../test/experimental/meta/ConceptsTest.qll | 1 + 3 files changed, 97 insertions(+), 45 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 7587e9187b2..2c7444fd08d 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -10,7 +10,7 @@ private import experimental.semmle.python.Concepts private module Stdlib { /** Gets a reference to the `os` module. */ - DataFlow::Node os(DataFlow::TypeTracker t) { + private DataFlow::Node os(DataFlow::TypeTracker t) { t.start() and result = DataFlow::importModule("os") or @@ -20,34 +20,38 @@ private module Stdlib { /** Gets a reference to the `os` module. */ DataFlow::Node os() { result = os(DataFlow::TypeTracker::end()) } - module os { - /** Gets a reference to the `os.system` function. */ - DataFlow::Node system(DataFlow::TypeTracker t) { + /** + * Gets a reference to the attribute `attr_name` of the `os` module. + * WARNING: Only holds for a few predefined attributes. + * + * For example, using `attr_name = "system"` will get all uses of `os.system`. + */ + private DataFlow::Node os_attr(string attr_name, DataFlow::TypeTracker t) { + attr_name in ["system", "popen", + // exec + "execl", "execle", "execlp", "execlpe", "execv", "execve", "execvp", "execvpe", + // spawn + "spawnl", "spawnle", "spawnlp", "spawnlpe", "spawnv", "spawnve", "spawnvp", "spawnvpe", + "posix_spawn", "posix_spawnp"] and + ( t.start() and - result = DataFlow::importMember("os", "system") + result = DataFlow::importMember("os", attr_name) or - t.startInAttr("system") and + t.startInAttr(attr_name) and result = os() or - exists(DataFlow::TypeTracker t2 | result = os::system(t2).track(t2, t)) - } + exists(DataFlow::TypeTracker t2 | result = os_attr(attr_name, t2).track(t2, t)) + ) + } - /** Gets a reference to the `os.system` function. */ - DataFlow::Node system() { result = os::system(DataFlow::TypeTracker::end()) } - - /** Gets a reference to the `os.popen` function. */ - DataFlow::Node popen(DataFlow::TypeTracker t) { - t.start() and - result = DataFlow::importMember("os", "popen") - or - t.startInAttr("popen") and - result = os() - or - exists(DataFlow::TypeTracker t2 | result = os::popen(t2).track(t2, t)) - } - - /** Gets a reference to the `os.popen` function. */ - DataFlow::Node popen() { result = os::popen(DataFlow::TypeTracker::end()) } + /** + * Gets a reference to the attribute `attr_name` of the `os` module. + * WARNING: Only holds for a few predefined attributes. + * + * For example, using `"system"` will get all uses of `os.system`. + */ + private DataFlow::Node os_attr(string attr_name) { + result = os_attr(attr_name, DataFlow::TypeTracker::end()) } /** @@ -55,7 +59,7 @@ private module Stdlib { * See https://docs.python.org/3/library/os.html#os.system */ private class OsSystemCall extends SystemCommandExecution::Range { - OsSystemCall() { this.asCfgNode().(CallNode).getFunction() = os::system().asCfgNode() } + OsSystemCall() { this.asCfgNode().(CallNode).getFunction() = os_attr("system").asCfgNode() } override DataFlow::Node getCommand() { result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0) @@ -67,7 +71,54 @@ private module Stdlib { * See https://docs.python.org/3/library/os.html#os.popen */ private class OsPopenCall extends SystemCommandExecution::Range { - OsPopenCall() { this.asCfgNode().(CallNode).getFunction() = os::popen().asCfgNode() } + OsPopenCall() { this.asCfgNode().(CallNode).getFunction() = os_attr("popen").asCfgNode() } + + override DataFlow::Node getCommand() { + result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0) + } + } + + /** + * A call to any of the `os.exec*` functions + * See https://docs.python.org/3.8/library/os.html#os.execl + */ + private class OsExecCall extends SystemCommandExecution::Range { + OsExecCall() { + this.asCfgNode().(CallNode).getFunction() = + os_attr(["execl", "execle", "execlp", "execlpe", "execv", "execve", "execvp", "execvpe"]) + .asCfgNode() + } + + override DataFlow::Node getCommand() { + result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0) + } + } + + /** + * A call to any of the `os.spawn*` functions + * See https://docs.python.org/3.8/library/os.html#os.spawnl + */ + private class OsSpawnCall extends SystemCommandExecution::Range { + OsSpawnCall() { + this.asCfgNode().(CallNode).getFunction() = + os_attr(["spawnl", "spawnle", "spawnlp", "spawnlpe", "spawnv", "spawnve", "spawnvp", + "spawnvpe"]).asCfgNode() + } + + override DataFlow::Node getCommand() { + result.asCfgNode() = this.asCfgNode().(CallNode).getArg(1) + } + } + + /** + * A call to any of the `os.posix_spawn*` functions + * See https://docs.python.org/3.8/library/os.html#os.posix_spawn + */ + private class OsPosixSpawnCall extends SystemCommandExecution::Range { + OsPosixSpawnCall() { + this.asCfgNode().(CallNode).getFunction() = + os_attr(["posix_spawn", "posix_spawnp"]).asCfgNode() + } override DataFlow::Node getCommand() { result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0) diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py index 93f29787dd2..458f7dc294e 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py @@ -29,38 +29,38 @@ def os_members(): ######################################## -# https://docs.python.org/3.8/library/os.html#os.execv +# https://docs.python.org/3.8/library/os.html#os.execl # # VS Code extension will ignore rest of program if encountering one of these, which we # don't want. We could use `if False`, but just to be 100% sure we don't do anything too # clever in our analysis that discards that code, I used `if UNKNOWN` instead if UNKNOWN: env = {"FOO": "foo"} - os.execl("executable", "", "arg0") # $f-:SystemCommandExecution_getCommand="executable" - os.execle("executable", "", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable" - os.execlp("executable", "", "arg0") # $f-:SystemCommandExecution_getCommand="executable" - os.execlpe("executable", "", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable" - os.execv("executable", ["", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" - os.execve("executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" - os.execvp("executable", ["", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" - os.execvpe("executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" + os.execl("executable", "", "arg0") # $SystemCommandExecution_getCommand="executable" + os.execle("executable", "", "arg0", env) # $SystemCommandExecution_getCommand="executable" + os.execlp("executable", "", "arg0") # $SystemCommandExecution_getCommand="executable" + os.execlpe("executable", "", "arg0", env) # $SystemCommandExecution_getCommand="executable" + os.execv("executable", ["", "arg0"]) # $SystemCommandExecution_getCommand="executable" + os.execve("executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" + os.execvp("executable", ["", "arg0"]) # $SystemCommandExecution_getCommand="executable" + os.execvpe("executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" ######################################## # https://docs.python.org/3.8/library/os.html#os.spawnl env = {"FOO": "foo"} -os.spawnl(os.P_WAIT, "executable", "", "arg0") # $f-:SystemCommandExecution_getCommand="executable" -os.spawnle(os.P_WAIT, "executable", "", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable" -os.spawnlp(os.P_WAIT, "executable", "", "arg0") # $f-:SystemCommandExecution_getCommand="executable" -os.spawnlpe(os.P_WAIT, "executable", "", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable" -os.spawnv(os.P_WAIT, "executable", ["", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" -os.spawnve(os.P_WAIT, "executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" -os.spawnvp(os.P_WAIT, "executable", ["", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" -os.spawnvpe(os.P_WAIT, "executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" +os.spawnl(os.P_WAIT, "executable", "", "arg0") # $SystemCommandExecution_getCommand="executable" +os.spawnle(os.P_WAIT, "executable", "", "arg0", env) # $SystemCommandExecution_getCommand="executable" +os.spawnlp(os.P_WAIT, "executable", "", "arg0") # $SystemCommandExecution_getCommand="executable" +os.spawnlpe(os.P_WAIT, "executable", "", "arg0", env) # $SystemCommandExecution_getCommand="executable" +os.spawnv(os.P_WAIT, "executable", ["", "arg0"]) # $SystemCommandExecution_getCommand="executable" +os.spawnve(os.P_WAIT, "executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" +os.spawnvp(os.P_WAIT, "executable", ["", "arg0"]) # $SystemCommandExecution_getCommand="executable" +os.spawnvpe(os.P_WAIT, "executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" # Added in Python 3.8 -os.posix_spawn("executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" -os.posix_spawnp("executable", ["", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable" +os.posix_spawn("executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" +os.posix_spawnp("executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" ######################################## diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll index 89b490fb7d4..258cffc694b 100644 --- a/python/ql/test/experimental/meta/ConceptsTest.qll +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -23,6 +23,7 @@ class SystemCommandExecutionTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(SystemCommandExecution sce, DataFlow::Node command | + exists(location.getFile().getRelativePath()) and command = sce.getCommand() and location = command.getLocation() and element = command.toString() and From f7f656418960208d97c25c686466b803e80d0f74 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 28 Sep 2020 11:13:04 +0200 Subject: [PATCH 139/185] Python: Model subprocess.Popen (and helpers) --- .../semmle/python/frameworks/Stdlib.qll | 131 ++++++++++++++++++ .../stdlib/SystemCommandExecution.py | 51 ++++--- 2 files changed, 156 insertions(+), 26 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 2c7444fd08d..a452ecfeb1f 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -9,6 +9,9 @@ private import experimental.dataflow.RemoteFlowSources private import experimental.semmle.python.Concepts private module Stdlib { + // --------------------------------------------------------------------------- + // os + // --------------------------------------------------------------------------- /** Gets a reference to the `os` module. */ private DataFlow::Node os(DataFlow::TypeTracker t) { t.start() and @@ -124,4 +127,132 @@ private module Stdlib { result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0) } } + + // --------------------------------------------------------------------------- + // subprocess + // --------------------------------------------------------------------------- + /** Gets a reference to the `subprocess` module. */ + private DataFlow::Node subprocess(DataFlow::TypeTracker t) { + t.start() and + result = DataFlow::importModule("subprocess") + or + exists(DataFlow::TypeTracker t2 | result = subprocess(t2).track(t2, t)) + } + + /** Gets a reference to the `subprocess` module. */ + DataFlow::Node subprocess() { result = subprocess(DataFlow::TypeTracker::end()) } + + /** + * Gets a reference to the attribute `attr_name` of the `subprocess` module. + * WARNING: Only holds for a few predefined attributes. + * + * For example, using `attr_name = "Popen"` will get all uses of `subprocess.Popen`. + */ + private DataFlow::Node subprocess_attr(string attr_name, DataFlow::TypeTracker t) { + attr_name in ["Popen", "call", "check_call", "check_output", "run"] and + ( + t.start() and + result = DataFlow::importMember("subprocess", attr_name) + or + t.startInAttr(attr_name) and + result = subprocess() + or + exists(DataFlow::TypeTracker t2 | result = subprocess_attr(attr_name, t2).track(t2, t)) + ) + } + + /** + * Gets a reference to the attribute `attr_name` of the `subprocess` module. + * WARNING: Only holds for a few predefined attributes. + * + * For example, using `attr_name = "Popen"` will get all uses of `subprocess.Popen`. + */ + private DataFlow::Node subprocess_attr(string attr_name) { + result = subprocess_attr(attr_name, DataFlow::TypeTracker::end()) + } + + /** + * A call to `subprocess.Popen` or helper functions (call, check_call, check_output, run) + * See https://docs.python.org/3.8/library/subprocess.html#subprocess.Popen + */ + private class SubprocessPopenCall extends SystemCommandExecution::Range { + CallNode call; + + SubprocessPopenCall() { + call = this.asCfgNode() and + call.getFunction() = + subprocess_attr(["Popen", "call", "check_call", "check_output", "run"]).asCfgNode() + } + + /** Gets the ControlFlowNode for the `args` argument, if any. */ + private ControlFlowNode get_args_arg() { + result = call.getArg(0) + or + result = call.getArgByName("args") + } + + /** Gets the ControlFlowNode for the `shell` argument, if any. */ + private ControlFlowNode get_shell_arg() { + result = call.getArg(8) + or + result = call.getArgByName("shell") + } + + private boolean get_shell_arg_value() { + not exists(this.get_shell_arg()) and + result = false + or + exists(ControlFlowNode shell_arg | shell_arg = this.get_shell_arg() | + result = shell_arg.getNode().(ImmutableLiteral).booleanValue() + or + // TODO: Track the "shell" argument to determine possible values + not shell_arg.getNode() instanceof ImmutableLiteral and + ( + result = true + or + result = false + ) + ) + } + + /** Gets the ControlFlowNode for the `executable` argument, if any. */ + private ControlFlowNode get_executable_arg() { + result = call.getArg(2) + or + result = call.getArgByName("executable") + } + + override DataFlow::Node getCommand() { + // TODO: Track arguments ("args" and "shell") + // TODO: Handle using `args=["sh", "-c", ]` + result.asCfgNode() = this.get_executable_arg() + or + exists(ControlFlowNode arg_args, boolean shell | + arg_args = get_args_arg() and + shell = get_shell_arg_value() + | + // When "executable" argument is set, and "shell" argument is `False`, the + // "args" argument will only be used to set the program name and arguments to + // the program, so we should not consider any of them as command execution. + not ( + exists(this.get_executable_arg()) and + shell = false + ) and + ( + // When the "args" argument is an iterable, first element is the command to + // run, so if we're able to, we only mark the first element as the command + // (and not the arguments to the command). + // + result.asCfgNode() = arg_args.(SequenceNode).getElement(0) + or + // Either the "args" argument is not a sequence (which is valid) or we where + // just not able to figure it out. Simply mark the "args" argument as the + // command. + // + not arg_args instanceof SequenceNode and + result.asCfgNode() = arg_args + ) + ) + } + } } diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py index 458f7dc294e..ea62564ed9f 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py @@ -66,44 +66,43 @@ os.posix_spawnp("executable", ["", "arg0"], env) # $SystemCommandExec import subprocess -subprocess.Popen("cmd1; cmd2", shell=True) # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" -subprocess.Popen("cmd1; cmd2", shell="truthy string") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" -subprocess.Popen(["cmd1; cmd2", "shell-arg"], shell=True) # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" -subprocess.Popen("cmd1; cmd2", shell=True, executable="/bin/bash") # $f-:SystemCommandExecution_getCommand="cmd1; cmd2" +subprocess.Popen("cmd1; cmd2", shell=True) # $SystemCommandExecution_getCommand="cmd1; cmd2" +subprocess.Popen("cmd1; cmd2", shell="truthy string") # $SystemCommandExecution_getCommand="cmd1; cmd2" +subprocess.Popen(["cmd1; cmd2", "shell-arg"], shell=True) # $SystemCommandExecution_getCommand="cmd1; cmd2" +subprocess.Popen("cmd1; cmd2", shell=True, executable="/bin/bash") # $SystemCommandExecution_getCommand="cmd1; cmd2" $SystemCommandExecution_getCommand="/bin/bash" -subprocess.Popen("executable") # $f-:SystemCommandExecution_getCommand="executable" -subprocess.Popen(["executable", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" -subprocess.Popen("", executable="executable") # $f-:SystemCommandExecution_getCommand="executable" -subprocess.Popen(["", "arg0"], executable="executable") # $f-:SystemCommandExecution_getCommand="executable" +subprocess.Popen("executable") # $SystemCommandExecution_getCommand="executable" +subprocess.Popen(["executable", "arg0"]) # $SystemCommandExecution_getCommand="executable" +subprocess.Popen("", executable="executable") # $SystemCommandExecution_getCommand="executable" +subprocess.Popen(["", "arg0"], executable="executable") # $SystemCommandExecution_getCommand="executable" # call/check_call/check_output/run all work like Popen from a command execution point of view -subprocess.call(["executable", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" -subprocess.check_call(["executable", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" -subprocess.check_output(["executable", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" -subprocess.run(["executable", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable" +subprocess.call(["executable", "arg0"]) # $SystemCommandExecution_getCommand="executable" +subprocess.check_call(["executable", "arg0"]) # $SystemCommandExecution_getCommand="executable" +subprocess.check_output(["executable", "arg0"]) # $SystemCommandExecution_getCommand="executable" +subprocess.run(["executable", "arg0"]) # $SystemCommandExecution_getCommand="executable" ######################################## # actively using known shell as the executable -subprocess.Popen(["/bin/sh", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="/bin/sh",$f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["/bin/bash", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="/bin/bash",$f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["/bin/dash", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="/bin/dash",$f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["/bin/zsh", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="/bin/zsh",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["/bin/sh", "-c", "vuln"]) # $SystemCommandExecution_getCommand="/bin/sh" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["/bin/bash", "-c", "vuln"]) # $SystemCommandExecution_getCommand="/bin/bash" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["/bin/dash", "-c", "vuln"]) # $SystemCommandExecution_getCommand="/bin/dash" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["/bin/zsh", "-c", "vuln"]) # $SystemCommandExecution_getCommand="/bin/zsh" $f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["sh", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="sh",$f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["bash", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="bash",$f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["dash", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="dash",$f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["zsh", "-c", "vuln"]) # $f-:SystemCommandExecution_getCommand="zsh",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["sh", "-c", "vuln"]) # $SystemCommandExecution_getCommand="sh" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["bash", "-c", "vuln"]) # $SystemCommandExecution_getCommand="bash" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["dash", "-c", "vuln"]) # $SystemCommandExecution_getCommand="dash" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["zsh", "-c", "vuln"]) # $SystemCommandExecution_getCommand="zsh" $f-:SystemCommandExecution_getCommand="vuln" # Check that we don't consider ANY argument a command injection sink -subprocess.Popen(["sh", "/bin/python"]) # $f-:SystemCommandExecution_getCommand="sh" - -subprocess.Popen(["cmd.exe", "/c", "vuln"]) # $f-:SystemCommandExecution_getCommand="cmd.exe",$f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["cmd.exe", "/C", "vuln"]) # $f-:SystemCommandExecution_getCommand="cmd.exe",$f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["cmd", "/c", "vuln"]) # $f-:SystemCommandExecution_getCommand="cmd",$f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["cmd", "/C", "vuln"]) # $f-:SystemCommandExecution_getCommand="cmd",$f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["sh", "/bin/python"]) # $SystemCommandExecution_getCommand="sh" +subprocess.Popen(["cmd.exe", "/c", "vuln"]) # $SystemCommandExecution_getCommand="cmd.exe" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["cmd.exe", "/C", "vuln"]) # $SystemCommandExecution_getCommand="cmd.exe" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["cmd", "/c", "vuln"]) # $SystemCommandExecution_getCommand="cmd" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["cmd", "/C", "vuln"]) # $SystemCommandExecution_getCommand="cmd" $f-:SystemCommandExecution_getCommand="vuln" ################################################################################ # Taint related From 3af5c720cc552608f1570aeb185dd8be3d39bc2a Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 28 Sep 2020 11:16:34 +0200 Subject: [PATCH 140/185] Python: Add test of more indirect command injection sinks --- .../frameworks/stdlib/SystemCommandExecution.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py index ea62564ed9f..8e50dcefc6b 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py @@ -104,6 +104,13 @@ subprocess.Popen(["cmd.exe", "/C", "vuln"]) # $SystemCommandExecution_getComman subprocess.Popen(["cmd", "/c", "vuln"]) # $SystemCommandExecution_getCommand="cmd" $f-:SystemCommandExecution_getCommand="vuln" subprocess.Popen(["cmd", "/C", "vuln"]) # $SystemCommandExecution_getCommand="cmd" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["", "-c", "vuln"], executable="/bin/bash") # $SystemCommandExecution_getCommand="/bin/bash" $f-:SystemCommandExecution_getCommand="vuln" + +if UNKNOWN: + os.execl("/bin/sh", "", "-c", "vuln") # $SystemCommandExecution_getCommand="/bin/sh" $f-:SystemCommandExecution_getCommand="vuln" + +os.spawnl(os.P_WAIT, "/bin/sh", "", "-c", "vuln") # $SystemCommandExecution_getCommand="/bin/sh" $f-:SystemCommandExecution_getCommand="vuln" + ################################################################################ # Taint related From 6cb2ca63a6f9e3883c07b1964603e7fa5dd7cf43 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 28 Sep 2020 11:23:06 +0200 Subject: [PATCH 141/185] Python: tests to show modeling is very syntactical --- .../frameworks/stdlib/SystemCommandExecution.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py index 8e50dcefc6b..8d6b1211614 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py @@ -111,6 +111,19 @@ if UNKNOWN: os.spawnl(os.P_WAIT, "/bin/sh", "", "-c", "vuln") # $SystemCommandExecution_getCommand="/bin/sh" $f-:SystemCommandExecution_getCommand="vuln" + +######################################## +# Passing arguments by reference + +args = ["/bin/sh", "-c", "vuln"] +subprocess.Popen(args) # $SystemCommandExecution_getCommand=args + +args = "" +use_shell = False +exe = "executable" +subprocess.Popen(args, shell=use_shell, executable=exe) # $f+:SystemCommandExecution_getCommand=args $SystemCommandExecution_getCommand=exe + + ################################################################################ # Taint related From 7609ce2d4718fbfc171e3ddfb4fdcbfb8cbc01c9 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Tue, 4 Aug 2020 18:10:27 +0100 Subject: [PATCH 142/185] C++: accept test changes from extractor frontend upgrade --- .../special_members/generated_copy/functions.expected | 2 -- .../library-tests/syntax-zoo/dataflow-ir-consistency.expected | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/cpp/ql/test/library-tests/special_members/generated_copy/functions.expected b/cpp/ql/test/library-tests/special_members/generated_copy/functions.expected index 2c85514d592..22282641b37 100644 --- a/cpp/ql/test/library-tests/special_members/generated_copy/functions.expected +++ b/cpp/ql/test/library-tests/special_members/generated_copy/functions.expected @@ -78,8 +78,6 @@ | copy.cpp:111:9:111:9 | MoveAssign | deleted | | | copy.cpp:111:9:111:9 | operator= | deleted | | | copy.cpp:113:17:113:25 | operator= | | | -| copy.cpp:120:9:120:9 | OnlyCtor | | | -| copy.cpp:120:9:120:9 | OnlyCtor | | | | copy.cpp:120:9:120:9 | OnlyCtor | deleted | | | copy.cpp:120:9:120:9 | operator= | deleted | | | copy.cpp:126:11:126:19 | operator= | | | diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index ee50bd638f9..b4175c9e499 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -539,8 +539,6 @@ uniqueNodeLocation | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | | file://:0:0:0:0 | p#0 | Node should have one location but has 0. | -| file://:0:0:0:0 | p#0 | Node should have one location but has 0. | -| file://:0:0:0:0 | p#0 | Node should have one location but has 0. | | file://:0:0:0:0 | p#1 | Node should have one location but has 0. | | file://:0:0:0:0 | p#1 | Node should have one location but has 0. | | file://:0:0:0:0 | p#1 | Node should have one location but has 0. | @@ -1418,7 +1416,7 @@ uniqueNodeLocation | whilestmt.c:39:6:39:11 | ReturnVoid | Node should have one location but has 4. | | whilestmt.c:39:6:39:11 | SideEffect | Node should have one location but has 4. | missingLocation -| Nodes without location: 36 | +| Nodes without location: 34 | uniqueNodeToString | break_labels.c:2:11:2:11 | i | Node should have one toString but has 2. | | break_labels.c:2:11:2:11 | i | Node should have one toString but has 2. | From 2bbaa4e17374b294f955ae9c895727d9fd5ca352 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Wed, 23 Sep 2020 10:40:04 +0200 Subject: [PATCH 143/185] Handle unsigned types in sign analysis (C# and Java) --- .../rangeanalysis/SignAnalysisCommon.qll | 38 +++++++++++-------- .../rangeanalysis/SignAnalysisSpecific.qll | 32 ++++++++++++---- .../dataflow/signanalysis/MissingSign.ql | 1 + .../dataflow/signanalysis/SignAnalysis.cs | 16 +++++++- .../signanalysis/SignAnalysis.expected | 29 ++++++++++++++ .../rangeanalysis/SignAnalysisCommon.qll | 38 +++++++++++-------- .../rangeanalysis/SignAnalysisSpecific.qll | 2 + .../dataflow/sign-analysis/A.java | 5 +++ .../sign-analysis/SignAnalysis.expected | 2 + 9 files changed, 122 insertions(+), 41 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll index 66b38433eef..c734e6acb0e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll @@ -245,24 +245,30 @@ Sign ssaDefSign(SsaVariable v) { /** Gets a possible sign for `e`. */ cached Sign exprSign(Expr e) { - result = certainExprSign(e) - or - not exists(certainExprSign(e)) and - ( - unknownSign(e) + exists(Sign s | + s = certainExprSign(e) or - exists(SsaVariable v | getARead(v) = e | result = ssaVariableSign(v, e)) - or - e = - any(VarAccess access | - not exists(SsaVariable v | getARead(v) = access) and - ( - result = fieldSign(getField(access.(FieldAccess))) or - not access instanceof FieldAccess + not exists(certainExprSign(e)) and + ( + unknownSign(e) + or + exists(SsaVariable v | getARead(v) = e | s = ssaVariableSign(v, e)) + or + e = + any(VarAccess access | + not exists(SsaVariable v | getARead(v) = access) and + ( + s = fieldSign(getField(access.(FieldAccess))) or + not access instanceof FieldAccess + ) ) - ) - or - result = specificSubExprSign(e) + or + s = specificSubExprSign(e) + ) + | + if e.getType() instanceof UnsignedNumericType and s = TNeg() + then result = TPos() + else result = s ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll index a47434eed0d..813834c3fd8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -71,14 +71,29 @@ private module Impl { predicate positiveExpression(Expr e) { e instanceof SizeofExpr } - class NumericOrCharType extends Type { - NumericOrCharType() { - this instanceof CharType or - this instanceof IntegralType or - this instanceof FloatingPointType or - this instanceof DecimalType or - this instanceof Enum or - this instanceof PointerType // should be similar to unsigned integers + abstract class NumericOrCharType extends Type { } + + class UnsignedNumericType extends NumericOrCharType { + UnsignedNumericType() { + this instanceof CharType + or + this instanceof UnsignedIntegralType + or + this instanceof PointerType + or + this instanceof Enum and this.(Enum).getUnderlyingType() instanceof UnsignedIntegralType + } + } + + class SignedNumericType extends NumericOrCharType { + SignedNumericType() { + this instanceof SignedIntegralType + or + this instanceof FloatingPointType + or + this instanceof DecimalType + or + this instanceof Enum and this.(Enum).getUnderlyingType() instanceof SignedIntegralType } } @@ -125,6 +140,7 @@ private module Impl { e.getType() instanceof NumericOrCharType and not e = getARead(_) and not e instanceof FieldAccess and + not e instanceof TypeAccess and // The expression types that are listed here are the ones handled in `specificSubExprSign`. // Keep them in sync. not e instanceof AssignExpr and diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql index b769f3eff6e..836980829a3 100644 --- a/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql @@ -4,6 +4,7 @@ import semmle.code.csharp.dataflow.SignAnalysis from Expr e where not exists(exprSign(e)) and + not e instanceof TypeAccess and ( e.getType() instanceof CharType or e.getType() instanceof IntegralType or diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs index 5a2df6428ff..e4f3781b10d 100644 --- a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs @@ -437,7 +437,7 @@ class SignAnalysis unsafe void PointerCast(byte* src, byte* dst) { - var x = (int)(src-dst); + var x = (int)(src - dst); if (x < 0) { System.Console.WriteLine(x); // strictly negative @@ -450,6 +450,20 @@ class SignAnalysis System.Console.WriteLine((int)to); } } + + uint Unsigned() { return 1; } + void UnsignedCheck(int i) + { + long l = Unsigned(); + if (l != 0) + { + System.Console.WriteLine(l); // strictly positive + } + + uint x = (uint)i; + x++; + System.Console.WriteLine(x); // strictly positive + } } // semmle-extractor-options: /r:System.Linq.dll \ No newline at end of file diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected index 31220df1a3c..ae54331a046 100644 --- a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected @@ -90,6 +90,7 @@ | SignAnalysis.cs:108:13:108:21 | Decimal de = ... | strictlyPositive | | SignAnalysis.cs:108:18:108:21 | 4.2 | strictlyPositive | | SignAnalysis.cs:109:34:109:35 | access to local variable de | strictlyPositive | +| SignAnalysis.cs:110:13:110:13 | access to local variable c | positive | | SignAnalysis.cs:110:13:110:19 | Char c = ... | strictlyPositive | | SignAnalysis.cs:110:17:110:19 | a | strictlyPositive | | SignAnalysis.cs:111:34:111:34 | access to local variable c | strictlyPositive | @@ -162,6 +163,8 @@ | SignAnalysis.cs:306:21:306:22 | -... | strictlyNegative | | SignAnalysis.cs:306:22:306:22 | 1 | strictlyPositive | | SignAnalysis.cs:309:38:309:38 | access to local variable x | strictlyNegative | +| SignAnalysis.cs:315:13:315:15 | access to local variable min | positive | +| SignAnalysis.cs:316:13:316:15 | access to local variable max | positive | | SignAnalysis.cs:316:13:316:31 | Char max = ... | strictlyPositive | | SignAnalysis.cs:316:19:316:31 | access to constant MaxValue | strictlyPositive | | SignAnalysis.cs:317:13:317:23 | Int32 c = ... | strictlyPositive | @@ -193,7 +196,13 @@ | SignAnalysis.cs:342:38:342:38 | access to local variable x | negative | | SignAnalysis.cs:348:62:348:62 | 5 | strictlyPositive | | SignAnalysis.cs:351:38:351:38 | access to local variable i | strictlyNegative | +| SignAnalysis.cs:357:13:357:13 | access to parameter i | positive | +| SignAnalysis.cs:359:38:359:38 | access to parameter i | strictlyPositive | | SignAnalysis.cs:371:38:371:38 | access to local variable y | strictlyNegative | +| SignAnalysis.cs:377:16:377:17 | access to local variable dp | positive | +| SignAnalysis.cs:377:16:377:22 | Single* dp = ... | positive | +| SignAnalysis.cs:377:21:377:22 | &... | positive | +| SignAnalysis.cs:378:18:378:19 | access to local variable dp | positive | | SignAnalysis.cs:381:38:381:38 | access to local variable x | strictlyNegative | | SignAnalysis.cs:385:50:385:99 | access to constant Explicit | strictlyPositive | | SignAnalysis.cs:385:109:385:110 | 15 | strictlyPositive | @@ -212,5 +221,25 @@ | SignAnalysis.cs:415:31:415:31 | access to local variable i | strictlyPositive | | SignAnalysis.cs:424:31:424:31 | access to local variable x | strictlyNegative | | SignAnalysis.cs:434:38:434:38 | access to local variable i | strictlyNegative | +| SignAnalysis.cs:440:23:440:25 | access to parameter src | positive | +| SignAnalysis.cs:440:29:440:31 | access to parameter dst | positive | | SignAnalysis.cs:443:38:443:38 | access to local variable x | strictlyNegative | | SignAnalysis.cs:446:31:446:32 | 10 | strictlyPositive | +| SignAnalysis.cs:448:22:448:23 | access to local variable to | positive | +| SignAnalysis.cs:448:22:448:29 | Byte* to = ... | positive | +| SignAnalysis.cs:448:27:448:29 | (...) ... | positive | +| SignAnalysis.cs:450:38:450:44 | (...) ... | positive | +| SignAnalysis.cs:450:43:450:44 | access to local variable to | positive | +| SignAnalysis.cs:454:30:454:30 | 1 | strictlyPositive | +| SignAnalysis.cs:454:30:454:30 | (...) ... | strictlyPositive | +| SignAnalysis.cs:457:14:457:27 | Int64 l = ... | positive | +| SignAnalysis.cs:457:18:457:27 | (...) ... | positive | +| SignAnalysis.cs:457:18:457:27 | call to method Unsigned | positive | +| SignAnalysis.cs:458:13:458:13 | access to local variable l | positive | +| SignAnalysis.cs:460:38:460:38 | access to local variable l | strictlyPositive | +| SignAnalysis.cs:463:14:463:14 | access to local variable x | positive | +| SignAnalysis.cs:463:14:463:24 | UInt32 x = ... | positive | +| SignAnalysis.cs:463:18:463:24 | (...) ... | positive | +| SignAnalysis.cs:464:9:464:9 | access to local variable x | positive | +| SignAnalysis.cs:464:9:464:11 | ...++ | positive | +| SignAnalysis.cs:465:34:465:34 | access to local variable x | strictlyPositive | diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll index 66b38433eef..c734e6acb0e 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll @@ -245,24 +245,30 @@ Sign ssaDefSign(SsaVariable v) { /** Gets a possible sign for `e`. */ cached Sign exprSign(Expr e) { - result = certainExprSign(e) - or - not exists(certainExprSign(e)) and - ( - unknownSign(e) + exists(Sign s | + s = certainExprSign(e) or - exists(SsaVariable v | getARead(v) = e | result = ssaVariableSign(v, e)) - or - e = - any(VarAccess access | - not exists(SsaVariable v | getARead(v) = access) and - ( - result = fieldSign(getField(access.(FieldAccess))) or - not access instanceof FieldAccess + not exists(certainExprSign(e)) and + ( + unknownSign(e) + or + exists(SsaVariable v | getARead(v) = e | s = ssaVariableSign(v, e)) + or + e = + any(VarAccess access | + not exists(SsaVariable v | getARead(v) = access) and + ( + s = fieldSign(getField(access.(FieldAccess))) or + not access instanceof FieldAccess + ) ) - ) - or - result = specificSubExprSign(e) + or + s = specificSubExprSign(e) + ) + | + if e.getType() instanceof UnsignedNumericType and s = TNeg() + then result = TPos() + else result = s ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll index 16784ccf0ec..8e030c9a53f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -53,6 +53,8 @@ private module Impl { private import SignAnalysisCommon private import SsaReadPositionCommon + class UnsignedNumericType = CharacterType; + float getNonIntegerValue(Expr e) { result = e.(LongLiteral).getValue().toFloat() or result = e.(FloatingPointLiteral).getValue().toFloat() or diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/A.java b/java/ql/test/library-tests/dataflow/sign-analysis/A.java index 158f4d5a18d..59d7d950334 100644 --- a/java/ql/test/library-tests/dataflow/sign-analysis/A.java +++ b/java/ql/test/library-tests/dataflow/sign-analysis/A.java @@ -9,4 +9,9 @@ public class A { return 0; } + + void unsigned(int x) { + char c = (char)x; + System.out.println(c); // positive + } } diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected index a1dedfa95b1..4b38711aaa8 100644 --- a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected +++ b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected @@ -1,3 +1,5 @@ | A.java:4:14:4:14 | x | strictlyNegative | | A.java:6:9:6:9 | x | positive | | A.java:7:14:7:14 | y | strictlyPositive | +| A.java:14:14:14:20 | (...)... | positive | +| A.java:15:24:15:24 | c | positive | From dfc44360129cb268330cccbdd84273b5883dcf7e Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Mon, 28 Sep 2020 15:34:47 +0100 Subject: [PATCH 144/185] JavaScript: Teach API graphs to recognise arguments supplied in partial function applications. --- .../ql/src/semmle/javascript/ApiGraphs.qll | 45 ++++++++++++++----- .../partial-invoke/VerifyAssertions.expected | 0 .../partial-invoke/VerifyAssertions.ql | 1 + .../ql/test/ApiGraphs/partial-invoke/index.js | 8 ++++ .../ApiGraphs/partial-invoke/package.json | 3 ++ 5 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 javascript/ql/test/ApiGraphs/partial-invoke/VerifyAssertions.expected create mode 100644 javascript/ql/test/ApiGraphs/partial-invoke/VerifyAssertions.ql create mode 100644 javascript/ql/test/ApiGraphs/partial-invoke/index.js create mode 100644 javascript/ql/test/ApiGraphs/partial-invoke/package.json diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll index 160ff9e47a2..beae51fedcb 100644 --- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll @@ -412,16 +412,9 @@ module API { rhs = f.getAReturn() ) or - exists(DataFlow::SourceNode src, DataFlow::InvokeNode invk | - use(base, src) and invk = trackUseNode(src).getAnInvocation() - | - exists(int i | - lbl = Label::parameter(i) and - rhs = invk.getArgument(i) - ) - or - lbl = Label::receiver() and - rhs = invk.(DataFlow::CallNode).getReceiver() + exists(int i | + lbl = Label::parameter(i) and + argumentPassing(base, i, rhs) ) or exists(DataFlow::SourceNode src, DataFlow::PropWrite pw | @@ -432,6 +425,30 @@ module API { ) } + /** + * Holds if `arg` is passed as the `i`th argument to a use of `base`, either by means of a + * full invocation, or in a partial function application. + * + * The receiver is considered to be argument -1. + */ + private predicate argumentPassing(TApiNode base, int i, DataFlow::Node arg) { + exists(DataFlow::SourceNode use, DataFlow::SourceNode pred | + use(base, use) and pred = trackUseNode(use) + | + arg = pred.getAnInvocation().getArgument(i) + or + arg = pred.getACall().getReceiver() and + i = -1 + or + exists(DataFlow::PartialInvokeNode pin, DataFlow::Node callback | pred.flowsTo(callback) | + pin.isPartialArgument(callback, arg, i) + or + arg = pin.getBoundReceiver(callback) and + i = -1 + ) + ) + } + /** * Holds if `rhs` is the right-hand side of a definition of node `nd`. */ @@ -719,10 +736,14 @@ private module Label { bindingset[s] string parameterByStringIndex(string s) { result = "parameter " + s and - s.toInt() >= 0 + s.toInt() >= -1 } - /** Gets the `parameter` edge label for the `i`th parameter. */ + /** + * Gets the `parameter` edge label for the `i`th parameter. + * + * The receiver is considered to be parameter -1. + */ bindingset[i] string parameter(int i) { result = parameterByStringIndex(i.toString()) } diff --git a/javascript/ql/test/ApiGraphs/partial-invoke/VerifyAssertions.expected b/javascript/ql/test/ApiGraphs/partial-invoke/VerifyAssertions.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/ql/test/ApiGraphs/partial-invoke/VerifyAssertions.ql b/javascript/ql/test/ApiGraphs/partial-invoke/VerifyAssertions.ql new file mode 100644 index 00000000000..b9c54e26072 --- /dev/null +++ b/javascript/ql/test/ApiGraphs/partial-invoke/VerifyAssertions.ql @@ -0,0 +1 @@ +import ApiGraphs.VerifyAssertions diff --git a/javascript/ql/test/ApiGraphs/partial-invoke/index.js b/javascript/ql/test/ApiGraphs/partial-invoke/index.js new file mode 100644 index 00000000000..1642e1454f3 --- /dev/null +++ b/javascript/ql/test/ApiGraphs/partial-invoke/index.js @@ -0,0 +1,8 @@ +const cp = require('child_process'); + +module.exports = function () { + return cp.spawn.bind( + cp, // def (parameter -1 (member spawn (member exports (module child_process)))) + "cat" // def (parameter 0 (member spawn (member exports (module child_process)))) + ); +}; \ No newline at end of file diff --git a/javascript/ql/test/ApiGraphs/partial-invoke/package.json b/javascript/ql/test/ApiGraphs/partial-invoke/package.json new file mode 100644 index 00000000000..279f51b6a5e --- /dev/null +++ b/javascript/ql/test/ApiGraphs/partial-invoke/package.json @@ -0,0 +1,3 @@ +{ + "name": "partial-invoke" +} \ No newline at end of file From e04404b713487c0876d4331384e538fba938bb49 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 28 Sep 2020 21:17:25 +0200 Subject: [PATCH 145/185] also recognize cookie writes are leading to cookie access --- javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql | 4 ++-- .../Security/CWE-352/MissingCsrfMiddleware.expected | 1 + .../query-tests/Security/CWE-352/MissingCsrfMiddlewareBad.js | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql index ca09a09f3a8..d63786ba08d 100644 --- a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql +++ b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql @@ -18,8 +18,8 @@ string cookieProperty() { result = "session" or result = "cookies" or result = " /** Gets a data flow node that flows to the base of an access to `cookies`, `session`, or `user`. */ private DataFlow::SourceNode nodeLeadingToCookieAccess(DataFlow::TypeBackTracker t) { t.start() and - exists(DataFlow::PropRead value | - value = result.getAPropertyRead(cookieProperty()).getAPropertyRead() and + exists(DataFlow::PropRef value | + value = result.getAPropertyRead(cookieProperty()).getAPropertyReference() and // Ignore accesses to values that are part of a CSRF or captcha check not value.getPropertyName().regexpMatch("(?i).*(csrf|xsrf|captcha).*") and // Ignore calls like `req.session.save()` diff --git a/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected b/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected index 6cf61160de1..bc6087f4354 100644 --- a/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected +++ b/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected @@ -1,6 +1,7 @@ | MissingCsrfMiddlewareBad.js:7:9:7:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:10:26:12:1 | functio ... il"];\\n} | here | | MissingCsrfMiddlewareBad.js:17:13:17:26 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:25:30:27:6 | errorCa ... \\n }) | here | | MissingCsrfMiddlewareBad.js:33:13:33:26 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:41:30:43:6 | errorCa ... \\n }) | here | +| MissingCsrfMiddlewareBad.js:33:13:33:26 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:45:31:47:6 | errorCa ... \\n }) | here | | csurf_api_example.js:42:37:42:50 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | csurf_api_example.js:42:53:45:3 | functio ... e')\\n } | here | | csurf_example.js:18:9:18:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | csurf_example.js:31:40:34:1 | functio ... sed')\\n} | here | | lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:26:42:29:1 | functio ... sed')\\n} | here | diff --git a/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddlewareBad.js b/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddlewareBad.js index fdcaf739451..d2d9b8bd4a1 100644 --- a/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddlewareBad.js +++ b/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddlewareBad.js @@ -41,4 +41,8 @@ app.post('/changeEmail', function (req, res) { app.post('/changeEmail', errorCatch(async function (req, res) { let newEmail = req.cookies["newEmail"]; })); + + app.post('/doLoginStuff', errorCatch(async function (req, res) { + req.session.user = loginStuff(req); + })); }) From 52d94f61770936720321554f17eb7b773d602e47 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 29 Sep 2020 10:12:46 +0200 Subject: [PATCH 146/185] use `getABoundCallbackParameter` instead of `getCallback` and `getParameter`. --- .../ql/src/semmle/javascript/frameworks/ClientRequests.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index fa9fb550330..830d0faeb8a 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -396,11 +396,11 @@ module ClientRequest { override DataFlow::Node getAResponseDataNode(string responseType, boolean promise) { promise = false and - result = this.getCallback(this.getNumArgument() - 1).getParameter(1) and + result = this.getABoundCallbackParameter(this.getNumArgument() - 1, 1) and responseType = "fetch.response" or promise = false and - result = this.getCallback(this.getNumArgument() - 1).getParameter(2) and + result = this.getABoundCallbackParameter(this.getNumArgument() - 1, 2) and responseType = "json" } } From 89195d7ada85c2ce74eae884e1fd372efb538c0f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 29 Sep 2020 10:13:48 +0200 Subject: [PATCH 147/185] add change note for needle --- change-notes/1.26/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.26/analysis-javascript.md b/change-notes/1.26/analysis-javascript.md index bcd727fba07..ad2ff09d9c0 100644 --- a/change-notes/1.26/analysis-javascript.md +++ b/change-notes/1.26/analysis-javascript.md @@ -14,6 +14,7 @@ - [json-stringify-safe](https://www.npmjs.com/package/json-stringify-safe) - [json3](https://www.npmjs.com/package/json3) - [lodash](https://www.npmjs.com/package/lodash) + - [needle](https://www.npmjs.com/package/needle) - [object-inspect](https://www.npmjs.com/package/object-inspect) - [pretty-format](https://www.npmjs.com/package/pretty-format) - [stringify-object](https://www.npmjs.com/package/stringify-object) From fee279f952297a39723bace5735f9c1220c2887d Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 29 Sep 2020 10:47:32 +0200 Subject: [PATCH 148/185] Python: Hotfix performance problem with flask methods This improves runtime for command injection query on https://lgtm.com/projects/g/alibaba/funcraft from +200 seconds (I did not care to wait more) down to ~55 seconds on my machine. This type of tracking predicate with string as additional argument apparently causes trouble :| --- .../semmle/python/frameworks/Flask.qll | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll index de72eb9128f..5b4079331d7 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll @@ -52,16 +52,35 @@ private module Flask { } private module FlaskRequestTracking { - private DataFlow::Node tainted_methods(string attr_name, DataFlow::TypeTracker t) { - attr_name in ["get_data", "get_json"] and - t.startInAttr(attr_name) and + /** Gets a reference to the `get_data` attribute of a Flask request. */ + private DataFlow::Node get_data(DataFlow::TypeTracker t) { + t.startInAttr("get_data") and result = flask::request() or - exists(DataFlow::TypeTracker t2 | result = tainted_methods(attr_name, t2).track(t2, t)) + exists(DataFlow::TypeTracker t2 | result = get_data(t2).track(t2, t)) } + /** Gets a reference to the `get_data` attribute of a Flask request. */ + DataFlow::Node get_data() { result = get_data(DataFlow::TypeTracker::end()) } + + /** Gets a reference to the `get_json` attribute of a Flask request. */ + private DataFlow::Node get_json(DataFlow::TypeTracker t) { + t.startInAttr("get_json") and + result = flask::request() + or + exists(DataFlow::TypeTracker t2 | result = get_json(t2).track(t2, t)) + } + + /** Gets a reference to the `get_json` attribute of a Flask request. */ + DataFlow::Node get_json() { result = get_json(DataFlow::TypeTracker::end()) } + + /** Gets a reference to either of the `get_json` or `get_data` attributes of a Flask request. */ DataFlow::Node tainted_methods(string attr_name) { - result = tainted_methods(attr_name, DataFlow::TypeTracker::end()) + result = get_data() and + attr_name = "get_data" + or + result = get_json() and + attr_name = "get_json" } } From 51f1f03f5ffc440ca8da5d491a8bc9190b3281d7 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 29 Sep 2020 11:56:10 +0200 Subject: [PATCH 149/185] add change note for js/missing-token-validation --- change-notes/1.26/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.26/analysis-javascript.md b/change-notes/1.26/analysis-javascript.md index bcd727fba07..674667c6b7b 100644 --- a/change-notes/1.26/analysis-javascript.md +++ b/change-notes/1.26/analysis-javascript.md @@ -39,6 +39,7 @@ | Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | More results | This query now recognizes more commands where colon, dash, and underscore are used. | | Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | More results | This query now detects more unsafe uses of nested option properties. | | Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | More results | This query now recognizes some unsafe uses of `importScripts()` inside WebWorkers. | +| Missing CSRF middleware (`js/missing-token-validation`) | More results | This query now recognizes writes to cookie and session variables as potentially vulnerable to CSRF attacks. | ## Changes to libraries From d184aa7c06910096b51bfd3baa773ae90b937fe4 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 29 Sep 2020 15:24:51 +0100 Subject: [PATCH 150/185] Make `FieldRead` and `FieldWrite` extend `LValue` and `RValue` --- java/ql/src/semmle/code/java/Expr.qll | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/java/ql/src/semmle/code/java/Expr.qll b/java/ql/src/semmle/code/java/Expr.qll index d79a82acbad..50f4a210d60 100755 --- a/java/ql/src/semmle/code/java/Expr.qll +++ b/java/ql/src/semmle/code/java/Expr.qll @@ -1912,14 +1912,10 @@ private module Qualifier { } /** An expression that assigns a value to a field. */ -class FieldWrite extends FieldAccess { - FieldWrite() { exists(Field f | f = getVariable() and isLValue()) } -} +class FieldWrite extends FieldAccess, LValue { } /** An expression that reads a field. */ -class FieldRead extends FieldAccess { - FieldRead() { exists(Field f | f = getVariable() and isRValue()) } -} +class FieldRead extends FieldAccess, RValue { } private predicate hasInstantiation(RefType t) { t instanceof TypeVariable or From deae9256ddbcbf2e47ed22a1d5df26e906bfd519 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 29 Sep 2020 15:50:28 +0200 Subject: [PATCH 151/185] add convenience method to API graphs --- javascript/ql/src/semmle/javascript/ApiGraphs.qll | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll index 160ff9e47a2..c5264a8d062 100644 --- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll @@ -35,6 +35,17 @@ module API { ) } + /** + * Gets a source-node corresponding to a use of the API component represented by this node. + * + * For example, `require('fs').readFileSync` is a use of the function `readFileSync` from the + * `fs` module, and `require('fs').readFileSync(file)` is a use of the result of that function. + * + * As another example, in the assignment `exports.plusOne = (x) => x+1` the two references to + * `x` are uses of the first parameter of `plusOne`. + */ + DataFlow::SourceNode getASourceUse() { Impl::use(this, result) } + /** * Gets a data-flow node corresponding to the right-hand side of a definition of the API * component represented by this node. From 38573316571092993a0e5e9b3804903087014b77 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 29 Sep 2020 16:19:32 +0200 Subject: [PATCH 152/185] avoid .getReturn().getAUse().(DataFlow::InvokeNode) in the SQL model --- .../src/semmle/javascript/frameworks/SQL.qll | 74 ++++++++++++------- .../frameworks/SQL/SqlString.expected | 1 + .../library-tests/frameworks/SQL/mssql2.js | 13 +++- 3 files changed, 58 insertions(+), 30 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll index 19eaf168c08..25d50462b5e 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll @@ -31,11 +31,17 @@ private module MySql { /** Gets the package name `mysql` or `mysql2`. */ API::Node mysql() { result = API::moduleImport(["mysql", "mysql2"]) } + /** Gets a reference to `mysql.createConnection`. */ + API::Node createConnectionCallee() { result = mysql().getMember("createConnection") } + /** Gets a call to `mysql.createConnection`. */ - API::Node createConnection() { result = mysql().getMember("createConnection").getReturn() } + API::Node createConnection() { result = createConnectionCallee().getReturn() } + + /** Gets a reference to `mysql.createPool`. */ + API::Node createPoolCallee() { result = mysql().getMember("createPool") } /** Gets a call to `mysql.createPool`. */ - API::Node createPool() { result = mysql().getMember("createPool").getReturn() } + API::Node createPool() { result = createPoolCallee().getReturn() } /** Gets a data flow node that contains a freshly created MySQL connection instance. */ API::Node connection() { @@ -48,7 +54,7 @@ private module MySql { private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { QueryCall() { exists(API::Node recv | recv = createPool() or recv = connection() | - this = recv.getMember("query").getReturn().getAUse() + this = recv.getMember("query").getASourceUse().getACall() ) } @@ -66,8 +72,8 @@ private module MySql { this = [mysql(), createPool(), connection()] .getMember(["escape", "escapeId"]) - .getReturn() - .getAUse() + .getASourceUse() + .getACall() .asExpr() and input = this.getArgument(0) and output = this @@ -79,9 +85,9 @@ private module MySql { string kind; Credentials() { - exists(API::Node call, string prop | - call in [createConnection(), createPool()] and - call.getAUse().asExpr().(CallExpr).hasOptionArgument(0, prop, this) and + exists(API::Node callee, string prop | + callee in [createConnectionCallee(), createPoolCallee()] and + this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and ( prop = "user" and kind = "user name" or @@ -98,8 +104,11 @@ private module MySql { * Provides classes modelling the `pg` package. */ private module Postgres { + /** Gets an expression of the form `require('pg').Client`. */ + API::Node newClientCallee() { result = API::moduleImport("pg").getMember("Client") } + /** Gets an expression of the form `new require('pg').Client()`. */ - API::Node newClient() { result = API::moduleImport("pg").getMember("Client").getInstance() } + API::Node newClient() { result = newClientCallee().getInstance() } /** Gets a data flow node that holds a freshly created Postgres client instance. */ API::Node client() { @@ -109,18 +118,21 @@ private module Postgres { result = newPool().getMember("connect").getParameter(0).getParameter(1) } - /** Gets an expression that constructs a new connection pool. */ - API::Node newPool() { + /** Gets a constructor that when invoked constructs a new connection pool. */ + API::Node newPoolCallee() { // new require('pg').Pool() - result = API::moduleImport("pg").getMember("Pool").getInstance() + result = API::moduleImport("pg").getMember("Pool") or // new require('pg-pool') - result = API::moduleImport("pg-pool").getInstance() + result = API::moduleImport("pg-pool") } + /** Gets an expression that constructs a new connection pool. */ + API::Node newPool() { result = newPoolCallee().getInstance() } + /** A call to the Postgres `query` method. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { - QueryCall() { this = [client(), newPool()].getMember("query").getReturn().getAUse() } + QueryCall() { this = [client(), newPool()].getMember("query").getASourceUse().getACall() } override DataFlow::Node getAQueryArgument() { result = getArgument(0) } } @@ -135,9 +147,9 @@ private module Postgres { string kind; Credentials() { - exists(DataFlow::InvokeNode call, string prop | - call = [client(), newPool()].getAUse() and - this = call.getOptionArgument(0, prop).asExpr() and + exists(string prop | + this = + [newClientCallee(), newPoolCallee()].getParameter(0).getMember(prop).getARhs().asExpr() and ( prop = "user" and kind = "user name" or @@ -178,7 +190,7 @@ private module Sqlite { meth = "prepare" or meth = "run" | - this = newDb().getMember(meth).getReturn().getAUse() + this = newDb().getMember(meth).getASourceUse().getACall() ) } @@ -222,7 +234,7 @@ private module MsSql { /** A call to a MsSql query method. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { - QueryCall() { this = request().getMember(["query", "batch"]).getReturn().getAUse() } + QueryCall() { this = request().getMember(["query", "batch"]).getASourceUse().getACall() } override DataFlow::Node getAQueryArgument() { result = getArgument(0) } } @@ -250,13 +262,13 @@ private module MsSql { string kind; Credentials() { - exists(DataFlow::InvokeNode call, string prop | + exists(API::Node callee, string prop | ( - call = mssql().getMember("connect").getReturn().getAUse() + callee = mssql().getMember("connect") or - call = mssql().getMember("ConnectionPool").getInstance().getAUse() + callee = mssql().getMember("ConnectionPool") ) and - this = call.getOptionArgument(0, prop).asExpr() and + this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and ( prop = "user" and kind = "user name" or @@ -281,7 +293,7 @@ private module Sequelize { /** A call to `Sequelize.query`. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { - QueryCall() { this = newSequelize().getMember("query").getReturn().getAUse() } + QueryCall() { this = newSequelize().getMember("query").getASourceUse().getACall() } override DataFlow::Node getAQueryArgument() { result = getArgument(0) } } @@ -300,7 +312,7 @@ private module Sequelize { Credentials() { exists(NewExpr ne, string prop | - ne = newSequelize().getAUse().asExpr() and + ne = sequelize().getASourceUse().getAnInstantiation().asExpr() and ( this = ne.getArgument(1) and prop = "username" or @@ -379,7 +391,10 @@ private module Spanner { class DatabaseRunCall extends SqlExecution { DatabaseRunCall() { this = - database().getMember(["run", "runPartitionedUpdate", "runStream"]).getReturn().getAUse() + database() + .getMember(["run", "runPartitionedUpdate", "runStream"]) + .getASourceUse() + .getACall() } } @@ -388,7 +403,7 @@ private module Spanner { */ class TransactionRunCall extends SqlExecution { TransactionRunCall() { - this = transaction().getMember(["run", "runStream", "runUpdate"]).getReturn().getAUse() + this = transaction().getMember(["run", "runStream", "runUpdate"]).getASourceUse().getACall() } } @@ -398,7 +413,10 @@ private module Spanner { class ExecuteSqlCall extends SqlExecution { ExecuteSqlCall() { this = - v1SpannerClient().getMember(["executeSql", "executeStreamingSql"]).getReturn().getAUse() + v1SpannerClient() + .getMember(["executeSql", "executeStreamingSql"]) + .getASourceUse() + .getACall() } override DataFlow::Node getAQueryArgument() { diff --git a/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected b/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected index b3e9e240944..b2e0cc4b046 100644 --- a/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected +++ b/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected @@ -3,6 +3,7 @@ | mssql2.js:5:15:5:34 | 'select 1 as number' | | mssql2.js:13:15:13:66 | 'create ... table' | | mssql2.js:22:24:22:43 | 'select 1 as number' | +| mssql2.js:29:30:29:81 | 'create ... table' | | mysql1.js:13:18:13:43 | 'SELECT ... lution' | | mysql1.js:18:18:22:1 | {\\n s ... vid']\\n} | | mysql1a.js:17:18:17:43 | 'SELECT ... lution' | diff --git a/javascript/ql/test/library-tests/frameworks/SQL/mssql2.js b/javascript/ql/test/library-tests/frameworks/SQL/mssql2.js index 9b64f06068a..f6a38828c5c 100644 --- a/javascript/ql/test/library-tests/frameworks/SQL/mssql2.js +++ b/javascript/ql/test/library-tests/frameworks/SQL/mssql2.js @@ -1,6 +1,6 @@ // Adapted from https://github.com/tediousjs/node-mssql#readme const sql = require('mssql') - + const request = new sql.Request() request.query('select 1 as number', (err, result) => { // ... error checks @@ -19,7 +19,16 @@ class C { this.req = req; } send() { - this.req.query('select 1 as number', (err, result) => {}) + this.req.query('select 1 as number', (err, result) => { }) } } new C(new sql.Request()); + +var obj = { + foo: function () { + return request.batch('create procedure #temporary as select * from table', (err, result) => { + // ... error checks + }) + } +} +obj.foo("foo", "bar", "baz"); // A API-graphs gotcha. \ No newline at end of file From adc05022f380f222c25be7f1875181a6b3ab3e4e Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 29 Sep 2020 18:21:41 +0200 Subject: [PATCH 153/185] update comment in test case Co-authored-by: Max Schaefer <54907921+max-schaefer@users.noreply.github.com> --- javascript/ql/test/library-tests/frameworks/SQL/mssql2.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/test/library-tests/frameworks/SQL/mssql2.js b/javascript/ql/test/library-tests/frameworks/SQL/mssql2.js index f6a38828c5c..23eb7148a2c 100644 --- a/javascript/ql/test/library-tests/frameworks/SQL/mssql2.js +++ b/javascript/ql/test/library-tests/frameworks/SQL/mssql2.js @@ -31,4 +31,4 @@ var obj = { }) } } -obj.foo("foo", "bar", "baz"); // A API-graphs gotcha. \ No newline at end of file +obj.foo("foo", "bar", "baz"); // An API-graphs gotcha: "baz" should not be considered a `SqlString` From 1596436f7e52070880cb404b9a9b0cc96e71bd36 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 29 Sep 2020 18:05:50 +0200 Subject: [PATCH 154/185] rename getASourceUse to getAReference --- .../ql/src/semmle/javascript/ApiGraphs.qll | 23 +++++++++---------- .../src/semmle/javascript/frameworks/SQL.qll | 20 ++++++++-------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll index c5264a8d062..7688df124f2 100644 --- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll @@ -21,30 +21,29 @@ module API { */ class Node extends Impl::TApiNode { /** - * Gets a data-flow node corresponding to a use of the API component represented by this node. + * Gets a `SourceNode` corresponding to a use of the API component represented by this node. * * For example, `require('fs').readFileSync` is a use of the function `readFileSync` from the * `fs` module, and `require('fs').readFileSync(file)` is a use of the result of that function. * * As another example, in the assignment `exports.plusOne = (x) => x+1` the two references to * `x` are uses of the first parameter of `plusOne`. + * + * Note: The result from this predicate is always a `DataFlow::SourceǸode`, use `getAUse()` if + * you want to follow purely local data-flow and get all `DataFlow::Node`s that corrospond to a + * use of this API node. */ - DataFlow::Node getAUse() { - exists(DataFlow::SourceNode src | Impl::use(this, src) | - Impl::trackUseNode(src).flowsTo(result) - ) + DataFlow::SourceNode getAReference() { + exists(DataFlow::SourceNode src | Impl::use(this, src) | result = Impl::trackUseNode(src)) } /** - * Gets a source-node corresponding to a use of the API component represented by this node. + * Gets a data-flow node corresponding to a use of the API component represented by this node. * - * For example, `require('fs').readFileSync` is a use of the function `readFileSync` from the - * `fs` module, and `require('fs').readFileSync(file)` is a use of the result of that function. - * - * As another example, in the assignment `exports.plusOne = (x) => x+1` the two references to - * `x` are uses of the first parameter of `plusOne`. + * This predicate is similar to `getAReference`, except this prediate also follows purely local + * data-flow. */ - DataFlow::SourceNode getASourceUse() { Impl::use(this, result) } + DataFlow::Node getAUse() { getAReference().flowsTo(result) } /** * Gets a data-flow node corresponding to the right-hand side of a definition of the API diff --git a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll index 25d50462b5e..232307cc014 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll @@ -54,7 +54,7 @@ private module MySql { private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { QueryCall() { exists(API::Node recv | recv = createPool() or recv = connection() | - this = recv.getMember("query").getASourceUse().getACall() + this = recv.getMember("query").getAReference().getACall() ) } @@ -72,7 +72,7 @@ private module MySql { this = [mysql(), createPool(), connection()] .getMember(["escape", "escapeId"]) - .getASourceUse() + .getAReference() .getACall() .asExpr() and input = this.getArgument(0) and @@ -132,7 +132,7 @@ private module Postgres { /** A call to the Postgres `query` method. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { - QueryCall() { this = [client(), newPool()].getMember("query").getASourceUse().getACall() } + QueryCall() { this = [client(), newPool()].getMember("query").getAReference().getACall() } override DataFlow::Node getAQueryArgument() { result = getArgument(0) } } @@ -190,7 +190,7 @@ private module Sqlite { meth = "prepare" or meth = "run" | - this = newDb().getMember(meth).getASourceUse().getACall() + this = newDb().getMember(meth).getAReference().getACall() ) } @@ -234,7 +234,7 @@ private module MsSql { /** A call to a MsSql query method. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { - QueryCall() { this = request().getMember(["query", "batch"]).getASourceUse().getACall() } + QueryCall() { this = request().getMember(["query", "batch"]).getAReference().getACall() } override DataFlow::Node getAQueryArgument() { result = getArgument(0) } } @@ -293,7 +293,7 @@ private module Sequelize { /** A call to `Sequelize.query`. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { - QueryCall() { this = newSequelize().getMember("query").getASourceUse().getACall() } + QueryCall() { this = newSequelize().getMember("query").getAReference().getACall() } override DataFlow::Node getAQueryArgument() { result = getArgument(0) } } @@ -312,7 +312,7 @@ private module Sequelize { Credentials() { exists(NewExpr ne, string prop | - ne = sequelize().getASourceUse().getAnInstantiation().asExpr() and + ne = sequelize().getAReference().getAnInstantiation().asExpr() and ( this = ne.getArgument(1) and prop = "username" or @@ -393,7 +393,7 @@ private module Spanner { this = database() .getMember(["run", "runPartitionedUpdate", "runStream"]) - .getASourceUse() + .getAReference() .getACall() } } @@ -403,7 +403,7 @@ private module Spanner { */ class TransactionRunCall extends SqlExecution { TransactionRunCall() { - this = transaction().getMember(["run", "runStream", "runUpdate"]).getASourceUse().getACall() + this = transaction().getMember(["run", "runStream", "runUpdate"]).getAReference().getACall() } } @@ -415,7 +415,7 @@ private module Spanner { this = v1SpannerClient() .getMember(["executeSql", "executeStreamingSql"]) - .getASourceUse() + .getAReference() .getACall() } From 69f4ac25c4885bc478e8c34b65785f287affd221 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 29 Sep 2020 18:11:31 +0200 Subject: [PATCH 155/185] renamings based on review --- .../src/semmle/javascript/frameworks/SQL.qll | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll index 232307cc014..3d74dbdae3e 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll @@ -32,28 +32,25 @@ private module MySql { API::Node mysql() { result = API::moduleImport(["mysql", "mysql2"]) } /** Gets a reference to `mysql.createConnection`. */ - API::Node createConnectionCallee() { result = mysql().getMember("createConnection") } - - /** Gets a call to `mysql.createConnection`. */ - API::Node createConnection() { result = createConnectionCallee().getReturn() } + API::Node createConnection() { result = mysql().getMember("createConnection") } /** Gets a reference to `mysql.createPool`. */ - API::Node createPoolCallee() { result = mysql().getMember("createPool") } + API::Node createPool() { result = mysql().getMember("createPool") } - /** Gets a call to `mysql.createPool`. */ - API::Node createPool() { result = createPoolCallee().getReturn() } + /** Gets a node that contains a MySQL pool created using `mysql.createPool()`. */ + API::Node pool() { result = createPool().getReturn() } /** Gets a data flow node that contains a freshly created MySQL connection instance. */ API::Node connection() { - result = createConnection() + result = createConnection().getReturn() or - result = createPool().getMember("getConnection").getParameter(0).getParameter(1) + result = pool().getMember("getConnection").getParameter(0).getParameter(1) } /** A call to the MySql `query` method. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { QueryCall() { - exists(API::Node recv | recv = createPool() or recv = connection() | + exists(API::Node recv | recv = pool() or recv = connection() | this = recv.getMember("query").getAReference().getACall() ) } @@ -70,7 +67,7 @@ private module MySql { class EscapingSanitizer extends SQL::SqlSanitizer, MethodCallExpr { EscapingSanitizer() { this = - [mysql(), createPool(), connection()] + [mysql(), pool(), connection()] .getMember(["escape", "escapeId"]) .getAReference() .getACall() @@ -86,7 +83,7 @@ private module MySql { Credentials() { exists(API::Node callee, string prop | - callee in [createConnectionCallee(), createPoolCallee()] and + callee in [createConnection(), createPool()] and this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and ( prop = "user" and kind = "user name" From c3f5a6dcac88dc9cf459858a80a42aecee0c49cf Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 29 Sep 2020 18:17:36 +0200 Subject: [PATCH 156/185] introduce API::Node::getACall() --- javascript/ql/src/semmle/javascript/ApiGraphs.qll | 5 +++++ .../ql/src/semmle/javascript/frameworks/SQL.qll | 15 ++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll index 7688df124f2..4cb1f4cb8e0 100644 --- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll @@ -45,6 +45,11 @@ module API { */ DataFlow::Node getAUse() { getAReference().flowsTo(result) } + /** + * Gets a call to a use of the API component represented by this node. + */ + DataFlow::CallNode getACall() { result = getAReference().getACall() } + /** * Gets a data-flow node corresponding to the right-hand side of a definition of the API * component represented by this node. diff --git a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll index 3d74dbdae3e..28ff91c0380 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll @@ -51,7 +51,7 @@ private module MySql { private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { QueryCall() { exists(API::Node recv | recv = pool() or recv = connection() | - this = recv.getMember("query").getAReference().getACall() + this = recv.getMember("query").getACall() ) } @@ -69,7 +69,6 @@ private module MySql { this = [mysql(), pool(), connection()] .getMember(["escape", "escapeId"]) - .getAReference() .getACall() .asExpr() and input = this.getArgument(0) and @@ -129,7 +128,7 @@ private module Postgres { /** A call to the Postgres `query` method. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { - QueryCall() { this = [client(), newPool()].getMember("query").getAReference().getACall() } + QueryCall() { this = [client(), newPool()].getMember("query").getACall() } override DataFlow::Node getAQueryArgument() { result = getArgument(0) } } @@ -187,7 +186,7 @@ private module Sqlite { meth = "prepare" or meth = "run" | - this = newDb().getMember(meth).getAReference().getACall() + this = newDb().getMember(meth).getACall() ) } @@ -231,7 +230,7 @@ private module MsSql { /** A call to a MsSql query method. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { - QueryCall() { this = request().getMember(["query", "batch"]).getAReference().getACall() } + QueryCall() { this = request().getMember(["query", "batch"]).getACall() } override DataFlow::Node getAQueryArgument() { result = getArgument(0) } } @@ -290,7 +289,7 @@ private module Sequelize { /** A call to `Sequelize.query`. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { - QueryCall() { this = newSequelize().getMember("query").getAReference().getACall() } + QueryCall() { this = newSequelize().getMember("query").getACall() } override DataFlow::Node getAQueryArgument() { result = getArgument(0) } } @@ -390,7 +389,6 @@ private module Spanner { this = database() .getMember(["run", "runPartitionedUpdate", "runStream"]) - .getAReference() .getACall() } } @@ -400,7 +398,7 @@ private module Spanner { */ class TransactionRunCall extends SqlExecution { TransactionRunCall() { - this = transaction().getMember(["run", "runStream", "runUpdate"]).getAReference().getACall() + this = transaction().getMember(["run", "runStream", "runUpdate"]).getACall() } } @@ -412,7 +410,6 @@ private module Spanner { this = v1SpannerClient() .getMember(["executeSql", "executeStreamingSql"]) - .getAReference() .getACall() } From 65441705ef9025149b2f39d12f4b6ac62f6aa344 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 29 Sep 2020 18:22:48 +0200 Subject: [PATCH 157/185] renamings based on review --- .../src/semmle/javascript/frameworks/SQL.qll | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll index 28ff91c0380..9089848b42f 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll @@ -66,11 +66,7 @@ private module MySql { /** A call to the `escape` or `escapeId` method that performs SQL sanitization. */ class EscapingSanitizer extends SQL::SqlSanitizer, MethodCallExpr { EscapingSanitizer() { - this = - [mysql(), pool(), connection()] - .getMember(["escape", "escapeId"]) - .getACall() - .asExpr() and + this = [mysql(), pool(), connection()].getMember(["escape", "escapeId"]).getACall().asExpr() and input = this.getArgument(0) and output = this } @@ -100,22 +96,19 @@ private module MySql { * Provides classes modelling the `pg` package. */ private module Postgres { - /** Gets an expression of the form `require('pg').Client`. */ - API::Node newClientCallee() { result = API::moduleImport("pg").getMember("Client") } + /** Gets a reference to the `Client` constructor in the `pg` package. E.g: `require('pg').Client`. */ + API::Node newClient() { result = API::moduleImport("pg").getMember("Client") } - /** Gets an expression of the form `new require('pg').Client()`. */ - API::Node newClient() { result = newClientCallee().getInstance() } - - /** Gets a data flow node that holds a freshly created Postgres client instance. */ + /** Gets a freshly created Postgres client instance. */ API::Node client() { - result = newClient() + result = newClient().getInstance() or // pool.connect(function(err, client) { ... }) - result = newPool().getMember("connect").getParameter(0).getParameter(1) + result = pool().getMember("connect").getParameter(0).getParameter(1) } /** Gets a constructor that when invoked constructs a new connection pool. */ - API::Node newPoolCallee() { + API::Node newPool() { // new require('pg').Pool() result = API::moduleImport("pg").getMember("Pool") or @@ -124,11 +117,11 @@ private module Postgres { } /** Gets an expression that constructs a new connection pool. */ - API::Node newPool() { result = newPoolCallee().getInstance() } + API::Node pool() { result = newPool().getInstance() } /** A call to the Postgres `query` method. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { - QueryCall() { this = [client(), newPool()].getMember("query").getACall() } + QueryCall() { this = [client(), pool()].getMember("query").getACall() } override DataFlow::Node getAQueryArgument() { result = getArgument(0) } } @@ -144,8 +137,7 @@ private module Postgres { Credentials() { exists(string prop | - this = - [newClientCallee(), newPoolCallee()].getParameter(0).getMember(prop).getARhs().asExpr() and + this = [newClient(), newPool()].getParameter(0).getMember(prop).getARhs().asExpr() and ( prop = "user" and kind = "user name" or @@ -386,10 +378,7 @@ private module Spanner { */ class DatabaseRunCall extends SqlExecution { DatabaseRunCall() { - this = - database() - .getMember(["run", "runPartitionedUpdate", "runStream"]) - .getACall() + this = database().getMember(["run", "runPartitionedUpdate", "runStream"]).getACall() } } @@ -407,10 +396,7 @@ private module Spanner { */ class ExecuteSqlCall extends SqlExecution { ExecuteSqlCall() { - this = - v1SpannerClient() - .getMember(["executeSql", "executeStreamingSql"]) - .getACall() + this = v1SpannerClient().getMember(["executeSql", "executeStreamingSql"]).getACall() } override DataFlow::Node getAQueryArgument() { From d5f8cbc50c55e5a63e36e6c8111b49f0f6901d63 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Tue, 29 Sep 2020 14:24:04 +0100 Subject: [PATCH 158/185] C++: Accept test changes in unnamed entity naming --- .../lambdas/captures/elements.expected | 30 +++++++++---------- .../copy_from_prototype.expected | 12 ++++---- .../templates/CPP-202/template_args.expected | 4 +-- .../templates/decls/decls.expected | 2 +- .../library-tests/unnamed/elements.expected | 6 ++-- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/cpp/ql/test/library-tests/lambdas/captures/elements.expected b/cpp/ql/test/library-tests/lambdas/captures/elements.expected index 25bb695ea97..8ba9cad4009 100644 --- a/cpp/ql/test/library-tests/lambdas/captures/elements.expected +++ b/cpp/ql/test/library-tests/lambdas/captures/elements.expected @@ -13,9 +13,9 @@ | captures.cpp:3:5:3:5 | (constructor) | | captures.cpp:3:5:3:5 | (constructor) | | captures.cpp:3:5:3:5 | (constructor) | -| captures.cpp:3:5:3:5 | declaration of (null) | -| captures.cpp:3:5:3:5 | declaration of (null) | -| captures.cpp:3:5:3:5 | definition of (null) | +| captures.cpp:3:5:3:5 | declaration of (constructor) | +| captures.cpp:3:5:3:5 | declaration of (constructor) | +| captures.cpp:3:5:3:5 | definition of (constructor) | | captures.cpp:3:5:3:5 | definition of operator= | | captures.cpp:3:5:3:5 | operator= | | captures.cpp:3:5:5:5 | [...](...){...} | @@ -50,9 +50,9 @@ | captures.cpp:9:5:9:5 | (constructor) | | captures.cpp:9:5:9:5 | (constructor) | | captures.cpp:9:5:9:5 | (constructor) | -| captures.cpp:9:5:9:5 | declaration of (null) | -| captures.cpp:9:5:9:5 | declaration of (null) | -| captures.cpp:9:5:9:5 | definition of (null) | +| captures.cpp:9:5:9:5 | declaration of (constructor) | +| captures.cpp:9:5:9:5 | declaration of (constructor) | +| captures.cpp:9:5:9:5 | definition of (constructor) | | captures.cpp:9:5:9:5 | definition of operator= | | captures.cpp:9:5:9:5 | operator= | | captures.cpp:9:5:11:5 | [...](...){...} | @@ -87,9 +87,9 @@ | captures.cpp:15:5:15:5 | (constructor) | | captures.cpp:15:5:15:5 | (constructor) | | captures.cpp:15:5:15:5 | (constructor) | -| captures.cpp:15:5:15:5 | declaration of (null) | -| captures.cpp:15:5:15:5 | declaration of (null) | -| captures.cpp:15:5:15:5 | definition of (null) | +| captures.cpp:15:5:15:5 | declaration of (constructor) | +| captures.cpp:15:5:15:5 | declaration of (constructor) | +| captures.cpp:15:5:15:5 | definition of (constructor) | | captures.cpp:15:5:15:5 | definition of operator= | | captures.cpp:15:5:15:5 | operator= | | captures.cpp:15:5:17:5 | [...](...){...} | @@ -129,9 +129,9 @@ | captures.cpp:22:19:22:19 | Unknown literal | | captures.cpp:22:19:22:19 | constructor init of field x | | captures.cpp:22:19:22:19 | constructor init of field y | -| captures.cpp:22:19:22:19 | declaration of (null) | -| captures.cpp:22:19:22:19 | definition of (null) | -| captures.cpp:22:19:22:19 | definition of (null) | +| captures.cpp:22:19:22:19 | declaration of (constructor) | +| captures.cpp:22:19:22:19 | definition of (constructor) | +| captures.cpp:22:19:22:19 | definition of (constructor) | | captures.cpp:22:19:22:19 | definition of operator= | | captures.cpp:22:19:22:19 | operator= | | captures.cpp:22:19:22:19 | return ... | @@ -187,9 +187,9 @@ | end_pos.cpp:9:15:9:15 | (constructor) | | end_pos.cpp:9:15:9:15 | Unknown literal | | end_pos.cpp:9:15:9:15 | constructor init of field ii | -| end_pos.cpp:9:15:9:15 | declaration of (null) | -| end_pos.cpp:9:15:9:15 | definition of (null) | -| end_pos.cpp:9:15:9:15 | definition of (null) | +| end_pos.cpp:9:15:9:15 | declaration of (constructor) | +| end_pos.cpp:9:15:9:15 | definition of (constructor) | +| end_pos.cpp:9:15:9:15 | definition of (constructor) | | end_pos.cpp:9:15:9:15 | definition of operator= | | end_pos.cpp:9:15:9:15 | operator= | | end_pos.cpp:9:15:9:15 | return ... | diff --git a/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected b/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected index f5ef2d77c98..5488cf803e6 100644 --- a/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected +++ b/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected @@ -2,8 +2,8 @@ | copy_from_prototype.cpp:3:7:3:7 | a | a::a(const a &) -> void | copy_from_prototype.cpp:3:7:3:7 | a | | | copy_from_prototype.cpp:3:7:3:7 | operator= | a::operator=(a &&) -> a & | copy_from_prototype.cpp:3:7:3:7 | a | | | copy_from_prototype.cpp:3:7:3:7 | operator= | a::operator=(const a &) -> a & | copy_from_prototype.cpp:3:7:3:7 | a | | -| copy_from_prototype.cpp:4:26:4:26 | a | a<>::a<(unnamed)>() -> void | copy_from_prototype.cpp:3:7:3:7 | a<> | 123 | -| copy_from_prototype.cpp:4:26:4:26 | a | a::a<(unnamed)>() -> void | copy_from_prototype.cpp:3:7:3:7 | a | | +| copy_from_prototype.cpp:4:26:4:26 | a | a<>::a<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:3:7:3:7 | a<> | 123 | +| copy_from_prototype.cpp:4:26:4:26 | a | a::a<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:3:7:3:7 | a | | | copy_from_prototype.cpp:7:7:7:7 | b | b::b() -> void | copy_from_prototype.cpp:7:7:7:7 | b | | | copy_from_prototype.cpp:7:7:7:7 | b | b::b(b &&) -> void | copy_from_prototype.cpp:7:7:7:7 | b | | | copy_from_prototype.cpp:7:7:7:7 | b | b::b(const b &) -> void | copy_from_prototype.cpp:7:7:7:7 | b | | @@ -13,8 +13,8 @@ | copy_from_prototype.cpp:13:7:13:7 | c | c::c(const c &) -> void | copy_from_prototype.cpp:13:7:13:7 | c | | | copy_from_prototype.cpp:13:7:13:7 | operator= | c::operator=(c &&) -> c & | copy_from_prototype.cpp:13:7:13:7 | c | | | copy_from_prototype.cpp:13:7:13:7 | operator= | c::operator=(const c &) -> c & | copy_from_prototype.cpp:13:7:13:7 | c | | -| copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed)>() -> void | copy_from_prototype.cpp:13:7:13:7 | c | X | -| copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed)>() -> void | copy_from_prototype.cpp:13:7:13:7 | c | | +| copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:13:7:13:7 | c | X | +| copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:13:7:13:7 | c | | | copy_from_prototype.cpp:17:7:17:7 | d | d::d() -> void | copy_from_prototype.cpp:17:7:17:7 | d | | | copy_from_prototype.cpp:17:7:17:7 | d | d::d(const d &) -> void | copy_from_prototype.cpp:17:7:17:7 | d | | | copy_from_prototype.cpp:17:7:17:7 | d | d::d(d &&) -> void | copy_from_prototype.cpp:17:7:17:7 | d | | @@ -24,7 +24,7 @@ | copy_from_prototype.cpp:22:8:22:8 | e | e::e(e &&) -> void | copy_from_prototype.cpp:22:8:22:8 | e | | | copy_from_prototype.cpp:22:8:22:8 | operator= | e::operator=(const e &) -> e & | copy_from_prototype.cpp:22:8:22:8 | e | | | copy_from_prototype.cpp:22:8:22:8 | operator= | e::operator=(e &&) -> e & | copy_from_prototype.cpp:22:8:22:8 | e | | -| copy_from_prototype.cpp:23:26:23:26 | e | e::e<(unnamed)>() -> void | copy_from_prototype.cpp:22:8:22:8 | e | 456 | -| copy_from_prototype.cpp:26:35:26:43 | e | e::e<(unnamed)>() -> void | copy_from_prototype.cpp:22:8:22:8 | e | 456 | +| copy_from_prototype.cpp:23:26:23:26 | e | e::e<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:22:8:22:8 | e | 456 | +| copy_from_prototype.cpp:26:35:26:43 | e | e::e<(unnamed template parameter)>() -> void | copy_from_prototype.cpp:22:8:22:8 | e | 456 | | file://:0:0:0:0 | operator= | __va_list_tag::operator=(__va_list_tag &&) -> __va_list_tag & | file://:0:0:0:0 | __va_list_tag | | | file://:0:0:0:0 | operator= | __va_list_tag::operator=(const __va_list_tag &) -> __va_list_tag & | file://:0:0:0:0 | __va_list_tag | | diff --git a/cpp/ql/test/library-tests/templates/CPP-202/template_args.expected b/cpp/ql/test/library-tests/templates/CPP-202/template_args.expected index f38eef610e4..c1d6e7fe9fa 100644 --- a/cpp/ql/test/library-tests/templates/CPP-202/template_args.expected +++ b/cpp/ql/test/library-tests/templates/CPP-202/template_args.expected @@ -1,7 +1,7 @@ | file://:0:0:0:0 | __va_list_tag | | | test.cpp:3:8:3:9 | s1<> | {...} | -| test.cpp:3:8:3:9 | s1<> | (null) | +| test.cpp:3:8:3:9 | s1<> | (unnamed template parameter constant) | | test.cpp:5:8:5:9 | s2 | T | | test.cpp:5:8:5:9 | s2 | T | -| test.cpp:7:8:7:9 | s3> | (unnamed) | +| test.cpp:7:8:7:9 | s3> | (unnamed template parameter) | | test.cpp:7:8:7:9 | s3> | T | diff --git a/cpp/ql/test/library-tests/templates/decls/decls.expected b/cpp/ql/test/library-tests/templates/decls/decls.expected index 43a691e53c9..cfb16b63828 100644 --- a/cpp/ql/test/library-tests/templates/decls/decls.expected +++ b/cpp/ql/test/library-tests/templates/decls/decls.expected @@ -7,7 +7,7 @@ | decls.cpp:4:30:4:34 | p#0 | | decls.cpp:4:30:4:34 | p#0 | | decls.cpp:6:17:6:17 | f | -| decls.cpp:8:18:8:18 | (unnamed) | +| decls.cpp:8:18:8:18 | (unnamed template parameter) | | decls.cpp:8:25:8:25 | g | | file://:0:0:0:0 | __va_list_tag | | file://:0:0:0:0 | auto | diff --git a/cpp/ql/test/library-tests/unnamed/elements.expected b/cpp/ql/test/library-tests/unnamed/elements.expected index 8df7d4578a2..52176aa66df 100644 --- a/cpp/ql/test/library-tests/unnamed/elements.expected +++ b/cpp/ql/test/library-tests/unnamed/elements.expected @@ -1,6 +1,6 @@ | file://:0:0:0:0 | | Other | | file://:0:0:0:0 | (global namespace) | Other | -| file://:0:0:0:0 | | Other | +| file://:0:0:0:0 | (unnamed global/namespace variable) | Other | | file://:0:0:0:0 | _Complex __float128 | Other | | file://:0:0:0:0 | _Complex double | Other | | file://:0:0:0:0 | _Complex float | Other | @@ -111,8 +111,8 @@ | test.c:0:0:0:0 | test.c | Other | | test.c:2:6:2:6 | a | Other | | test.c:2:6:2:6 | definition of a | Other | -| test.c:2:10:2:18 | | Variable access | +| test.c:2:10:2:18 | (unnamed global/namespace variable) | Variable access | | test.c:2:10:2:18 | array to pointer conversion | Other | | test.c:2:10:2:18 | initializer for a | Other | -| test.c:2:17:2:18 | initializer for | Other | +| test.c:2:17:2:18 | initializer for (unnamed global/namespace variable) | Other | | test.c:2:17:2:18 | {...} | Other | From 68f6d9332523df633415e8550818a760c57f985b Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Wed, 30 Sep 2020 09:49:56 +0200 Subject: [PATCH 159/185] C++: Autoformat fixup --- cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 9bbedfc16a8..09409eb30f2 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -184,7 +184,7 @@ class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode { /** * INTERNAL: do not use. - * + * * A node that represents the value of a variable after a function call that * may have changed the variable because it's passed by reference or because an * iterator for it was passed by value or by reference. From e0b25798ff46378de19b717b995f5fcc60b02293 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 30 Sep 2020 10:36:08 +0200 Subject: [PATCH 160/185] remove type-tracking from `getAReference`, and rewrite qldocs --- .../ql/src/semmle/javascript/ApiGraphs.qll | 38 ++++++++++++------- .../src/semmle/javascript/frameworks/SQL.qll | 2 +- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll index 4cb1f4cb8e0..8d8e124259e 100644 --- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll @@ -21,34 +21,44 @@ module API { */ class Node extends Impl::TApiNode { /** - * Gets a `SourceNode` corresponding to a use of the API component represented by this node. + * Gets a data-flow corresponding to a use of the API component represented by this node. * * For example, `require('fs').readFileSync` is a use of the function `readFileSync` from the - * `fs` module, and `require('fs').readFileSync(file)` is a use of the result of that function. + * `fs` module, and `require('fs').readFileSync(file)` is a use of the return of that function. + * + * The use is type-tracked, meaning that in `f(obj.foo); function f(x) {};` both `obj.foo` and + * `x` are uses of the `foo` member from `obj`. * * As another example, in the assignment `exports.plusOne = (x) => x+1` the two references to * `x` are uses of the first parameter of `plusOne`. - * - * Note: The result from this predicate is always a `DataFlow::SourceǸode`, use `getAUse()` if - * you want to follow purely local data-flow and get all `DataFlow::Node`s that corrospond to a - * use of this API node. */ - DataFlow::SourceNode getAReference() { - exists(DataFlow::SourceNode src | Impl::use(this, src) | result = Impl::trackUseNode(src)) + DataFlow::Node getAUse() { + exists(DataFlow::SourceNode src | Impl::use(this, src) | + Impl::trackUseNode(src).flowsTo(result) + ) } /** - * Gets a data-flow node corresponding to a use of the API component represented by this node. + * Gets a reference to the API component represented by this node. * - * This predicate is similar to `getAReference`, except this prediate also follows purely local - * data-flow. + * For example, `require('fs').readFileSync` is a reference to the `readFileSync` member from the + * `fs` module. + * + * No local data-flow or type-tracking happens on the result, which means that in + * `const x = fs.readFile` only `fs.readFile` is a reference to the `readFile` member of `fs`, + * neither `x` nor any node that `x` flows to is a reference to this API component. */ - DataFlow::Node getAUse() { getAReference().flowsTo(result) } + DataFlow::SourceNode getAReference() { Impl::use(this, result) } /** - * Gets a call to a use of the API component represented by this node. + * Gets a call to the function represented by this API component. */ - DataFlow::CallNode getACall() { result = getAReference().getACall() } + DataFlow::CallNode getACall() { result = getReturn().getAReference() } + + /** + * Gets an instantiation of the function represented by this API component. + */ + DataFlow::NewNode getAnInstantiation() { result = getInstance().getAReference() } /** * Gets a data-flow node corresponding to the right-hand side of a definition of the API diff --git a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll index 9089848b42f..86393cf5d58 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll @@ -300,7 +300,7 @@ private module Sequelize { Credentials() { exists(NewExpr ne, string prop | - ne = sequelize().getAReference().getAnInstantiation().asExpr() and + ne = sequelize().getAnInstantiation().asExpr() and ( this = ne.getArgument(1) and prop = "username" or From aa6fad558cfa863c24cc2857c3e57414a8391261 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 30 Sep 2020 11:15:53 +0200 Subject: [PATCH 161/185] Python: Minor cleanup in taint-step tests --- .../defaultAdditionalTaintStep-py3/test_string.py | 4 ++-- .../tainttracking/defaultAdditionalTaintStep/test_string.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_string.py b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_string.py index 530d33a24c5..2f63f8af460 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_string.py +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_string.py @@ -22,7 +22,7 @@ def str_methods(): def binary_decode_encode(): - print("\n#percent_fmt") + print("\n# binary_decode_encode") tb = TAINTED_BYTES import base64 @@ -42,7 +42,7 @@ def binary_decode_encode(): def f_strings(): - print("\n#f_strings") + print("\n# f_strings") ts = TAINTED_STRING ensure_tainted(f"foo {ts} bar") diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py index 4ede960d2f8..91f69904a97 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py @@ -107,7 +107,7 @@ def non_syntactic(): def percent_fmt(): - print("\n#percent_fmt") + print("\n# percent_fmt") ts = TAINTED_STRING tainted_fmt = ts + " %s %s" ensure_tainted( @@ -118,7 +118,7 @@ def percent_fmt(): def binary_decode_encode(): - print("\n#percent_fmt") + print("\n# binary_decode_encode") tb = TAINTED_BYTES import base64 From efa248471831fe521f88a781d7f61ee9ea7f2b13 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 30 Sep 2020 11:16:15 +0200 Subject: [PATCH 162/185] Python: Add taint test for os.path.join Surprisingly the first two just worked, due to our very general handling of any `join` methods :D --- .../dataflow/internal/TaintTrackingPrivate.qll | 2 +- .../defaultAdditionalTaintStep/TestTaint.expected | 3 +++ .../defaultAdditionalTaintStep/test_string.py | 12 ++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll b/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll index 68e1e9fc283..4087ab1b964 100644 --- a/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll @@ -101,7 +101,7 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT nodeFrom.getNode() = object and method_name in ["partition", "rpartition", "rsplit", "split", "splitlines"] or - // List[str] -> str + // Iterable[str] -> str // TODO: check if these should be handled differently in regards to content method_name = "join" and nodeFrom.getNode() = call.getArg(0) diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/TestTaint.expected b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/TestTaint.expected index c1a903382df..83424353c41 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/TestTaint.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/TestTaint.expected @@ -137,6 +137,9 @@ | test_string.py:143 | fail | binary_decode_encode | base64.decodestring(..) | | test_string.py:148 | fail | binary_decode_encode | quopri.encodestring(..) | | test_string.py:149 | fail | binary_decode_encode | quopri.decodestring(..) | +| test_string.py:158 | ok | test_os_path_join | os.path.join(..) | +| test_string.py:159 | ok | test_os_path_join | os.path.join(..) | +| test_string.py:160 | fail | test_os_path_join | os.path.join(..) | | test_unpacking.py:16 | ok | unpacking | a | | test_unpacking.py:16 | ok | unpacking | b | | test_unpacking.py:16 | ok | unpacking | c | diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py index 91f69904a97..a7422f0ad4c 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py @@ -150,6 +150,17 @@ def binary_decode_encode(): ) +def test_os_path_join(): + import os + print("\n# test_os_path_join") + ts = TAINTED_STRING + ensure_tainted( + os.path.join(ts, "foo", "bar"), + os.path.join(ts), + os.path.join("foo", "bar", ts), + ) + + # Make tests runable str_operations() @@ -157,3 +168,4 @@ str_methods() non_syntactic() percent_fmt() binary_decode_encode() +test_os_path_join() From 0542c3b91e299f6d2efdff9e624106ddb63cd522 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 30 Sep 2020 11:42:36 +0200 Subject: [PATCH 163/185] Python: Model os.path.join and add taint-step --- .../internal/TaintTrackingPrivate.qll | 1 - .../semmle/python/frameworks/Stdlib.qll | 50 +++++++++++++++++++ .../TestTaint.expected | 2 +- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll b/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll index 4087ab1b964..d49ff207370 100644 --- a/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll @@ -130,7 +130,6 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT // f-strings nodeTo.asExpr().(Fstring).getAValue() = nodeFrom.asExpr() // TODO: Handle encode/decode from base64/quopri - // TODO: Handle os.path.join // TODO: Handle functions in https://docs.python.org/3/library/binascii.html } diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 7587e9187b2..05ea6763bd8 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -5,9 +5,11 @@ private import python private import experimental.dataflow.DataFlow +private import experimental.dataflow.TaintTracking private import experimental.dataflow.RemoteFlowSources private import experimental.semmle.python.Concepts +/** Provides models for the Python standard library. */ private module Stdlib { /** Gets a reference to the `os` module. */ DataFlow::Node os(DataFlow::TypeTracker t) { @@ -20,6 +22,7 @@ private module Stdlib { /** Gets a reference to the `os` module. */ DataFlow::Node os() { result = os(DataFlow::TypeTracker::end()) } + /** Provides models for the `os` module. */ module os { /** Gets a reference to the `os.system` function. */ DataFlow::Node system(DataFlow::TypeTracker t) { @@ -48,6 +51,41 @@ private module Stdlib { /** Gets a reference to the `os.popen` function. */ DataFlow::Node popen() { result = os::popen(DataFlow::TypeTracker::end()) } + + /** Gets a reference to the `os.path` module. */ + private DataFlow::Node path(DataFlow::TypeTracker t) { + t.start() and + ( + result = DataFlow::importMember("os", "path") + or + result = DataFlow::importModule("os.path") + ) + or + t.startInAttr("path") and + result = os() + or + exists(DataFlow::TypeTracker t2 | result = path(t2).track(t2, t)) + } + + /** Gets a reference to the `os.path` module. */ + DataFlow::Node path() { result = path(DataFlow::TypeTracker::end()) } + + /** Provides models for the `os.path` module */ + module path { + /** Gets a reference to the `os.path.join` function. */ + private DataFlow::Node join(DataFlow::TypeTracker t) { + t.start() and + result = DataFlow::importMember("os.path", "join") + or + t.startInAttr("join") and + result = os::path() + or + exists(DataFlow::TypeTracker t2 | result = join(t2).track(t2, t)) + } + + /** Gets a reference to the `os.join` module. */ + DataFlow::Node join() { result = join(DataFlow::TypeTracker::end()) } + } } /** @@ -73,4 +111,16 @@ private module Stdlib { result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0) } } + + /** An additional taint step for calls to `os.path.join` */ + private class OsPathJoinCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { + override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(CallNode call | + nodeTo.asCfgNode() = call and + call.getFunction() = os::path::join().asCfgNode() and + call.getAnArg() = nodeFrom.asCfgNode() + ) + // TODO: Handle pathlib (like we do for os.path.join) + } + } } diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/TestTaint.expected b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/TestTaint.expected index 83424353c41..28dea1f3851 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/TestTaint.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/TestTaint.expected @@ -139,7 +139,7 @@ | test_string.py:149 | fail | binary_decode_encode | quopri.decodestring(..) | | test_string.py:158 | ok | test_os_path_join | os.path.join(..) | | test_string.py:159 | ok | test_os_path_join | os.path.join(..) | -| test_string.py:160 | fail | test_os_path_join | os.path.join(..) | +| test_string.py:160 | ok | test_os_path_join | os.path.join(..) | | test_unpacking.py:16 | ok | unpacking | a | | test_unpacking.py:16 | ok | unpacking | b | | test_unpacking.py:16 | ok | unpacking | c | From 1595fed2d6760b11953238c946796f918ba0f613 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 30 Sep 2020 11:44:37 +0200 Subject: [PATCH 164/185] Python: Add preliminary taint tests for pathlib --- .../TestTaint.expected | 12 ++++ .../test_pathlib.py | 60 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_pathlib.py diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/TestTaint.expected b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/TestTaint.expected index 7bca8bd1604..ec24283300f 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/TestTaint.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/TestTaint.expected @@ -1,6 +1,18 @@ | test_collections.py:16 | ok | test_access | tainted_list.copy() | | test_collections.py:24 | ok | list_clear | tainted_list | | test_collections.py:27 | fail | list_clear | tainted_list | +| test_pathlib.py:26 | fail | test_basic | tainted_path | +| test_pathlib.py:28 | fail | test_basic | tainted_pure_path | +| test_pathlib.py:29 | fail | test_basic | tainted_pure_posix_path | +| test_pathlib.py:30 | fail | test_basic | tainted_pure_windows_path | +| test_pathlib.py:32 | fail | test_basic | BinaryExpr | +| test_pathlib.py:33 | fail | test_basic | BinaryExpr | +| test_pathlib.py:35 | fail | test_basic | tainted_path.joinpath(..) | +| test_pathlib.py:36 | fail | test_basic | pathlib.Path(..).joinpath(..) | +| test_pathlib.py:37 | fail | test_basic | pathlib.Path(..).joinpath(..) | +| test_pathlib.py:39 | fail | test_basic | str(..) | +| test_pathlib.py:49 | fail | test_basic | tainted_posix_path | +| test_pathlib.py:55 | fail | test_basic | tainted_windows_path | | test_string.py:17 | ok | str_methods | ts.casefold() | | test_string.py:19 | ok | str_methods | ts.format_map(..) | | test_string.py:20 | ok | str_methods | "{unsafe}".format_map(..) | diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_pathlib.py b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_pathlib.py new file mode 100644 index 00000000000..802bfd98b12 --- /dev/null +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_pathlib.py @@ -0,0 +1,60 @@ +# Add taintlib to PATH so it can be imported during runtime without any hassle +import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__)))) +from taintlib import * + +# This has no runtime impact, but allows autocomplete to work +from typing import Iterable, TYPE_CHECKING +if TYPE_CHECKING: + from ..taintlib import * + +# Actual tests + +import pathlib +# pathlib was added in 3.4 + +def test_basic(): + print("\n# test_basic") + ts = TAINTED_STRING + + tainted_path = pathlib.Path(ts) + + tainted_pure_path = pathlib.PurePath(ts) + tainted_pure_posix_path = pathlib.PurePosixPath(ts) + tainted_pure_windows_path = pathlib.PureWindowsPath(ts) + + ensure_tainted( + tainted_path, + + tainted_pure_path, + tainted_pure_posix_path, + tainted_pure_windows_path, + + pathlib.Path("foo") / ts, + ts / pathlib.Path("foo"), + + tainted_path.joinpath("foo", "bar"), + pathlib.Path("foo").joinpath(tainted_path, "bar"), + pathlib.Path("foo").joinpath("bar", tainted_path), + + str(tainted_path), + + # TODO: Tainted methods and attributes + # https://docs.python.org/3.8/library/pathlib.html#methods-and-properties + ) + + if os.name == "posix": + tainted_posix_path = pathlib.PosixPath(ts) + + ensure_tainted( + tainted_posix_path, + ) + + if os.name == "nt": + tainted_windows_path = pathlib.WindowsPath(ts) + ensure_tainted( + tainted_windows_path, + ) + +# Make tests runable + +test_basic() From a2d12f0440762bee7c2a61d8a26e96fb7f11efc8 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 30 Sep 2020 13:00:10 +0200 Subject: [PATCH 165/185] Python: Update CommandInjection.expected --- .../CWE-078/CommandInjection.expected | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected index 7f71e143109..8e79a98c6c3 100644 --- a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected @@ -1,11 +1,25 @@ edges | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | +| command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | +| command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | +| command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:972:11:972:13 | SSA variable cmd | +| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:972:11:972:13 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:981:33:981:35 | ControlFlowNode for cmd | +| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:972:11:972:13 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:987:33:987:35 | ControlFlowNode for cmd | nodes | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| command_injection.py:25:23:25:25 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:972:11:972:13 | SSA variable cmd | semmle.label | SSA variable cmd | +| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:981:33:981:35 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | +| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:987:33:987:35 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | #select | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:25:23:25:25 | ControlFlowNode for cmd | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | a user-provided value | +| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:981:33:981:35 | ControlFlowNode for cmd | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:981:33:981:35 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | a user-provided value | +| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:987:33:987:35 | ControlFlowNode for cmd | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:987:33:987:35 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | a user-provided value | From b720bfdd11d39d776e7fb84d7f7d9772fff1cd13 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 30 Sep 2020 13:15:06 +0200 Subject: [PATCH 166/185] Apply suggestions from code review Co-authored-by: Asger F --- javascript/ql/src/semmle/javascript/ApiGraphs.qll | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll index 8d8e124259e..d1995255e84 100644 --- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll @@ -21,13 +21,13 @@ module API { */ class Node extends Impl::TApiNode { /** - * Gets a data-flow corresponding to a use of the API component represented by this node. + * Gets a data-flow node corresponding to a use of the API component represented by this node. * * For example, `require('fs').readFileSync` is a use of the function `readFileSync` from the * `fs` module, and `require('fs').readFileSync(file)` is a use of the return of that function. * - * The use is type-tracked, meaning that in `f(obj.foo); function f(x) {};` both `obj.foo` and - * `x` are uses of the `foo` member from `obj`. + * This includes indirect uses found via data flow, meaning that in + * `f(obj.foo); function f(x) {};` both `obj.foo` and `x` are uses of the `foo` member from `obj`. * * As another example, in the assignment `exports.plusOne = (x) => x+1` the two references to * `x` are uses of the first parameter of `plusOne`. @@ -44,9 +44,10 @@ module API { * For example, `require('fs').readFileSync` is a reference to the `readFileSync` member from the * `fs` module. * - * No local data-flow or type-tracking happens on the result, which means that in - * `const x = fs.readFile` only `fs.readFile` is a reference to the `readFile` member of `fs`, - * neither `x` nor any node that `x` flows to is a reference to this API component. + * Unlike `getAUse()`, this predicate only gets the immediate references, not the indirect uses + * found via data flow. This means that in `const x = fs.readFile` only `fs.readFile` is a reference + * to the `readFile` member of `fs`, neither `x` nor any node that `x` flows to is a reference to + * this API component. */ DataFlow::SourceNode getAReference() { Impl::use(this, result) } @@ -56,7 +57,7 @@ module API { DataFlow::CallNode getACall() { result = getReturn().getAReference() } /** - * Gets an instantiation of the function represented by this API component. + * Gets a `new` call to the function represented by this API component. */ DataFlow::NewNode getAnInstantiation() { result = getInstance().getAReference() } From 9c1253c8af933e36fa921bd91e44e6cb430834fe Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 30 Sep 2020 13:29:21 +0200 Subject: [PATCH 167/185] Python: Remove flow out of CommandInjection sinks --- .../Security-new-dataflow/CWE-078/CommandInjection.ql | 11 +++++++++++ .../CWE-078/CommandInjection.expected | 10 ---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql index 3f200b77516..6e241a81a6a 100755 --- a/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql +++ b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql @@ -29,6 +29,17 @@ class CommandInjectionConfiguration extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink = any(SystemCommandExecution e).getCommand() } + + // Since the implementation of os.popen looks like + // ```py + // def popen(cmd, mode="r", buffering=-1): + // ... + // proc = subprocess.Popen(cmd, ...) + // ``` + // any time we would report flow to the `os.popen` sink, we can ALSO report the flow + // from the `cmd` parameter to the `subprocess.Popen` sink -- obviously we don't want + // that, so to prevent that we remove any taint edges out of a sink. + override predicate isSanitizerOut(DataFlow::Node node) { isSink(node) } } from CommandInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected index 8e79a98c6c3..68088064555 100644 --- a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected @@ -2,10 +2,6 @@ edges | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | -| command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | -| command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:972:11:972:13 | SSA variable cmd | -| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:972:11:972:13 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:981:33:981:35 | ControlFlowNode for cmd | -| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:972:11:972:13 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:987:33:987:35 | ControlFlowNode for cmd | nodes | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | @@ -13,13 +9,7 @@ nodes | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | -| command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | -| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:972:11:972:13 | SSA variable cmd | semmle.label | SSA variable cmd | -| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:981:33:981:35 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | -| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:987:33:987:35 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | #select | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | a user-provided value | -| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:981:33:981:35 | ControlFlowNode for cmd | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:981:33:981:35 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | a user-provided value | -| file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:987:33:987:35 | ControlFlowNode for cmd | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | file:///home/rasmus/.pyenv/versions/3.8.0/lib/python3.8/os.py:987:33:987:35 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | a user-provided value | From b24e9590333be9252cd00cfa396a7fa76dd944e5 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 30 Sep 2020 13:29:47 +0200 Subject: [PATCH 168/185] add `getAnInvocation` to the ApiGraphs API --- javascript/ql/src/semmle/javascript/ApiGraphs.qll | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll index d1995255e84..2ffdca7b92f 100644 --- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll @@ -61,6 +61,11 @@ module API { */ DataFlow::NewNode getAnInstantiation() { result = getInstance().getAReference() } + /** + * Gets an invocation (with our without `new`) to the function represented by this API component. + */ + DataFlow::InvokeNode getAnInvocation() { result = getACall() or result = getAnInstantiation() } + /** * Gets a data-flow node corresponding to the right-hand side of a definition of the API * component represented by this node. From 4adc26eb629f373fcfe7a9be200436bc087d8ac4 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 30 Sep 2020 13:31:56 +0200 Subject: [PATCH 169/185] Python: Fix command injection example code `subprocess.Popen(["ls", "-la"], shell=True)` correspond to running `sh -c "ls" -la` So it doesn't follow the pattern of the rest of the test file. --- .../CWE-078/CommandInjection.expected | 4 ++++ .../Security-new-dataflow/CWE-078/command_injection.py | 2 +- .../Security/CWE-078/CommandInjection.expected | 10 +++++----- .../query-tests/Security/CWE-078/command_injection.py | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected index 68088064555..665617aa713 100644 --- a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected @@ -1,15 +1,19 @@ edges | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | +| command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | nodes | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | #select | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | a user-provided value | diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py index d50baf6563b..ce533b5e1ee 100644 --- a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py @@ -16,7 +16,7 @@ def command_injection1(): def command_injection2(): files = request.args.get('files', '') # Don't let files be `; rm -rf /` - subprocess.Popen(["ls", files], shell = True) + subprocess.Popen("ls " + files, shell=True) @app.route("/command3") diff --git a/python/ql/test/query-tests/Security/CWE-078/CommandInjection.expected b/python/ql/test/query-tests/Security/CWE-078/CommandInjection.expected index 3d1cf22a2d1..d417f55c68c 100644 --- a/python/ql/test/query-tests/Security/CWE-078/CommandInjection.expected +++ b/python/ql/test/query-tests/Security/CWE-078/CommandInjection.expected @@ -7,10 +7,10 @@ edges | command_injection.py:12:23:12:27 | externally controlled string | command_injection.py:12:15:12:27 | externally controlled string | | command_injection.py:17:13:17:24 | dict of externally controlled string | command_injection.py:17:13:17:41 | externally controlled string | | command_injection.py:17:13:17:24 | dict of externally controlled string | command_injection.py:17:13:17:41 | externally controlled string | -| command_injection.py:17:13:17:41 | externally controlled string | command_injection.py:19:29:19:33 | externally controlled string | -| command_injection.py:17:13:17:41 | externally controlled string | command_injection.py:19:29:19:33 | externally controlled string | -| command_injection.py:19:29:19:33 | externally controlled string | command_injection.py:19:22:19:34 | sequence of externally controlled string | -| command_injection.py:19:29:19:33 | externally controlled string | command_injection.py:19:22:19:34 | sequence of externally controlled string | +| command_injection.py:17:13:17:41 | externally controlled string | command_injection.py:19:30:19:34 | externally controlled string | +| command_injection.py:17:13:17:41 | externally controlled string | command_injection.py:19:30:19:34 | externally controlled string | +| command_injection.py:19:30:19:34 | externally controlled string | command_injection.py:19:22:19:34 | externally controlled string | +| command_injection.py:19:30:19:34 | externally controlled string | command_injection.py:19:22:19:34 | externally controlled string | | command_injection.py:24:11:24:22 | dict of externally controlled string | command_injection.py:24:11:24:37 | externally controlled string | | command_injection.py:24:11:24:22 | dict of externally controlled string | command_injection.py:24:11:24:37 | externally controlled string | | command_injection.py:24:11:24:37 | externally controlled string | command_injection.py:25:23:25:25 | externally controlled string | @@ -25,6 +25,6 @@ edges | command_injection.py:32:22:32:26 | externally controlled string | command_injection.py:32:14:32:26 | externally controlled string | #select | command_injection.py:12:15:12:27 | BinaryExpr | command_injection.py:10:13:10:24 | dict of externally controlled string | command_injection.py:12:15:12:27 | externally controlled string | This command depends on $@. | command_injection.py:10:13:10:24 | Attribute | a user-provided value | -| command_injection.py:19:22:19:34 | List | command_injection.py:17:13:17:24 | dict of externally controlled string | command_injection.py:19:22:19:34 | sequence of externally controlled string | This command depends on $@. | command_injection.py:17:13:17:24 | Attribute | a user-provided value | +| command_injection.py:19:22:19:34 | BinaryExpr | command_injection.py:17:13:17:24 | dict of externally controlled string | command_injection.py:19:22:19:34 | externally controlled string | This command depends on $@. | command_injection.py:17:13:17:24 | Attribute | a user-provided value | | command_injection.py:25:22:25:36 | List | command_injection.py:24:11:24:22 | dict of externally controlled string | command_injection.py:25:22:25:36 | first item in sequence of externally controlled string | This command depends on $@. | command_injection.py:24:11:24:22 | Attribute | a user-provided value | | command_injection.py:32:14:32:26 | BinaryExpr | command_injection.py:30:13:30:24 | dict of externally controlled string | command_injection.py:32:14:32:26 | externally controlled string | This command depends on $@. | command_injection.py:30:13:30:24 | Attribute | a user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-078/command_injection.py b/python/ql/test/query-tests/Security/CWE-078/command_injection.py index 2122a076ec9..ee5629c1b4a 100644 --- a/python/ql/test/query-tests/Security/CWE-078/command_injection.py +++ b/python/ql/test/query-tests/Security/CWE-078/command_injection.py @@ -16,7 +16,7 @@ def command_injection1(): def command_injection2(): files = request.args.get('files', '') # Don't let files be `; rm -rf /` - subprocess.Popen(["ls", files], shell = True) + subprocess.Popen("ls " + files, shell=True) @app.route("/command3") From 03d8fc7296b18e5244d2b72eb3b7f2634a40bb3f Mon Sep 17 00:00:00 2001 From: Faten Healy <5361987+fatenhealy@users.noreply.github.com> Date: Wed, 30 Sep 2020 22:18:36 +1000 Subject: [PATCH 170/185] changed to AES --- python/ql/src/Security/CWE-327/examples/broken_crypto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/Security/CWE-327/examples/broken_crypto.py b/python/ql/src/Security/CWE-327/examples/broken_crypto.py index 690ed0096b1..6e229339cdf 100644 --- a/python/ql/src/Security/CWE-327/examples/broken_crypto.py +++ b/python/ql/src/Security/CWE-327/examples/broken_crypto.py @@ -1,4 +1,4 @@ -from Crypto.Cipher import DES, Blowfish +from Crypto.Cipher import DES, AES cipher = DES.new(SECRET_KEY) From eb973b39fe780a8915bd90e8a5573a0530200c2a Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 30 Sep 2020 15:12:17 +0200 Subject: [PATCH 171/185] Update javascript/ql/src/semmle/javascript/frameworks/SQL.qll Co-authored-by: Max Schaefer <54907921+max-schaefer@users.noreply.github.com> --- javascript/ql/src/semmle/javascript/frameworks/SQL.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll index 86393cf5d58..c94750e5b3d 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll @@ -96,7 +96,7 @@ private module MySql { * Provides classes modelling the `pg` package. */ private module Postgres { - /** Gets a reference to the `Client` constructor in the `pg` package. E.g: `require('pg').Client`. */ + /** Gets a reference to the `Client` constructor in the `pg` package, for example `require('pg').Client`. */ API::Node newClient() { result = API::moduleImport("pg").getMember("Client") } /** Gets a freshly created Postgres client instance. */ From bfb653a34a02719211564a41d1dcf24056bfb75d Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 30 Sep 2020 15:15:27 +0200 Subject: [PATCH 172/185] rename getAReference to getAnImmediateUse --- javascript/ql/src/semmle/javascript/ApiGraphs.qll | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll index 2ffdca7b92f..16b459c3cae 100644 --- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll @@ -39,27 +39,27 @@ module API { } /** - * Gets a reference to the API component represented by this node. + * Gets an immediate use of the API component represented by this node. * - * For example, `require('fs').readFileSync` is a reference to the `readFileSync` member from the - * `fs` module. + * For example, `require('fs').readFileSync` is a an immediate use of the `readFileSync` member + * from the `fs` module. * * Unlike `getAUse()`, this predicate only gets the immediate references, not the indirect uses * found via data flow. This means that in `const x = fs.readFile` only `fs.readFile` is a reference * to the `readFile` member of `fs`, neither `x` nor any node that `x` flows to is a reference to * this API component. */ - DataFlow::SourceNode getAReference() { Impl::use(this, result) } + DataFlow::SourceNode getAnImmediateUse() { Impl::use(this, result) } /** * Gets a call to the function represented by this API component. */ - DataFlow::CallNode getACall() { result = getReturn().getAReference() } + DataFlow::CallNode getACall() { result = getReturn().getAnImmediateUse() } /** * Gets a `new` call to the function represented by this API component. */ - DataFlow::NewNode getAnInstantiation() { result = getInstance().getAReference() } + DataFlow::NewNode getAnInstantiation() { result = getInstance().getAnImmediateUse() } /** * Gets an invocation (with our without `new`) to the function represented by this API component. From c4a2e1d6d169429349df26df5151c4ad51401408 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 30 Sep 2020 17:24:50 +0200 Subject: [PATCH 173/185] Python: Rewrite attribute lookup helpers for better performance Not that they actually had a huge problem right now, just that using the old pattern HAS lead to bad performance in the past. See https://github.com/github/codeql/pull/4361 --- .../semmle/python/frameworks/Stdlib.qll | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index a452ecfeb1f..dc5977ecf27 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -29,7 +29,7 @@ private module Stdlib { * * For example, using `attr_name = "system"` will get all uses of `os.system`. */ - private DataFlow::Node os_attr(string attr_name, DataFlow::TypeTracker t) { + private DataFlow::Node os_attr(DataFlow::TypeTracker t, string attr_name) { attr_name in ["system", "popen", // exec "execl", "execle", "execlp", "execlpe", "execv", "execve", "execvp", "execvpe", @@ -41,10 +41,24 @@ private module Stdlib { result = DataFlow::importMember("os", attr_name) or t.startInAttr(attr_name) and - result = os() - or - exists(DataFlow::TypeTracker t2 | result = os_attr(attr_name, t2).track(t2, t)) + result = DataFlow::importModule("os") ) + or + // Due to bad performance when using normal setup with `os_attr(t2, attr_name).track(t2, t)` + // we have inlined that code and forced a join + exists(DataFlow::TypeTracker t2 | + exists(DataFlow::StepSummary summary | + os_attr_first_join(t2, attr_name, result, summary) and + t = t2.append(summary) + ) + ) + } + + pragma[nomagic] + private predicate os_attr_first_join( + DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary + ) { + DataFlow::StepSummary::step(os_attr(t2, attr_name), res, summary) } /** @@ -54,7 +68,7 @@ private module Stdlib { * For example, using `"system"` will get all uses of `os.system`. */ private DataFlow::Node os_attr(string attr_name) { - result = os_attr(attr_name, DataFlow::TypeTracker::end()) + result = os_attr(DataFlow::TypeTracker::end(), attr_name) } /** @@ -148,17 +162,31 @@ private module Stdlib { * * For example, using `attr_name = "Popen"` will get all uses of `subprocess.Popen`. */ - private DataFlow::Node subprocess_attr(string attr_name, DataFlow::TypeTracker t) { + private DataFlow::Node subprocess_attr(DataFlow::TypeTracker t, string attr_name) { attr_name in ["Popen", "call", "check_call", "check_output", "run"] and ( t.start() and result = DataFlow::importMember("subprocess", attr_name) or t.startInAttr(attr_name) and - result = subprocess() - or - exists(DataFlow::TypeTracker t2 | result = subprocess_attr(attr_name, t2).track(t2, t)) + result = DataFlow::importModule("subprocess") ) + or + // Due to bad performance when using normal setup with `subprocess_attr(t2, attr_name).track(t2, t)` + // we have inlined that code and forced a join + exists(DataFlow::TypeTracker t2 | + exists(DataFlow::StepSummary summary | + subprocess_attr_first_join(t2, attr_name, result, summary) and + t = t2.append(summary) + ) + ) + } + + pragma[nomagic] + private predicate subprocess_attr_first_join( + DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary + ) { + DataFlow::StepSummary::step(subprocess_attr(t2, attr_name), res, summary) } /** @@ -168,7 +196,7 @@ private module Stdlib { * For example, using `attr_name = "Popen"` will get all uses of `subprocess.Popen`. */ private DataFlow::Node subprocess_attr(string attr_name) { - result = subprocess_attr(attr_name, DataFlow::TypeTracker::end()) + result = subprocess_attr(DataFlow::TypeTracker::end(), attr_name) } /** From e0ca4dafb855e15f4cbddab272379cafa8d1115e Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Wed, 30 Sep 2020 16:31:45 +0100 Subject: [PATCH 174/185] Add support for Variable.is_constinit() --- cpp/ql/src/semmle/code/cpp/Variable.qll | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/Variable.qll b/cpp/ql/src/semmle/code/cpp/Variable.qll index 597facea442..527d8879b96 100644 --- a/cpp/ql/src/semmle/code/cpp/Variable.qll +++ b/cpp/ql/src/semmle/code/cpp/Variable.qll @@ -144,6 +144,11 @@ class Variable extends Declaration, @variable { */ predicate isConstexpr() { this.hasSpecifier("is_constexpr") } + /** + * Holds if this variable is declared `constinit`. + */ + predicate isConstinit() { this.hasSpecifier("declared_constinit") } + /** * Holds if this variable is `thread_local`. */ From e712d16e7e31273e397d88299f635d22c75aef9c Mon Sep 17 00:00:00 2001 From: Aditya Sharad Date: Wed, 30 Sep 2020 15:10:49 -0700 Subject: [PATCH 175/185] JavaScript: Track taint through RegExp.prototype.exec for URL redirection Regexp literals are currently handled, but not `RegExp` objects. --- .../ClientSideUrlRedirectCustomizations.qll | 2 +- .../ClientSideUrlRedirect.expected | 48 +++++++++++++++++++ .../CWE-601/ClientSideUrlRedirect/tst.js | 16 +++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll index 64c76a84b82..a74a9f58a0f 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll @@ -65,7 +65,7 @@ module ClientSideUrlRedirect { or exists(MethodCallExpr mce | queryAccess.asExpr() = mce and - mce = any(RegExpLiteral re).flow().(DataFlow::SourceNode).getAMethodCall("exec").asExpr() and + mce = any(DataFlow::RegExpCreationNode re).getAMethodCall("exec").asExpr() and nd.asExpr() = mce.getArgument(0) ) } diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected index b7dca317a88..b5621666d73 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected +++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected @@ -133,6 +133,30 @@ nodes | tst.js:6:34:6:50 | document.location | | tst.js:6:34:6:50 | document.location | | tst.js:6:34:6:55 | documen ... on.href | +| tst.js:10:19:10:81 | new Reg ... n.href) | +| tst.js:10:19:10:84 | new Reg ... ref)[1] | +| tst.js:10:19:10:84 | new Reg ... ref)[1] | +| tst.js:10:59:10:75 | document.location | +| tst.js:10:59:10:75 | document.location | +| tst.js:10:59:10:80 | documen ... on.href | +| tst.js:14:20:14:56 | indirec ... n.href) | +| tst.js:14:20:14:59 | indirec ... ref)[1] | +| tst.js:14:20:14:59 | indirec ... ref)[1] | +| tst.js:14:34:14:50 | document.location | +| tst.js:14:34:14:50 | document.location | +| tst.js:14:34:14:55 | documen ... on.href | +| tst.js:18:19:18:81 | new Reg ... n.href) | +| tst.js:18:19:18:84 | new Reg ... ref)[1] | +| tst.js:18:19:18:84 | new Reg ... ref)[1] | +| tst.js:18:59:18:75 | document.location | +| tst.js:18:59:18:75 | document.location | +| tst.js:18:59:18:80 | documen ... on.href | +| tst.js:22:20:22:56 | indirec ... n.href) | +| tst.js:22:20:22:59 | indirec ... ref)[1] | +| tst.js:22:20:22:59 | indirec ... ref)[1] | +| tst.js:22:34:22:50 | document.location | +| tst.js:22:34:22:50 | document.location | +| tst.js:22:34:22:55 | documen ... on.href | edges | sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url | | sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url | @@ -260,6 +284,26 @@ edges | tst.js:6:34:6:50 | document.location | tst.js:6:34:6:55 | documen ... on.href | | tst.js:6:34:6:50 | document.location | tst.js:6:34:6:55 | documen ... on.href | | tst.js:6:34:6:55 | documen ... on.href | tst.js:6:20:6:56 | indirec ... n.href) | +| tst.js:10:19:10:81 | new Reg ... n.href) | tst.js:10:19:10:84 | new Reg ... ref)[1] | +| tst.js:10:19:10:81 | new Reg ... n.href) | tst.js:10:19:10:84 | new Reg ... ref)[1] | +| tst.js:10:59:10:75 | document.location | tst.js:10:59:10:80 | documen ... on.href | +| tst.js:10:59:10:75 | document.location | tst.js:10:59:10:80 | documen ... on.href | +| tst.js:10:59:10:80 | documen ... on.href | tst.js:10:19:10:81 | new Reg ... n.href) | +| tst.js:14:20:14:56 | indirec ... n.href) | tst.js:14:20:14:59 | indirec ... ref)[1] | +| tst.js:14:20:14:56 | indirec ... n.href) | tst.js:14:20:14:59 | indirec ... ref)[1] | +| tst.js:14:34:14:50 | document.location | tst.js:14:34:14:55 | documen ... on.href | +| tst.js:14:34:14:50 | document.location | tst.js:14:34:14:55 | documen ... on.href | +| tst.js:14:34:14:55 | documen ... on.href | tst.js:14:20:14:56 | indirec ... n.href) | +| tst.js:18:19:18:81 | new Reg ... n.href) | tst.js:18:19:18:84 | new Reg ... ref)[1] | +| tst.js:18:19:18:81 | new Reg ... n.href) | tst.js:18:19:18:84 | new Reg ... ref)[1] | +| tst.js:18:59:18:75 | document.location | tst.js:18:59:18:80 | documen ... on.href | +| tst.js:18:59:18:75 | document.location | tst.js:18:59:18:80 | documen ... on.href | +| tst.js:18:59:18:80 | documen ... on.href | tst.js:18:19:18:81 | new Reg ... n.href) | +| tst.js:22:20:22:56 | indirec ... n.href) | tst.js:22:20:22:59 | indirec ... ref)[1] | +| tst.js:22:20:22:56 | indirec ... n.href) | tst.js:22:20:22:59 | indirec ... ref)[1] | +| tst.js:22:34:22:50 | document.location | tst.js:22:34:22:55 | documen ... on.href | +| tst.js:22:34:22:50 | document.location | tst.js:22:34:22:55 | documen ... on.href | +| tst.js:22:34:22:55 | documen ... on.href | tst.js:22:20:22:56 | indirec ... n.href) | #select | sanitizer.js:4:27:4:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:4:27:4:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | | sanitizer.js:16:27:16:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:16:27:16:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value | @@ -296,3 +340,7 @@ edges | tst13.js:53:28:53:28 | e | tst13.js:52:34:52:34 | e | tst13.js:53:28:53:28 | e | Untrusted URL redirection due to $@. | tst13.js:52:34:52:34 | e | user-provided value | | tst.js:2:19:2:72 | /.*redi ... ref)[1] | tst.js:2:47:2:63 | document.location | tst.js:2:19:2:72 | /.*redi ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:2:47:2:63 | document.location | user-provided value | | tst.js:6:20:6:59 | indirec ... ref)[1] | tst.js:6:34:6:50 | document.location | tst.js:6:20:6:59 | indirec ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:6:34:6:50 | document.location | user-provided value | +| tst.js:10:19:10:84 | new Reg ... ref)[1] | tst.js:10:59:10:75 | document.location | tst.js:10:19:10:84 | new Reg ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:10:59:10:75 | document.location | user-provided value | +| tst.js:14:20:14:59 | indirec ... ref)[1] | tst.js:14:34:14:50 | document.location | tst.js:14:20:14:59 | indirec ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:14:34:14:50 | document.location | user-provided value | +| tst.js:18:19:18:84 | new Reg ... ref)[1] | tst.js:18:59:18:75 | document.location | tst.js:18:19:18:84 | new Reg ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:18:59:18:75 | document.location | user-provided value | +| tst.js:22:20:22:59 | indirec ... ref)[1] | tst.js:22:34:22:50 | document.location | tst.js:22:20:22:59 | indirec ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:22:34:22:50 | document.location | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst.js b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst.js index a77903120ab..7994c1e3558 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/tst.js @@ -5,3 +5,19 @@ window.location = /.*redirect=([^&]*).*/.exec(document.location.href)[1]; var indirect = /.*redirect=([^&]*).*/; window.location = indirect.exec(document.location.href)[1]; }); + +// NOT OK +window.location = new RegExp('.*redirect=([^&]*).*').exec(document.location.href)[1]; + +(function(){ + var indirect = new RegExp('.*redirect=([^&]*).*') + window.location = indirect.exec(document.location.href)[1]; +}); + +// NOT OK +window.location = new RegExp(/.*redirect=([^&]*).*/).exec(document.location.href)[1]; + +(function(){ + var indirect = new RegExp(/.*redirect=([^&]*).*/) + window.location = indirect.exec(document.location.href)[1]; +}); From fbd62abd64307acd1e1e3a3eb72335afa4d66a98 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 1 Oct 2020 11:24:54 +0200 Subject: [PATCH 176/185] prevent crash when TemplateLiteral is used in import --- .../js/parser/TypeScriptASTConverter.java | 13 +- .../tests/ts/input/importNonStrings.ts | 3 + .../ts/output/trap/importNonStrings.ts.trap | 252 ++++++++++++++++++ 3 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 javascript/extractor/tests/ts/input/importNonStrings.ts create mode 100644 javascript/extractor/tests/ts/output/trap/importNonStrings.ts.trap diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java index 0e7031c4f9a..d2e5a0448c3 100644 --- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java +++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java @@ -1421,8 +1421,19 @@ public class TypeScriptASTConverter { importStart = advance(importStart, m.group(0)); } } + + Node rawPath = convertChild(node, "argument"); + ITypeExpression path; + if (rawPath instanceof ITypeExpression) { + path = (ITypeExpression)rawPath; + } else if (rawPath instanceof TemplateLiteral) { + // this is a type-error, so we just fall back to some behavior that does not crash the extractor. + path = new Literal(rawPath.getLoc(), TokenType.string, ((TemplateLiteral)rawPath).getQuasis().stream().map(q -> q.getRaw()).collect(Collectors.joining(""))); + } else { + throw new ParseError("Unsupported syntax in import", getSourceLocation(node).getStart()); + } + // Find the ending parenthesis in `import(path)` by skipping whitespace after `path`. - ITypeExpression path = convertChild(node, "argument"); String endSrc = loc.getSource().substring(path.getLoc().getEnd().getOffset() - loc.getStart().getOffset()); Matcher m = WHITESPACE_END_PAREN.matcher(endSrc); diff --git a/javascript/extractor/tests/ts/input/importNonStrings.ts b/javascript/extractor/tests/ts/input/importNonStrings.ts new file mode 100644 index 00000000000..6ab15963636 --- /dev/null +++ b/javascript/extractor/tests/ts/input/importNonStrings.ts @@ -0,0 +1,3 @@ +type X = import(3); +type Y = import(`Foo`); +type Z = import(Y); diff --git a/javascript/extractor/tests/ts/output/trap/importNonStrings.ts.trap b/javascript/extractor/tests/ts/output/trap/importNonStrings.ts.trap new file mode 100644 index 00000000000..fdcbc0d00a1 --- /dev/null +++ b/javascript/extractor/tests/ts/output/trap/importNonStrings.ts.trap @@ -0,0 +1,252 @@ +#10000=@"/importNonStrings.ts;sourcefile" +files(#10000,"/importNonStrings.ts","importNonStrings","ts",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +#20002=* +lines(#20002,#20001,"type X = import(3);"," +") +#20003=@"loc,{#10000},1,1,1,19" +locations_default(#20003,#10000,1,1,1,19) +hasLocation(#20002,#20003) +#20004=* +lines(#20004,#20001,"type Y = import(`Foo`);"," +") +#20005=@"loc,{#10000},2,1,2,23" +locations_default(#20005,#10000,2,1,2,23) +hasLocation(#20004,#20005) +#20006=* +lines(#20006,#20001,"type Z = import(Y);"," +") +#20007=@"loc,{#10000},3,1,3,19" +locations_default(#20007,#10000,3,1,3,19) +hasLocation(#20006,#20007) +numlines(#20001,3,3,0) +#20008=* +tokeninfo(#20008,7,#20001,0,"type") +#20009=@"loc,{#10000},1,1,1,4" +locations_default(#20009,#10000,1,1,1,4) +hasLocation(#20008,#20009) +#20010=* +tokeninfo(#20010,6,#20001,1,"X") +#20011=@"loc,{#10000},1,6,1,6" +locations_default(#20011,#10000,1,6,1,6) +hasLocation(#20010,#20011) +#20012=* +tokeninfo(#20012,8,#20001,2,"=") +#20013=@"loc,{#10000},1,8,1,8" +locations_default(#20013,#10000,1,8,1,8) +hasLocation(#20012,#20013) +#20014=* +tokeninfo(#20014,7,#20001,3,"import") +#20015=@"loc,{#10000},1,10,1,15" +locations_default(#20015,#10000,1,10,1,15) +hasLocation(#20014,#20015) +#20016=* +tokeninfo(#20016,8,#20001,4,"(") +#20017=@"loc,{#10000},1,16,1,16" +locations_default(#20017,#10000,1,16,1,16) +hasLocation(#20016,#20017) +#20018=* +tokeninfo(#20018,3,#20001,5,"3") +#20019=@"loc,{#10000},1,17,1,17" +locations_default(#20019,#10000,1,17,1,17) +hasLocation(#20018,#20019) +#20020=* +tokeninfo(#20020,8,#20001,6,")") +#20021=@"loc,{#10000},1,18,1,18" +locations_default(#20021,#10000,1,18,1,18) +hasLocation(#20020,#20021) +#20022=* +tokeninfo(#20022,8,#20001,7,";") +#20023=@"loc,{#10000},1,19,1,19" +locations_default(#20023,#10000,1,19,1,19) +hasLocation(#20022,#20023) +#20024=* +tokeninfo(#20024,7,#20001,8,"type") +#20025=@"loc,{#10000},2,1,2,4" +locations_default(#20025,#10000,2,1,2,4) +hasLocation(#20024,#20025) +#20026=* +tokeninfo(#20026,6,#20001,9,"Y") +#20027=@"loc,{#10000},2,6,2,6" +locations_default(#20027,#10000,2,6,2,6) +hasLocation(#20026,#20027) +#20028=* +tokeninfo(#20028,8,#20001,10,"=") +#20029=@"loc,{#10000},2,8,2,8" +locations_default(#20029,#10000,2,8,2,8) +hasLocation(#20028,#20029) +#20030=* +tokeninfo(#20030,7,#20001,11,"import") +#20031=@"loc,{#10000},2,10,2,15" +locations_default(#20031,#10000,2,10,2,15) +hasLocation(#20030,#20031) +#20032=* +tokeninfo(#20032,8,#20001,12,"(") +#20033=@"loc,{#10000},2,16,2,16" +locations_default(#20033,#10000,2,16,2,16) +hasLocation(#20032,#20033) +#20034=* +tokeninfo(#20034,4,#20001,13,"`Foo`") +#20035=@"loc,{#10000},2,17,2,21" +locations_default(#20035,#10000,2,17,2,21) +hasLocation(#20034,#20035) +#20036=* +tokeninfo(#20036,8,#20001,14,")") +#20037=@"loc,{#10000},2,22,2,22" +locations_default(#20037,#10000,2,22,2,22) +hasLocation(#20036,#20037) +#20038=* +tokeninfo(#20038,8,#20001,15,";") +#20039=@"loc,{#10000},2,23,2,23" +locations_default(#20039,#10000,2,23,2,23) +hasLocation(#20038,#20039) +#20040=* +tokeninfo(#20040,7,#20001,16,"type") +#20041=@"loc,{#10000},3,1,3,4" +locations_default(#20041,#10000,3,1,3,4) +hasLocation(#20040,#20041) +#20042=* +tokeninfo(#20042,6,#20001,17,"Z") +#20043=@"loc,{#10000},3,6,3,6" +locations_default(#20043,#10000,3,6,3,6) +hasLocation(#20042,#20043) +#20044=* +tokeninfo(#20044,8,#20001,18,"=") +#20045=@"loc,{#10000},3,8,3,8" +locations_default(#20045,#10000,3,8,3,8) +hasLocation(#20044,#20045) +#20046=* +tokeninfo(#20046,7,#20001,19,"import") +#20047=@"loc,{#10000},3,10,3,15" +locations_default(#20047,#10000,3,10,3,15) +hasLocation(#20046,#20047) +#20048=* +tokeninfo(#20048,8,#20001,20,"(") +#20049=@"loc,{#10000},3,16,3,16" +locations_default(#20049,#10000,3,16,3,16) +hasLocation(#20048,#20049) +#20050=* +tokeninfo(#20050,6,#20001,21,"Y") +#20051=@"loc,{#10000},3,17,3,17" +locations_default(#20051,#10000,3,17,3,17) +hasLocation(#20050,#20051) +#20052=* +tokeninfo(#20052,8,#20001,22,")") +#20053=@"loc,{#10000},3,18,3,18" +locations_default(#20053,#10000,3,18,3,18) +hasLocation(#20052,#20053) +#20054=* +tokeninfo(#20054,8,#20001,23,";") +#20055=@"loc,{#10000},3,19,3,19" +locations_default(#20055,#10000,3,19,3,19) +hasLocation(#20054,#20055) +#20056=* +tokeninfo(#20056,0,#20001,24,"") +#20057=@"loc,{#10000},4,1,4,0" +locations_default(#20057,#10000,4,1,4,0) +hasLocation(#20056,#20057) +toplevels(#20001,0) +#20058=@"loc,{#10000},1,1,4,0" +locations_default(#20058,#10000,1,1,4,0) +hasLocation(#20001,#20058) +#20059=@"local_type_name;{X};{#20000}" +local_type_names(#20059,"X",#20000) +#20060=@"local_type_name;{Y};{#20000}" +local_type_names(#20060,"Y",#20000) +#20061=@"local_type_name;{Z};{#20000}" +local_type_names(#20061,"Z",#20000) +#20062=* +stmts(#20062,35,#20001,0,"type X = import(3);") +hasLocation(#20062,#20003) +stmt_containers(#20062,#20001) +#20063=* +typeexprs(#20063,1,#20062,0,"X") +hasLocation(#20063,#20011) +enclosing_stmt(#20063,#20062) +expr_containers(#20063,#20001) +literals("X","X",#20063) +typedecl(#20063,#20059) +#20064=* +typeexprs(#20064,30,#20062,1,"import(3)") +#20065=@"loc,{#10000},1,10,1,18" +locations_default(#20065,#10000,1,10,1,18) +hasLocation(#20064,#20065) +enclosing_stmt(#20064,#20062) +expr_containers(#20064,#20001) +#20066=* +typeexprs(#20066,4,#20064,0,"3") +hasLocation(#20066,#20019) +enclosing_stmt(#20066,#20062) +expr_containers(#20066,#20001) +literals("3","3",#20066) +#20067=* +stmts(#20067,35,#20001,1,"type Y ... `Foo`);") +hasLocation(#20067,#20005) +stmt_containers(#20067,#20001) +#20068=* +typeexprs(#20068,1,#20067,0,"Y") +hasLocation(#20068,#20027) +enclosing_stmt(#20068,#20067) +expr_containers(#20068,#20001) +literals("Y","Y",#20068) +typedecl(#20068,#20060) +#20069=* +typeexprs(#20069,30,#20067,1,"import(`Foo`)") +#20070=@"loc,{#10000},2,10,2,22" +locations_default(#20070,#10000,2,10,2,22) +hasLocation(#20069,#20070) +enclosing_stmt(#20069,#20067) +expr_containers(#20069,#20001) +#20071=* +typeexprs(#20071,3,#20069,0,"`Foo`") +hasLocation(#20071,#20035) +enclosing_stmt(#20071,#20067) +expr_containers(#20071,#20001) +literals("Foo","`Foo`",#20071) +#20072=* +stmts(#20072,35,#20001,2,"type Z = import(Y);") +hasLocation(#20072,#20007) +stmt_containers(#20072,#20001) +#20073=* +typeexprs(#20073,1,#20072,0,"Z") +hasLocation(#20073,#20043) +enclosing_stmt(#20073,#20072) +expr_containers(#20073,#20001) +literals("Z","Z",#20073) +typedecl(#20073,#20061) +#20074=* +typeexprs(#20074,30,#20072,1,"import(Y)") +#20075=@"loc,{#10000},3,10,3,18" +locations_default(#20075,#10000,3,10,3,18) +hasLocation(#20074,#20075) +enclosing_stmt(#20074,#20072) +expr_containers(#20074,#20001) +#20076=* +typeexprs(#20076,0,#20074,0,"Y") +hasLocation(#20076,#20051) +enclosing_stmt(#20076,#20072) +expr_containers(#20076,#20001) +literals("Y","Y",#20076) +typebind(#20076,#20060) +#20077=* +entry_cfg_node(#20077,#20001) +#20078=@"loc,{#10000},1,1,1,0" +locations_default(#20078,#10000,1,1,1,0) +hasLocation(#20077,#20078) +#20079=* +exit_cfg_node(#20079,#20001) +hasLocation(#20079,#20057) +successor(#20072,#20079) +successor(#20067,#20072) +successor(#20062,#20067) +successor(#20077,#20062) +numlines(#10000,3,3,0) +filetype(#10000,"typescript") From 9b3509f0ba20c4ad0ffd31dd8f88be4b2e55e874 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 1 Oct 2020 12:51:44 +0200 Subject: [PATCH 177/185] Python: Highlight problem with missing use-use flow --- .../CWE-078/CommandInjection.expected | 8 ++++++ .../CWE-078/command_injection.py | 26 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected index 665617aa713..288508c23d4 100644 --- a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected @@ -3,6 +3,8 @@ edges | command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | +| command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | command_injection.py:39:15:39:21 | ControlFlowNode for command | +| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:53:15:53:21 | ControlFlowNode for command | nodes | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | @@ -12,8 +14,14 @@ nodes | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| command_injection.py:39:15:39:21 | ControlFlowNode for command | semmle.label | ControlFlowNode for command | +| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| command_injection.py:53:15:53:21 | ControlFlowNode for command | semmle.label | ControlFlowNode for command | #select | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:39:15:39:21 | ControlFlowNode for command | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | command_injection.py:39:15:39:21 | ControlFlowNode for command | This command depends on $@. | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:53:15:53:21 | ControlFlowNode for command | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:53:15:53:21 | ControlFlowNode for command | This command depends on $@. | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | a user-provided value | diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py index ce533b5e1ee..d92690c57fe 100644 --- a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py @@ -31,5 +31,31 @@ def others(): # Don't let files be `; rm -rf /` os.popen("ls " + files) +@app.route("/multiple") +def multiple(): + command = request.args.get('command', '') + # We should mark flow to both calls here, which conflicts with removing flow out of + # a sink due to use-use flow. + os.system(command) + os.system(command) + + +@app.route("/not-into-sink-impl") +def not_into_sink_impl(): + """When there is flow to a sink such as `os.popen(cmd)`, we don't want to highlight that there is also + flow through the actual `popen` function to the internal call to `subprocess.Popen` -- we would usually + see that flow since we extract the `os.py` file from the standard library. + + os.popen implementation: + https://github.com/python/cpython/blob/fa7ce080175f65d678a7d5756c94f82887fc9803/Lib/os.py#L974 + """ + command = request.args.get('command', '') + os.system(command) + os.popen(command) + subprocess.call(command) + subprocess.check_call(command) + subprocess.run(command) + + # TODO: popen2 module for Python 2 only https://devdocs.io/python~2.7/library/popen2 # (deprecated since Python 2.6, but still functional in Python 2.7.17) From 3247b300aeea54f6a167f488559634485ecd30b1 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 1 Oct 2020 12:55:11 +0200 Subject: [PATCH 178/185] Python: Fix problem with missing use-use flow --- .../CWE-078/CommandInjection.ql | 35 ++++++++++++------- .../CWE-078/CommandInjection.expected | 15 ++++++++ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql index 6e241a81a6a..1f826acb932 100755 --- a/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql +++ b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql @@ -27,19 +27,30 @@ class CommandInjectionConfiguration extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { - sink = any(SystemCommandExecution e).getCommand() + sink = any(SystemCommandExecution e).getCommand() and + // Since the implementation of os.popen looks like + // ```py + // def popen(cmd, mode="r", buffering=-1): + // ... + // proc = subprocess.Popen(cmd, ...) + // ``` + // any time we would report flow to the `os.popen` sink, we can ALSO report the flow + // from the `cmd` parameter to the `subprocess.Popen` sink -- obviously we don't want + // that. + // + // However, simply removing taint edges out of a sink is not a good enough solution, + // since we would only flag one of the `os.system` calls in the following example due + // to use-use flow + // ```py + // os.system(cmd) + // os.system(cmd) + // ``` + // + // Best solution I could come up with is to exclude all sinks inside the standard + // library -- this does have a downside: If we have overlooked a function in the + // standard library that internally runs a command, we no longer give an alert :| + not sink.getLocation().getFile().inStdlib() } - - // Since the implementation of os.popen looks like - // ```py - // def popen(cmd, mode="r", buffering=-1): - // ... - // proc = subprocess.Popen(cmd, ...) - // ``` - // any time we would report flow to the `os.popen` sink, we can ALSO report the flow - // from the `cmd` parameter to the `subprocess.Popen` sink -- obviously we don't want - // that, so to prevent that we remove any taint edges out of a sink. - override predicate isSanitizerOut(DataFlow::Node node) { isSink(node) } } from CommandInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected index 288508c23d4..03748127520 100644 --- a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/CommandInjection.expected @@ -4,7 +4,12 @@ edges | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | command_injection.py:39:15:39:21 | ControlFlowNode for command | +| command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | command_injection.py:40:15:40:21 | ControlFlowNode for command | | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:53:15:53:21 | ControlFlowNode for command | +| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:54:14:54:20 | ControlFlowNode for command | +| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:55:21:55:27 | ControlFlowNode for command | +| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:56:27:56:33 | ControlFlowNode for command | +| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:57:20:57:26 | ControlFlowNode for command | nodes | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | @@ -16,12 +21,22 @@ nodes | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:39:15:39:21 | ControlFlowNode for command | semmle.label | ControlFlowNode for command | +| command_injection.py:40:15:40:21 | ControlFlowNode for command | semmle.label | ControlFlowNode for command | | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:53:15:53:21 | ControlFlowNode for command | semmle.label | ControlFlowNode for command | +| command_injection.py:54:14:54:20 | ControlFlowNode for command | semmle.label | ControlFlowNode for command | +| command_injection.py:55:21:55:27 | ControlFlowNode for command | semmle.label | ControlFlowNode for command | +| command_injection.py:56:27:56:33 | ControlFlowNode for command | semmle.label | ControlFlowNode for command | +| command_injection.py:57:20:57:26 | ControlFlowNode for command | semmle.label | ControlFlowNode for command | #select | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:39:15:39:21 | ControlFlowNode for command | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | command_injection.py:39:15:39:21 | ControlFlowNode for command | This command depends on $@. | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:40:15:40:21 | ControlFlowNode for command | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | command_injection.py:40:15:40:21 | ControlFlowNode for command | This command depends on $@. | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:53:15:53:21 | ControlFlowNode for command | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:53:15:53:21 | ControlFlowNode for command | This command depends on $@. | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:54:14:54:20 | ControlFlowNode for command | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:54:14:54:20 | ControlFlowNode for command | This command depends on $@. | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:55:21:55:27 | ControlFlowNode for command | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:55:21:55:27 | ControlFlowNode for command | This command depends on $@. | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:56:27:56:33 | ControlFlowNode for command | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:56:27:56:33 | ControlFlowNode for command | This command depends on $@. | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:57:20:57:26 | ControlFlowNode for command | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:57:20:57:26 | ControlFlowNode for command | This command depends on $@. | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | a user-provided value | From 4dec2171dabf38374b13d25c755e48bc7bb1e841 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 1 Oct 2020 13:21:56 +0200 Subject: [PATCH 179/185] add http request server data as a RemoteFlowSource --- .../src/semmle/javascript/frameworks/HTTP.qll | 17 +++++++++++++++++ .../frameworks/NodeJSLib/src/http.js | 6 ++++++ .../frameworks/NodeJSLib/tests.expected | 15 +++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll index b3faa55501a..044ad8b0cd7 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll @@ -470,6 +470,23 @@ module HTTP { */ abstract Expr getServer(); } + + /** + * A parameter containing data received by a NodeJS HTTP server. + * E.g. `chunk` in: `http.createServer().on('request', (req, res) => req.on("data", (chunk) => ...))`. + */ + private class ServerRequestDataEvent extends RemoteFlowSource, DataFlow::ParameterNode { + RequestSource req; + + ServerRequestDataEvent() { + exists(DataFlow::MethodCallNode mcn | mcn = req.ref().getAMethodCall(EventEmitter::on()) | + mcn.getArgument(0).mayHaveStringValue("data") and + this = mcn.getABoundCallbackParameter(1, 0) + ) + } + + override string getSourceType() { result = "NodeJS HTTP server data event" } + } } /** diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/src/http.js b/javascript/ql/test/library-tests/frameworks/NodeJSLib/src/http.js index 11a52f701f9..12dc11efcaf 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/src/http.js +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/src/http.js @@ -68,3 +68,9 @@ function getArrowHandler() { return (req,res) => f(); } http.createServer(getArrowHandler()); + +http.createServer(function (req, res) { + req.on("data", chunk => { // RemoteFlowSource + res.send(chunk); + }) +}); \ No newline at end of file diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected b/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected index 6234075f5f1..2e048dc43b8 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected @@ -8,6 +8,7 @@ test_isCreateServer | src/http.js:60:1:60:33 | createS ... res){}) | | src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | | src/http.js:70:1:70:36 | http.cr ... dler()) | +| src/http.js:72:1:76:2 | http.cr ... })\\n}) | | src/https.js:4:14:10:2 | https.c ... foo;\\n}) | | src/https.js:12:1:16:2 | https.c ... r");\\n}) | | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | @@ -53,6 +54,8 @@ test_ResponseExpr | src/http.js:63:3:63:5 | res | src/http.js:62:19:65:1 | functio ... r2");\\n} | | src/http.js:64:3:64:5 | res | src/http.js:62:19:65:1 | functio ... r2");\\n} | | src/http.js:68:17:68:19 | res | src/http.js:68:12:68:27 | (req,res) => f() | +| src/http.js:72:34:72:36 | res | src/http.js:72:19:76:1 | functio ... \\n })\\n} | +| src/http.js:74:5:74:7 | res | src/http.js:72:19:76:1 | functio ... \\n })\\n} | | src/https.js:4:47:4:49 | res | src/https.js:4:33:10:1 | functio ... .foo;\\n} | | src/https.js:7:3:7:5 | res | src/https.js:4:33:10:1 | functio ... .foo;\\n} | | src/https.js:12:34:12:36 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} | @@ -87,6 +90,7 @@ test_RouteSetup_getServer | src/http.js:60:1:60:33 | createS ... res){}) | src/http.js:60:1:60:33 | createS ... res){}) | | src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | | src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:70:1:70:36 | http.cr ... dler()) | +| src/http.js:72:1:76:2 | http.cr ... })\\n}) | src/http.js:72:1:76:2 | http.cr ... })\\n}) | | src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:14:10:2 | https.c ... foo;\\n}) | | src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:1:16:2 | https.c ... r");\\n}) | | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | @@ -112,6 +116,7 @@ test_ServerDefinition | src/http.js:60:1:60:33 | createS ... res){}) | | src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | | src/http.js:70:1:70:36 | http.cr ... dler()) | +| src/http.js:72:1:76:2 | http.cr ... })\\n}) | | src/https.js:4:14:10:2 | https.c ... foo;\\n}) | | src/https.js:12:1:16:2 | https.c ... r");\\n}) | | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | @@ -142,6 +147,8 @@ test_RouteHandler_getAResponseExpr | src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:63:3:63:5 | res | | src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:64:3:64:5 | res | | src/http.js:68:12:68:27 | (req,res) => f() | src/http.js:68:17:68:19 | res | +| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:72:34:72:36 | res | +| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:74:5:74:7 | res | | src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:47:4:49 | res | | src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:7:3:7:5 | res | | src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:34:12:36 | res | @@ -169,6 +176,7 @@ test_ServerDefinition_getARouteHandler | src/http.js:60:1:60:33 | createS ... res){}) | src/http.js:60:14:60:32 | function(req,res){} | | src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | src/http.js:62:19:65:1 | functio ... r2");\\n} | | src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:68:12:68:27 | (req,res) => f() | +| src/http.js:72:1:76:2 | http.cr ... })\\n}) | src/http.js:72:19:76:1 | functio ... \\n })\\n} | | src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:33:10:1 | functio ... .foo;\\n} | | src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:20:16:1 | functio ... ar");\\n} | | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | @@ -198,6 +206,7 @@ test_RouteSetup_getARouteHandler | src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:67:1:69:1 | return of function getArrowHandler | | src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:68:12:68:27 | (req,res) => f() | | src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:70:19:70:35 | getArrowHandler() | +| src/http.js:72:1:76:2 | http.cr ... })\\n}) | src/http.js:72:19:76:1 | functio ... \\n })\\n} | | src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:33:10:1 | functio ... .foo;\\n} | | src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:20:16:1 | functio ... ar");\\n} | | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | @@ -224,6 +233,7 @@ test_RemoteFlowSources | src/http.js:30:28:30:32 | chunk | | src/http.js:40:23:40:30 | authInfo | | src/http.js:45:23:45:27 | error | +| src/http.js:73:18:73:22 | chunk | | src/https.js:6:26:6:32 | req.url | | src/https.js:8:3:8:20 | req.headers.cookie | | src/https.js:9:3:9:17 | req.headers.foo | @@ -238,6 +248,7 @@ test_RouteHandler | src/http.js:60:14:60:32 | function(req,res){} | src/http.js:60:1:60:33 | createS ... res){}) | | src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:62:1:65:2 | http.cr ... 2");\\n}) | | src/http.js:68:12:68:27 | (req,res) => f() | src/http.js:70:1:70:36 | http.cr ... dler()) | +| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:72:1:76:2 | http.cr ... })\\n}) | | src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:14:10:2 | https.c ... foo;\\n}) | | src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:1:16:2 | https.c ... r");\\n}) | | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:18:14:18:35 | http.cr ... er(get) | @@ -259,6 +270,8 @@ test_RequestExpr | src/http.js:62:28:62:30 | req | src/http.js:62:19:65:1 | functio ... r2");\\n} | | src/http.js:63:17:63:19 | req | src/http.js:62:19:65:1 | functio ... r2");\\n} | | src/http.js:68:13:68:15 | req | src/http.js:68:12:68:27 | (req,res) => f() | +| src/http.js:72:29:72:31 | req | src/http.js:72:19:76:1 | functio ... \\n })\\n} | +| src/http.js:73:3:73:5 | req | src/http.js:72:19:76:1 | functio ... \\n })\\n} | | src/https.js:4:42:4:44 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | | src/https.js:6:26:6:28 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | | src/https.js:8:3:8:5 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | @@ -296,6 +309,8 @@ test_RouteHandler_getARequestExpr | src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:62:28:62:30 | req | | src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:63:17:63:19 | req | | src/http.js:68:12:68:27 | (req,res) => f() | src/http.js:68:13:68:15 | req | +| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:72:29:72:31 | req | +| src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:73:3:73:5 | req | | src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:42:4:44 | req | | src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:6:26:6:28 | req | | src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:8:3:8:5 | req | From 578ea1ae43950adea8082fd7bb4eac65463b5d62 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Thu, 1 Oct 2020 13:09:52 +0100 Subject: [PATCH 180/185] Fix OWASP broken links --- cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp | 2 +- csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp | 2 +- csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp | 2 +- csharp/ql/src/Security Features/CWE-643/XPathInjection.qhelp | 2 +- csharp/ql/src/experimental/CWE-099/TaintedWebClient.qhelp | 2 +- java/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp | 2 +- java/ql/src/Security/CWE/CWE-022/ZipSlip.qhelp | 2 +- java/ql/src/Security/CWE/CWE-209/StackTraceExposure.qhelp | 2 +- .../src/experimental/Security/CWE/CWE-643/XPathInjection.qhelp | 2 +- javascript/ql/src/Security/CWE-022/TaintedPath.qhelp | 2 +- javascript/ql/src/Security/CWE-022/ZipSlip.qhelp | 2 +- javascript/ql/src/Security/CWE-209/StackTraceExposure.qhelp | 2 +- javascript/ql/src/Security/CWE-643/XpathInjection.qhelp | 2 +- python/ql/src/Security/CWE-022/PathInjection.qhelp | 2 +- python/ql/src/Security/CWE-022/TarSlip.qhelp | 2 +- python/ql/src/Security/CWE-209/StackTraceExposure.qhelp | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp index d7432369f0e..eba2ede58f5 100644 --- a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp +++ b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp @@ -39,7 +39,7 @@ access all the system's passwords.

  • OWASP: -Path Traversal. +Path Traversal.
  • diff --git a/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp b/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp index fec61771992..e838d8c56a4 100644 --- a/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp +++ b/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp @@ -41,7 +41,7 @@ sent back to the user, giving them access to all the system's passwords.

  • OWASP: -Path Traversal. +Path Traversal.
  • diff --git a/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp b/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp index 9ee9ce6d0a1..bee5d819836 100644 --- a/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp +++ b/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp @@ -71,7 +71,7 @@ Snyk:
  • OWASP: -Path Traversal. +Path Traversal.
  • diff --git a/csharp/ql/src/Security Features/CWE-643/XPathInjection.qhelp b/csharp/ql/src/Security Features/CWE-643/XPathInjection.qhelp index 7241f6f97c4..cf50b4ee174 100644 --- a/csharp/ql/src/Security Features/CWE-643/XPathInjection.qhelp +++ b/csharp/ql/src/Security Features/CWE-643/XPathInjection.qhelp @@ -42,7 +42,7 @@ variables in an XsltArgumentList. -
  • OWASP: Testing for XPath Injection.
  • +
  • OWASP: Testing for XPath Injection.
  • OWASP: XPath Injection.
  • MSDN: User Defined Functions and Variables.
  • diff --git a/csharp/ql/src/experimental/CWE-099/TaintedWebClient.qhelp b/csharp/ql/src/experimental/CWE-099/TaintedWebClient.qhelp index 77721307eda..d7f195905a8 100644 --- a/csharp/ql/src/experimental/CWE-099/TaintedWebClient.qhelp +++ b/csharp/ql/src/experimental/CWE-099/TaintedWebClient.qhelp @@ -51,7 +51,7 @@ system's passwords.

  • OWASP: -Path Traversal. +Path Traversal.
  • diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp b/java/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp index 29bab0e42d2..b32d3c65039 100644 --- a/java/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp +++ b/java/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp @@ -39,7 +39,7 @@ giving them access to all the system's passwords.

  • OWASP: -Path Traversal. +Path Traversal.
  • diff --git a/java/ql/src/Security/CWE/CWE-022/ZipSlip.qhelp b/java/ql/src/Security/CWE/CWE-022/ZipSlip.qhelp index 97c389508b3..adea1b89c49 100644 --- a/java/ql/src/Security/CWE/CWE-022/ZipSlip.qhelp +++ b/java/ql/src/Security/CWE/CWE-022/ZipSlip.qhelp @@ -63,7 +63,7 @@ Snyk:
  • OWASP: -Path Traversal. +Path Traversal.
  • diff --git a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.qhelp b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.qhelp index b772b7e3c57..d0d7e1b0e50 100644 --- a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.qhelp +++ b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.qhelp @@ -38,7 +38,7 @@ information.

    -
  • OWASP: Information Leak.
  • +
  • OWASP: Improper Error Handling.
  • CERT Java Coding Standard: ERR01-J. diff --git a/java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.qhelp index 555a6f7fb95..91d110b80aa 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.qhelp @@ -38,7 +38,7 @@ The fifth example is a dom4j XPath injection example. -
  • OWASP: Testing for XPath Injection.
  • +
  • OWASP: Testing for XPath Injection.
  • OWASP: XPath Injection.
  • diff --git a/javascript/ql/src/Security/CWE-022/TaintedPath.qhelp b/javascript/ql/src/Security/CWE-022/TaintedPath.qhelp index b9cb415a018..8dab2fcc4fc 100644 --- a/javascript/ql/src/Security/CWE-022/TaintedPath.qhelp +++ b/javascript/ql/src/Security/CWE-022/TaintedPath.qhelp @@ -50,7 +50,7 @@ system's passwords. -
  • OWASP: Path Traversal.
  • +
  • OWASP: Path Traversal.
  • npm: sanitize-filename package.
  • diff --git a/javascript/ql/src/Security/CWE-022/ZipSlip.qhelp b/javascript/ql/src/Security/CWE-022/ZipSlip.qhelp index 7a361947216..d9dc0fb67a8 100644 --- a/javascript/ql/src/Security/CWE-022/ZipSlip.qhelp +++ b/javascript/ql/src/Security/CWE-022/ZipSlip.qhelp @@ -60,7 +60,7 @@ Snyk:
  • OWASP: -Path Traversal. +Path Traversal.
  • diff --git a/javascript/ql/src/Security/CWE-209/StackTraceExposure.qhelp b/javascript/ql/src/Security/CWE-209/StackTraceExposure.qhelp index ea37581a799..20a9c65cbc7 100644 --- a/javascript/ql/src/Security/CWE-209/StackTraceExposure.qhelp +++ b/javascript/ql/src/Security/CWE-209/StackTraceExposure.qhelp @@ -52,6 +52,6 @@ will not see the information: -
  • OWASP: Information Leak.
  • +
  • OWASP: Improper Error Handling.
  • diff --git a/javascript/ql/src/Security/CWE-643/XpathInjection.qhelp b/javascript/ql/src/Security/CWE-643/XpathInjection.qhelp index 346b213d180..3378b9b4078 100644 --- a/javascript/ql/src/Security/CWE-643/XpathInjection.qhelp +++ b/javascript/ql/src/Security/CWE-643/XpathInjection.qhelp @@ -33,7 +33,7 @@ by xpath: -
  • OWASP: Testing for XPath Injection.
  • +
  • OWASP: Testing for XPath Injection.
  • OWASP: XPath Injection.
  • npm: xpath.
  • diff --git a/python/ql/src/Security/CWE-022/PathInjection.qhelp b/python/ql/src/Security/CWE-022/PathInjection.qhelp index 0abc683b4ca..200a4e78f98 100644 --- a/python/ql/src/Security/CWE-022/PathInjection.qhelp +++ b/python/ql/src/Security/CWE-022/PathInjection.qhelp @@ -55,7 +55,7 @@ known prefix. This ensures that regardless of the user input, the resulting path -
  • OWASP: Path Traversal.
  • +
  • OWASP: Path Traversal.
  • npm: werkzeug.utils.secure_filename.
  • diff --git a/python/ql/src/Security/CWE-022/TarSlip.qhelp b/python/ql/src/Security/CWE-022/TarSlip.qhelp index a2fbc5dfdc6..07bab31a6ce 100644 --- a/python/ql/src/Security/CWE-022/TarSlip.qhelp +++ b/python/ql/src/Security/CWE-022/TarSlip.qhelp @@ -60,7 +60,7 @@ Snyk:
  • OWASP: -Path Traversal. +Path Traversal.
  • Python Library Reference: diff --git a/python/ql/src/Security/CWE-209/StackTraceExposure.qhelp b/python/ql/src/Security/CWE-209/StackTraceExposure.qhelp index 86ecdbdc0d8..6670aa08284 100644 --- a/python/ql/src/Security/CWE-209/StackTraceExposure.qhelp +++ b/python/ql/src/Security/CWE-209/StackTraceExposure.qhelp @@ -47,6 +47,6 @@ log, but remote users will not see the information. -
  • OWASP: Information Leak.
  • +
  • OWASP: Improper Error Handling.
  • From bc68578c8bfd2c6710c930314e2cf6f1878ae9da Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 1 Oct 2020 21:11:47 +0200 Subject: [PATCH 181/185] C#: Remove deprecated external queries --- .../examples/filters/BumpMetricBy10.ql | 11 -------- .../examples/filters/EditDefectMessage.ql | 11 -------- .../examples/filters/ExcludeGeneratedCode.ql | 14 ---------- .../external/examples/filters/FromSource.ql | 12 --------- .../external/tests/DefectFromExternalData.ql | 19 ------------- .../tests/DefectFromExternalDefect.ql | 17 ------------ .../tests/DefectFromExternalMetric.ql | 17 ------------ csharp/ql/src/external/tests/MetricFilter.ql | 13 --------- .../tests/MetricFromExternalDefect.ql | 21 --------------- .../tests/MetricFromExternalMetric.ql | 27 ------------------- 10 files changed, 162 deletions(-) delete mode 100644 csharp/ql/src/external/examples/filters/BumpMetricBy10.ql delete mode 100644 csharp/ql/src/external/examples/filters/EditDefectMessage.ql delete mode 100644 csharp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql delete mode 100644 csharp/ql/src/external/examples/filters/FromSource.ql delete mode 100644 csharp/ql/src/external/tests/DefectFromExternalData.ql delete mode 100644 csharp/ql/src/external/tests/DefectFromExternalDefect.ql delete mode 100644 csharp/ql/src/external/tests/DefectFromExternalMetric.ql delete mode 100644 csharp/ql/src/external/tests/MetricFilter.ql delete mode 100644 csharp/ql/src/external/tests/MetricFromExternalDefect.ql delete mode 100644 csharp/ql/src/external/tests/MetricFromExternalMetric.ql diff --git a/csharp/ql/src/external/examples/filters/BumpMetricBy10.ql b/csharp/ql/src/external/examples/filters/BumpMetricBy10.ql deleted file mode 100644 index 209d580bc19..00000000000 --- a/csharp/ql/src/external/examples/filters/BumpMetricBy10.ql +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @name Edit the value of a metric - * @description Add 10 to a metric's value - * @deprecated - */ - -import csharp -import external.MetricFilter - -from MetricResult res -select res, res.getValue() + 10 diff --git a/csharp/ql/src/external/examples/filters/EditDefectMessage.ql b/csharp/ql/src/external/examples/filters/EditDefectMessage.ql deleted file mode 100644 index 4fbf4146550..00000000000 --- a/csharp/ql/src/external/examples/filters/EditDefectMessage.ql +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @name Edit the message of a query - * @description Change the string in the select to edit the message - * @deprecated - */ - -import csharp -import external.DefectFilter - -from DefectResult res -select res, "Filtered query result: " + res.getMessage() diff --git a/csharp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql b/csharp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql deleted file mode 100644 index 2a1257427b2..00000000000 --- a/csharp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @name Filter: removed results from generated code - * @description Shows how to exclude certain files or folders from results. - * @deprecated - */ - -import csharp -import external.DefectFilter - -predicate generatedFile(File f) { f.getAbsolutePath().matches("%generated%") } - -from DefectResult res -where not generatedFile(res.getFile()) -select res, res.getMessage() diff --git a/csharp/ql/src/external/examples/filters/FromSource.ql b/csharp/ql/src/external/examples/filters/FromSource.ql deleted file mode 100644 index 2f714ec2225..00000000000 --- a/csharp/ql/src/external/examples/filters/FromSource.ql +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @name Filter: only keep results from source - * @description Shows how to filter for only certain files - * @deprecated - */ - -import csharp -import external.DefectFilter - -from DefectResult res -where res.getFile().fromSource() -select res, res.getMessage() diff --git a/csharp/ql/src/external/tests/DefectFromExternalData.ql b/csharp/ql/src/external/tests/DefectFromExternalData.ql deleted file mode 100644 index 70557f357a3..00000000000 --- a/csharp/ql/src/external/tests/DefectFromExternalData.ql +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @name Defect from external data - * @description Insert description here... - * @kind problem - * @problem.severity warning - * @deprecated - */ - -import csharp -import external.ExternalArtifact - -// custom://[FileUtil][2011-01-02][false][1.1][6][Message 2] -from ExternalData d, File u -where - d.getQueryPath() = "external-data.ql" and - u.getStem() = d.getField(0) -select u, - d.getField(5) + ", " + d.getFieldAsDate(1) + ", " + d.getField(2) + ", " + d.getFieldAsFloat(3) + - ", " + d.getFieldAsInt(4) + ": " + d.getNumFields() diff --git a/csharp/ql/src/external/tests/DefectFromExternalDefect.ql b/csharp/ql/src/external/tests/DefectFromExternalDefect.ql deleted file mode 100644 index 57138a6879f..00000000000 --- a/csharp/ql/src/external/tests/DefectFromExternalDefect.ql +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @name Defect from external defect - * @description Create a defect from external data - * @kind problem - * @problem.severity warning - * @deprecated - */ - -import csharp -import external.ExternalArtifact - -class DuplicateCode extends ExternalDefect { - DuplicateCode() { getQueryPath() = "duplicate-code/duplicateCode.ql" } -} - -from DuplicateCode d -select d, "External Defect " + d.getMessage() diff --git a/csharp/ql/src/external/tests/DefectFromExternalMetric.ql b/csharp/ql/src/external/tests/DefectFromExternalMetric.ql deleted file mode 100644 index 88d38d5f00b..00000000000 --- a/csharp/ql/src/external/tests/DefectFromExternalMetric.ql +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @name Defect from external metric - * @description Create a defect from external data - * @kind problem - * @problem.severity warning - * @deprecated - */ - -import csharp -import external.ExternalArtifact - -from ExternalMetric m, File f -where - m.getQueryPath() = "filesBuilt.ql" and - m.getValue() = 1.0 and - m.getFile() = f -select f, "File is built" diff --git a/csharp/ql/src/external/tests/MetricFilter.ql b/csharp/ql/src/external/tests/MetricFilter.ql deleted file mode 100644 index 9f79465be75..00000000000 --- a/csharp/ql/src/external/tests/MetricFilter.ql +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @name Metric filter - * @description Only include results in large files (200) lines of code. - * @kind treemap - * @deprecated - */ - -import csharp -import external.MetricFilter - -from MetricResult res -where res.getFile().getNumberOfLinesOfCode() > 200 -select res, res.getValue() diff --git a/csharp/ql/src/external/tests/MetricFromExternalDefect.ql b/csharp/ql/src/external/tests/MetricFromExternalDefect.ql deleted file mode 100644 index 4894de28761..00000000000 --- a/csharp/ql/src/external/tests/MetricFromExternalDefect.ql +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @name Metric from external defect - * @description Find number of duplicate code entries in a file - * @treemap.warnOn lowValues - * @metricType file - * @kind treemap - * @deprecated - */ - -import csharp -import external.ExternalArtifact - -class DuplicateCode extends ExternalDefect { - DuplicateCode() { getQueryPath() = "duplicate-code/duplicateCode.ql" } -} - -predicate numDuplicateEntries(File f, int i) { i = count(DuplicateCode d | d.getFile() = f) } - -from File f, int i -where numDuplicateEntries(f, i) -select f, i diff --git a/csharp/ql/src/external/tests/MetricFromExternalMetric.ql b/csharp/ql/src/external/tests/MetricFromExternalMetric.ql deleted file mode 100644 index 4d2ab4088ae..00000000000 --- a/csharp/ql/src/external/tests/MetricFromExternalMetric.ql +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @name Metric from external metric - * @description Each file in a folder gets as metric value the number of files built in that folder - * @treemap.warnOn lowValues - * @metricType file - * @kind treemap - * @deprecated - */ - -import csharp -import external.ExternalArtifact - -predicate numBuiltFiles(Folder fold, int i) { - i = - count(File f | - exists(ExternalMetric m | - m.getQueryPath() = "filesBuilt.ql" and - m.getValue() = 1.0 and - m.getFile() = f - ) and - f.getParentContainer() = fold - ) -} - -from File f, int i -where numBuiltFiles(f.getParentContainer(), i) -select f, i From de07d9e5d98ccd02f9b9c27ab3e38a4970789d0c Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 2 Oct 2020 13:34:33 +0200 Subject: [PATCH 182/185] Python: Highlight that os.popen is not only problem for extra alerts --- .../Security-new-dataflow/CWE-078/CommandInjection.ql | 6 +++++- .../Security-new-dataflow/CWE-078/command_injection.py | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql index 1f826acb932..49aa1cd1885 100755 --- a/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql +++ b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql @@ -28,7 +28,7 @@ class CommandInjectionConfiguration extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink = any(SystemCommandExecution e).getCommand() and - // Since the implementation of os.popen looks like + // Since the implementation of standard library functions such `os.popen` looks like // ```py // def popen(cmd, mode="r", buffering=-1): // ... @@ -49,6 +49,10 @@ class CommandInjectionConfiguration extends TaintTracking::Configuration { // Best solution I could come up with is to exclude all sinks inside the standard // library -- this does have a downside: If we have overlooked a function in the // standard library that internally runs a command, we no longer give an alert :| + // + // This does not only affect `os.popen`, but also the helper functions in `subprocess`. See + // https://github.com/python/cpython/blob/fa7ce080175f65d678a7d5756c94f82887fc9803/Lib/os.py#L974 + // https://github.com/python/cpython/blob/fa7ce080175f65d678a7d5756c94f82887fc9803/Lib/subprocess.py#L341 not sink.getLocation().getFile().inStdlib() } } diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py index d92690c57fe..75125f59fca 100644 --- a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py @@ -46,8 +46,8 @@ def not_into_sink_impl(): flow through the actual `popen` function to the internal call to `subprocess.Popen` -- we would usually see that flow since we extract the `os.py` file from the standard library. - os.popen implementation: - https://github.com/python/cpython/blob/fa7ce080175f65d678a7d5756c94f82887fc9803/Lib/os.py#L974 + os.popen implementation: https://github.com/python/cpython/blob/fa7ce080175f65d678a7d5756c94f82887fc9803/Lib/os.py#L974 + subprocess.call implementation: https://github.com/python/cpython/blob/fa7ce080175f65d678a7d5756c94f82887fc9803/Lib/subprocess.py#L341 """ command = request.args.get('command', '') os.system(command) From 68eacef23c91bd6a576a6d835a054a12bb4bf670 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 2 Oct 2020 13:38:54 +0200 Subject: [PATCH 183/185] Python: Refactor OsExecCall and friends for better readability --- .../semmle/python/frameworks/Stdlib.qll | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 35ade8e326e..5fa36c67e09 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -128,9 +128,10 @@ private module Stdlib { */ private class OsExecCall extends SystemCommandExecution::Range { OsExecCall() { - this.asCfgNode().(CallNode).getFunction() = - os_attr(["execl", "execle", "execlp", "execlpe", "execv", "execve", "execvp", "execvpe"]) - .asCfgNode() + exists(string name | + name in ["execl", "execle", "execlp", "execlpe", "execv", "execve", "execvp", "execvpe"] and + this.asCfgNode().(CallNode).getFunction() = os_attr(name).asCfgNode() + ) } override DataFlow::Node getCommand() { @@ -144,9 +145,11 @@ private module Stdlib { */ private class OsSpawnCall extends SystemCommandExecution::Range { OsSpawnCall() { - this.asCfgNode().(CallNode).getFunction() = - os_attr(["spawnl", "spawnle", "spawnlp", "spawnlpe", "spawnv", "spawnve", "spawnvp", - "spawnvpe"]).asCfgNode() + exists(string name | + name in ["spawnl", "spawnle", "spawnlp", "spawnlpe", "spawnv", "spawnve", "spawnvp", + "spawnvpe"] and + this.asCfgNode().(CallNode).getFunction() = os_attr(name).asCfgNode() + ) } override DataFlow::Node getCommand() { @@ -247,8 +250,10 @@ private module Stdlib { SubprocessPopenCall() { call = this.asCfgNode() and - call.getFunction() = - subprocess_attr(["Popen", "call", "check_call", "check_output", "run"]).asCfgNode() + exists(string name | + name in ["Popen", "call", "check_call", "check_output", "run"] and + call.getFunction() = subprocess_attr(name).asCfgNode() + ) } /** Gets the ControlFlowNode for the `args` argument, if any. */ From eb67986916e55bd253c7a9fe995bdf7e3240a593 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 2 Oct 2020 14:11:07 +0200 Subject: [PATCH 184/185] Python: Exlucde only command injection sinks in os and subprocess --- .../CWE-078/CommandInjection.ql | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql index 49aa1cd1885..b1672ad995a 100755 --- a/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql +++ b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql @@ -35,25 +35,26 @@ class CommandInjectionConfiguration extends TaintTracking::Configuration { // proc = subprocess.Popen(cmd, ...) // ``` // any time we would report flow to the `os.popen` sink, we can ALSO report the flow - // from the `cmd` parameter to the `subprocess.Popen` sink -- obviously we don't want - // that. + // from the `cmd` parameter to the `subprocess.Popen` sink -- obviously we don't + // want that. // // However, simply removing taint edges out of a sink is not a good enough solution, - // since we would only flag one of the `os.system` calls in the following example due - // to use-use flow + // since we would only flag one of the `os.system` calls in the following example + // due to use-use flow // ```py // os.system(cmd) // os.system(cmd) // ``` // - // Best solution I could come up with is to exclude all sinks inside the standard - // library -- this does have a downside: If we have overlooked a function in the - // standard library that internally runs a command, we no longer give an alert :| + // Best solution I could come up with is to exclude all sinks inside the `os` and + // `subprocess` modules. This does have a downside: If we have overlooked a function + // in any of these, that internally runs a command, we no longer give an alert :| // - // This does not only affect `os.popen`, but also the helper functions in `subprocess`. See + // This does not only affect `os.popen`, but also the helper functions in + // `subprocess`. See: // https://github.com/python/cpython/blob/fa7ce080175f65d678a7d5756c94f82887fc9803/Lib/os.py#L974 // https://github.com/python/cpython/blob/fa7ce080175f65d678a7d5756c94f82887fc9803/Lib/subprocess.py#L341 - not sink.getLocation().getFile().inStdlib() + not sink.getScope().getEnclosingModule().getName() in ["os", "subprocess"] } } From e5b9ac8d9c38af3d214b52943bc1375eea7a49f2 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 2 Oct 2020 14:12:41 +0200 Subject: [PATCH 185/185] Python: Use getCommand as tag in ConceptsTest --- .../stdlib/SystemCommandExecution.py | 104 +++++++++--------- .../test/experimental/meta/ConceptsTest.qll | 4 +- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py index 8d6b1211614..ad4223a8c4b 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/SystemCommandExecution.py @@ -13,8 +13,8 @@ import os # can't use a string literal with spaces in the tags of an InlineExpectationsTest, so using variables :| -os.popen("cmd1; cmd2") # $SystemCommandExecution_getCommand="cmd1; cmd2" -os.system("cmd1; cmd2") # $SystemCommandExecution_getCommand="cmd1; cmd2" +os.popen("cmd1; cmd2") # $getCommand="cmd1; cmd2" +os.system("cmd1; cmd2") # $getCommand="cmd1; cmd2" def os_members(): @@ -24,8 +24,8 @@ def os_members(): # :| from os import popen, system - popen("cmd1; cmd2") # $SystemCommandExecution_getCommand="cmd1; cmd2" - system("cmd1; cmd2") # $SystemCommandExecution_getCommand="cmd1; cmd2" + popen("cmd1; cmd2") # $getCommand="cmd1; cmd2" + system("cmd1; cmd2") # $getCommand="cmd1; cmd2" ######################################## @@ -36,92 +36,92 @@ def os_members(): # clever in our analysis that discards that code, I used `if UNKNOWN` instead if UNKNOWN: env = {"FOO": "foo"} - os.execl("executable", "", "arg0") # $SystemCommandExecution_getCommand="executable" - os.execle("executable", "", "arg0", env) # $SystemCommandExecution_getCommand="executable" - os.execlp("executable", "", "arg0") # $SystemCommandExecution_getCommand="executable" - os.execlpe("executable", "", "arg0", env) # $SystemCommandExecution_getCommand="executable" - os.execv("executable", ["", "arg0"]) # $SystemCommandExecution_getCommand="executable" - os.execve("executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" - os.execvp("executable", ["", "arg0"]) # $SystemCommandExecution_getCommand="executable" - os.execvpe("executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" + os.execl("executable", "", "arg0") # $getCommand="executable" + os.execle("executable", "", "arg0", env) # $getCommand="executable" + os.execlp("executable", "", "arg0") # $getCommand="executable" + os.execlpe("executable", "", "arg0", env) # $getCommand="executable" + os.execv("executable", ["", "arg0"]) # $getCommand="executable" + os.execve("executable", ["", "arg0"], env) # $getCommand="executable" + os.execvp("executable", ["", "arg0"]) # $getCommand="executable" + os.execvpe("executable", ["", "arg0"], env) # $getCommand="executable" ######################################## # https://docs.python.org/3.8/library/os.html#os.spawnl env = {"FOO": "foo"} -os.spawnl(os.P_WAIT, "executable", "", "arg0") # $SystemCommandExecution_getCommand="executable" -os.spawnle(os.P_WAIT, "executable", "", "arg0", env) # $SystemCommandExecution_getCommand="executable" -os.spawnlp(os.P_WAIT, "executable", "", "arg0") # $SystemCommandExecution_getCommand="executable" -os.spawnlpe(os.P_WAIT, "executable", "", "arg0", env) # $SystemCommandExecution_getCommand="executable" -os.spawnv(os.P_WAIT, "executable", ["", "arg0"]) # $SystemCommandExecution_getCommand="executable" -os.spawnve(os.P_WAIT, "executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" -os.spawnvp(os.P_WAIT, "executable", ["", "arg0"]) # $SystemCommandExecution_getCommand="executable" -os.spawnvpe(os.P_WAIT, "executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" +os.spawnl(os.P_WAIT, "executable", "", "arg0") # $getCommand="executable" +os.spawnle(os.P_WAIT, "executable", "", "arg0", env) # $getCommand="executable" +os.spawnlp(os.P_WAIT, "executable", "", "arg0") # $getCommand="executable" +os.spawnlpe(os.P_WAIT, "executable", "", "arg0", env) # $getCommand="executable" +os.spawnv(os.P_WAIT, "executable", ["", "arg0"]) # $getCommand="executable" +os.spawnve(os.P_WAIT, "executable", ["", "arg0"], env) # $getCommand="executable" +os.spawnvp(os.P_WAIT, "executable", ["", "arg0"]) # $getCommand="executable" +os.spawnvpe(os.P_WAIT, "executable", ["", "arg0"], env) # $getCommand="executable" # Added in Python 3.8 -os.posix_spawn("executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" -os.posix_spawnp("executable", ["", "arg0"], env) # $SystemCommandExecution_getCommand="executable" +os.posix_spawn("executable", ["", "arg0"], env) # $getCommand="executable" +os.posix_spawnp("executable", ["", "arg0"], env) # $getCommand="executable" ######################################## import subprocess -subprocess.Popen("cmd1; cmd2", shell=True) # $SystemCommandExecution_getCommand="cmd1; cmd2" -subprocess.Popen("cmd1; cmd2", shell="truthy string") # $SystemCommandExecution_getCommand="cmd1; cmd2" -subprocess.Popen(["cmd1; cmd2", "shell-arg"], shell=True) # $SystemCommandExecution_getCommand="cmd1; cmd2" -subprocess.Popen("cmd1; cmd2", shell=True, executable="/bin/bash") # $SystemCommandExecution_getCommand="cmd1; cmd2" $SystemCommandExecution_getCommand="/bin/bash" +subprocess.Popen("cmd1; cmd2", shell=True) # $getCommand="cmd1; cmd2" +subprocess.Popen("cmd1; cmd2", shell="truthy string") # $getCommand="cmd1; cmd2" +subprocess.Popen(["cmd1; cmd2", "shell-arg"], shell=True) # $getCommand="cmd1; cmd2" +subprocess.Popen("cmd1; cmd2", shell=True, executable="/bin/bash") # $getCommand="cmd1; cmd2" $getCommand="/bin/bash" -subprocess.Popen("executable") # $SystemCommandExecution_getCommand="executable" -subprocess.Popen(["executable", "arg0"]) # $SystemCommandExecution_getCommand="executable" -subprocess.Popen("", executable="executable") # $SystemCommandExecution_getCommand="executable" -subprocess.Popen(["", "arg0"], executable="executable") # $SystemCommandExecution_getCommand="executable" +subprocess.Popen("executable") # $getCommand="executable" +subprocess.Popen(["executable", "arg0"]) # $getCommand="executable" +subprocess.Popen("", executable="executable") # $getCommand="executable" +subprocess.Popen(["", "arg0"], executable="executable") # $getCommand="executable" # call/check_call/check_output/run all work like Popen from a command execution point of view -subprocess.call(["executable", "arg0"]) # $SystemCommandExecution_getCommand="executable" -subprocess.check_call(["executable", "arg0"]) # $SystemCommandExecution_getCommand="executable" -subprocess.check_output(["executable", "arg0"]) # $SystemCommandExecution_getCommand="executable" -subprocess.run(["executable", "arg0"]) # $SystemCommandExecution_getCommand="executable" +subprocess.call(["executable", "arg0"]) # $getCommand="executable" +subprocess.check_call(["executable", "arg0"]) # $getCommand="executable" +subprocess.check_output(["executable", "arg0"]) # $getCommand="executable" +subprocess.run(["executable", "arg0"]) # $getCommand="executable" ######################################## # actively using known shell as the executable -subprocess.Popen(["/bin/sh", "-c", "vuln"]) # $SystemCommandExecution_getCommand="/bin/sh" $f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["/bin/bash", "-c", "vuln"]) # $SystemCommandExecution_getCommand="/bin/bash" $f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["/bin/dash", "-c", "vuln"]) # $SystemCommandExecution_getCommand="/bin/dash" $f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["/bin/zsh", "-c", "vuln"]) # $SystemCommandExecution_getCommand="/bin/zsh" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["/bin/sh", "-c", "vuln"]) # $getCommand="/bin/sh" $f-:getCommand="vuln" +subprocess.Popen(["/bin/bash", "-c", "vuln"]) # $getCommand="/bin/bash" $f-:getCommand="vuln" +subprocess.Popen(["/bin/dash", "-c", "vuln"]) # $getCommand="/bin/dash" $f-:getCommand="vuln" +subprocess.Popen(["/bin/zsh", "-c", "vuln"]) # $getCommand="/bin/zsh" $f-:getCommand="vuln" -subprocess.Popen(["sh", "-c", "vuln"]) # $SystemCommandExecution_getCommand="sh" $f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["bash", "-c", "vuln"]) # $SystemCommandExecution_getCommand="bash" $f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["dash", "-c", "vuln"]) # $SystemCommandExecution_getCommand="dash" $f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["zsh", "-c", "vuln"]) # $SystemCommandExecution_getCommand="zsh" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["sh", "-c", "vuln"]) # $getCommand="sh" $f-:getCommand="vuln" +subprocess.Popen(["bash", "-c", "vuln"]) # $getCommand="bash" $f-:getCommand="vuln" +subprocess.Popen(["dash", "-c", "vuln"]) # $getCommand="dash" $f-:getCommand="vuln" +subprocess.Popen(["zsh", "-c", "vuln"]) # $getCommand="zsh" $f-:getCommand="vuln" # Check that we don't consider ANY argument a command injection sink -subprocess.Popen(["sh", "/bin/python"]) # $SystemCommandExecution_getCommand="sh" +subprocess.Popen(["sh", "/bin/python"]) # $getCommand="sh" -subprocess.Popen(["cmd.exe", "/c", "vuln"]) # $SystemCommandExecution_getCommand="cmd.exe" $f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["cmd.exe", "/C", "vuln"]) # $SystemCommandExecution_getCommand="cmd.exe" $f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["cmd", "/c", "vuln"]) # $SystemCommandExecution_getCommand="cmd" $f-:SystemCommandExecution_getCommand="vuln" -subprocess.Popen(["cmd", "/C", "vuln"]) # $SystemCommandExecution_getCommand="cmd" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["cmd.exe", "/c", "vuln"]) # $getCommand="cmd.exe" $f-:getCommand="vuln" +subprocess.Popen(["cmd.exe", "/C", "vuln"]) # $getCommand="cmd.exe" $f-:getCommand="vuln" +subprocess.Popen(["cmd", "/c", "vuln"]) # $getCommand="cmd" $f-:getCommand="vuln" +subprocess.Popen(["cmd", "/C", "vuln"]) # $getCommand="cmd" $f-:getCommand="vuln" -subprocess.Popen(["", "-c", "vuln"], executable="/bin/bash") # $SystemCommandExecution_getCommand="/bin/bash" $f-:SystemCommandExecution_getCommand="vuln" +subprocess.Popen(["", "-c", "vuln"], executable="/bin/bash") # $getCommand="/bin/bash" $f-:getCommand="vuln" if UNKNOWN: - os.execl("/bin/sh", "", "-c", "vuln") # $SystemCommandExecution_getCommand="/bin/sh" $f-:SystemCommandExecution_getCommand="vuln" + os.execl("/bin/sh", "", "-c", "vuln") # $getCommand="/bin/sh" $f-:getCommand="vuln" -os.spawnl(os.P_WAIT, "/bin/sh", "", "-c", "vuln") # $SystemCommandExecution_getCommand="/bin/sh" $f-:SystemCommandExecution_getCommand="vuln" +os.spawnl(os.P_WAIT, "/bin/sh", "", "-c", "vuln") # $getCommand="/bin/sh" $f-:getCommand="vuln" ######################################## # Passing arguments by reference args = ["/bin/sh", "-c", "vuln"] -subprocess.Popen(args) # $SystemCommandExecution_getCommand=args +subprocess.Popen(args) # $getCommand=args args = "" use_shell = False exe = "executable" -subprocess.Popen(args, shell=use_shell, executable=exe) # $f+:SystemCommandExecution_getCommand=args $SystemCommandExecution_getCommand=exe +subprocess.Popen(args, shell=use_shell, executable=exe) # $f+:getCommand=args $getCommand=exe ################################################################################ diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll index 258cffc694b..eb798823d50 100644 --- a/python/ql/test/experimental/meta/ConceptsTest.qll +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -19,7 +19,7 @@ string value_from_expr(Expr e) { class SystemCommandExecutionTest extends InlineExpectationsTest { SystemCommandExecutionTest() { this = "SystemCommandExecutionTest" } - override string getARelevantTag() { result = "SystemCommandExecution_getCommand" } + override string getARelevantTag() { result = "getCommand" } override predicate hasActualResult(Location location, string element, string tag, string value) { exists(SystemCommandExecution sce, DataFlow::Node command | @@ -28,7 +28,7 @@ class SystemCommandExecutionTest extends InlineExpectationsTest { location = command.getLocation() and element = command.toString() and value = value_from_expr(command.asExpr()) and - tag = "SystemCommandExecution_getCommand" + tag = "getCommand" ) } }