Compare commits

..

162 Commits

Author SHA1 Message Date
Max Schaefer
eb3d0f5b0e Add merlyn.md which explains the changes on this branch. 2021-04-19 15:04:21 +01:00
Max Schaefer
09cf8e8b01 Remove RequestHeaderAccess. 2021-04-19 15:04:21 +01:00
Max Schaefer
bd8212c090 Remove RequestInputAccess. 2021-04-19 15:04:21 +01:00
Max Schaefer
f106d186e4 Remove MultipartyRemoteFlow. 2021-04-19 15:04:21 +01:00
Max Schaefer
e2c84407b4 Revert changes to Express::RequestInputAccess in c45d84f8f3 and 9cacfab7c6. 2021-04-19 15:04:21 +01:00
Max Schaefer
67b15125c7 Revert changes to Express::RequestInputAccess in d84f1b47c2. 2021-04-19 15:04:21 +01:00
Max Schaefer
caf763a969 Revert changes to Express::RequestInputAccess in ed48efe5b4. 2021-04-19 15:04:21 +01:00
Max Schaefer
4f8f5048f3 Revert changes to Express::RequestInputAccess in 83f0514475. 2021-04-19 15:04:21 +01:00
Max Schaefer
2366679d9b Revert changes to Express::RequestInputAccess in e2fbf8a68c. 2021-04-19 15:04:21 +01:00
Max Schaefer
66399c055e Remove MicroBodyParserCall. 2021-04-19 15:04:21 +01:00
Max Schaefer
85c02a430e Remove ServerRequestDataEvent. 2021-04-19 15:04:20 +01:00
Max Schaefer
29945b8ed0 Remove VueRouterFlowSource. 2021-04-19 15:04:20 +01:00
Max Schaefer
a8ef1bc32a Remove ServerlessHandlerEventAsRemoteFlow. 2021-04-19 15:04:20 +01:00
Max Schaefer
0781a138af Remove ReceivedItemAsRemoteFlow. 2021-04-19 15:04:20 +01:00
Max Schaefer
6fd67c4d8e Remove ReactRouterSource. 2021-04-19 15:04:19 +01:00
Max Schaefer
89747ecf83 Revert changes to `PostMessageEventHandler in cb7de27. 2021-04-19 15:03:51 +01:00
Max Schaefer
c013e3f9c3 Remove NodeJSNetServerItemAsRemoteFlow. 2021-04-19 15:03:51 +01:00
Max Schaefer
3b14b27635 Remove NextParams. 2021-04-19 15:03:51 +01:00
Max Schaefer
2ae32be934 Revert changes to ClientRequestData from 0b55aed626. 2021-04-19 15:03:51 +01:00
Max Schaefer
6647f6b9c4 Remove FormidableRemoteFlow. 2021-04-19 15:03:51 +01:00
Max Schaefer
41ceb291de Remove BusBoyRemoteFlow. 2021-04-19 15:03:51 +01:00
Max Schaefer
615418d2e3 Remove AngularSource. 2021-04-19 15:03:49 +01:00
Max Schaefer
0ba76f7d0e Revert "JS: Move $() sink into separate dataflow config"
This reverts commit 50a015c73e.
2021-04-19 15:03:11 +01:00
Max Schaefer
d97a10ef8a Revert "JS: Address review comments"
This reverts commit c91cdb5194.
2021-04-19 14:57:18 +01:00
Shati Patel
2d618d6b92 Merge pull request #5625 from shati-patel/docs/cli-manual
Docs: Link to CodeQL CLI manual from the sidebar
2021-04-09 15:30:24 +01:00
Jonas Jensen
e1d0bbb021 Merge pull request #5607 from MathiasVP/smart-pointer-ast-read-store-steps
C++: read and store steps for smart pointers in AST dataflow
2021-04-09 16:11:48 +02:00
CodeQL CI
6fd4a8afff Merge pull request #5567 from asgerf/js/sql-models
Approved by esbena
2021-04-09 07:11:10 -07:00
CodeQL CI
be2fe6e171 Merge pull request #5630 from erik-krogh/urlStep
Approved by esbena
2021-04-09 07:05:43 -07:00
CodeQL CI
8d2768b2ce Merge pull request #5634 from erik-krogh/fileSource
Approved by asgerf
2021-04-09 07:04:42 -07:00
Anders Schack-Mulligen
701e815368 Merge pull request #5628 from hvitved/java/remove-unique
Java: Remove `unique` wrapper from `DataFlow::Node::getEnclosingCallable()`
2021-04-09 15:21:26 +02:00
Mathias Vorreiter Pedersen
cd310eb9d5 C++: Remove unused import. 2021-04-09 15:08:48 +02:00
Tamás Vajk
992a4df12f Merge pull request #5619 from tamasvajk/feature/fix-default-argument-value-extraction
C# Improve default argument value extraction
2021-04-09 14:58:35 +02:00
Mathias Vorreiter Pedersen
996cda9b97 C++: Fix incorrect test annotation. 2021-04-09 14:46:46 +02:00
Mathias Vorreiter Pedersen
80d5b17900 C++: Remove the dataflow rule for smart_ptr -> *smart_ptr. 2021-04-09 14:20:51 +02:00
Mathias Vorreiter Pedersen
cae0060a89 C++: Replace the new rules in DataFlowUtil with a dataflow model for pointer wrapper classes. 2021-04-09 14:06:58 +02:00
Tamas Vajk
46197e6e69 Address review comments 2021-04-09 13:39:37 +02:00
Erik Krogh Kristensen
595bdedb22 rename predicate to getStem, and update regexp 2021-04-09 13:07:54 +02:00
CodeQL CI
652e8b4872 Merge pull request #5586 from asgerf/js/tsconfig-file-inclusion-handling
Approved by esbena
2021-04-09 02:50:51 -07:00
Tom Hvitved
c9c4c067b6 Merge pull request #5633 from hvitved/csharp/get-a-source-type-perf
C#: Improve performance of `Dispatch::SimpleTypeDataFlow::getASourceType()`
2021-04-09 11:42:34 +02:00
Tamás Vajk
a335bb0115 Merge pull request #5609 from tamasvajk/feature/dapper
C#: Dapper support
2021-04-09 10:52:17 +02:00
CodeQL CI
ad267404c9 Merge pull request #5137 from asgerf/js/redux-less
Approved by erik-krogh
2021-04-09 01:24:19 -07:00
Tamas Vajk
d7f0b9a7fa Add change note 2021-04-09 09:58:37 +02:00
Tamas Vajk
749db379ca Address code review findings 2021-04-09 09:55:37 +02:00
Tamas Vajk
dbb3d3dc17 Add change note 2021-04-09 09:50:55 +02:00
Tamás Vajk
8adaee05b6 Merge pull request #5453 from tamasvajk/feature/use_codeql_stubs
C#: Adjust make_stubs.py to use codeql instead of odasa
2021-04-08 16:16:05 +02:00
Anders Schack-Mulligen
6109ef5e88 Merge pull request #5475 from Marcono1234/marcono1234/minus-literal
Java: Improve documentation regarding minus in front of numeric literals
2021-04-08 16:11:14 +02:00
Asger Feldthaus
7d300b53d7 JS: Autoformat 2021-04-08 15:06:48 +01:00
Anders Schack-Mulligen
d42a01cb3a qldoc fixup 2021-04-08 15:45:21 +02:00
Tamas Vajk
e5160929eb Remove ODASA reference from make_stubs.py 2021-04-08 15:04:02 +02:00
Erik Krogh Kristensen
30ba69d991 treat "files" in a package.json as main modules, if "main" is not present 2021-04-08 14:42:12 +02:00
Tom Hvitved
036e181bc1 C#: Improve performance of Dispatch::SimpleTypeDataFlow::getASourceType() 2021-04-08 14:27:28 +02:00
Tom Hvitved
716568ebd1 Merge pull request #5623 from hvitved/csharp/enclosing
C#: Compute enclosing callable as a transitive closure
2021-04-08 14:20:09 +02:00
Tom Hvitved
9820116734 Merge pull request #5603 from hvitved/csharp/dataflow/no-unique
C#: Remove `unique` wrappers from `DataFlow::Node::get(EnclosingCallable|ControlFlowNode)`
2021-04-08 14:19:34 +02:00
Asger Feldthaus
52a2260dc7 JS: Rename change note file 2021-04-08 12:52:23 +01:00
Rasmus Wriedt Larsen
c738f387b1 Merge pull request #5624 from tausbn/python-make-callcfgnode-a-localsourcenode
Python: Improve `CallCfgNode` interface
2021-04-08 13:38:24 +02:00
Taus
cf5f760ecd Merge pull request #5582 from RasmusWL/all-tuple
Python: Add support for `__all__` assigned to tuple
2021-04-08 13:03:27 +02:00
Tamas Vajk
a790eb8110 Fix for unconstrained generic types 2021-04-08 12:20:01 +02:00
Tamas Vajk
a8cbdc92b9 Add more test cases 2021-04-08 12:17:19 +02:00
Tamas Vajk
551a7ce9e5 Fix expression value of struct default argument values 2021-04-08 12:14:53 +02:00
Tamas Vajk
c069c3384e Fix tests 2021-04-08 12:07:36 +02:00
Tamas Vajk
cb9a9db356 C# Improve default argument value extraction 2021-04-08 12:07:22 +02:00
Tamas Vajk
2ac1e60406 C#: Add parameter default value tests 2021-04-08 12:04:18 +02:00
Jonas Jensen
51bab81f56 Merge pull request #5622 from MathiasVP/inline-is-before
C++: Inline Location::isBefore
2021-04-08 11:24:33 +02:00
Erik Krogh Kristensen
99dd5330c2 add taint-step for URL construction in js/request-forgery 2021-04-08 11:10:33 +02:00
CodeQL CI
a9527fd913 Merge pull request #5621 from erik-krogh/shellSink
Approved by esbena
2021-04-08 09:47:45 +01:00
Tom Hvitved
2faf52b6bd Java: Remove unique wrapper from DataFlow::Node::getEnclosingCallable()` 2021-04-08 10:07:19 +02:00
Shati Patel
4cf0b8e725 Merge pull request #5626 from shati-patel/docs/broken-links
Docs: Fix broken link to cached "RemoteFlowSource"
2021-04-07 19:01:33 +01:00
Shati Patel
f372274857 Docs: Fix broken links 2021-04-07 18:02:29 +01:00
Shati Patel
2373bf2dfb Docs: Link to CodeQL CLI manual from the sidebar 2021-04-07 17:55:05 +01:00
Tom Hvitved
1cf30d2a9e C#: Compute enclosing callable as a transitive closure 2021-04-07 17:44:41 +02:00
Jonas Jensen
ab58cb3d44 Merge pull request #5604 from MathiasVP/fix-false-positive-in-assign-where-compare-meant
C++: Fix FP in cpp/assign-where-compare-meant
2021-04-07 16:54:45 +02:00
CodeQL CI
f0491af64c Merge pull request #5529 from erik-krogh/socketInput
Approved by esbena
2021-04-07 15:03:13 +01:00
Asger F
0c724a8427 Merge pull request #5304 from asgerf/js/non-alert-data
JS: Implement new metric queries for line counting
2021-04-07 14:52:51 +01:00
Mathias Vorreiter Pedersen
03b12dbc6d C++: Inline Location::isBefore. 2021-04-07 15:45:08 +02:00
Erik Krogh Kristensen
365b4d722d backtrack string-concatenations from shell-execution sinks 2021-04-07 15:34:54 +02:00
Taus
903f364dab Python: Improve CallCfgNode interface
Call nodes are always local sources (specifically sources of the return
value of the call), and so inheriting from `LocalSourceNode` will have
no effect on results, but _should_ make it a bit more smooth to use the
API.
2021-04-07 13:31:12 +00:00
CodeQL CI
073a43ce74 Merge pull request #5606 from erik-krogh/shellInput
Approved by esbena
2021-04-07 14:30:31 +01:00
Shati Patel
461d4e45af Merge pull request #5608 from shati-patel/docs/telemetry-settings
Docs: Mention telemetry in "customizing settings"
2021-04-07 13:44:32 +01:00
Erik Krogh Kristensen
c9f54ea1ad update expected output 2021-04-07 12:37:17 +00:00
Asger Feldthaus
ee13ff71d6 JS: Add another change note 2021-04-07 12:29:06 +01:00
Asger Feldthaus
26cddc7d04 JS: Update test output 2021-04-07 12:28:45 +01:00
Asger Feldthaus
69973d0fa2 JS: Autoformat 2021-04-07 11:24:11 +01:00
Erik Krogh Kristensen
a66083d685 change "Uncontrolled path" to "Path concatenation" 2021-04-07 08:23:07 +00:00
CodeQL CI
fd4e8f8282 Merge pull request #5526 from erik-krogh/quotedShell
Approved by esbena
2021-04-07 08:39:01 +01:00
CodeQL CI
61880ba90a Merge pull request #5530 from erik-krogh/moreFS
Approved by esbena
2021-04-07 08:37:23 +01:00
Robert Marsh
e22ec50dee Merge pull request #5613 from github/hmakholm/pr/fix-redos
Fix ReDOS in cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql
2021-04-06 15:54:27 -07:00
Henning Makholm
2d615ef503 Fix ReDOS in cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql
The sub-regex `(\s|.)*` aims to capture arbitrary string content
(in contrast to `.*` which doesn't match newlines), but it is
unsafe, since non-newline whitespace can match both alternatives.

This caused an evaluator crash in the wild.

Replace with `[\s\S]*`, which matches everything in a safe way.
2021-04-06 20:10:57 +02:00
Tamas Vajk
ffcb345916 C#: Add Dapper support to SQL injection queries 2021-04-06 17:06:20 +02:00
Shati Patel
9a41c80626 Merge pull request #5574 from github/smowton/admin/update-supported-go-version
Update supported Go version to 1.16
2021-04-06 14:54:36 +01:00
Shati Patel
695b02a94c Docs: Mention telemetry in "customizing settings" 2021-04-06 14:30:17 +01:00
Erik Krogh Kristensen
2c1cc9ead6 use local variable instead of module.exports in example
Co-authored-by: Esben Sparre Andreasen <esbena@github.com>
2021-04-06 15:17:31 +02:00
Tom Hvitved
f45916efda Merge pull request #5605 from hvitved/csharp/exclude-dependency-queries
C#: Remove mentions of `exclude-dependency-queries.yml`
2021-04-06 14:58:49 +02:00
Mathias Vorreiter Pedersen
8382e85901 C++: Add flow into the source of read step and out of the target of a store step for smart pointers in AST dataflow. 2021-04-06 14:05:55 +02:00
Mathias Vorreiter Pedersen
f07d844362 C++: Add a test containing missing read/store dataflow steps for smart pointers. 2021-04-06 13:59:27 +02:00
Tamas Vajk
98001c494f C#: Add Dapper stub and new SqlInjection test cases 2021-04-06 13:30:31 +02:00
Erik Krogh Kristensen
41b89669a9 add joined paths as a sink to js/shell-command-constructed-from-input 2021-04-06 12:14:00 +02:00
Rasmus Wriedt Larsen
bc49bc7095 Python: Add variable with underscore to __all__ tests 2021-04-06 11:54:25 +02:00
Tom Hvitved
e0e58b24ea C#: Remove mentions of exclude-dependency-queries.yml 2021-04-06 11:50:36 +02:00
Rasmus Wriedt Larsen
224d3790b5 Python: Highlight all_indirect.py is not super important
At least not in my mind
2021-04-06 11:50:04 +02:00
Rasmus Wriedt Larsen
b11703cc74 Python: all_dybamic2 => all_indirect 2021-04-06 11:49:55 +02:00
Mathias Vorreiter Pedersen
5eb1f8abbd C++: Add change-note. 2021-04-06 11:47:57 +02:00
Rasmus Wriedt Larsen
0ebb24ebeb Merge pull request #5398 from yoff/python-api-enhancements
Python: Add small api enhancements determined useful during documentation work
2021-04-06 11:44:51 +02:00
Tom Hvitved
667b26b5d9 Merge pull request #5540 from hvitved/csharp/ssa-impl-tweaks
C#: Performance tweaks in `SsaImplCommon.qll`
2021-04-06 11:43:08 +02:00
Mathias Vorreiter Pedersen
a5f4d43d61 C++: Fix false positive by adding another allow-list pattern in AssignWhereCompareMeant. 2021-04-06 11:01:38 +02:00
Mathias Vorreiter Pedersen
7045597139 C++: Add testcase with false positive from #5318. 2021-04-06 10:58:15 +02:00
Erik Krogh Kristensen
c194598d37 recognize headers/url from the HTTP request to a server WebSocket. 2021-04-06 10:11:27 +02:00
Tom Hvitved
e852540254 C#: Remove unique wrappers from DataFlow::Node::get(EnclosingCallable|ControlFlowNode) 2021-04-06 09:56:09 +02:00
Rasmus Lerchedahl Petersen
c777f1d8d7 Merge branch 'main' of github.com:github/codeql into python-api-enhancements 2021-04-06 09:31:26 +02:00
yoff
a23d8deb10 Merge pull request #5483 from RasmusWL/minor-fixup-django
Python: Better text for getSourceType in Django
2021-04-06 08:30:58 +02:00
Asger Feldthaus
32500c834d JS: Change note 2021-04-01 16:41:03 +01:00
Asger Feldthaus
acc28df785 JS: Bugfix in tsconfig file inclusion handling 2021-04-01 16:33:05 +01:00
Asger Feldthaus
564a6873f8 JS: Add baseUrl test 2021-04-01 16:33:05 +01:00
Asger Feldthaus
c4ab6fb7b4 JS: Add ImportGraph meta query 2021-04-01 16:33:05 +01:00
Asger Feldthaus
f07030ba97 JS: Update AdditionalFlowStep -> SharedFlowStep 2021-04-01 13:16:47 +01:00
Asger Feldthaus
a9566728b5 JS: Update an import of Unit type 2021-04-01 13:16:47 +01:00
Asger Feldthaus
7119eda009 JS: Add redux change note 2021-04-01 13:16:47 +01:00
Asger Feldthaus
86bc0eb853 JS: Autoformat 2021-04-01 13:16:47 +01:00
Asger Feldthaus
b43989e6a1 JS: Use API nodes to track dispatch/dispatched value sources 2021-04-01 13:16:47 +01:00
Asger Feldthaus
2850b8e952 JS: Fix RangeAnalysis after BasicBlock.dominates change 2021-04-01 13:16:47 +01:00
Asger Feldthaus
cbfa5ad303 JS: Change type of a parameter 2021-04-01 13:16:47 +01:00
Asger Feldthaus
cee1a12489 JS: Fix typo in qldoc 2021-04-01 13:16:47 +01:00
Asger Feldthaus
c926a47d50 JS: QLDoc and test for HeuristicConnectEntryPoint 2021-04-01 13:16:47 +01:00
Asger Feldthaus
cca38a64be JS: Add test for flow to a closure body under a type guard 2021-04-01 13:16:46 +01:00
Asger Feldthaus
53def60e4f JS: Add test for if-based type check 2021-04-01 13:16:46 +01:00
Asger Feldthaus
1ce7c3448f JS: Address some review comments 2021-04-01 13:16:46 +01:00
Asger Feldthaus
fd7cbd0c96 JS: Tweak BasicBlock.dominates and friends 2021-04-01 13:16:46 +01:00
Asger Feldthaus
8fa3fb0561 JS: Redux model 2021-04-01 13:16:46 +01:00
Asger Feldthaus
314839fc09 JS: Add @reduxjs/toolkit to composed functions 2021-04-01 13:16:46 +01:00
Asger Feldthaus
c1651ad30c JS: Factor out Unit type 2021-04-01 13:16:46 +01:00
Asger Feldthaus
125d1465c8 JS: Add DataFlow::functionForwardingStep 2021-04-01 13:16:46 +01:00
Asger Feldthaus
a3421e7ab2 JS: Add getALocalUse 2021-04-01 13:16:45 +01:00
Rasmus Wriedt Larsen
95ac2c8edd Python: Add another dynamic __all__ test 2021-03-31 17:31:55 +02:00
Rasmus Wriedt Larsen
ab3edf37d7 Python: Handle __all__ assigned to a tuple
Examples where this is used in real code:

- 76c0b32f82/django/core/files/temp.py (L24)
- 76c0b32f82/django/contrib/gis/gdal/__init__.py (L44-L49)
2021-03-31 17:25:19 +02:00
Rasmus Wriedt Larsen
43306f4700 Python: Add tests for Module.declaredInAll 2021-03-31 17:24:17 +02:00
Asger Feldthaus
57784dc746 JS: Update test output 2021-03-31 09:23:47 +01:00
Chris Smowton
4f9b6d1192 Update supported Go version to 1.16 2021-03-31 08:56:27 +01:00
Asger Feldthaus
f8bbda0cdc JS: Change note 2021-03-30 13:54:01 +01:00
Asger Feldthaus
9db235ac36 JS: Improve @google-cloud/spanner model 2021-03-30 13:54:00 +01:00
Asger Feldthaus
35f294f096 JS: Improve sequelize model 2021-03-30 13:54:00 +01:00
Asger Feldthaus
93500bd95a JS: Improve mssql model 2021-03-30 11:34:01 +01:00
Asger Feldthaus
95937c9ac7 JS: Improve sqlite3 model 2021-03-30 11:34:01 +01:00
Asger Feldthaus
0b21b273ed JS: Improve pg model 2021-03-30 11:33:59 +01:00
Asger Feldthaus
937a620f4d JS: Improve mysql2 model 2021-03-30 11:33:42 +01:00
Tom Hvitved
e345064a53 C#: Performance tweaks in SsaImplCommon.qll 2021-03-26 13:24:34 +01:00
Erik Krogh Kristensen
5e59f6d558 Update javascript/ql/src/semmle/javascript/security/dataflow/ShellCommandInjectionFromEnvironmentCustomizations.qll
Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com>
2021-03-25 19:03:37 +01:00
Erik Krogh Kristensen
3d49b8cb91 consider quoted string concatenations as sanitizers for js/shell-command-injection-from-environment 2021-03-25 15:17:02 +01:00
Erik Krogh Kristensen
3b82452d76 detect fs modules that pass through a reduce call 2021-03-25 14:47:43 +01:00
Rasmus Lerchedahl Petersen
a9af135d7e Python: Remove getALocalTaintSource
and `taintFlowsTo` for now..
2021-03-24 01:22:21 +01:00
Marcono1234
993999f64f Java: Add test for negative numeric literals 2021-03-22 17:43:34 +01:00
Tamas Vajk
7a0bfd1a69 Skip through any stub preamble 2021-03-22 12:29:13 +01:00
Rasmus Wriedt Larsen
f800bf243f Python: Better text for getSourceType in Django 2021-03-22 01:39:19 +01:00
Marcono1234
1534b387bb Java: Improve documentation regarding minus in front of numeric literals 2021-03-22 00:54:14 +01:00
Asger Feldthaus
405c1f3fc7 JS: Update test suite 2021-03-19 16:45:31 +00:00
Asger Feldthaus
fa2ae1420a JS: Rename Diagnostics folder to Summary 2021-03-19 16:43:23 +00:00
Asger Feldthaus
347cbe422d JS: Remove the other summary queries 2021-03-19 16:42:43 +00:00
Asger Feldthaus
0c0556bb38 JS: Update LinesOfCode.ql to match the style from C++ 2021-03-19 16:42:05 +00:00
Asger Feldthaus
6ca425f033 JS: Implement new metric queries for line counting 2021-03-19 16:34:29 +00:00
Tamas Vajk
79d6731ed8 C#: Adjust make_stubs.py to use codeql instead of odasa 2021-03-19 11:01:28 +01:00
Rasmus Lerchedahl Petersen
b3ff3f7ee7 PythonÆ adjust test expectations
I suspect it has to do with ParameterNode being a LocalSourceNode,
but I really have no idea...
2021-03-17 15:11:17 +01:00
Rasmus Lerchedahl Petersen
8f467003d2 Python: More review suggestions 2021-03-17 15:11:17 +01:00
yoff
63b732ce1f Apply suggestions from code review
Co-authored-by: Taus <tausbn@github.com>
2021-03-17 15:11:17 +01:00
Rasmus Lerchedahl Petersen
4d856d4461 Python: Add small api enhancements
determined useful during documentation work.
2021-03-17 15:11:17 +01:00
185 changed files with 4194 additions and 1447 deletions

View File

@@ -70,4 +70,3 @@
## Changes to libraries ## Changes to libraries
* The predicate `TypeAnnotation.hasQualifiedName` now works in more cases when the imported library was not present during extraction. * The predicate `TypeAnnotation.hasQualifiedName` now works in more cases when the imported library was not present during extraction.
* The class `DomBasedXss::Configuration` has been deprecated, as it has been split into `DomBasedXss::HtmlInjectionConfiguration` and `DomBasedXss::JQueryHtmlOrSelectorInjectionConfiguration`. Unless specifically working with jQuery sinks, subclasses should instead be based on `HtmlInjectionConfiguration`. To use both configurations in a query, see [Xss.ql](https://github.com/github/codeql/blob/main/javascript/ql/src/Security/CWE-079/Xss.ql) for an example.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* The 'Assignment where comparison was intended' (cpp/assign-where-compare-meant) query has been improved to flag fewer benign assignments in conditionals.

View File

@@ -54,7 +54,7 @@ class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
override predicate isWhitelisted() { override predicate isWhitelisted() {
this.getConversion().(ParenthesisExpr).isParenthesised() this.getConversion().(ParenthesisExpr).isParenthesised()
or or
// whitelist this assignment if all comparison operations in the expression that this // Allow this assignment if all comparison operations in the expression that this
// assignment is part of, are not parenthesized. In that case it seems like programmer // assignment is part of, are not parenthesized. In that case it seems like programmer
// is fine with unparenthesized comparison operands to binary logical operators, and // is fine with unparenthesized comparison operands to binary logical operators, and
// the parenthesis around this assignment was used to call it out as an assignment. // the parenthesis around this assignment was used to call it out as an assignment.
@@ -62,6 +62,21 @@ class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
forex(ComparisonOperation op | op = getComparisonOperand*(this.getParent+()) | forex(ComparisonOperation op | op = getComparisonOperand*(this.getParent+()) |
not op.isParenthesised() not op.isParenthesised()
) )
or
// Match a pattern like:
// ```
// if((a = b) && use_value(a)) { ... }
// ```
// where the assignment is meant to update the value of `a` before it's used in some other boolean
// subexpression that is guarenteed to be evaluate _after_ the assignment.
this.isParenthesised() and
exists(LogicalAndExpr parent, Variable var, VariableAccess access |
var = this.getLValue().(VariableAccess).getTarget() and
access = var.getAnAccess() and
not access.isUsedAsLValue() and
parent.getRightOperand() = access.getParent*() and
parent.getLeftOperand() = this.getParent*()
)
} }
} }

View File

@@ -93,7 +93,7 @@ class QuotedCommandInCreateProcessFunctionConfiguration extends DataFlow2::Confi
bindingset[s] bindingset[s]
predicate isQuotedOrNoSpaceApplicationNameOnCmd(string s) { predicate isQuotedOrNoSpaceApplicationNameOnCmd(string s) {
s.regexpMatch("\"([^\"])*\"(\\s|.)*") // The first element (path) is quoted s.regexpMatch("\"([^\"])*\"[\\s\\S]*") // The first element (path) is quoted
or or
s.regexpMatch("[^\\s]+") // There are no spaces in the string s.regexpMatch("[^\\s]+") // There are no spaces in the string
} }

View File

@@ -72,6 +72,7 @@ class Location extends @location {
} }
/** Holds if `this` comes on a line strictly before `l`. */ /** Holds if `this` comes on a line strictly before `l`. */
pragma[inline]
predicate isBefore(Location l) { predicate isBefore(Location l) {
this.getFile() = l.getFile() and this.getEndLine() < l.getStartLine() this.getFile() = l.getFile() and this.getEndLine() < l.getStartLine()
} }

View File

@@ -1,4 +1,5 @@
import semmle.code.cpp.models.interfaces.Taint import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.PointerWrapper import semmle.code.cpp.models.interfaces.PointerWrapper
/** /**
@@ -14,6 +15,23 @@ private class UniqueOrSharedPtr extends Class, PointerWrapper {
} }
} }
/** Any function that unwraps a pointer wrapper class to reveal the underlying pointer. */
private class PointerWrapperDataFlow extends DataFlowFunction {
PointerWrapperDataFlow() {
this = any(PointerWrapper wrapper).getAnUnwrapperFunction() and
not this.getUnspecifiedType() instanceof ReferenceType
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierAddress() and output.isReturnValue()
or
input.isQualifierObject() and output.isReturnValueDeref()
or
input.isReturnValueDeref() and
output.isQualifierObject()
}
}
/** /**
* The `std::make_shared` and `std::make_unique` template functions. * The `std::make_shared` and `std::make_unique` template functions.
*/ */

View File

@@ -3260,15 +3260,59 @@
| smart_pointer.cpp:47:11:47:11 | p | smart_pointer.cpp:47:10:47:10 | call to operator* | TAINT | | smart_pointer.cpp:47:11:47:11 | p | smart_pointer.cpp:47:10:47:10 | call to operator* | TAINT |
| smart_pointer.cpp:51:30:51:50 | call to make_shared | smart_pointer.cpp:52:10:52:10 | p | | | smart_pointer.cpp:51:30:51:50 | call to make_shared | smart_pointer.cpp:52:10:52:10 | p | |
| smart_pointer.cpp:51:52:51:57 | call to source | smart_pointer.cpp:51:30:51:50 | call to make_shared | TAINT | | smart_pointer.cpp:51:52:51:57 | call to source | smart_pointer.cpp:51:30:51:50 | call to make_shared | TAINT |
| smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | TAINT | | smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | |
| smart_pointer.cpp:52:12:52:14 | ref arg call to get | smart_pointer.cpp:52:10:52:10 | ref arg p | |
| smart_pointer.cpp:56:30:56:50 | call to make_unique | smart_pointer.cpp:57:10:57:10 | p | | | smart_pointer.cpp:56:30:56:50 | call to make_unique | smart_pointer.cpp:57:10:57:10 | p | |
| smart_pointer.cpp:56:52:56:57 | call to source | smart_pointer.cpp:56:30:56:50 | call to make_unique | TAINT | | smart_pointer.cpp:56:52:56:57 | call to source | smart_pointer.cpp:56:30:56:50 | call to make_unique | TAINT |
| smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | TAINT | | smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | |
| smart_pointer.cpp:57:12:57:14 | ref arg call to get | smart_pointer.cpp:57:10:57:10 | ref arg p | |
| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:66:10:66:10 | p | | | smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:66:10:66:10 | p | |
| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:67:10:67:10 | p | | | smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:67:10:67:10 | p | |
| smart_pointer.cpp:65:48:65:53 | call to source | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT | | smart_pointer.cpp:65:48:65:53 | call to source | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT |
| smart_pointer.cpp:65:58:65:58 | 0 | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT | | smart_pointer.cpp:65:58:65:58 | 0 | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT |
| smart_pointer.cpp:66:10:66:10 | p | smart_pointer.cpp:66:11:66:11 | call to operator-> | |
| smart_pointer.cpp:66:10:66:10 | ref arg p | smart_pointer.cpp:67:10:67:10 | p | | | smart_pointer.cpp:66:10:66:10 | ref arg p | smart_pointer.cpp:67:10:67:10 | p | |
| smart_pointer.cpp:67:10:67:10 | p | smart_pointer.cpp:67:11:67:11 | call to operator-> | |
| smart_pointer.cpp:76:45:76:45 | p | smart_pointer.cpp:77:3:77:3 | p | |
| smart_pointer.cpp:76:45:76:45 | p | smart_pointer.cpp:78:8:78:8 | p | |
| smart_pointer.cpp:76:45:76:45 | p | smart_pointer.cpp:79:8:79:8 | p | |
| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:81:3:81:3 | q | |
| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:82:8:82:8 | q | |
| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:83:8:83:8 | q | |
| smart_pointer.cpp:76:67:76:67 | q | smart_pointer.cpp:84:8:84:8 | q | |
| smart_pointer.cpp:77:3:77:3 | p | smart_pointer.cpp:77:4:77:4 | call to operator-> | |
| smart_pointer.cpp:77:3:77:3 | ref arg p | smart_pointer.cpp:78:8:78:8 | p | |
| smart_pointer.cpp:77:3:77:3 | ref arg p | smart_pointer.cpp:79:8:79:8 | p | |
| smart_pointer.cpp:77:3:77:17 | ... = ... | smart_pointer.cpp:77:6:77:6 | x [post update] | |
| smart_pointer.cpp:77:3:77:17 | ... = ... | smart_pointer.cpp:78:11:78:11 | x | |
| smart_pointer.cpp:77:4:77:4 | call to operator-> [post update] | smart_pointer.cpp:77:3:77:3 | ref arg p | |
| smart_pointer.cpp:77:10:77:15 | call to source | smart_pointer.cpp:77:3:77:17 | ... = ... | |
| smart_pointer.cpp:78:8:78:8 | p | smart_pointer.cpp:78:9:78:9 | call to operator-> | |
| smart_pointer.cpp:78:8:78:8 | ref arg p | smart_pointer.cpp:79:8:79:8 | p | |
| smart_pointer.cpp:79:8:79:8 | p | smart_pointer.cpp:79:9:79:9 | call to operator-> | |
| smart_pointer.cpp:81:3:81:3 | q | smart_pointer.cpp:81:4:81:4 | call to operator-> | |
| smart_pointer.cpp:81:3:81:3 | ref arg q | smart_pointer.cpp:82:8:82:8 | q | |
| smart_pointer.cpp:81:3:81:3 | ref arg q | smart_pointer.cpp:83:8:83:8 | q | |
| smart_pointer.cpp:81:3:81:3 | ref arg q | smart_pointer.cpp:84:8:84:8 | q | |
| smart_pointer.cpp:81:3:81:20 | ... = ... | smart_pointer.cpp:81:9:81:9 | x [post update] | |
| smart_pointer.cpp:81:3:81:20 | ... = ... | smart_pointer.cpp:82:14:82:14 | x | |
| smart_pointer.cpp:81:4:81:4 | call to operator-> [post update] | smart_pointer.cpp:81:3:81:3 | ref arg q | |
| smart_pointer.cpp:81:13:81:18 | call to source | smart_pointer.cpp:81:3:81:20 | ... = ... | |
| smart_pointer.cpp:82:8:82:8 | q | smart_pointer.cpp:82:9:82:9 | call to operator-> | |
| smart_pointer.cpp:82:8:82:8 | ref arg q | smart_pointer.cpp:83:8:83:8 | q | |
| smart_pointer.cpp:82:8:82:8 | ref arg q | smart_pointer.cpp:84:8:84:8 | q | |
| smart_pointer.cpp:83:8:83:8 | q | smart_pointer.cpp:83:9:83:9 | call to operator-> | |
| smart_pointer.cpp:83:8:83:8 | ref arg q | smart_pointer.cpp:84:8:84:8 | q | |
| smart_pointer.cpp:84:8:84:8 | q | smart_pointer.cpp:84:9:84:9 | call to operator-> | |
| smart_pointer.cpp:87:17:87:18 | pa | smart_pointer.cpp:88:5:88:6 | pa | |
| smart_pointer.cpp:88:5:88:20 | ... = ... | smart_pointer.cpp:88:9:88:9 | x [post update] | |
| smart_pointer.cpp:88:13:88:18 | call to source | smart_pointer.cpp:88:5:88:20 | ... = ... | |
| smart_pointer.cpp:92:25:92:50 | call to unique_ptr | smart_pointer.cpp:93:11:93:11 | p | |
| smart_pointer.cpp:92:25:92:50 | call to unique_ptr | smart_pointer.cpp:94:8:94:8 | p | |
| smart_pointer.cpp:93:11:93:11 | p | smart_pointer.cpp:93:13:93:15 | call to get | |
| smart_pointer.cpp:93:11:93:11 | ref arg p | smart_pointer.cpp:94:8:94:8 | p | |
| smart_pointer.cpp:93:13:93:15 | ref arg call to get | smart_pointer.cpp:93:11:93:11 | ref arg p | |
| smart_pointer.cpp:94:8:94:8 | p | smart_pointer.cpp:94:9:94:9 | call to operator-> | |
| 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: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: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:41:12:41:18 | source1 | |

View File

@@ -66,3 +66,30 @@ void test_shared_field_member() {
sink(p->x); // $ MISSING: ast,ir sink(p->x); // $ MISSING: ast,ir
sink(p->y); // not tainted sink(p->y); // not tainted
} }
struct B {
A a1;
A a2;
int z;
};
void test_operator_arrow(std::unique_ptr<A> p, std::unique_ptr<B> q) {
p->x = source();
sink(p->x); // $ ast MISSING: ir
sink(p->y);
q->a1.x = source();
sink(q->a1.x); // $ ast MISSING: ir
sink(q->a1.y);
sink(q->a2.x);
}
void taint_x(A* pa) {
pa->x = source();
}
void reverse_taint_smart_pointer() {
std::unique_ptr<A> p = std::unique_ptr<A>(new A);
taint_x(p.get());
sink(p->x); // $ ast MISSING: ir
}

View File

@@ -19,3 +19,7 @@
| test.cpp:144:32:144:36 | ... = ... | Use of '=' where '==' may have been intended. | | test.cpp:144:32:144:36 | ... = ... | Use of '=' where '==' may have been intended. |
| test.cpp:150:32:150:36 | ... = ... | Use of '=' where '==' may have been intended. | | test.cpp:150:32:150:36 | ... = ... | Use of '=' where '==' may have been intended. |
| test.cpp:153:46:153:50 | ... = ... | Use of '=' where '==' may have been intended. | | test.cpp:153:46:153:50 | ... = ... | Use of '=' where '==' may have been intended. |
| test.cpp:166:22:166:27 | ... = ... | Use of '=' where '==' may have been intended. |
| test.cpp:168:24:168:29 | ... = ... | Use of '=' where '==' may have been intended. |
| test.cpp:169:23:169:28 | ... = ... | Use of '=' where '==' may have been intended. |
| test.cpp:171:7:171:12 | ... = ... | Use of '=' where '==' may have been intended. |

View File

@@ -153,3 +153,21 @@ void f3(int x, int y) {
if((x == 10) || ((z == z) && (x == 1)) && (y = 2)) { // BAD if((x == 10) || ((z == z) && (x == 1)) && (y = 2)) { // BAD
} }
} }
bool use(int);
void f4(int x, bool b) {
if((x = 10) && use(x)) {} // GOOD: This is likely just a short-hand way of writing an assignment
// followed by a boolean check.
if((x = 10) && b && use(x)) {} // GOOD: Same reason as above
if((x = 10) && use(x) && b) {} // GOOD: Same reason as above
if((x = 10) && (use(x) && b)) {} // GOOD: Same reason as above
if(use(x) && b && (x = 10)) {} // BAD: The assignment is the last thing that happens in the comparison.
// This doesn't match the usual pattern.
if((use(x) && b) && (x = 10)) {} // BAD: Same reason as above
if(use(x) && (b && (x = 10))) {} // BAD: Same reason as above
if((x = 10) || use(x)) {} // BAD: This doesn't follow the usual style of writing an assignment in
// a boolean check.
}

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* Support for the Dapper ORM library has been added to the SQL injection checks.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* The extractor has been improved to store default argument values for parameters that are extracted from referenced assemblies.

View File

@@ -164,6 +164,39 @@ namespace Semmle.Extraction.CSharp.Entities
} }
} }
/// <summary>
/// Creates a generated expression for a default argument value.
/// </summary>
public static Expression? CreateGenerated(Context cx, IParameterSymbol parameter, IExpressionParentEntity parent,
int childIndex, Extraction.Entities.Location location)
{
if (!parameter.HasExplicitDefaultValue)
{
return null;
}
var defaultValue = parameter.ExplicitDefaultValue;
if (parameter.Type is INamedTypeSymbol nt && nt.EnumUnderlyingType is not null)
{
// = (MyEnum)1, = MyEnum.Value1, = default(MyEnum), = new MyEnum()
// we're generating a (MyEnum)value cast expression:
defaultValue ??= 0;
Action<Expression, int> createChild = (parent, index) => Literal.CreateGenerated(cx, parent, index, nt.EnumUnderlyingType, defaultValue, location);
return Cast.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, createChild, location);
}
if (defaultValue is null)
{
// = null, = default, = default(T), = new MyStruct()
// we're generating a default expression:
return Default.CreateGenerated(cx, parent, childIndex, location, parameter.Type.IsReferenceType ? ValueAsString(null) : null);
}
// const literal:
return Literal.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, location);
}
/// <summary> /// <summary>
/// Adapt the operator kind depending on whether it's a dynamic call or a user-operator call. /// Adapt the operator kind depending on whether it's a dynamic call or a user-operator call.
/// </summary> /// </summary>

View File

@@ -14,5 +14,20 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{ {
TypeAccess.Create(Context, Syntax.Type, this, 0); TypeAccess.Create(Context, Syntax.Type, this, 0);
} }
public static Expression CreateGenerated(Context cx, IExpressionParentEntity parent, int childIndex, Extraction.Entities.Location location, string? value)
{
var info = new ExpressionInfo(
cx,
null,
location,
ExprKind.DEFAULT,
parent,
childIndex,
true,
value);
return new Expression(info);
}
} }
} }

View File

@@ -4,6 +4,7 @@ using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Entities; using Semmle.Extraction.Entities;
using System.IO; using System.IO;
using System;
namespace Semmle.Extraction.CSharp.Entities namespace Semmle.Extraction.CSharp.Entities
{ {
@@ -124,6 +125,17 @@ namespace Semmle.Extraction.CSharp.Entities
trapFile.param_location(this, Context.CreateLocation()); trapFile.param_location(this, Context.CreateLocation());
} }
if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol))
{
var defaultValueSyntax = GetDefaultValueFromSyntax(Symbol);
Action defaultValueExpressionCreation = defaultValueSyntax is not null
? () => Expression.Create(Context, defaultValueSyntax.Value, this, 0)
: () => Expression.CreateGenerated(Context, Symbol, this, 0, Location);
Context.PopulateLater(defaultValueExpressionCreation);
}
if (!IsSourceDeclaration || !Symbol.FromSource()) if (!IsSourceDeclaration || !Symbol.FromSource())
return; return;
@@ -139,36 +151,28 @@ namespace Semmle.Extraction.CSharp.Entities
TypeMention.Create(Context, syntax.Type!, this, type); TypeMention.Create(Context, syntax.Type!, this, type);
} }
} }
}
if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol)) private static EqualsValueClauseSyntax? GetDefaultValueFromSyntax(IParameterSymbol symbol)
{
// This is a slight bug in the dbscheme
// We should really define param_default(param, string)
// And use parameter child #0 to encode the default expression.
var defaultValue = GetParameterDefaultValue(symbol);
if (defaultValue is null)
{ {
// This is a slight bug in the dbscheme // In case this parameter belongs to an accessor of an indexer, we need
// We should really define param_default(param, string) // to get the default value from the corresponding parameter belonging
// And use parameter child #0 to encode the default expression. // to the indexer itself
var defaultValue = GetParameterDefaultValue(Symbol); if (symbol.ContainingSymbol is IMethodSymbol method)
if (defaultValue is null)
{ {
// In case this parameter belongs to an accessor of an indexer, we need var i = method.Parameters.IndexOf(symbol);
// to get the default value from the corresponding parameter belonging if (method.AssociatedSymbol is IPropertySymbol indexer)
// to the indexer itself defaultValue = GetParameterDefaultValue(indexer.Parameters[i]);
var method = (IMethodSymbol)Symbol.ContainingSymbol;
if (method is not null)
{
var i = method.Parameters.IndexOf(Symbol);
var indexer = (IPropertySymbol?)method.AssociatedSymbol;
if (indexer is not null)
defaultValue = GetParameterDefaultValue(indexer.Parameters[i]);
}
}
if (defaultValue is not null)
{
Context.PopulateLater(() =>
{
Expression.Create(Context, defaultValue.Value, this, 0);
});
} }
} }
return defaultValue;
} }
public override bool IsSourceDeclaration => Symbol.IsSourceDeclaration(); public override bool IsSourceDeclaration => Symbol.IsSourceDeclaration();

View File

@@ -43,12 +43,6 @@ if not foundCS:
print("Test directory does not contain .cs files. Please specify a working qltest directory.") print("Test directory does not contain .cs files. Please specify a working qltest directory.")
exit(1) exit(1)
cmd = ['odasa', 'selfTest']
print('Running ' + ' '.join(cmd))
if subprocess.check_call(cmd):
print("odasa selfTest failed. Ensure odasa is on your current path.")
exit(1)
csharpQueries = os.path.abspath(os.path.dirname(sys.argv[0])) csharpQueries = os.path.abspath(os.path.dirname(sys.argv[0]))
outputFile = os.path.join(testDir, 'stubs.cs') outputFile = os.path.join(testDir, 'stubs.cs')
@@ -58,56 +52,75 @@ if os.path.isfile(outputFile):
os.remove(outputFile) # It would interfere with the test. os.remove(outputFile) # It would interfere with the test.
print("Removed previous", outputFile) print("Removed previous", outputFile)
cmd = ['odasa', 'qltest', '--optimize', '--leave-temp-files', testDir] cmd = ['codeql', 'test', 'run', '--keep-databases', testDir]
print('Running ' + ' '.join(cmd)) print('Running ' + ' '.join(cmd))
if subprocess.check_call(cmd): if subprocess.check_call(cmd):
print("qltest failed. Please fix up the test before proceeding.") print("codeql test failed. Please fix up the test before proceeding.")
exit(1) exit(1)
dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj", "db-csharp") dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj")
if not os.path.isdir(dbDir): if not os.path.isdir(dbDir):
print("Expected database directory " + dbDir + " not found. Please contact Semmle.") print("Expected database directory " + dbDir + " not found.")
exit(1) exit(1)
cmd = ['odasa', 'runQuery', '--query', os.path.join(csharpQueries, 'MinimalStubsFromSource.ql'), '--db', dbDir, '--output-file', outputFile] cmd = ['codeql', 'query', 'run', os.path.join(
csharpQueries, 'MinimalStubsFromSource.ql'), '--database', dbDir, '--output', outputFile]
print('Running ' + ' '.join(cmd)) print('Running ' + ' '.join(cmd))
if subprocess.check_call(cmd): if subprocess.check_call(cmd):
print('Failed to run the query to generate output file. Please contact Semmle.') print('Failed to run the query to generate output file.')
exit(1) exit(1)
# Remove the leading " and trailing " bytes from the file # Remove the leading and trailing bytes from the file
len = os.stat(outputFile).st_size length = os.stat(outputFile).st_size
f = open(outputFile, "rb") if length < 20:
try: contents = b''
quote = f.read(1) else:
if quote != b'"': f = open(outputFile, "rb")
print("Unexpected character in file. Please contact Semmle.") try:
contents = f.read(len-3) countTillSlash = 0
quote = f.read(1) foundSlash = False
if quote != b'"': slash = f.read(1)
print("Unexpected end character. Please contact Semmle.", quote) while slash != b'':
finally: if slash == b'/':
f.close() foundSlash = True
break
countTillSlash += 1
slash = f.read(1)
if not foundSlash:
countTillSlash = 0
f.seek(0)
quote = f.read(countTillSlash)
print("Start characters in file skipped.", quote)
post = b'\x0e\x01\x08#select\x01\x01\x00s\x00'
contents = f.read(length - len(post) - countTillSlash)
quote = f.read(len(post))
if quote != post:
print("Unexpected end character in file.", quote)
finally:
f.close()
f = open(outputFile, "wb") f = open(outputFile, "wb")
f.write(contents) f.write(contents)
f.close() f.close()
cmd = ['odasa', 'qltest', '--optimize', testDir] cmd = ['codeql', 'test', 'run', testDir]
print('Running ' + ' '.join(cmd)) print('Running ' + ' '.join(cmd))
if subprocess.check_call(cmd): if subprocess.check_call(cmd):
print('\nTest failed. You may need to fix up', outputFile) print('\nTest failed. You may need to fix up', outputFile)
print('It may help to view', outputFile, ' in Visual Studio') print('It may help to view', outputFile, ' in Visual Studio')
print("Next steps:") print("Next steps:")
print('1. Look at the compilation errors, and fix up', outputFile, 'so that the test compiles') print('1. Look at the compilation errors, and fix up',
print('2. Re-run odasa qltest --optimize "' + testDir + '"') outputFile, 'so that the test compiles')
print('3. git add "' + outputFile + '"') print('2. Re-run codeql test run "' + testDir + '"')
exit(1) print('3. git add "' + outputFile + '"')
exit(1)
print("\nStub generation successful! Next steps:") print("\nStub generation successful! Next steps:")
print('1. Edit "semmle-extractor-options" in the .cs files to remove unused references') print('1. Edit "semmle-extractor-options" in the .cs files to remove unused references')
print('2. Re-run odasa qltest --optimize "' + testDir + '"') print('2. Re-run codeql test run "' + testDir + '"')
print('3. git add "' + outputFile + '"') print('3. git add "' + outputFile + '"')
print('4. Commit your changes.') print('4. Commit your changes.')

View File

@@ -2,5 +2,3 @@
- qlpack: codeql-csharp - qlpack: codeql-csharp
- apply: code-scanning-selectors.yml - apply: code-scanning-selectors.yml
from: codeql-suite-helpers from: codeql-suite-helpers
- apply: codeql-suites/exclude-dependency-queries.yml
from: codeql-csharp

View File

@@ -2,5 +2,3 @@
- qlpack: codeql-csharp - qlpack: codeql-csharp
- apply: security-and-quality-selectors.yml - apply: security-and-quality-selectors.yml
from: codeql-suite-helpers from: codeql-suite-helpers
- apply: codeql-suites/exclude-dependency-queries.yml
from: codeql-csharp

View File

@@ -2,5 +2,3 @@
- qlpack: codeql-csharp - qlpack: codeql-csharp
- apply: security-extended-selectors.yml - apply: security-extended-selectors.yml
from: codeql-suite-helpers from: codeql-suite-helpers
- apply: codeql-suites/exclude-dependency-queries.yml
from: codeql-csharp

View File

@@ -321,10 +321,9 @@ private module SsaDefReaches {
} }
pragma[noinline] pragma[noinline]
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) { private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
result = getABasicBlockSuccessor(bb) and ssaDefReachesEndOfBlock(bb, def, _) and
not defOccursInBlock(_, bb, def.getSourceVariable()) and not defOccursInBlock(_, bb, def.getSourceVariable())
ssaDefReachesEndOfBlock(bb, def, _)
} }
/** /**
@@ -337,7 +336,11 @@ private module SsaDefReaches {
defOccursInBlock(def, bb1, _) and defOccursInBlock(def, bb1, _) and
bb2 = getABasicBlockSuccessor(bb1) bb2 = getABasicBlockSuccessor(bb1)
or or
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid)) exists(BasicBlock mid |
varBlockReaches(def, bb1, mid) and
ssaDefReachesThroughBlock(def, mid) and
bb2 = getABasicBlockSuccessor(mid)
)
} }
/** /**
@@ -355,17 +358,10 @@ private module SsaDefReaches {
private import SsaDefReaches private import SsaDefReaches
pragma[noinline] pragma[nomagic]
private predicate ssaDefReachesEndOfBlockRec(BasicBlock bb, Definition def, SourceVariable v) { predicate liveThrough(BasicBlock bb, SourceVariable v) {
exists(BasicBlock idom | ssaDefReachesEndOfBlock(idom, def, v) | liveAtExit(bb, v) and
// The construction of SSA form ensures that each read of a variable is not ssaRef(bb, _, v, SsaDef())
// dominated by its definition. An SSA definition therefore reaches a
// control flow node if it is the _closest_ SSA definition that dominates
// the node. If two definitions dominate a node then one must dominate the
// other, so therefore the definition of _closest_ is given by the dominator
// tree. Thus, reaching definitions can be calculated in terms of dominance.
idom = getImmediateBasicBlockDominator(bb)
)
} }
/** /**
@@ -382,9 +378,14 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
liveAtExit(bb, v) liveAtExit(bb, v)
) )
or or
ssaDefReachesEndOfBlockRec(bb, def, v) and // The construction of SSA form ensures that each read of a variable is
liveAtExit(bb, v) and // dominated by its definition. An SSA definition therefore reaches a
not ssaRef(bb, _, v, SsaDef()) // control flow node if it is the _closest_ SSA definition that dominates
// the node. If two definitions dominate a node then one must dominate the
// other, so therefore the definition of _closest_ is given by the dominator
// tree. Thus, reaching definitions can be calculated in terms of dominance.
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
liveThrough(bb, pragma[only_bind_into](v))
} }
/** /**

View File

@@ -27,9 +27,6 @@ class Element extends DotNet::Element, @element {
/** Gets a location of this element, including sources and assemblies. */ /** Gets a location of this element, including sources and assemblies. */
override Location getALocation() { none() } override Location getALocation() { none() }
/** Holds if this element is from an assembly. */
predicate fromLibrary() { this.getFile().fromLibrary() }
/** Gets the parent of this element, if any. */ /** Gets the parent of this element, if any. */
Element getParent() { result.getAChild() = this } Element getParent() { result.getAChild() = this }

View File

@@ -1,83 +0,0 @@
/**
* INTERNAL: Do not use.
*
* Provides efficient cached predicates for finding enclosing statements and callables.
*
* There are a number of difficulties. There can be expressions without
* enclosing statements (for example initialisers for fields and constructors)
* or enclosing callables (even if we consider constructor initialisers
* to be enclosed by constructors, field initialisers don't have callables).
*
* The only cases where a `Stmt` has an `Expr` parent are delegate and lambda
* expressions, which are both callable.
*/
import Stmt
private import semmle.code.csharp.ExprOrStmtParent
/**
* INTERNAL: Do not use.
*/
cached
module Internal {
/**
* INTERNAL: Do not use.
*
* Holds if `c` is the enclosing callable of statement `s`.
*/
cached
predicate enclosingCallable(Stmt s, Callable c) {
// Compute the enclosing callable for a statement. This walks up through
// enclosing statements until it hits a callable. It's unambiguous, since
// if a statement has no parent statement, it's either the method body
// or the body of an anonymous function declaration, in each of which cases the
// non-statement parent is in fact the enclosing callable.
c.getAChildStmt+() = s
}
private Expr getAChildExpr(ExprOrStmtParent p) {
result = p.getAChildExpr() or
result = p.(AssignOperation).getExpandedAssignment()
}
/**
* INTERNAL: Do not use.
*
* Holds if `s` is the enclosing statement of expression `e`.
*/
cached
predicate enclosingStmt(Expr e, Stmt s) {
// Compute the enclosing statement for an expression. Note that this need
// not exist, since expressions can occur in contexts where they have no
// enclosing statement (examples include field initialisers, both inline
// and explicit on constructor definitions, and annotation arguments).
getAChildExpr+(s) = e
}
private predicate childExprOfCallable(Callable parent, Expr child) {
child = getAChildExpr(parent)
or
exists(Expr mid | childExprOfCallable(parent, mid) |
not mid instanceof Callable and
child = getAChildExpr(mid)
)
}
/**
* INTERNAL: Do not use.
*
* Holds if `c` is the enclosing callable of expression `e`.
*/
cached
predicate exprEnclosingCallable(Expr e, Callable c) {
// Compute the enclosing callable of an expression. Note that expressions in
// lambda functions should have the lambdas as enclosing callables, and their
// enclosing statement may be the same as the enclosing statement of the
// lambda; thus, it is *not* safe to go up to the enclosing statement and
// take its own enclosing callable.
childExprOfCallable(c, e)
or
not childExprOfCallable(_, e) and
exists(Stmt s | enclosingStmt(e, s) | enclosingCallable(s, c))
}
}

View File

@@ -138,6 +138,54 @@ private module Cached {
) )
else expr_parent(child, i, parent) else expr_parent(child, i, parent)
} }
private Expr getAChildExpr(ExprOrStmtParent parent) {
result = parent.getAChildExpr() or
result = parent.(AssignOperation).getExpandedAssignment()
}
private ControlFlowElement getAChild(ExprOrStmtParent parent) {
result = getAChildExpr(parent)
or
result = parent.getAChildStmt()
}
pragma[inline]
private ControlFlowElement enclosingStart(ControlFlowElement cfe) {
result = cfe
or
getAChild(result).(AnonymousFunctionExpr) = cfe
}
private predicate parent(ControlFlowElement child, ExprOrStmtParent parent) {
child = getAChild(parent) and
not child instanceof Callable
}
/** Holds if the enclosing body of `cfe` is `body`. */
cached
predicate enclosingBody(ControlFlowElement cfe, ControlFlowElement body) {
body = any(Callable c).getBody() and
parent*(enclosingStart(cfe), body)
}
/** Holds if the enclosing callable of `cfe` is `c`. */
cached
predicate enclosingCallable(ControlFlowElement cfe, Callable c) {
enclosingBody(cfe, c.getBody())
or
parent*(enclosingStart(cfe), c.(Constructor).getInitializer())
}
/** Holds if the enclosing statement of expression `e` is `s`. */
cached
predicate enclosingStmt(Expr e, Stmt s) {
// Compute the enclosing statement for an expression. Note that this need
// not exist, since expressions can occur in contexts where they have no
// enclosing statement (examples include field initialisers, both inline
// and explicit on constructor definitions, and annotation arguments).
getAChildExpr+(s) = e
}
} }
import Cached import Cached

View File

@@ -8,7 +8,7 @@ import Element
import Location import Location
import Member import Member
import exprs.Expr import exprs.Expr
private import semmle.code.csharp.Enclosing::Internal private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.System
private import TypeRef private import TypeRef

View File

@@ -321,10 +321,9 @@ private module SsaDefReaches {
} }
pragma[noinline] pragma[noinline]
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) { private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
result = getABasicBlockSuccessor(bb) and ssaDefReachesEndOfBlock(bb, def, _) and
not defOccursInBlock(_, bb, def.getSourceVariable()) and not defOccursInBlock(_, bb, def.getSourceVariable())
ssaDefReachesEndOfBlock(bb, def, _)
} }
/** /**
@@ -337,7 +336,11 @@ private module SsaDefReaches {
defOccursInBlock(def, bb1, _) and defOccursInBlock(def, bb1, _) and
bb2 = getABasicBlockSuccessor(bb1) bb2 = getABasicBlockSuccessor(bb1)
or or
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid)) exists(BasicBlock mid |
varBlockReaches(def, bb1, mid) and
ssaDefReachesThroughBlock(def, mid) and
bb2 = getABasicBlockSuccessor(mid)
)
} }
/** /**
@@ -355,17 +358,10 @@ private module SsaDefReaches {
private import SsaDefReaches private import SsaDefReaches
pragma[noinline] pragma[nomagic]
private predicate ssaDefReachesEndOfBlockRec(BasicBlock bb, Definition def, SourceVariable v) { predicate liveThrough(BasicBlock bb, SourceVariable v) {
exists(BasicBlock idom | ssaDefReachesEndOfBlock(idom, def, v) | liveAtExit(bb, v) and
// The construction of SSA form ensures that each read of a variable is not ssaRef(bb, _, v, SsaDef())
// dominated by its definition. An SSA definition therefore reaches a
// control flow node if it is the _closest_ SSA definition that dominates
// the node. If two definitions dominate a node then one must dominate the
// other, so therefore the definition of _closest_ is given by the dominator
// tree. Thus, reaching definitions can be calculated in terms of dominance.
idom = getImmediateBasicBlockDominator(bb)
)
} }
/** /**
@@ -382,9 +378,14 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
liveAtExit(bb, v) liveAtExit(bb, v)
) )
or or
ssaDefReachesEndOfBlockRec(bb, def, v) and // The construction of SSA form ensures that each read of a variable is
liveAtExit(bb, v) and // dominated by its definition. An SSA definition therefore reaches a
not ssaRef(bb, _, v, SsaDef()) // control flow node if it is the _closest_ SSA definition that dominates
// the node. If two definitions dominate a node then one must dominate the
// other, so therefore the definition of _closest_ is given by the dominator
// tree. Thus, reaching definitions can be calculated in terms of dominance.
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
liveThrough(bb, pragma[only_bind_into](v))
} }
/** /**

View File

@@ -47,14 +47,14 @@ class Node extends TNode {
cached cached
final DataFlowCallable getEnclosingCallable() { final DataFlowCallable getEnclosingCallable() {
Stages::DataFlowStage::forceCachingInSameStage() and Stages::DataFlowStage::forceCachingInSameStage() and
result = unique(DataFlowCallable c | c = this.(NodeImpl).getEnclosingCallableImpl() | c) result = this.(NodeImpl).getEnclosingCallableImpl()
} }
/** Gets the control flow node corresponding to this node, if any. */ /** Gets the control flow node corresponding to this node, if any. */
cached cached
final ControlFlow::Node getControlFlowNode() { final ControlFlow::Node getControlFlowNode() {
Stages::DataFlowStage::forceCachingInSameStage() and Stages::DataFlowStage::forceCachingInSameStage() and
result = unique(ControlFlow::Node n | n = this.(NodeImpl).getControlFlowNodeImpl() | n) result = this.(NodeImpl).getControlFlowNodeImpl()
} }
/** Gets a textual representation of this node. */ /** Gets a textual representation of this node. */

View File

@@ -321,10 +321,9 @@ private module SsaDefReaches {
} }
pragma[noinline] pragma[noinline]
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) { private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
result = getABasicBlockSuccessor(bb) and ssaDefReachesEndOfBlock(bb, def, _) and
not defOccursInBlock(_, bb, def.getSourceVariable()) and not defOccursInBlock(_, bb, def.getSourceVariable())
ssaDefReachesEndOfBlock(bb, def, _)
} }
/** /**
@@ -337,7 +336,11 @@ private module SsaDefReaches {
defOccursInBlock(def, bb1, _) and defOccursInBlock(def, bb1, _) and
bb2 = getABasicBlockSuccessor(bb1) bb2 = getABasicBlockSuccessor(bb1)
or or
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid)) exists(BasicBlock mid |
varBlockReaches(def, bb1, mid) and
ssaDefReachesThroughBlock(def, mid) and
bb2 = getABasicBlockSuccessor(mid)
)
} }
/** /**
@@ -355,17 +358,10 @@ private module SsaDefReaches {
private import SsaDefReaches private import SsaDefReaches
pragma[noinline] pragma[nomagic]
private predicate ssaDefReachesEndOfBlockRec(BasicBlock bb, Definition def, SourceVariable v) { predicate liveThrough(BasicBlock bb, SourceVariable v) {
exists(BasicBlock idom | ssaDefReachesEndOfBlock(idom, def, v) | liveAtExit(bb, v) and
// The construction of SSA form ensures that each read of a variable is not ssaRef(bb, _, v, SsaDef())
// dominated by its definition. An SSA definition therefore reaches a
// control flow node if it is the _closest_ SSA definition that dominates
// the node. If two definitions dominate a node then one must dominate the
// other, so therefore the definition of _closest_ is given by the dominator
// tree. Thus, reaching definitions can be calculated in terms of dominance.
idom = getImmediateBasicBlockDominator(bb)
)
} }
/** /**
@@ -382,9 +378,14 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
liveAtExit(bb, v) liveAtExit(bb, v)
) )
or or
ssaDefReachesEndOfBlockRec(bb, def, v) and // The construction of SSA form ensures that each read of a variable is
liveAtExit(bb, v) and // dominated by its definition. An SSA definition therefore reaches a
not ssaRef(bb, _, v, SsaDef()) // control flow node if it is the _closest_ SSA definition that dominates
// the node. If two definitions dominate a node then one must dominate the
// other, so therefore the definition of _closest_ is given by the dominator
// tree. Thus, reaching definitions can be calculated in terms of dominance.
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
liveThrough(bb, pragma[only_bind_into](v))
} }
/** /**

View File

@@ -321,10 +321,9 @@ private module SsaDefReaches {
} }
pragma[noinline] pragma[noinline]
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) { private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
result = getABasicBlockSuccessor(bb) and ssaDefReachesEndOfBlock(bb, def, _) and
not defOccursInBlock(_, bb, def.getSourceVariable()) and not defOccursInBlock(_, bb, def.getSourceVariable())
ssaDefReachesEndOfBlock(bb, def, _)
} }
/** /**
@@ -337,7 +336,11 @@ private module SsaDefReaches {
defOccursInBlock(def, bb1, _) and defOccursInBlock(def, bb1, _) and
bb2 = getABasicBlockSuccessor(bb1) bb2 = getABasicBlockSuccessor(bb1)
or or
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) | bb2 = getAMaybeLiveSuccessor(def, mid)) exists(BasicBlock mid |
varBlockReaches(def, bb1, mid) and
ssaDefReachesThroughBlock(def, mid) and
bb2 = getABasicBlockSuccessor(mid)
)
} }
/** /**
@@ -355,17 +358,10 @@ private module SsaDefReaches {
private import SsaDefReaches private import SsaDefReaches
pragma[noinline] pragma[nomagic]
private predicate ssaDefReachesEndOfBlockRec(BasicBlock bb, Definition def, SourceVariable v) { predicate liveThrough(BasicBlock bb, SourceVariable v) {
exists(BasicBlock idom | ssaDefReachesEndOfBlock(idom, def, v) | liveAtExit(bb, v) and
// The construction of SSA form ensures that each read of a variable is not ssaRef(bb, _, v, SsaDef())
// dominated by its definition. An SSA definition therefore reaches a
// control flow node if it is the _closest_ SSA definition that dominates
// the node. If two definitions dominate a node then one must dominate the
// other, so therefore the definition of _closest_ is given by the dominator
// tree. Thus, reaching definitions can be calculated in terms of dominance.
idom = getImmediateBasicBlockDominator(bb)
)
} }
/** /**
@@ -382,9 +378,14 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
liveAtExit(bb, v) liveAtExit(bb, v)
) )
or or
ssaDefReachesEndOfBlockRec(bb, def, v) and // The construction of SSA form ensures that each read of a variable is
liveAtExit(bb, v) and // dominated by its definition. An SSA definition therefore reaches a
not ssaRef(bb, _, v, SsaDef()) // control flow node if it is the _closest_ SSA definition that dominates
// the node. If two definitions dominate a node then one must dominate the
// other, so therefore the definition of _closest_ is given by the dominator
// tree. Thus, reaching definitions can be calculated in terms of dominance.
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
liveThrough(bb, pragma[only_bind_into](v))
} }
/** /**

View File

@@ -636,31 +636,41 @@ private module Internal {
) )
} }
private predicate stepExpr0(Expr succ, Expr pred) { private predicate stepExpr(Expr pred, Expr succ) {
Steps::stepOpen(pred, succ) Steps::stepOpen(pred, succ) and
or
exists(Assignable a |
a instanceof Field or
a instanceof Property
|
succ.(AssignableRead) = a.getAnAccess() and
pred = a.getAnAssignedValue() and
a = any(Modifiable m | not m.isEffectivelyPublic())
)
}
private predicate stepExpr(Expr succ, Expr pred) {
stepExpr0(succ, pred) and
// Do not step through down casts // Do not step through down casts
not downCast(succ) and not downCast(succ) and
// Only step when we may learn more about the actual type // Only step when we may learn more about the actual type
typeMayBeImprecise(succ.getType()) typeMayBeImprecise(succ.getType())
} }
private predicate stepTC(Expr succ, Expr pred) = fastTC(stepExpr/2)(succ, pred) private class AnalyzableFieldOrProperty extends Assignable, Modifiable {
AnalyzableFieldOrProperty() {
(
this instanceof Field or
this instanceof Property
) and
not this.isEffectivelyPublic() and
exists(this.getAnAssignedValue())
}
AssignableRead getARead() { result = this.getAnAccess() }
}
private class Source extends Expr { private class Source extends Expr {
Source() { not stepExpr(this, _) } Source() {
not stepExpr(_, this) and
not this = any(AnalyzableFieldOrProperty a).getARead()
}
Type getType(boolean isExact) {
result = this.getType() and
if
this instanceof ObjectCreation or
this instanceof BaseAccess
then isExact = true
else isExact = false
}
} }
private class Sink extends Expr { private class Sink extends Expr {
@@ -680,24 +690,38 @@ private module Internal {
this = any(DispatchCallImpl c).getArgument(_) this = any(DispatchCallImpl c).getArgument(_)
} }
Source getASource() { stepTC(this, result) } pragma[nomagic]
Expr getAPred() { stepExpr*(result, this) }
pragma[nomagic]
AnalyzableFieldOrProperty getAPredRead() { this.getAPred() = result.getARead() }
} }
/** Holds if the expression `e` has an exact type. */ /** Gets a source type for sink expression `e`, using simple data flow. */
private predicate hasExactType(Expr e) { Type getASourceType(Sink sink, boolean isExact) {
e instanceof ObjectCreation or result = sink.getAPred().(Source).getType(isExact)
e instanceof BaseAccess or
result = sink.getAPredRead().(RelevantFieldOrProperty).getASourceType(isExact)
} }
/** Gets a source type for expression `e`, using simple data flow. */ private class RelevantFieldOrProperty extends AnalyzableFieldOrProperty {
Type getASourceType(Sink e, boolean isExact) { RelevantFieldOrProperty() {
exists(Source s | this = any(Sink s).getAPredRead()
s = e.getASource() or or
s = e this = any(RelevantFieldOrProperty a).getAPredRead()
| }
result = s.getType() and
if hasExactType(s) then isExact = true else isExact = false pragma[nomagic]
) Expr getAPred() { stepExpr*(result, this.getAnAssignedValue()) }
pragma[nomagic]
AnalyzableFieldOrProperty getAPredRead() { this.getAPred() = result.getARead() }
Type getASourceType(boolean isExact) {
result = this.getAPred().(Source).getType(isExact)
or
result = this.getAPredRead().(RelevantFieldOrProperty).getASourceType(isExact)
}
} }
} }

View File

@@ -20,7 +20,7 @@ import semmle.code.csharp.Location
import semmle.code.csharp.Stmt import semmle.code.csharp.Stmt
import semmle.code.csharp.Type import semmle.code.csharp.Type
private import dotnet private import dotnet
private import semmle.code.csharp.Enclosing::Internal private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.TypeRef private import semmle.code.csharp.TypeRef
@@ -55,7 +55,7 @@ class Expr extends DotNet::Expr, ControlFlowElement, @expr {
final Stmt getEnclosingStmt() { enclosingStmt(this, result) } final Stmt getEnclosingStmt() { enclosingStmt(this, result) }
/** Gets the enclosing callable of this expression, if any. */ /** Gets the enclosing callable of this expression, if any. */
override Callable getEnclosingCallable() { exprEnclosingCallable(this, result) } override Callable getEnclosingCallable() { enclosingCallable(this, result) }
/** /**
* Holds if this expression is generated by the compiler and does not appear * Holds if this expression is generated by the compiler and does not appear

View File

@@ -0,0 +1,42 @@
/**
* Classes for modeling Dapper.
*/
import csharp
private import semmle.code.csharp.frameworks.system.Data
/** Definitions relating to the `Dapper` package. */
module Dapper {
/** The namespace `Dapper`. */
class DapperNamespace extends Namespace {
DapperNamespace() { this.hasQualifiedName("Dapper") }
}
/** A class in `Dapper`. */
class DapperClass extends Class {
DapperClass() { this.getParent() instanceof DapperNamespace }
}
/** A struct in `Dapper`. */
class DapperStruct extends Struct {
DapperStruct() { this.getParent() instanceof DapperNamespace }
}
/** The `Dapper.SqlMapper` class. */
class SqlMapperClass extends DapperClass {
SqlMapperClass() { this.hasName("SqlMapper") }
/** Gets a DB query method. */
ExtensionMethod getAQueryMethod() {
result = this.getAMethod() and
result.getName().regexpMatch("Query.*|Execute.*") and
result.getExtendedType() instanceof SystemDataIDbConnectionInterface and
result.isPublic()
}
}
/** The `Dapper.CommandDefinition` struct. */
class CommandDefinitionStruct extends DapperStruct {
CommandDefinitionStruct() { this.hasName("CommandDefinition") }
}
}

View File

@@ -5,6 +5,8 @@ private import semmle.code.csharp.frameworks.system.Data
private import semmle.code.csharp.frameworks.system.data.SqlClient private import semmle.code.csharp.frameworks.system.data.SqlClient
private import semmle.code.csharp.frameworks.EntityFramework private import semmle.code.csharp.frameworks.EntityFramework
private import semmle.code.csharp.frameworks.NHibernate private import semmle.code.csharp.frameworks.NHibernate
private import semmle.code.csharp.frameworks.Dapper
private import semmle.code.csharp.dataflow.DataFlow4
/** An expression containing a SQL command. */ /** An expression containing a SQL command. */
abstract class SqlExpr extends Expr { abstract class SqlExpr extends Expr {
@@ -83,3 +85,37 @@ class MicrosoftSqlHelperMethodCallSqlExpr extends SqlExpr, MethodCall {
) )
} }
} }
/** A `Dapper.SqlMapper` method that is taking a SQL string argument. */
class DapperSqlMethodCallSqlExpr extends SqlExpr, MethodCall {
DapperSqlMethodCallSqlExpr() {
this.getTarget() = any(Dapper::SqlMapperClass c).getAQueryMethod()
}
override Expr getSql() { result = this.getArgumentForName("sql") }
}
/** A `Dapper.CommandDefinition` creation that is taking a SQL string argument and is passed to a `Dapper.SqlMapper` method. */
class DapperCommandDefinitionMethodCallSqlExpr extends SqlExpr, ObjectCreation {
DapperCommandDefinitionMethodCallSqlExpr() {
this.getObjectType() instanceof Dapper::CommandDefinitionStruct and
exists(Conf c | c.hasFlow(DataFlow::exprNode(this), _))
}
override Expr getSql() { result = this.getArgumentForName("commandText") }
}
private class Conf extends DataFlow4::Configuration {
Conf() { this = "DapperCommandDefinitionFlowConfig" }
override predicate isSource(DataFlow::Node node) {
node.asExpr().(ObjectCreation).getObjectType() instanceof Dapper::CommandDefinitionStruct
}
override predicate isSink(DataFlow::Node node) {
exists(MethodCall mc |
mc.getTarget() = any(Dapper::SqlMapperClass c).getAQueryMethod() and
node.asExpr() = mc.getArgumentForName("command")
)
}
}

View File

@@ -28,6 +28,9 @@ class Element extends @dotnet_element {
/** Holds if this element is from source code. */ /** Holds if this element is from source code. */
predicate fromSource() { this.getFile().fromSource() } predicate fromSource() { this.getFile().fromSource() }
/** Holds if this element is from an assembly. */
predicate fromLibrary() { this.getFile().fromLibrary() }
/** /**
* Gets the "language" of this program element, as defined by the extension of the filename. * Gets the "language" of this program element, as defined by the extension of the filename.
* For example, C# has language "cs", and Visual Basic has language "vb". * For example, C# has language "cs", and Visual Basic has language "vb".

View File

@@ -11,9 +11,15 @@ class SourceControlFlowElement extends ControlFlowElement {
} }
class SourceControlFlowNode extends ControlFlow::Node { class SourceControlFlowNode extends ControlFlow::Node {
SourceControlFlowNode() { not this.getLocation().getFile() instanceof StubFile } SourceControlFlowNode() {
not this.getLocation().getFile() instanceof StubFile and
not this.getLocation().getFile().fromLibrary()
}
} }
class SourceBasicBlock extends ControlFlow::BasicBlock { class SourceBasicBlock extends ControlFlow::BasicBlock {
SourceBasicBlock() { not this.getLocation().getFile() instanceof StubFile } SourceBasicBlock() {
not this.getLocation().getFile() instanceof StubFile and
not this.getLocation().getFile().fromLibrary()
}
} }

View File

@@ -1,6 +1,7 @@
import csharp import csharp
query predicate countSplits(ControlFlowElement cfe, int i) { query predicate countSplits(ControlFlowElement cfe, int i) {
not cfe.fromLibrary() and
i = strictcount(ControlFlow::Nodes::ElementNode n | n.getElement() = cfe) i = strictcount(ControlFlow::Nodes::ElementNode n | n.getElement() = cfe)
} }

View File

@@ -1,4 +1,5 @@
import csharp import csharp
from DefaultValueExpr l from DefaultValueExpr l
where l.fromSource()
select l, l.getValue() select l, l.getValue()

View File

@@ -2,5 +2,7 @@ import csharp
import semmle.code.csharp.dataflow.TaintTracking import semmle.code.csharp.dataflow.TaintTracking
from DataFlow::Node pred, DataFlow::Node succ from DataFlow::Node pred, DataFlow::Node succ
where TaintTracking::localTaintStep(pred, succ) where
TaintTracking::localTaintStep(pred, succ) and
not pred.asExpr().fromLibrary()
select pred, succ select pred, succ

View File

@@ -1,5 +1,5 @@
import csharp import csharp
from DataFlow::Node pred, DataFlow::Node succ from DataFlow::Node pred, DataFlow::Node succ
where DataFlow::localFlowStep(pred, succ) where not pred.asExpr().fromLibrary() and DataFlow::localFlowStep(pred, succ)
select pred, succ select pred, succ

View File

@@ -1,5 +1,7 @@
import csharp import csharp
from DataFlow::Node pred, DataFlow::Node succ from DataFlow::Node pred, DataFlow::Node succ
where TaintTracking::localTaintStep(pred, succ) where
not pred.asExpr().fromLibrary() and
TaintTracking::localTaintStep(pred, succ)
select pred, succ select pred, succ

View File

@@ -4,5 +4,7 @@ import semmle.code.csharp.dataflow.ModulusAnalysis
import semmle.code.csharp.dataflow.Bound import semmle.code.csharp.dataflow.Bound
from ControlFlow::Nodes::ExprNode e, Bound b, int delta, int mod from ControlFlow::Nodes::ExprNode e, Bound b, int delta, int mod
where exprModulus(e, b, delta, mod) where
not e.getExpr().fromLibrary() and
exprModulus(e, b, delta, mod)
select e, b.toString(), delta, mod select e, b.toString(), delta, mod

View File

@@ -18,4 +18,5 @@ string getASignString(ControlFlow::Nodes::ExprNode e) {
} }
from ControlFlow::Nodes::ExprNode e from ControlFlow::Nodes::ExprNode e
where not e.getExpr().fromLibrary()
select e, strictconcat(string s | s = getASignString(e) | s, " ") select e, strictconcat(string s | s = getASignString(e) | s, " ")

View File

@@ -1,5 +1,5 @@
import csharp import csharp
from DataFlow::Node pred, DataFlow::Node succ from DataFlow::Node pred, DataFlow::Node succ
where DataFlow::localFlowStep(pred, succ) where not pred.asExpr().fromLibrary() and DataFlow::localFlowStep(pred, succ)
select pred, succ select pred, succ

View File

@@ -1,4 +1,5 @@
import csharp import csharp
from Parameter p from Parameter p
where p.fromSource()
select p, p.getDefaultValue() select p, p.getDefaultValue()

View File

@@ -1,6 +1,7 @@
import csharp import csharp
query predicate edges(ControlFlow::Node node, ControlFlow::Node successor, string attr, string val) { query predicate edges(ControlFlow::Node node, ControlFlow::Node successor, string attr, string val) {
not node.getElement().fromLibrary() and
exists(ControlFlow::SuccessorType t | successor = node.getASuccessorByType(t) | exists(ControlFlow::SuccessorType t | successor = node.getASuccessorByType(t) |
attr = "semmle.label" and attr = "semmle.label" and
val = t.toString() val = t.toString()

View File

@@ -0,0 +1,17 @@
public class Parameters
{
public void M1(int a, object b, string c) => throw null;
public void M2(int a, object b = null, string c = "default string") => throw null;
public void M3(int a = 1, object b = null, string c = "null") => throw null;
public void M4(int a = default, object b = default) => throw null;
public void M5(int a = new int(), object b = default) => throw null;
public void M6(MyStruct s1, MyStruct s2 = default(MyStruct), MyStruct s3 = new MyStruct()) => throw null;
public void M7(MyEnum e1, MyEnum e2 = default(MyEnum), MyEnum e3 = new MyEnum(), MyEnum e4 = MyEnum.A, MyEnum e5 = (MyEnum)5) => throw null;
public void M8<T>(T t = default) => throw null;
public void M9<T>(T t = default) where T : struct => throw null;
public void M10<T>(T t = default) where T : class => throw null;
public struct MyStruct { }
public enum MyEnum { A = 1, B = 2 }
}

View File

@@ -0,0 +1,17 @@
public class ParametersDll
{
public void M1(int a, object b, string c) => throw null;
public void M2(int a, object b = null, string c = "default string") => throw null;
public void M3(int a = 1, object b = null, string c = "null") => throw null;
public void M4(int a = default, object b = default) => throw null;
public void M5(int a = new int(), object b = default) => throw null;
public void M6(MyStruct s1, MyStruct s2 = default(MyStruct), MyStruct s3 = new MyStruct()) => throw null;
public void M7(MyEnum e1, MyEnum e2 = default(MyEnum), MyEnum e3 = new MyEnum(), MyEnum e4 = MyEnum.A, MyEnum e5 = (MyEnum)5) => throw null;
public void M8<T>(T t = default) => throw null;
public void M9<T>(T t = default) where T : struct => throw null;
public void M10<T>(T t = default) where T : class => throw null;
public struct MyStruct { }
public enum MyEnum { A = 1, B = 2 }
}

Binary file not shown.

View File

@@ -0,0 +1,50 @@
noDefaultValue
| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:24:3:24 | a | 0 |
| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:34:3:34 | b | 1 |
| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:44:3:44 | c | 2 |
| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:24:4:24 | a | 0 |
| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:29:8:30 | s1 | 0 |
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:27:9:28 | e1 | 0 |
| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | a | 0 |
| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | b | 1 |
| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | c | 2 |
| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | a | 0 |
| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s1 | 0 |
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e1 | 0 |
withDefaultValue
| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:34:4:34 | b | 1 | Parameters.cs:4:38:4:41 | null | null |
| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:51:4:51 | c | 2 | Parameters.cs:4:55:4:70 | "default string" | default string |
| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:24:5:24 | a | 0 | Parameters.cs:5:28:5:28 | 1 | 1 |
| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:38:5:38 | b | 1 | Parameters.cs:5:42:5:45 | null | null |
| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:55:5:55 | c | 2 | Parameters.cs:5:59:5:64 | "null" | null |
| Parameters.cs:6:17:6:18 | M4 | Parameters.cs:6:24:6:24 | a | 0 | Parameters.cs:6:28:6:34 | (...) ... | 0 |
| Parameters.cs:6:17:6:18 | M4 | Parameters.cs:6:44:6:44 | b | 1 | Parameters.cs:6:48:6:54 | default | null |
| Parameters.cs:7:17:7:18 | M5 | Parameters.cs:7:24:7:24 | a | 0 | Parameters.cs:7:28:7:36 | object creation of type Int32 | 0 |
| Parameters.cs:7:17:7:18 | M5 | Parameters.cs:7:46:7:46 | b | 1 | Parameters.cs:7:50:7:56 | default | null |
| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:42:8:43 | s2 | 1 | Parameters.cs:8:47:8:63 | default(...) | - |
| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:75:8:76 | s3 | 2 | Parameters.cs:8:80:8:93 | object creation of type MyStruct | - |
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:38:9:39 | e2 | 1 | Parameters.cs:9:43:9:57 | default(...) | 0 |
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:67:9:68 | e3 | 2 | Parameters.cs:9:72:9:83 | object creation of type MyEnum | 0 |
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:93:9:94 | e4 | 3 | Parameters.cs:9:98:9:105 | access to constant A | 1 |
| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:115:9:116 | e5 | 4 | Parameters.cs:9:120:9:128 | (...) ... | 5 |
| Parameters.cs:11:17:11:21 | M8 | Parameters.cs:11:25:11:25 | t | 0 | Parameters.cs:11:29:11:35 | (...) ... | - |
| Parameters.cs:12:17:12:21 | M9 | Parameters.cs:12:25:12:25 | t | 0 | Parameters.cs:12:29:12:35 | (...) ... | - |
| Parameters.cs:13:17:13:22 | M10 | Parameters.cs:13:26:13:26 | t | 0 | Parameters.cs:13:30:13:36 | (...) ... | null |
| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | c | 2 | Parameters.dll:0:0:0:0 | "default string" | default string |
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 1 | 1 |
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | c | 2 | Parameters.dll:0:0:0:0 | "null" | null |
| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 0 | 0 |
| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 0 | 0 |
| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null |
| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s2 | 1 | Parameters.dll:0:0:0:0 | default | - |
| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s3 | 2 | Parameters.dll:0:0:0:0 | default | - |
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e2 | 1 | Parameters.dll:0:0:0:0 | (...) ... | 0 |
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e3 | 2 | Parameters.dll:0:0:0:0 | (...) ... | 0 |
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e4 | 3 | Parameters.dll:0:0:0:0 | (...) ... | 1 |
| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e5 | 4 | Parameters.dll:0:0:0:0 | (...) ... | 5 |
| Parameters.dll:0:0:0:0 | M8 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | - |
| Parameters.dll:0:0:0:0 | M9 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | - |
| Parameters.dll:0:0:0:0 | M10 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | null |

View File

@@ -0,0 +1,19 @@
import csharp
private predicate fromTestLocation(Element e) {
e.fromSource() or e.getFile().getStem() = "Parameters"
}
query predicate noDefaultValue(Parameterizable container, Parameter p, int i) {
fromTestLocation(container) and
not p.hasDefaultValue() and
container.getParameter(i) = p
}
query predicate withDefaultValue(Parameterizable container, Parameter p, int i, Expr e, string value) {
fromTestLocation(container) and
p.hasDefaultValue() and
container.getParameter(i) = p and
p.getDefaultValue() = e and
if exists(e.getValue()) then value = e.getValue() else value = "-"
}

View File

@@ -17,4 +17,6 @@ class UnknownLocalVariableDeclExpr extends LocalVariableDeclAndInitExpr {
override string toString() { result = "(unknown type) " + this.getName() } override string toString() { result = "(unknown type) " + this.getName() }
} }
query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) { n2 = n1.getASuccessor() } query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) {
not n1.getElement().fromLibrary() and n2 = n1.getASuccessor()
}

View File

@@ -1,4 +1,4 @@
// semmle-extractor-options: /r:System.ComponentModel.Primitives.dll /r:System.ComponentModel.TypeConverter.dll /r:System.Data.Common.dll ${testdir}/../../../resources/stubs/EntityFramework.cs ${testdir}/../../../resources/stubs/System.Data.cs ${testdir}/../../../resources/stubs/System.Windows.cs // semmle-extractor-options: /r:System.ComponentModel.Primitives.dll /r:System.ComponentModel.TypeConverter.dll /r:System.Data.Common.dll ${testdir}/../../../resources/stubs/EntityFramework.cs ${testdir}/../../../resources/stubs/System.Data.cs ${testdir}/../../../resources/stubs/System.Windows.cs ${testdir}/../../../resources/stubs/Dapper.cs /r:System.Linq.Expressions.dll
using System; using System;

View File

@@ -5,6 +5,13 @@ edges
| SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:74:56:74:61 | access to local variable query1 | | SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:74:56:74:61 | access to local variable query1 |
| SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:75:55:75:60 | access to local variable query1 | | SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:75:55:75:60 | access to local variable query1 |
| SqlInjection.cs:87:21:87:29 | access to property Text : String | SqlInjection.cs:88:50:88:55 | access to local variable query1 | | SqlInjection.cs:87:21:87:29 | access to property Text : String | SqlInjection.cs:88:50:88:55 | access to local variable query1 |
| SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | SqlInjectionDapper.cs:21:55:21:59 | access to local variable query |
| SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | SqlInjectionDapper.cs:30:66:30:70 | access to local variable query |
| SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | SqlInjectionDapper.cs:39:63:39:67 | access to local variable query |
| SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | SqlInjectionDapper.cs:49:47:49:51 | access to local variable query |
| SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | SqlInjectionDapper.cs:58:42:58:46 | access to local variable query |
| SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | SqlInjectionDapper.cs:67:42:67:46 | access to local variable query |
| SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | SqlInjectionDapper.cs:77:52:77:56 | access to local variable query |
nodes nodes
| SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | semmle.label | access to field categoryTextBox : TextBox | | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | semmle.label | access to field categoryTextBox : TextBox |
| SqlInjection.cs:38:21:38:40 | access to property Text : String | semmle.label | access to property Text : String | | SqlInjection.cs:38:21:38:40 | access to property Text : String | semmle.label | access to property Text : String |
@@ -15,8 +22,29 @@ nodes
| SqlInjection.cs:75:55:75:60 | access to local variable query1 | semmle.label | access to local variable query1 | | SqlInjection.cs:75:55:75:60 | access to local variable query1 | semmle.label | access to local variable query1 |
| SqlInjection.cs:87:21:87:29 | access to property Text : String | semmle.label | access to property Text : String | | SqlInjection.cs:87:21:87:29 | access to property Text : String | semmle.label | access to property Text : String |
| SqlInjection.cs:88:50:88:55 | access to local variable query1 | semmle.label | access to local variable query1 | | SqlInjection.cs:88:50:88:55 | access to local variable query1 | semmle.label | access to local variable query1 |
| SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | semmle.label | access to property Text : String |
| SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | semmle.label | access to local variable query |
| SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | semmle.label | access to property Text : String |
| SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | semmle.label | access to local variable query |
| SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | semmle.label | access to property Text : String |
| SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | semmle.label | access to local variable query |
| SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | semmle.label | access to property Text : String |
| SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | semmle.label | access to local variable query |
| SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | semmle.label | access to property Text : String |
| SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | semmle.label | access to local variable query |
| SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | semmle.label | access to property Text : String |
| SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | semmle.label | access to local variable query |
| SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | semmle.label | access to property Text : String |
| SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | semmle.label | access to local variable query |
#select #select
| SqlInjection.cs:39:50:39:55 | access to local variable query1 | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | SqlInjection.cs:39:50:39:55 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | this ASP.NET user input | | SqlInjection.cs:39:50:39:55 | access to local variable query1 | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | SqlInjection.cs:39:50:39:55 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | this ASP.NET user input |
| SqlInjection.cs:74:56:74:61 | access to local variable query1 | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:74:56:74:61 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | this ASP.NET user input | | SqlInjection.cs:74:56:74:61 | access to local variable query1 | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:74:56:74:61 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | this ASP.NET user input |
| SqlInjection.cs:75:55:75:60 | access to local variable query1 | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:75:55:75:60 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | this ASP.NET user input | | SqlInjection.cs:75:55:75:60 | access to local variable query1 | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:75:55:75:60 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | this ASP.NET user input |
| SqlInjection.cs:88:50:88:55 | access to local variable query1 | SqlInjection.cs:87:21:87:29 | access to property Text : String | SqlInjection.cs:88:50:88:55 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:87:21:87:29 | access to property Text : String | this TextBox text | | SqlInjection.cs:88:50:88:55 | access to local variable query1 | SqlInjection.cs:87:21:87:29 | access to property Text : String | SqlInjection.cs:88:50:88:55 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:87:21:87:29 | access to property Text : String | this TextBox text |
| SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | this TextBox text |
| SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | this TextBox text |
| SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | this TextBox text |
| SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | this TextBox text |
| SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | this TextBox text |
| SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | this TextBox text |
| SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | this TextBox text |

View File

@@ -0,0 +1,95 @@
using System;
namespace Test
{
using System.Data;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Web.UI.WebControls;
using System.Threading.Tasks;
using Dapper;
class SqlInjectionDapper
{
string connectionString;
public void Bad01()
{
using (var connection = new SqlConnection(connectionString))
{
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
var result = connection.Query<object>(query);
}
}
public async Task Bad02()
{
using (var connection = new SqlConnection(connectionString))
{
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
var result = await connection.QueryAsync<object>(query);
}
}
public async Task Bad03()
{
using (var connection = new SqlConnection(connectionString))
{
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
var result = await connection.QueryFirstAsync(query);
}
}
public async Task Bad04()
{
using (var connection = new SqlConnection(connectionString))
{
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
await connection.ExecuteAsync(query);
}
}
public void Bad05()
{
using (var connection = new SqlConnection(connectionString))
{
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
connection.ExecuteScalar(query);
}
}
public void Bad06()
{
using (var connection = new SqlConnection(connectionString))
{
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
connection.ExecuteReader(query);
}
}
public async Task Bad07()
{
using (var connection = new SqlConnection(connectionString))
{
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
var comDef = new CommandDefinition(query);
var result = await connection.QueryFirstAsync(comDef);
}
}
public async Task Ok07()
{
using (var connection = new SqlConnection(connectionString))
{
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE";
var comDef = new CommandDefinition(query);
// no call to any query method
}
}
System.Windows.Forms.TextBox box1;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,34 @@
// This file contains auto-generated code.
// original-extractor-options: /r:Dapper.dll /r:System.Data.SqlClient.dll ...
namespace Dapper
{
// Generated from `Dapper.CommandDefinition` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null`
public struct CommandDefinition
{
public CommandDefinition(string commandText, object parameters = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null, Dapper.CommandFlags flags = CommandFlags.Buffered, System.Threading.CancellationToken cancellationToken = default) => throw null;
}
// Generated from `Dapper.CommandFlags` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null`
[System.Flags]
public enum CommandFlags
{
None = 0x0,
Buffered = 0x1,
Pipelined = 0x2,
NoCache = 0x4
}
// Generated from `Dapper.SqlMapper` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null`
static public class SqlMapper
{
public static System.Collections.Generic.IEnumerable<T> Query<T>(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
public static System.Data.IDataReader ExecuteReader(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
public static System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<T>> QueryAsync<T>(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
public static System.Threading.Tasks.Task<dynamic> QueryFirstAsync(this System.Data.IDbConnection cnn, Dapper.CommandDefinition command) => throw null;
public static System.Threading.Tasks.Task<dynamic> QueryFirstAsync(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
public static System.Threading.Tasks.Task<int> ExecuteAsync(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
public static object ExecuteScalar(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null;
}
}

View File

@@ -21,11 +21,3 @@ Learn more about the files you can use when running CodeQL processes and the res
- :doc:`SARIF output <sarif-output>`: CodeQL supports SARIF as an output format for sharing static analysis results. - :doc:`SARIF output <sarif-output>`: CodeQL supports SARIF as an output format for sharing static analysis results.
- :doc:`Exit codes <exit-codes>`: The CodeQL CLI reports the status of each command it runs as an exit code. - :doc:`Exit codes <exit-codes>`: The CodeQL CLI reports the status of each command it runs as an exit code.
This exit code provides information for subsequent commands or for other tools that rely on the CodeQL CLI. This exit code provides information for subsequent commands or for other tools that rely on the CodeQL CLI.
.. _cli-commands:
CodeQL CLI manual
-----------------
To view detailed information about each CodeQL CLI command,
including its usage and options, add the ``--help`` flag or visit the "`CodeQL CLI manual <../manual>`__."

View File

@@ -18,4 +18,4 @@ CodeQL CLI
using-the-codeql-cli using-the-codeql-cli
codeql-cli-reference codeql-cli-reference
CodeQL CLI manual <https://codeql.github.com/docs/codeql-cli/manual>

View File

@@ -58,6 +58,11 @@ Configuring settings for testing queries
To increase the number of threads used for testing queries, you can update the **Running Tests > Number Of Threads** setting. To increase the number of threads used for testing queries, you can update the **Running Tests > Number Of Threads** setting.
Configuring settings for telemetry and data collection
--------------------------------------------------------
You can configure whether the CodeQL extension collects telemetry data. This is disabled by default. For more information, see ":doc:`About telemetry in CodeQL for Visual Studio Code <about-telemetry-in-codeql-for-visual-studio-code>`."
Further reading Further reading
---------------- ----------------

View File

@@ -130,7 +130,7 @@ System and Network
- `FileSystemWriteAccess <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$FileSystemWriteAccess.html>`__ -- writing to the contents of a file - `FileSystemWriteAccess <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$FileSystemWriteAccess.html>`__ -- writing to the contents of a file
- `PersistentReadAccess <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$PersistentReadAccess.html>`__ -- reading from persistent storage, like cookies - `PersistentReadAccess <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$PersistentReadAccess.html>`__ -- reading from persistent storage, like cookies
- `PersistentWriteAccess <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$PersistentWriteAccess.html>`__ -- writing to persistent storage - `PersistentWriteAccess <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$PersistentWriteAccess.html>`__ -- writing to persistent storage
- `RemoteFlowSource <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/security/dataflow/RemoteFlowSources.qll/type.RemoteFlowSources$RemoteFlowSource.html>`__ -- source of untrusted user input - `RemoteFlowSource <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/security/dataflow/RemoteFlowSources.qll/type.RemoteFlowSources$Cached$RemoteFlowSource.html>`__ -- source of untrusted user input
- `SystemCommandExecution <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$SystemCommandExecution.html>`__ -- execution of a system command - `SystemCommandExecution <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Concepts.qll/type.Concepts$SystemCommandExecution.html>`__ -- execution of a system command
Files Files

View File

@@ -12,7 +12,7 @@ You can model potential sources of untrusted user input in your code without mak
Specifying remote flow sources in external files is currently in beta and subject to change. Specifying remote flow sources in external files is currently in beta and subject to change.
As mentioned in the :doc:`Data flow cheat sheet for JavaScript <data-flow-cheat-sheet-for-javascript>`, the CodeQL libraries for JavaScript As mentioned in the :doc:`Data flow cheat sheet for JavaScript <data-flow-cheat-sheet-for-javascript>`, the CodeQL libraries for JavaScript
provide a class `RemoteFlowSource <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/security/dataflow/RemoteFlowSources.qll/type.RemoteFlowSources$RemoteFlowSource.html>`__ to represent sources of untrusted user input, sometimes also referred to as remote flow provide a class `RemoteFlowSource <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/security/dataflow/RemoteFlowSources.qll/type.RemoteFlowSources$Cached$RemoteFlowSource.html>`__ to represent sources of untrusted user input, sometimes also referred to as remote flow
sources. sources.
To model a new source of untrusted input, such as a previously unmodelled library API, you can To model a new source of untrusted input, such as a previously unmodelled library API, you can

View File

@@ -14,7 +14,7 @@
C#,C# up to 8.0,"Microsoft Visual Studio up to 2019 with .NET up to 4.8, C#,C# up to 8.0,"Microsoft Visual Studio up to 2019 with .NET up to 4.8,
.NET Core up to 3.1","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``" .NET Core up to 3.1","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
Go (aka Golang), "Go up to 1.15", "Go 1.11 or more recent", ``.go`` Go (aka Golang), "Go up to 1.16", "Go 1.11 or more recent", ``.go``
Java,"Java 7 to 15 [3]_","javac (OpenJDK and Oracle JDK), Java,"Java 7 to 15 [3]_","javac (OpenJDK and Oracle JDK),
Eclipse compiler for Java (ECJ) [4]_",``.java`` Eclipse compiler for Java (ECJ) [4]_",``.java``

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* Added additional taint steps modeling the Spring StringUtils class (`org.springframework.util.StringUtils`).

View File

@@ -638,7 +638,21 @@ class BooleanLiteral extends Literal, @booleanliteral {
override string getAPrimaryQlClass() { result = "BooleanLiteral" } override string getAPrimaryQlClass() { result = "BooleanLiteral" }
} }
/** An integer literal. For example, `23`. */ /**
* An integer literal. For example, `23`.
*
* An integer literal can never be negative except when:
* - It is written in binary, octal or hexadecimal notation
* - It is written in decimal notation, has the value `2147483648` and is preceded
* by a minus; in this case the value of the IntegerLiteral is -2147483648 and
* the preceding minus will *not* be modeled as `MinusExpr`.
*
* In all other cases the preceding minus, if any, will be modeled as a separate
* `MinusExpr`.
*
* The last exception is necessary because `2147483648` on its own would not be
* a valid integer literal (and could also not be parsed as CodeQL `int`).
*/
class IntegerLiteral extends Literal, @integerliteral { class IntegerLiteral extends Literal, @integerliteral {
/** Gets the int representation of this literal. */ /** Gets the int representation of this literal. */
int getIntValue() { result = getValue().toInt() } int getIntValue() { result = getValue().toInt() }
@@ -646,12 +660,32 @@ class IntegerLiteral extends Literal, @integerliteral {
override string getAPrimaryQlClass() { result = "IntegerLiteral" } override string getAPrimaryQlClass() { result = "IntegerLiteral" }
} }
/** A long literal. For example, `23l`. */ /**
* A long literal. For example, `23L`.
*
* A long literal can never be negative except when:
* - It is written in binary, octal or hexadecimal notation
* - It is written in decimal notation, has the value `9223372036854775808` and
* is preceded by a minus; in this case the value of the LongLiteral is
* -9223372036854775808 and the preceding minus will *not* be modeled as
* `MinusExpr`.
*
* In all other cases the preceding minus, if any, will be modeled as a separate
* `MinusExpr`.
*
* The last exception is necessary because `9223372036854775808` on its own
* would not be a valid long literal.
*/
class LongLiteral extends Literal, @longliteral { class LongLiteral extends Literal, @longliteral {
override string getAPrimaryQlClass() { result = "LongLiteral" } override string getAPrimaryQlClass() { result = "LongLiteral" }
} }
/** A floating point literal. For example, `4.2f`. */ /**
* A float literal. For example, `4.2f`.
*
* A float literal is never negative; a preceding minus, if any, will always
* be modeled as separate `MinusExpr`.
*/
class FloatingPointLiteral extends Literal, @floatingpointliteral { class FloatingPointLiteral extends Literal, @floatingpointliteral {
/** /**
* Gets the value of this literal as CodeQL 64-bit `float`. The value will * Gets the value of this literal as CodeQL 64-bit `float`. The value will
@@ -662,7 +696,12 @@ class FloatingPointLiteral extends Literal, @floatingpointliteral {
override string getAPrimaryQlClass() { result = "FloatingPointLiteral" } override string getAPrimaryQlClass() { result = "FloatingPointLiteral" }
} }
/** A double literal. For example, `4.2`. */ /**
* A double literal. For example, `4.2`.
*
* A double literal is never negative; a preceding minus, if any, will always
* be modeled as separate `MinusExpr`.
*/
class DoubleLiteral extends Literal, @doubleliteral { class DoubleLiteral extends Literal, @doubleliteral {
/** /**
* Gets the value of this literal as CodeQL 64-bit `float`. The result will * Gets the value of this literal as CodeQL 64-bit `float`. The result will

View File

@@ -80,19 +80,15 @@ class Node extends TNode {
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType() result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType()
} }
private Callable getEnclosingCallableImpl() { /** Gets the callable in which this node occurs. */
Callable getEnclosingCallable() {
result = this.asExpr().getEnclosingCallable() or result = this.asExpr().getEnclosingCallable() or
result = this.asParameter().getCallable() or result = this.asParameter().getCallable() or
result = this.(ImplicitVarargsArray).getCall().getEnclosingCallable() or result = this.(ImplicitVarargsArray).getCall().getEnclosingCallable() or
result = this.(InstanceParameterNode).getCallable() or result = this.(InstanceParameterNode).getCallable() or
result = this.(ImplicitInstanceAccess).getInstanceAccess().getEnclosingCallable() or result = this.(ImplicitInstanceAccess).getInstanceAccess().getEnclosingCallable() or
result = this.(MallocNode).getClassInstanceExpr().getEnclosingCallable() or result = this.(MallocNode).getClassInstanceExpr().getEnclosingCallable() or
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getEnclosingCallableImpl() result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getEnclosingCallable()
}
/** Gets the callable in which this node occurs. */
Callable getEnclosingCallable() {
result = unique(DataFlowCallable c | c = this.getEnclosingCallableImpl() | c)
} }
private Type getImprovedTypeBound() { private Type getImprovedTypeBound() {

View File

@@ -32,7 +32,6 @@ import semmle.code.java.frameworks.spring.SpringQualifier
import semmle.code.java.frameworks.spring.SpringRef import semmle.code.java.frameworks.spring.SpringRef
import semmle.code.java.frameworks.spring.SpringReplacedMethod import semmle.code.java.frameworks.spring.SpringReplacedMethod
import semmle.code.java.frameworks.spring.SpringSet import semmle.code.java.frameworks.spring.SpringSet
import semmle.code.java.frameworks.spring.SpringStringUtils
import semmle.code.java.frameworks.spring.SpringValue import semmle.code.java.frameworks.spring.SpringValue
import semmle.code.java.frameworks.spring.SpringXMLElement import semmle.code.java.frameworks.spring.SpringXMLElement
import semmle.code.java.frameworks.spring.metrics.MetricSpringBean import semmle.code.java.frameworks.spring.metrics.MetricSpringBean

View File

@@ -1,51 +0,0 @@
/** Definitions of flow steps through utility methods of `org.springframework.util.SpringUtils`. */
import java
private import semmle.code.java.dataflow.ExternalFlow
private class SpringStringUtilsModel extends SummaryModelCsv {
override predicate row(string row) {
row =
[
"org.springframework.util;StringUtils;false;addStringToArray;;;Argument[0..1];ReturnValue;taint",
"org.springframework.util;StringUtils;false;applyRelativePath;;;Argument[0..1];ReturnValue;taint",
"org.springframework.util;StringUtils;false;arrayToCommaDelimitedString;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;arrayToDelimitedString;;;Argument[0..1];ReturnValue;taint",
"org.springframework.util;StringUtils;false;capitalize;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;cleanPath;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;collectionToCommaDelimitedString;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;collectionToDelimitedString;;;Argument[0..1];ReturnValue;taint",
"org.springframework.util;StringUtils;false;collectionToDelimitedString;(java.util.Collection,java.lang.String,java.lang.String,java.lang.String);;Argument[2..3];ReturnValue;taint",
"org.springframework.util;StringUtils;false;commaDelimitedListToSet;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;commaDelimitedListToStringArray;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;concatenateStringArrays;;;Argument[0..1];ReturnValue;taint",
"org.springframework.util;StringUtils;false;delete;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;deleteAny;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;delimitedListToStringArray;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;getFilename;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;getFilenameExtension;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;mergeStringArrays;;;Argument[0..1];ReturnValue;taint",
"org.springframework.util;StringUtils;false;quote;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;quoteIfString;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;removeDuplicateStrings;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;replace;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;replace;;;Argument[2];ReturnValue;taint",
"org.springframework.util;StringUtils;false;sortStringArray;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;split;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;splitArrayElementsIntoProperties;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;stripFilenameExtension;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;tokenizeToStringArray;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;toStringArray;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;trimAllWhitespace;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;trimArrayElements;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;trimLeadingCharacter;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;trimLeadingWhitespace;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;trimTrailingCharacter;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;trimTrailingWhitespace;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;trimWhitespace;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;uncapitalize;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;unqualify;;;Argument[0];ReturnValue;taint",
"org.springframework.util;StringUtils;false;uriDecode;;;Argument[0];ReturnValue;taint"
]
}
}

View File

@@ -1,125 +0,0 @@
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Locale;
import java.lang.String;
class StringUtilsTest {
String taint() { return "tainted"; }
String[] taintArray() { return null; }
Locale taintLocale() { return null; }
Collection<String> taintedCollection() { return null; }
Enumeration<String> taintedEnumeration() { return null; }
void sink(Object o) {}
void test() throws Exception {
sink(StringUtils.addStringToArray(null, taint())); // $hasTaintFlow
sink(StringUtils.addStringToArray(taintArray(), "")); // $hasTaintFlow
sink(StringUtils.applyRelativePath("/", taint())); // $hasTaintFlow
sink(StringUtils.applyRelativePath(taint(), "../../test")); // $hasTaintFlow
sink(StringUtils.arrayToCommaDelimitedString(taintArray())); // $hasTaintFlow
sink(StringUtils.arrayToDelimitedString(taintArray(), ":")); // $hasTaintFlow
sink(StringUtils.arrayToDelimitedString(null, taint())); // $hasTaintFlow
sink(StringUtils.capitalize(taint())); // $hasTaintFlow
sink(StringUtils.cleanPath(taint())); // $hasTaintFlow
sink(StringUtils.collectionToCommaDelimitedString(taintedCollection())); // $hasTaintFlow
sink(StringUtils.collectionToDelimitedString(taintedCollection(), ":")); // $hasTaintFlow
sink(StringUtils.collectionToDelimitedString(null, taint())); // $hasTaintFlow
sink(StringUtils.collectionToDelimitedString(taintedCollection(), ":", "", "")); // $hasTaintFlow
sink(StringUtils.collectionToDelimitedString(null, taint(), "", "")); // $hasTaintFlow
sink(StringUtils.collectionToDelimitedString(null, ":", taint(), "")); // $hasTaintFlow
sink(StringUtils.collectionToDelimitedString(null, ":", "", taint())); // $hasTaintFlow
sink(StringUtils.commaDelimitedListToSet(taint())); // $hasTaintFlow
sink(StringUtils.commaDelimitedListToStringArray(taint())); // $hasTaintFlow
sink(StringUtils.concatenateStringArrays(taintArray(), null)); // $hasTaintFlow
sink(StringUtils.concatenateStringArrays(null, taintArray())); // $hasTaintFlow
sink(StringUtils.delete(taint(), "")); // $hasTaintFlow
sink(StringUtils.deleteAny(taint(), "")); // $hasTaintFlow
sink(StringUtils.delimitedListToStringArray(taint(), ":")); // $hasTaintFlow
sink(StringUtils.delimitedListToStringArray(taint(), ":", ".")); // $hasTaintFlow
sink(StringUtils.getFilename(taint())); // $hasTaintFlow
sink(StringUtils.getFilenameExtension(taint())); // $hasTaintFlow
sink(StringUtils.mergeStringArrays(taintArray(), null)); // $hasTaintFlow
sink(StringUtils.mergeStringArrays(null, taintArray())); // $hasTaintFlow
sink(StringUtils.parseLocale(taint()));
sink(StringUtils.parseLocaleString(taint()));
sink(StringUtils.parseTimeZoneString(taint()));
sink(StringUtils.quote(taint())); // $hasTaintFlow
sink(StringUtils.quoteIfString(taint())); // $hasTaintFlow
sink(StringUtils.removeDuplicateStrings(taintArray())); // $hasTaintFlow
sink(StringUtils.replace(taint(), "", "")); // $hasTaintFlow
sink(StringUtils.replace("", "", taint())); // $hasTaintFlow
sink(StringUtils.sortStringArray(taintArray())); // $hasTaintFlow
sink(StringUtils.split(taint(), "")); // $hasTaintFlow
sink(StringUtils.splitArrayElementsIntoProperties(taintArray(), "")); // $hasTaintFlow
sink(StringUtils.splitArrayElementsIntoProperties(taintArray(), "", "")); // $hasTaintFlow
sink(StringUtils.stripFilenameExtension(taint())); // $hasTaintFlow
sink(StringUtils.tokenizeToStringArray(taint(), "")); // $hasTaintFlow
sink(StringUtils.tokenizeToStringArray(taint(), "", true, true)); // $hasTaintFlow
sink(StringUtils.toLanguageTag(taintLocale()));
sink(StringUtils.toStringArray(taintedCollection())); // $hasTaintFlow
sink(StringUtils.toStringArray(taintedEnumeration())); // $hasTaintFlow
sink(StringUtils.trimAllWhitespace(taint())); // $hasTaintFlow
sink(StringUtils.trimArrayElements(taintArray())); // $hasTaintFlow
sink(StringUtils.trimLeadingCharacter(taint(), 'a')); // $hasTaintFlow
sink(StringUtils.trimLeadingWhitespace(taint())); // $hasTaintFlow
sink(StringUtils.trimTrailingCharacter(taint(), 'a')); // $hasTaintFlow
sink(StringUtils.trimTrailingWhitespace(taint())); // $hasTaintFlow
sink(StringUtils.trimWhitespace(taint())); // $hasTaintFlow
sink(StringUtils.uncapitalize(taint())); // $hasTaintFlow
sink(StringUtils.unqualify(taint())); // $hasTaintFlow
sink(StringUtils.unqualify(taint(), '.')); // $hasTaintFlow
sink(StringUtils.uriDecode(taint(), java.nio.charset.StandardCharsets.UTF_8)); // $hasTaintFlow
}
}

View File

@@ -1,53 +0,0 @@
import java
import semmle.code.java.frameworks.spring.Spring
import semmle.code.java.dataflow.TaintTracking
import TestUtilities.InlineExpectationsTest
class TaintFlowConf extends TaintTracking::Configuration {
TaintFlowConf() { this = "qltest:frameworks:spring-taint-flow" }
override predicate isSource(DataFlow::Node n) {
exists(string name | name.matches("taint%") |
n.asExpr().(MethodAccess).getMethod().hasName(name)
)
}
override predicate isSink(DataFlow::Node n) {
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
}
}
class ValueFlowConf extends DataFlow::Configuration {
ValueFlowConf() { this = "qltest:frameworks:spring-value-flow" }
override predicate isSource(DataFlow::Node n) {
n.asExpr().(MethodAccess).getMethod().hasName("taint")
}
override predicate isSink(DataFlow::Node n) {
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
}
}
class HasFlowTest extends InlineExpectationsTest {
HasFlowTest() { this = "HasFlowTest" }
override string getARelevantTag() { result = ["hasTaintFlow", "hasValueFlow"] }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasTaintFlow" and
exists(DataFlow::Node src, DataFlow::Node sink, TaintFlowConf conf | conf.hasFlow(src, sink) |
not any(ValueFlowConf vconf).hasFlow(src, sink) and
sink.getLocation() = location and
element = sink.toString() and
value = ""
)
or
tag = "hasValueFlow" and
exists(DataFlow::Node src, DataFlow::Node sink, ValueFlowConf conf | conf.hasFlow(src, sink) |
sink.getLocation() = location and
element = sink.toString() and
value = ""
)
}
}

View File

@@ -1 +0,0 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.2.3

View File

@@ -0,0 +1,16 @@
class NumericLiterals {
void negativeLiterals() {
float f = -1f;
double d = -1d;
int i1 = -2147483647;
int i2 = -2147483648; // CodeQL models minus as part of literal
int i3 = -0b10000000000000000000000000000000; // binary
int i4 = -020000000000; // octal
int i5 = -0x80000000; // hex
long l1 = -9223372036854775807L;
long l2 = -9223372036854775808L; // CodeQL models minus as part of literal
long l3 = -0b1000000000000000000000000000000000000000000000000000000000000000L; // binary
long l4 = -01000000000000000000000L; // octal
long l5 = -0x8000000000000000L; // hex
}
}

View File

@@ -0,0 +1,12 @@
| NumericLiterals.java:3:14:3:15 | 1f | 1.0 | NumericLiterals.java:3:13:3:15 | -... |
| NumericLiterals.java:4:15:4:16 | 1d | 1.0 | NumericLiterals.java:4:14:4:16 | -... |
| NumericLiterals.java:5:13:5:22 | 2147483647 | 2147483647 | NumericLiterals.java:5:12:5:22 | -... |
| NumericLiterals.java:6:12:6:22 | -2147483648 | -2147483648 | NumericLiterals.java:6:7:6:22 | i2 |
| NumericLiterals.java:7:13:7:46 | 0b10000000000000000000000000000000 | -2147483648 | NumericLiterals.java:7:12:7:46 | -... |
| NumericLiterals.java:8:13:8:24 | 020000000000 | -2147483648 | NumericLiterals.java:8:12:8:24 | -... |
| NumericLiterals.java:9:13:9:22 | 0x80000000 | -2147483648 | NumericLiterals.java:9:12:9:22 | -... |
| NumericLiterals.java:10:14:10:33 | 9223372036854775807L | 9223372036854775807 | NumericLiterals.java:10:13:10:33 | -... |
| NumericLiterals.java:11:13:11:33 | -9223372036854775808L | -9223372036854775808 | NumericLiterals.java:11:8:11:33 | l2 |
| NumericLiterals.java:12:14:12:80 | 0b1000000000000000000000000000000000000000000000000000000000000000L | -9223372036854775808 | NumericLiterals.java:12:13:12:80 | -... |
| NumericLiterals.java:13:14:13:37 | 01000000000000000000000L | -9223372036854775808 | NumericLiterals.java:13:13:13:37 | -... |
| NumericLiterals.java:14:14:14:32 | 0x8000000000000000L | -9223372036854775808 | NumericLiterals.java:14:13:14:32 | -... |

View File

@@ -0,0 +1,9 @@
import java
from Literal l
where
l instanceof IntegerLiteral or
l instanceof LongLiteral or
l instanceof FloatingPointLiteral or
l instanceof DoubleLiteral
select l, l.getValue(), l.getParent()

View File

@@ -1,270 +0,0 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.TimeZone;
import org.springframework.lang.Nullable;
public abstract class StringUtils {
public static boolean isEmpty(@Nullable Object str) {
return false;
}
public static boolean hasLength(@Nullable CharSequence str) {
return false;
}
public static boolean hasLength(@Nullable String str) {
return false;
}
public static boolean hasText(@Nullable CharSequence str) {
return false;
}
public static boolean hasText(@Nullable String str) {
return false;
}
public static boolean containsWhitespace(@Nullable CharSequence str) {
return false;
}
public static boolean containsWhitespace(@Nullable String str) {
return false;
}
public static String trimWhitespace(String str) {
return null;
}
public static String trimAllWhitespace(String str) {
return null;
}
public static String trimLeadingWhitespace(String str) {
return null;
}
public static String trimTrailingWhitespace(String str) {
return null;
}
public static String trimLeadingCharacter(String str, char leadingCharacter) {
return null;
}
public static String trimTrailingCharacter(String str, char trailingCharacter) {
return null;
}
public static boolean startsWithIgnoreCase(@Nullable String str, @Nullable String prefix) {
return false;
}
public static boolean endsWithIgnoreCase(@Nullable String str, @Nullable String suffix) {
return false;
}
public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
return false;
}
public static int countOccurrencesOf(String str, String sub) {
return 0;
}
public static String replace(String inString, String oldPattern, @Nullable String newPattern) {
return null;
}
public static String delete(String inString, String pattern) {
return null;
}
public static String deleteAny(String inString, @Nullable String charsToDelete) {
return null;
}
public static String quote(@Nullable String str) {
return null;
}
public static Object quoteIfString(@Nullable Object obj) {
return null;
}
public static String unqualify(String qualifiedName) {
return null;
}
public static String unqualify(String qualifiedName, char separator) {
return null;
}
public static String capitalize(String str) {
return null;
}
public static String uncapitalize(String str) {
return null;
}
public static String getFilename(@Nullable String path) {
return null;
}
public static String getFilenameExtension(@Nullable String path) {
return null;
}
public static String stripFilenameExtension(String path) {
return null;
}
public static String applyRelativePath(String path, String relativePath) {
return null;
}
public static String cleanPath(String path) {
return null;
}
public static boolean pathEquals(String path1, String path2) {
return false;
}
public static String uriDecode(String source, Charset charset) {
return null;
}
public static Locale parseLocale(String localeValue) {
return null;
}
public static Locale parseLocaleString(String localeString) {
return null;
}
public static String toLanguageTag(Locale locale) {
return null;
}
public static TimeZone parseTimeZoneString(String timeZoneString) {
return null;
}
public static String[] toStringArray(@Nullable Collection<String> collection) {
return null;
}
public static String[] toStringArray(@Nullable Enumeration<String> enumeration) {
return null;
}
public static String[] addStringToArray(@Nullable String[] array, String str) {
return null;
}
public static String[] concatenateStringArrays(
@Nullable String[] array1, @Nullable String[] array2) {
return null;
}
public static String[] mergeStringArrays(@Nullable String[] array1, @Nullable String[] array2) {
return null;
}
public static String[] sortStringArray(String[] array) {
return null;
}
public static String[] trimArrayElements(String[] array) {
return null;
}
public static String[] removeDuplicateStrings(String[] array) {
return null;
}
public static String[] split(@Nullable String toSplit, @Nullable String delimiter) {
return null;
}
public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) {
return null;
}
public static Properties splitArrayElementsIntoProperties(
String[] array, String delimiter, @Nullable String charsToDelete) {
return null;
}
public static String[] tokenizeToStringArray(@Nullable String str, String delimiters) {
return null;
}
public static String[] tokenizeToStringArray(
@Nullable String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
return null;
}
public static String[] delimitedListToStringArray(
@Nullable String str, @Nullable String delimiter) {
return null;
}
public static String[] delimitedListToStringArray(
@Nullable String str, @Nullable String delimiter, @Nullable String charsToDelete) {
return null;
}
public static String[] commaDelimitedListToStringArray(@Nullable String str) {
return null;
}
public static Set<String> commaDelimitedListToSet(@Nullable String str) {
return null;
}
public static String collectionToDelimitedString(
@Nullable Collection<?> coll, String delim, String prefix, String suffix) {
return null;
}
public static String collectionToDelimitedString(@Nullable Collection<?> coll, String delim) {
return null;
}
public static String collectionToCommaDelimitedString(@Nullable Collection<?> coll) {
return null;
}
public static String arrayToDelimitedString(@Nullable Object[] arr, String delim) {
return null;
}
public static String arrayToCommaDelimitedString(@Nullable Object[] arr) {
return null;
}
}

View File

@@ -0,0 +1,3 @@
lgtm,codescanning
* The SQL library models for `mysql`, `mysql2`, `mssql`, `pg`, `sqlite3`, `sequelize`, and `@google-cloud/spanner` have improved,
leading to more SQL injection sinks.

View File

@@ -0,0 +1,5 @@
lgtm,codescanning
* Fixed a bug which caused some imports to be resolved incorrectly
for projects containing multiple `tsconfig.json` files.
* Fixed a bug which could cause some files in the `node_modules` folder
to be extracted even though they should be excluded.

View File

@@ -0,0 +1,3 @@
lgtm,codescanning
* Support for Redux has improved. The security queries can now track taint through reducer functions and state managed by Redux.
Affected packages are `redux`, `react-redux`, `@reduxjs/toolkit`, `redux-actions`, `redux-persist`, `reduce-reducers`, `redux-immutable`, and `immer`.

View File

@@ -21,7 +21,7 @@ public class ParsedProject {
/** Absolute paths to the files included in this project. */ /** Absolute paths to the files included in this project. */
public Set<File> getOwnFiles() { public Set<File> getOwnFiles() {
return allFiles; return ownFiles;
} }
/** Absolute paths to the files included in or referenced by this project. */ /** Absolute paths to the files included in or referenced by this project. */

View File

@@ -15,13 +15,8 @@ import javascript
import semmle.javascript.security.dataflow.DomBasedXss::DomBasedXss import semmle.javascript.security.dataflow.DomBasedXss::DomBasedXss
import DataFlow::PathGraph import DataFlow::PathGraph
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where where cfg.hasFlowPath(source, sink)
(
cfg instanceof HtmlInjectionConfiguration or
cfg instanceof JQueryHtmlOrSelectorInjectionConfiguration
) and
cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, select sink.getNode(), source, sink,
sink.getNode().(Sink).getVulnerabilityKind() + " vulnerability due to $@.", source.getNode(), sink.getNode().(Sink).getVulnerabilityKind() + " vulnerability due to $@.", source.getNode(),
"user-provided value" "user-provided value"

View File

@@ -0,0 +1,11 @@
/**
* @id js/summary/lines-of-code
* @name Total lines of JavaScript and TypeScript code in the database
* @description The total number of lines of JavaScript or TypeScript code across all files checked into the repository, except in `node_modules`. This is a useful metric of the size of a database. For all files that were seen during extraction, this query counts the lines of code, excluding whitespace or comments.
* @kind metric
* @tags summary
*/
import javascript
select sum(File f | not f.getATopLevel().isExterns() | f.getNumberOfLinesOfCode())

View File

@@ -105,6 +105,7 @@ import semmle.javascript.frameworks.PropertyProjection
import semmle.javascript.frameworks.Puppeteer import semmle.javascript.frameworks.Puppeteer
import semmle.javascript.frameworks.React import semmle.javascript.frameworks.React
import semmle.javascript.frameworks.ReactNative import semmle.javascript.frameworks.ReactNative
import semmle.javascript.frameworks.Redux
import semmle.javascript.frameworks.Request import semmle.javascript.frameworks.Request
import semmle.javascript.frameworks.RxJS import semmle.javascript.frameworks.RxJS
import semmle.javascript.frameworks.ServerLess import semmle.javascript.frameworks.ServerLess

View File

@@ -0,0 +1,15 @@
/**
* @name Import graph
* @description An edge in the import graph.
* @kind problem
* @problem.severity recommendation
* @id js/meta/alerts/import-graph
* @tags meta
* @precision very-low
*/
import javascript
from Import imprt, Module target
where target = imprt.getImportedModule()
select imprt, "Import targeting $@", target, target.getFile().getRelativePath()

View File

@@ -307,7 +307,7 @@ class ReachableBasicBlock extends BasicBlock {
/** /**
* Holds if this basic block strictly dominates `bb`. * Holds if this basic block strictly dominates `bb`.
*/ */
cached pragma[inline]
predicate strictlyDominates(ReachableBasicBlock bb) { bbIDominates+(this, bb) } predicate strictlyDominates(ReachableBasicBlock bb) { bbIDominates+(this, bb) }
/** /**
@@ -315,15 +315,13 @@ class ReachableBasicBlock extends BasicBlock {
* *
* This predicate is reflexive: each reachable basic block dominates itself. * This predicate is reflexive: each reachable basic block dominates itself.
*/ */
predicate dominates(ReachableBasicBlock bb) { pragma[inline]
bb = this or predicate dominates(ReachableBasicBlock bb) { bbIDominates*(this, bb) }
strictlyDominates(bb)
}
/** /**
* Holds if this basic block strictly post-dominates `bb`. * Holds if this basic block strictly post-dominates `bb`.
*/ */
cached pragma[inline]
predicate strictlyPostDominates(ReachableBasicBlock bb) { bbIPostDominates+(this, bb) } predicate strictlyPostDominates(ReachableBasicBlock bb) { bbIPostDominates+(this, bb) }
/** /**
@@ -331,10 +329,8 @@ class ReachableBasicBlock extends BasicBlock {
* *
* This predicate is reflexive: each reachable basic block post-dominates itself. * This predicate is reflexive: each reachable basic block post-dominates itself.
*/ */
predicate postDominates(ReachableBasicBlock bb) { pragma[inline]
bb = this or predicate postDominates(ReachableBasicBlock bb) { bbIPostDominates*(this, bb) }
strictlyPostDominates(bb)
}
} }
/** /**

View File

@@ -51,7 +51,7 @@ class PackageJSON extends JSONObject {
string getAFile() { result = getFiles().getElementStringValue(_) } string getAFile() { result = getFiles().getElementStringValue(_) }
/** Gets the main module of this package. */ /** Gets the main module of this package. */
string getMain() { result = getPropStringValue("main") } string getMain() { result = MainModulePath::of(this).getValue() }
/** Gets the path of a command defined for this package. */ /** Gets the path of a command defined for this package. */
string getBin(string cmd) { string getBin(string cmd) {

View File

@@ -80,6 +80,13 @@ File tryExtensions(Folder dir, string basename, int priority) {
) )
} }
/**
* Gets `name` without a file extension.
* Or `name`, if `name` has no file extension.
*/
bindingset[name]
private string getStem(string name) { result = name.regexpCapture("(.+?)(?:\\.([^.]+))?", 1) }
/** /**
* Gets the main module described by `pkg` with the given `priority`. * Gets the main module described by `pkg` with the given `priority`.
*/ */
@@ -90,9 +97,8 @@ File resolveMainModule(PackageJSON pkg, int priority) {
result = tryExtensions(main.resolve(), "index", priority) result = tryExtensions(main.resolve(), "index", priority)
or or
not exists(main.resolve()) and not exists(main.resolve()) and
not exists(main.getExtension()) and
exists(int n | n = main.getNumComponent() | exists(int n | n = main.getNumComponent() |
result = tryExtensions(main.resolveUpTo(n - 1), main.getComponent(n - 1), priority) result = tryExtensions(main.resolveUpTo(n - 1), getStem(main.getComponent(n - 1)), priority)
) )
) )
or or
@@ -101,6 +107,27 @@ File resolveMainModule(PackageJSON pkg, int priority) {
tryExtensions([folder, folder.getChildContainer(["src", "lib"])], "index", tryExtensions([folder, folder.getChildContainer(["src", "lib"])], "index",
priority - prioritiesPerCandidate()) priority - prioritiesPerCandidate())
) )
or
// if there is no main module, then we look for files that are explicitly included in the published package.
exists(PathExpr file |
// `FilesPath` only exists if there is no main module for a given package.
file = FilesPath::of(pkg) and priority = 100 // fixing the priority, because there might be multiple files in the package.
|
result = file.resolve()
or
result = min(int i, File f | f = tryExtensions(file.resolve(), "index", i) | f order by i)
or
// resolve "file.js" to e.g. "file.ts".
not exists(file.resolve()) and
exists(int n | n = file.getNumComponent() |
result =
min(int i, File res |
res = tryExtensions(file.resolveUpTo(n - 1), getStem(file.getComponent(n - 1)), i)
|
res order by i
)
)
)
} }
/** /**
@@ -126,3 +153,31 @@ class MainModulePath extends PathExpr, @json_string {
module MainModulePath { module MainModulePath {
MainModulePath of(PackageJSON pkg) { result.getPackageJSON() = pkg } MainModulePath of(PackageJSON pkg) { result.getPackageJSON() = pkg }
} }
/**
* A JSON string in a `package.json` file specifying a file that should be included in the published package.
* These files are often imported directly from a client when a "main" module is not specified.
* For performance reasons this only exists if there is no "main" field in the `package.json` file.
*/
private class FilesPath extends PathExpr, @json_string {
PackageJSON pkg;
FilesPath() {
this = pkg.getPropValue("files").(JSONArray).getElementValue(_) and
not exists(MainModulePath::of(pkg))
}
/** Gets the `package.json` file in which this path occurs. */
PackageJSON getPackageJSON() { result = pkg }
override string getValue() { result = this.(JSONString).getValue() }
override Folder getAdditionalSearchRoot(int priority) {
priority = 0 and
result = pkg.getFile().getParentContainer()
}
}
private module FilesPath {
FilesPath of(PackageJSON pkg) { result.getPackageJSON() = pkg }
}

View File

@@ -606,10 +606,10 @@ module RangeAnalysis {
cfg2BB = cfg2.getBasicBlock() and cfg2BB = cfg2.getBasicBlock() and
cfg2RBB = cfg2BB.(ReachableBasicBlock) and cfg2RBB = cfg2BB.(ReachableBasicBlock) and
( (
cfg1RBB.strictlyDominates(cfg2BB) and cfg2BB.getImmediateDominator+() = cfg1RBB and
cfg = cfg2 cfg = cfg2
or or
cfg2RBB.strictlyDominates(cfg1RBB) and cfg1BB.getImmediateDominator+() = cfg2BB and
cfg = cfg1 cfg = cfg1
) )
) )
@@ -681,7 +681,7 @@ module RangeAnalysis {
midBB = midcfg.getBasicBlock() and midBB = midcfg.getBasicBlock() and
midRBB = midBB.(ReachableBasicBlock) and midRBB = midBB.(ReachableBasicBlock) and
cfgBB = cfg.getBasicBlock() and cfgBB = cfg.getBasicBlock() and
midRBB.strictlyDominates(cfgBB) cfgBB.getImmediateDominator+() = midRBB
) )
} }

View File

@@ -0,0 +1,10 @@
/** Provides the `Unit` class. */
/** The unit type. */
private newtype TUnit = TMkUnit()
/** The trivial type with a single element. */
class Unit extends TUnit {
/** Gets a textual representation of this element. */
string toString() { result = "Unit" }
}

View File

@@ -72,7 +72,7 @@ private import javascript
private import internal.FlowSteps private import internal.FlowSteps
private import internal.AccessPaths private import internal.AccessPaths
private import internal.CallGraphs private import internal.CallGraphs
private import internal.Unit private import semmle.javascript.Unit
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages
/** /**

View File

@@ -1683,4 +1683,59 @@ module DataFlow {
import TypeTracking import TypeTracking
predicate localTaintStep = TaintTracking::localTaintStep/2; predicate localTaintStep = TaintTracking::localTaintStep/2;
/**
* Holds if the function in `succ` forwards all its arguments to a call to `pred` and returns
* its result. This can thus be seen as a step `pred -> succ` used for tracking function values
* through "wrapper functions", since the `succ` function partially replicates behavior of `pred`.
*
* Examples:
* ```js
* function f(x) {
* return g(x); // step: g -> f
* }
*
* function doExec(x) {
* console.log(x);
* return exec(x); // step: exec -> doExec
* }
*
* function doEither(x, y) {
* if (x > y) {
* return foo(x, y); // step: foo -> doEither
* } else {
* return bar(x, y); // step: bar -> doEither
* }
* }
*
* function wrapWithLogging(f) {
* return (x) => {
* console.log(x);
* return f(x); // step: f -> anonymous function
* }
* }
* wrapWithLogging(g); // step: g -> wrapWithLogging(g)
* ```
*/
predicate functionForwardingStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::FunctionNode function, DataFlow::CallNode call |
call.flowsTo(function.getReturnNode()) and
forall(int i | exists([call.getArgument(i), function.getParameter(i)]) |
function.getParameter(i).flowsTo(call.getArgument(i))
) and
pred = call.getCalleeNode() and
succ = function
)
or
// Given a generic wrapper function like,
//
// function wrap(f) { return (x, y) => f(x, y) };
//
// add steps through calls to that function: `g -> wrap(g)`
exists(DataFlow::FunctionNode wrapperFunction, SourceNode param, Node paramUse |
FlowSteps::argumentPassing(succ, pred, wrapperFunction.getFunction(), param) and
param.flowsTo(paramUse) and
functionForwardingStep(paramUse, wrapperFunction.getReturnNode().getALocalSource())
)
}
} }

View File

@@ -52,6 +52,11 @@ class SourceNode extends DataFlow::Node {
*/ */
predicate flowsToExpr(Expr sink) { flowsTo(DataFlow::valueNode(sink)) } predicate flowsToExpr(Expr sink) { flowsTo(DataFlow::valueNode(sink)) }
/**
* Gets a node into which data may flow from this node in zero or more local steps.
*/
DataFlow::Node getALocalUse() { flowsTo(result) }
/** /**
* Gets a reference (read or write) of property `propName` on this node. * Gets a reference (read or write) of property `propName` on this node.
*/ */

View File

@@ -15,7 +15,7 @@
import javascript import javascript
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
private import semmle.javascript.dataflow.internal.Unit private import semmle.javascript.Unit
private import semmle.javascript.dataflow.InferredTypes private import semmle.javascript.dataflow.InferredTypes
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages

View File

@@ -9,7 +9,7 @@
private import javascript private import javascript
private import internal.FlowSteps private import internal.FlowSteps
private import internal.StepSummary private import internal.StepSummary
private import internal.Unit private import semmle.javascript.Unit
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop) private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop)

View File

@@ -4,14 +4,9 @@
*/ */
private import javascript private import javascript
private import semmle.javascript.Unit
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages
private newtype TUnit = MkUnit()
private class Unit extends TUnit {
string toString() { result = "unit" }
}
/** /**
* Internal extension point for adding flow edges prior to call graph construction * Internal extension point for adding flow edges prior to call graph construction
* and type tracking. * and type tracking.

View File

@@ -1,9 +0,0 @@
private newtype TUnit = MkUnit()
/**
* A class with only one instance.
*/
class Unit extends TUnit {
/** Gets a textual representation of this element. */
final string toString() { result = "Unit" }
}

View File

@@ -105,35 +105,6 @@ module Angular2 {
result = urlSegment().getAPropertyRead("parameters") and kind.isPath() result = urlSegment().getAPropertyRead("parameters") and kind.isPath()
} }
/** Gets a reference to a `Params` object, usually containing values from the URL. */
DataFlow::SourceNode paramDictionaryObject() { result = paramDictionaryObject(_) }
/**
* A value from `@angular/router` derived from the URL.
*/
class AngularSource extends ClientSideRemoteFlowSource {
ClientSideRemoteFlowKind kind;
AngularSource() {
this = paramMap(kind).getAMethodCall(["get", "getAll"])
or
this = paramDictionaryObject(kind)
or
this = activatedRouteProp("fragment") and kind.isFragment()
or
this = urlSegment().getAPropertyRead("path") and kind.isPath()
or
// Note that Router.url and RouterStateSnapshot.url are strings, not UrlSegment[]
this = router().getAPropertyRead("url") and kind.isUrl()
or
this = routerStateSnapshot().getAPropertyRead("url") and kind.isUrl()
}
override string getSourceType() { result = "Angular route parameter" }
override ClientSideRemoteFlowKind getKind() { result = kind }
}
/** Gets a reference to a `DomSanitizer` object. */ /** Gets a reference to a `DomSanitizer` object. */
DataFlow::SourceNode domSanitizer() { DataFlow::SourceNode domSanitizer() {
result.hasUnderlyingType(["@angular/platform-browser", "@angular/core"], "DomSanitizer") result.hasUnderlyingType(["@angular/platform-browser", "@angular/core"], "DomSanitizer")

View File

@@ -88,7 +88,7 @@ module FunctionCompositionCall {
RightToLeft() { RightToLeft() {
this = DataFlow::moduleImport(["compose-function"]).getACall() this = DataFlow::moduleImport(["compose-function"]).getACall()
or or
this = DataFlow::moduleMember(["redux", "ramda"], "compose").getACall() this = DataFlow::moduleMember(["redux", "ramda", "@reduxjs/toolkit"], "compose").getACall()
or or
this = LodashUnderscore::member("flowRight").getACall() this = LodashUnderscore::member("flowRight").getACall()
} }

View File

@@ -489,39 +489,32 @@ module Express {
string kind; string kind;
RequestInputAccess() { RequestInputAccess() {
kind = "parameter" and exists(DataFlow::Node request | request = DataFlow::valueNode(rh.getARequestExpr()) |
this =
[
getAQueryObjectReference(DataFlow::TypeTracker::end(), rh),
getAParamsObjectReference(DataFlow::TypeTracker::end(), rh)
].getAPropertyRead()
or
exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() |
kind = "parameter" and kind = "parameter" and
this = request.getAMethodCall("param") (
this.(DataFlow::MethodCallNode).calls(request, "param")
or
exists(DataFlow::PropRead base, string propName |
// `req.params.name` or `req.query.name`
base.accesses(request, propName) and
this = base.getAPropertyReference(_)
|
propName = "params" or
propName = "query"
)
)
or or
// `req.originalUrl` // `req.originalUrl`
kind = "url" and kind = "url" and
this = request.getAPropertyRead("originalUrl") this.(DataFlow::PropRef).accesses(request, "originalUrl")
or or
// `req.cookies` // `req.cookies`
kind = "cookie" and kind = "cookie" and
this = request.getAPropertyRead("cookies") this.(DataFlow::PropRef).accesses(request, "cookies")
or
// `req.files`, treated the same as `req.body`.
// `express-fileupload` uses .files, and `multer` uses .files or .file
kind = "body" and
this = request.getAPropertyRead(["files", "file"])
) )
or or
kind = "body" and kind = "body" and
this.asExpr() = rh.getARequestBodyAccess() this.asExpr() = rh.getARequestBodyAccess()
or
// `value` in `router.param('foo', (req, res, next, value) => { ... })`
kind = "parameter" and
exists(RouteSetup setup | rh = setup.getARouteHandler() |
this = DataFlow::parameterNode(rh.getRouteHandlerParameter("parameter"))
)
} }
override RouteHandler getRouteHandler() { result = rh } override RouteHandler getRouteHandler() { result = rh }

View File

@@ -125,42 +125,6 @@ module Fastify {
} }
} }
/**
* An access to a user-controlled Fastify request input.
*/
private class RequestInputAccess extends HTTP::RequestInputAccess {
RouteHandler rh;
string kind;
RequestInputAccess() {
exists(string name | this = rh.getARequestSource().ref().getAPropertyRead(name) |
kind = "parameter" and
name = ["params", "query"]
or
kind = "body" and
name = "body"
)
}
override RouteHandler getRouteHandler() { result = rh }
override string getKind() { result = kind }
override predicate isUserControlledObject() {
kind = "body" and
(
usesFastifyPlugin(rh,
DataFlow::moduleImport(["fastify-xml-body-parser", "fastify-formbody"]))
or
usesMiddleware(rh,
any(ExpressLibraries::BodyParser bodyParser | bodyParser.producesUserControlledObjects()))
)
or
kind = "parameter" and
usesFastifyPlugin(rh, DataFlow::moduleImport("fastify-qs"))
}
}
/** /**
* Holds if `rh` uses `plugin`. * Holds if `rh` uses `plugin`.
*/ */
@@ -193,25 +157,6 @@ module Fastify {
) )
} }
/**
* An access to a header on a Fastify request.
*/
private class RequestHeaderAccess extends HTTP::RequestHeaderAccess {
RouteHandler rh;
RequestHeaderAccess() {
this = rh.getARequestSource().ref().getAPropertyRead("headers").getAPropertyRead()
}
override string getAHeaderName() {
result = this.(DataFlow::PropRead).getPropertyName().toLowerCase()
}
override RouteHandler getRouteHandler() { result = rh }
override string getKind() { result = "header" }
}
/** /**
* An argument passed to the `send` or `end` method of an HTTP response object. * An argument passed to the `send` or `end` method of an HTTP response object.
*/ */

View File

@@ -4,60 +4,3 @@
import javascript import javascript
/**
* A source of remote flow from the `Busboy` library.
*/
private class BusBoyRemoteFlow extends RemoteFlowSource {
BusBoyRemoteFlow() {
this =
API::moduleImport("busboy")
.getInstance()
.getMember("on")
.getParameter(1)
.getAParameter()
.getAnImmediateUse()
}
override string getSourceType() { result = "parsed user value from Busbuy" }
}
/**
* A source of remote flow from the `Formidable` library parsing a HTTP request.
*/
private class FormidableRemoteFlow extends RemoteFlowSource {
FormidableRemoteFlow() {
exists(API::Node formidable |
formidable = API::moduleImport("formidable").getReturn()
or
formidable = API::moduleImport("formidable").getMember("formidable").getReturn()
or
formidable =
API::moduleImport("formidable").getMember(["IncomingForm", "Formidable"]).getInstance()
|
this =
formidable.getMember("parse").getACall().getABoundCallbackParameter(1, any(int i | i > 0))
)
}
override string getSourceType() { result = "parsed user value from Formidable" }
}
/**
* A source of remote flow from the `Multiparty` library.
*/
private class MultipartyRemoteFlow extends RemoteFlowSource {
MultipartyRemoteFlow() {
exists(API::Node form | form = API::moduleImport("multiparty").getMember("Form").getInstance() |
exists(API::CallNode parse | parse = form.getMember("parse").getACall() |
this = parse.getParameter(1).getAParameter().getAnImmediateUse()
)
or
exists(API::CallNode on | on = form.getMember("on").getACall() |
on.getArgument(0).mayHaveStringValue(["part", "file", "field"]) and
this = on.getParameter(1).getAParameter().getAnImmediateUse()
)
)
}
override string getSourceType() { result = "parsed user value from Multiparty" }
}

View File

@@ -475,23 +475,6 @@ module HTTP {
*/ */
abstract Expr getServer(); 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" }
}
} }
/** /**

Some files were not shown because too many files have changed in this diff Show More