Compare commits

..

420 Commits

Author SHA1 Message Date
Philip Ginsbach
ab4bb65c27 set CompileForOverlayEval in java pack 2025-07-03 16:46:22 +01:00
Jeroen Ketema
3c73f141c4 C++: Update stats file 2025-07-02 21:46:14 +02:00
Jeroen Ketema
2697798f05 C++: Add upgrade and downgrade scripts 2025-07-02 21:46:12 +02:00
Jeroen Ketema
eede720aa8 C++: Uncomment cases in the dbscheme 2025-07-02 21:46:09 +02:00
Paolo Tranquilli
33a2801bb7 Merge pull request #19956 from github/redsun82/java-fix-tests
Java: disable failing maven fetches expectations for now
2025-07-02 17:32:05 +02:00
Paolo Tranquilli
4d3546f7c9 Java: disable failing maven fetches expectations for now 2025-07-02 17:16:41 +02:00
Jeroen Ketema
d17c931939 Merge pull request #19952 from jketema/comment-cleanup
C++: Remove QLtest related comment from integration test
2025-07-02 13:59:15 +02:00
Jeroen Ketema
e47f16b100 Merge pull request #19947 from jketema/function-confusion
C++: Move builtin function identification to its own table
2025-07-02 12:56:18 +02:00
Paolo Tranquilli
c4ec0765ea Merge pull request #19951 from github/aibaars/rust-workflows
Rust: add trailing newline to  rust-cwe.md
2025-07-02 12:26:48 +02:00
Jeroen Ketema
def0ee90c3 C++: Remove QLtest related comment from integration test
I forgot to remove this in https://github.com/github/codeql/pull/19410
2025-07-02 12:14:38 +02:00
Arthur Baars
9e54bc6918 Rust: add trailing newline to rust-cwe.md 2025-07-02 11:39:00 +02:00
Tom Hvitved
d10002c735 Merge pull request #19927 from hvitved/rust/type-inference-overlap3
Rust: Disambiguate more method calls based on argument types
2025-07-02 11:36:37 +02:00
Jeroen Ketema
1103644737 C++: Add upgrade and downgrade scripts 2025-07-01 23:38:24 +02:00
Jeroen Ketema
3418451bee C++: Update stats file 2025-07-01 23:16:26 +02:00
Jeroen Ketema
19d6f665b4 Merge pull request #19676 from mrigankpawagi/patch-1
Fixes in cpp/global-use-before-init
2025-07-01 19:17:29 +02:00
Jeroen Ketema
65b21286a1 C++: Move builtin function identification to its own table 2025-07-01 18:00:44 +02:00
Jeroen Ketema
7c2fd28585 Merge pull request #19938 from jketema/external
C++: Remove unused `external_package` tables from the dbscheme
2025-07-01 16:50:31 +02:00
Mrigank Pawagi
fe24cc876a Merge branch 'main' into patch-1 2025-07-01 20:04:13 +05:30
Arthur Baars
4c6c395b1b Merge pull request #19939 from github/aibaars/rust-workflows
Rust: add to `generate-code-scanning-query-list.py` and `shared-code-metrics.py` scripts
2025-07-01 16:12:11 +02:00
Jeroen Ketema
02e5541953 Merge branch 'main' into patch-1 2025-07-01 15:58:48 +02:00
Mrigank Pawagi
b821b21500 Create 2025-07-01-global-vars-ubi-query-fixes.md.md 2025-07-01 13:12:38 +00:00
Tom Hvitved
add2e0fd9d Rust: Extend methodResolutionDependsOnArgument to parameterized implementations 2025-07-01 14:22:06 +02:00
Tom Hvitved
961e6201ea Rust: Add more type inference tests 2025-07-01 14:22:04 +02:00
Tom Hvitved
b813010b75 Merge pull request #19903 from hvitved/rust/type-inference-overlap2
Rust: Apply inherent method prioritization inside type inference loop
2025-07-01 14:21:15 +02:00
Tom Hvitved
d6b051ed30 Merge pull request #19936 from hvitved/rust/path-resolution-prelude-always
Rust: Assume prelude is always available in path resolution
2025-07-01 13:13:35 +02:00
Owen Mansel-Chan
811ed3ccde Merge pull request #19892 from owen-mc/fix-markdown-query-help-formatting
Fix markdown query help formatting
2025-07-01 12:05:35 +01:00
Tom Hvitved
219a622299 Merge pull request #19926 from hvitved/ruby/restrict-string-component-length
Ruby: Do not compute `StringlikeLiteralImpl.getStringValue` for large strings
2025-07-01 12:45:51 +02:00
Arthur Baars
c08d98d159 Rust: add to querylist and shared code metrics scripts 2025-07-01 12:16:42 +02:00
Tom Hvitved
072339137a Rust: Update expected test output 2025-07-01 10:34:16 +02:00
Jeroen Ketema
f3c5870d44 C++: Update stats file 2025-07-01 10:21:51 +02:00
Tom Hvitved
bd1f46b75c Rust: Assume prelude is always available in path resolution 2025-07-01 10:18:02 +02:00
Jeroen Ketema
8ac69b9116 C++: Add upgrade and downgrade scripts 2025-07-01 10:17:43 +02:00
Tom Hvitved
e88d7baa7d Rust: Apply inherent method prioritization inside type inference loop 2025-07-01 10:17:26 +02:00
Tom Hvitved
e5f0ef6ae8 Rust: Add more type inference tests 2025-07-01 10:17:25 +02:00
Jeroen Ketema
7779f14654 C++: Remove unused external_package tables from the dbscheme 2025-07-01 10:13:04 +02:00
Jeroen Ketema
a791640b52 Merge pull request #19935 from jketema/sync-dbscheme-cpp
C++: synchronize dbscheme
2025-07-01 09:51:29 +02:00
Michael Nebel
233b54c7fa Merge pull request #19891 from michaelnebel/michaelnebel/freezemoresuites
Go/Ruby/Python: Freeze quality queries in `security-and-quality`.
2025-07-01 09:04:19 +02:00
Tom Hvitved
2ee3401cfb Merge pull request #19873 from github/redsun82/rust-item-reorg
Rust: make `AssocItem` and `ExternItem` subclasses of `Item`
2025-07-01 08:58:48 +02:00
Jeroen Ketema
d5c7905009 Merge pull request #19907 from github/idrissrio/no-string-representation
C++: fix `(no string representation)` for `ConstructorInit`
2025-07-01 08:13:31 +02:00
Jeroen Ketema
98798b6f73 C++: Update stats file 2025-06-30 20:12:49 +02:00
Jeroen Ketema
1772193982 Merge pull request #19933 from jketema/arm-change
C++: Add Arm64 change note
2025-06-30 19:19:32 +02:00
Mrigank Pawagi
cf60b62981 fix formatting
Co-authored-by: Jeroen Ketema <93738568+jketema@users.noreply.github.com>
2025-06-30 16:40:03 +00:00
Jeroen Ketema
44523aeec4 C++: Add Arm64 change note 2025-06-30 18:01:03 +02:00
idrissrio
62e55edbad C++: accept new test results after changes 2025-06-30 17:11:59 +02:00
idrissrio
6a291cc474 C++: fix (no string representation) for ConstructorInit 2025-06-30 17:11:58 +02:00
Taus
184dd5bf10 Merge pull request #19895 from github/tausbn/python-fix-match-as-identifier
Python: Allow use of `match` as an identifier
2025-06-30 16:24:23 +02:00
Paolo Tranquilli
e7959dfde6 Rust: recreate wrongfully deleted upgrade script directory 2025-06-30 15:38:42 +02:00
Jami
de09122de3 Merge pull request #19175 from jcogs33/jcogs33/java/call-to-thread-run
Java: update `java/call-to-thread-run`
2025-06-30 09:31:08 -04:00
Jeroen Ketema
3a3c222e46 C++: Add upgrade and downgrade scripts 2025-06-30 15:28:55 +02:00
Jeroen Ketema
617edf0b70 C++: synchronize dbscheme 2025-06-30 15:28:45 +02:00
Paolo Tranquilli
9e4cdbc53f Merge branch 'main' into redsun82/rust-item-reorg 2025-06-30 14:56:23 +02:00
Paolo Tranquilli
15aa0bbb34 Merge pull request #19866 from github/redsun82/codegen-new-parent-child
Codegen: improve implementation of generated parent/child relationship
2025-06-30 14:52:24 +02:00
Tom Hvitved
97412f4077 Merge pull request #19916 from hvitved/rust/fix-capture-inconsistencies
Rust: Fix variable capture inconsistencies
2025-06-30 14:18:00 +02:00
Tom Hvitved
41a403c904 Ruby: Do not compute StringlikeLiteralImpl.getStringValue for large strings 2025-06-30 13:01:57 +02:00
Jeroen Ketema
23b9db8f6f Merge pull request #19904 from jketema/ffbl
C++: Sync the product-flow field flow branch limits with the default one
2025-06-30 11:17:55 +02:00
Kasper Svendsen
3d7343273e Merge pull request #19813 from github/kaspersv/overlay-java-discarding
Overlay: Add manual Java overlay annotations & discard predicates
2025-06-30 11:17:31 +02:00
Asger F
7c38c48fd7 Merge pull request #19769 from trailofbits/VF/Nest-improvements
Improve NestJS sources and dependency injection
2025-06-30 10:42:18 +02:00
Asger F
3247babfa5 Merge pull request #19762 from trailofbits/VF/type-orm-model-improvements
Improve TypeORM model
2025-06-30 10:40:38 +02:00
Jeroen Ketema
6ae1656ec4 Merge pull request #17581 from jketema/loc-table-merge
C++: Merge the location tables
2025-06-30 10:33:46 +02:00
Tom Hvitved
57661df306 Rust: Fix variable capture inconsistencies 2025-06-30 10:19:42 +02:00
Paolo Tranquilli
9cf037fdb9 Merge branch 'main' into redsun82/codegen-new-parent-child 2025-06-30 10:17:56 +02:00
Tom Hvitved
632cde689b Merge pull request #19702 from geoffw0/lifetime
Rust: New query rust/access-after-lifetime-ended
2025-06-30 10:00:11 +02:00
Paolo Tranquilli
e3a61f5f18 Merge pull request #19899 from github/redsun82/copilot-instructions
Create copilot-instructions.md
2025-06-30 09:11:29 +02:00
Kasper Svendsen
c7194a4012 Overlay: Add missing QLDoc 2025-06-30 08:40:46 +02:00
Michael Nebel
a74f60bb84 Merge pull request #19910 from github/workflow/coverage/update
Update CSV framework coverage reports
2025-06-30 08:34:32 +02:00
Kasper Svendsen
5b09ecd769 Merge pull request #19780 from github/kaspersv/overlay-annotations-script-ci
Overlay: Add CI workflow to check overlay annotations
2025-06-30 08:11:14 +02:00
Jami Cogswell
42904113b4 Java: add qhelp references 2025-06-29 22:50:10 -04:00
Jami Cogswell
87ab4d0160 Java: remove java/run-method-called-on-java-lang-thread-directly
using existing query java/call-to-thread-run instead
2025-06-29 22:42:31 -04:00
Jami Cogswell
12e7bbbae8 Java: update existing tests to services tests 2025-06-29 22:41:47 -04:00
Jami Cogswell
1172f82a4b Java: update existing tests to inline expectations 2025-06-29 22:21:41 -04:00
Jami Cogswell
e266918871 Java: add previous-id 2025-06-29 22:21:06 -04:00
Jami Cogswell
7a2023b863 Java: move original files 2025-06-29 22:13:49 -04:00
github-actions[bot]
81ec3b6566 Add changed framework coverage reports 2025-06-30 00:26:21 +00:00
Mrigank Pawagi
809d1d55a8 remove cases involving sizeof 2025-06-28 17:16:04 +00:00
Nicolas Will
38fdf7eea0 Merge pull request #19880 from bdrodes/operation_step_refactor
Crypto: Refactor OpenSSL operation step data-flow logic
2025-06-27 17:19:11 +02:00
Kasper Svendsen
e02affd327 Merge pull request #19901 from github/kaspersv/overlay-guards-inline
Overlay: Add missing `overlay[caller?]` annotation
2025-06-27 15:13:09 +02:00
Jeroen Ketema
89c91cc1a2 C++: Add change note 2025-06-27 15:06:03 +02:00
Jeroen Ketema
99a24f9650 C++: Fix macro handling after extractor changes 2025-06-27 14:42:33 +02:00
Jeroen Ketema
bf131dc84b C++: Update stats file 2025-06-27 14:42:32 +02:00
Jeroen Ketema
7f47e31fb5 C++: Add upgrade and downgrade scripts 2025-06-27 14:42:20 +02:00
Jeroen Ketema
b4caba7c0e C++: Merge the location tables 2025-06-27 14:42:08 +02:00
REDMOND\brodes
9f0c62b572 Crypto: Address PR comments. 2025-06-27 08:33:01 -04:00
Ben Rodes
122a004851 Update cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/MACAlgorithmInstance.qll
Co-authored-by: Nicolas Will <nicolaswill@github.com>
2025-06-27 08:28:05 -04:00
Jeroen Ketema
3e31cd3ce5 C++: Sync the product-flow field flow branch limits with the default one 2025-06-27 12:59:54 +02:00
Nicolas Will
976364fcaa Merge branch 'main' into operation_step_refactor 2025-06-27 12:05:14 +02:00
Kasper Svendsen
5cddd384c7 Merge branch 'main' into kaspersv/overlay-annotations-script-ci 2025-06-27 11:19:52 +02:00
Kasper Svendsen
6038396115 Merge pull request #19898 from kaspersv/kaspersv/shared-overlay-annotation
Overlay: Add overlay annotation to shared lib
2025-06-27 11:18:55 +02:00
Michael Nebel
143a91efc4 Re-use the security-extended selector in the security-and-frozen-quality selector. 2025-06-27 11:08:08 +02:00
Jeroen Ketema
ad5ee1c498 Merge pull request #19894 from jketema/pretty
C++: Pretty print MaD ids in test output
2025-06-27 11:00:52 +02:00
Kasper Svendsen
5096ce405f Overlay: Add missing overlay[caller?] annotation 2025-06-27 10:50:28 +02:00
Tom Hvitved
3fb8758ae1 Merge pull request #19886 from hvitved/rust/dataflow-caching
Rust: Cache `DataFlow::Node.{toString,getLocation}`
2025-06-27 10:33:50 +02:00
Michael Nebel
2f208bddb6 Merge pull request #19877 from michaelnebel/csharp/microsoftdatasqlclient
C#: Models for Microsoft.Data.SqlClient.
2025-06-27 10:24:38 +02:00
Nora Dimitrijević
f568d41264 Merge pull request #19888 from d10c/d10c/missing-diff-informed-tests
Java, Ruby: add missing .qlref tests
2025-06-27 09:28:41 +02:00
Paolo Tranquilli
c88049a9f4 Create copilot-instructions.md 2025-06-27 09:06:08 +02:00
Tom Hvitved
db0fc7be5d Merge pull request #19881 from hvitved/rust/dataflow-traits
Rust: Data flow through trait methods
2025-06-27 08:55:48 +02:00
Kasper Svendsen
2863c7094a Overlay: Add overlay annotation to shared lib 2025-06-27 08:54:05 +02:00
Jonas Jensen
b446fe74c2 Merge pull request #19846 from jbj/diff-informed-CleartextStorageCookie
Java: Diff-informed CleartextStorageCookie.ql
2025-06-27 08:45:11 +02:00
Kasper Svendsen
f0125e574c Merge branch 'main' into kaspersv/overlay-annotations-script-ci 2025-06-27 08:31:34 +02:00
Kasper Svendsen
e6ef6a3326 Merge branch 'main' into kaspersv/overlay-java-discarding 2025-06-27 08:28:34 +02:00
Kasper Svendsen
da1b99b921 Merge pull request #19779 from github/kaspersv/overlay-java-annotations
Overlay: Add overlay annotations to Java & shared libraries
2025-06-27 08:26:33 +02:00
Joe Farebrother
4cbaeb10e9 Merge pull request #19641 from joefarebrother/python-qual-file-not-closed
Python: Improve performance of FileNotClosed query by using basic block reachability
2025-06-26 23:35:38 +01:00
Jeroen Ketema
0996e6083e C++: Pretty print MaD ids in test output 2025-06-26 23:38:32 +02:00
REDMOND\brodes
0aee4f76f9 Crypto: Minor change to force CI/CD checks to restart, prior ql check failures do not make sense. 2025-06-26 16:35:01 -04:00
REDMOND\brodes
dc8d22a468 Crypto: Fix JCA to account for new key gen instance API in model.qll. 2025-06-26 15:48:10 -04:00
REDMOND\brodes
505d8806c7 Crypto: Add key input support for the graph for key generation operations. 2025-06-26 11:51:49 -04:00
Taus
cd0e46314c Python: Add change note 2025-06-26 15:36:02 +00:00
Taus
ad53518644 Python: Regenerate parser files 2025-06-26 15:34:44 +00:00
Taus
e04821e9e3 Python: Allow use of match as an identifier
This previously only worked in certain circumstances. In particular,
assignments such as `match[1] = ...` or even just `match[1]` would fail
to parse correctly.

Fixing this turned out to be less trivial than anticipated. Consider the
fact that
```
match [1]: case (...)
```
can either look the start of a `match` statement, or it could be a type
ascription, ascribing the value of `case(...)` (a call) to the item at
index 1 of `match`.

To fix this, then, we give `match` the identifier and `match` the
statement the same precendence in the grammar, and additionally also
mark a conflict between `match_statement` and `primary_expression`. This
causes the conflict to be resolved dynamically, and seems to do the
right thing in all cases.
2025-06-26 15:33:00 +00:00
Nicolas Will
c54e68c855 Merge branch 'main' into pr/19880 2025-06-26 16:47:38 +02:00
Jeroen Ketema
ec09d36667 Merge pull request #19832 from ebickle/feature/oracle-model
C++:  Support SQL Injection sinks for Oracle Call Interface (OCI)
2025-06-26 16:33:55 +02:00
Nicolas Will
0a97357216 Merge pull request #19814 from bdrodes/codescanning_fixes_cpp
Crypto: Fix QL-for-QL alerts and refactor type standardization
2025-06-26 16:33:19 +02:00
Paolo Tranquilli
4799861225 Merge branch 'redsun82/codegen-new-parent-child' into redsun82/rust-item-reorg 2025-06-26 16:29:42 +02:00
Owen Mansel-Chan
2ed451c9e3 Reformat references 2025-06-26 15:20:07 +01:00
Owen Mansel-Chan
10bb88825e Add full stop at the end of each reference 2025-06-26 15:20:06 +01:00
Owen Mansel-Chan
297cdb53aa Update guide to specify a full stop at the end of each reference 2025-06-26 15:20:04 +01:00
Eric Bickle
1142efbc03 Merge branch 'main' into feature/oracle-model 2025-06-26 06:48:40 -07:00
Eric Bickle
3083bdb0b4 C++: Update MaD line numbers in flow.expected 2025-06-26 06:47:24 -07:00
Owen Mansel-Chan
9f0f40d6ce Add "Correct Usage" and "Incorrect Usage" headings 2025-06-26 14:40:49 +01:00
Owen Mansel-Chan
9521994adc Fix format of markdown query help files 2025-06-26 14:40:07 +01:00
Tom Hvitved
9a48459951 Add change note 2025-06-26 15:14:08 +02:00
Nicolas Will
652e7ba15b Merge branch 'main' into codescanning_fixes_cpp 2025-06-26 14:54:36 +02:00
Michael Nebel
37b3ca036a Python: Freeze the quality queries in the security-and-quality suite. 2025-06-26 14:45:05 +02:00
Michael Nebel
d926a6a47d Go: Freeze the quality queries in the security-and-quality suite. 2025-06-26 14:35:21 +02:00
Michael Nebel
7fecf7466f Ruby: Freeze the quality queries in the security-and-quality suite. 2025-06-26 14:26:28 +02:00
Michael Nebel
145ada53f2 C#/Java/JavaScript: Re-factor query suites to use the new selector. 2025-06-26 14:19:27 +02:00
Nick Rolfe
5a176d6fbd Merge pull request #19878 from github/nickrolfe/ql-overlay
Ruby/Rust/QL: simplify generation of overlay-related tables/predicates
2025-06-26 08:10:10 -04:00
Michael Nebel
3efbed56b0 Shared: Modify the frozen selector to only include security queries. 2025-06-26 14:09:43 +02:00
Michael Nebel
1fbf3a39fb Shared: Add a copy of the security-and-quality selector. 2025-06-26 14:05:46 +02:00
Tom Hvitved
b70aa804e5 Rust: Cache DataFlow::Node.{toString,getLocation} 2025-06-26 13:49:37 +02:00
Nora Dimitrijević
89f1ee0301 Ruby: add meta/TaintedNodes.ql test 2025-06-26 13:22:07 +02:00
Nora Dimitrijević
e0b3a2c5f9 Java: convert ArbitraryApkInstallation test to .qlref 2025-06-26 13:22:05 +02:00
Kasper Svendsen
712e64e4a8 Overlay: Add overlay annotations to shared Guards library 2025-06-26 13:19:49 +02:00
Kasper Svendsen
9d2dd782d9 Merge remote-tracking branch 'github/main' into kaspersv/overlay-java-annotations 2025-06-26 13:18:25 +02:00
Jeroen Ketema
a5737dded3 Merge branch 'main' into feature/oracle-model 2025-06-26 12:48:55 +02:00
Tamás Vajk
ae36f94d5e Merge pull request #19844 from tamasvajk/tamasvajk/threadpoolexecutor
Java: Add `java/javautilconcurrentscheduledthreadpoolexecutor` query for zero thread pool size
2025-06-26 12:36:09 +02:00
Paolo Tranquilli
de72e68d2c Merge branch 'main' into redsun82/codegen-new-parent-child 2025-06-26 12:14:53 +02:00
Paolo Tranquilli
afc78ced50 Merge pull request #19874 from github/redsun82/codegen-use-one-test-file
Codegen: use one generated test file per directory
2025-06-26 11:59:40 +02:00
Anders Schack-Mulligen
321a4afd5c Merge pull request #19883 from aschackmull/java/fix-assert-cfg
Java: Fix assert CFG by properly tagging the false successor.
2025-06-26 11:43:27 +02:00
Tamas Vajk
1bd543a8a2 Improve readability of the ID 2025-06-26 11:36:32 +02:00
Kasper Svendsen
64f27e2adf Java: Add abstraction for discardable locatables 2025-06-26 11:35:37 +02:00
Tamás Vajk
1e0dd2a935 Apply suggestion from @michaelnebel
Co-authored-by: Michael Nebel <michaelnebel@github.com>
2025-06-26 11:34:43 +02:00
Anders Schack-Mulligen
7750f1244c Merge pull request #19884 from aschackmull/guards/eqtest-refactor
Guards: Refactor EqualityTest interface.
2025-06-26 11:04:55 +02:00
Anders Schack-Mulligen
c091fc585b Java: Account for AssertionError possibly not being extracted. 2025-06-26 11:03:59 +02:00
Anders Schack-Mulligen
326f2b0498 Java: Accept qltest change showing FP removal. 2025-06-26 11:03:39 +02:00
Anders Schack-Mulligen
f07d9dda39 Guards: Refactor EqualityTest interface. 2025-06-26 10:26:40 +02:00
Jeroen Ketema
b16e710d3b Merge pull request #19870 from jketema/jketema/stats
C++: Update stats file after DCA and extractor changes
2025-06-26 10:21:35 +02:00
Anders Schack-Mulligen
1d4c8197ec Java: Fix assert CFG by properly tagging the false successor. 2025-06-26 10:18:14 +02:00
Jonas Jensen
fc2b18ae8a Java: Diff-informed CleartextStorageCookie.ql
This query shares implementation with several other queries about
cleartext storage, but it's the only one of them that's in the
code-scanning suite. The sharing mechanism remains the same as before,
but now each query has to override `getASelectedLocation` to become
diff-informed.

Two other data-flow configurations are used in this query, but they
can't easily be made diff-informed.
2025-06-26 09:31:11 +02:00
Paolo Tranquilli
9a8ef3acf7 Merge branch 'main' into redsun82/codegen-new-parent-child 2025-06-26 09:30:41 +02:00
Vasco-jofra
8a7516528d Update formatting 2025-06-26 09:29:07 +02:00
Anders Schack-Mulligen
4d2c67857f Merge pull request #19573 from aschackmull/guardslib
Shared/Java: Add shared Guards library and switch Java to use it.
2025-06-26 09:28:32 +02:00
Michael Nebel
cfadd30f98 C#: Add change-note. 2025-06-26 08:52:18 +02:00
Michael Nebel
becd46a47e C#: Add MaD models for Microsoft.Data.SqlClient. 2025-06-26 08:51:10 +02:00
Michael Nebel
f3eafd33ff C#: Exclude Microsoft.Data.SqlClient.SqlCommand from the best effort SqlSink creation. 2025-06-26 08:46:49 +02:00
Michael Nebel
ed7f68279f C#: Add cs/sql-injection tests for APIs in Microsoft.Data.SqlClient. 2025-06-26 08:44:50 +02:00
Anders Schack-Mulligen
6f4adb8892 Shared: address review comments. 2025-06-26 07:17:37 +02:00
Anders Schack-Mulligen
5ddddaecdc Java: Add change note. 2025-06-26 07:17:36 +02:00
Anders Schack-Mulligen
4645856f09 Java: document FP 2025-06-26 07:17:36 +02:00
Anders Schack-Mulligen
73810a6d85 Java: Fix perf issue. 2025-06-26 07:17:35 +02:00
Anders Schack-Mulligen
5a34a1a51b Shared: Try caching. 2025-06-26 07:17:35 +02:00
Anders Schack-Mulligen
d4c897f8e2 Java: Fix perf issue. 2025-06-26 07:17:35 +02:00
Anders Schack-Mulligen
42b1b12aa1 Java: Fix qltests 2025-06-26 07:17:34 +02:00
Anders Schack-Mulligen
5c0dcd980d Java: Switch to the shared Guards library. 2025-06-26 07:17:34 +02:00
Anders Schack-Mulligen
cc13193cb6 Java: Replace some references to basicNullGuard. 2025-06-26 07:17:33 +02:00
Anders Schack-Mulligen
0607fefc57 Java: Refactor integerGuard. 2025-06-26 07:17:33 +02:00
Anders Schack-Mulligen
a2778eee75 Java: Refactor clearlyNotNullExpr into a base case that does not rely on SSA. 2025-06-26 07:17:32 +02:00
Anders Schack-Mulligen
22d5dc999a Shared: Bugfix for unique value implication. 2025-06-26 07:17:32 +02:00
Anders Schack-Mulligen
378209a6ad Shared: Simplify and improve joins. 2025-06-26 07:17:31 +02:00
Anders Schack-Mulligen
b19bff9a4e Shared: Switch case guards to be the case statements. 2025-06-26 07:17:31 +02:00
Anders Schack-Mulligen
f772493f4c Shared: Elaborate qldoc. 2025-06-26 07:17:31 +02:00
Anders Schack-Mulligen
73ae613b7a Shared: Many tweaks to Guards. 2025-06-26 07:17:30 +02:00
Anders Schack-Mulligen
c212d0ac8f Shared: Improve shared guards lib. 2025-06-26 07:17:30 +02:00
Anders Schack-Mulligen
16c5b57953 Shared: Extend the shared Guards library with support for exception branch points. 2025-06-26 07:17:29 +02:00
Anders Schack-Mulligen
14b87f97b9 Shared: Extend the shared Guards library with support for custom wrappers. 2025-06-26 07:17:29 +02:00
Anders Schack-Mulligen
1d75008eba Shared: Add a shared Guards library inspired by the Java and C# versions. 2025-06-26 07:17:28 +02:00
Anders Schack-Mulligen
994c1f6427 Java: Add hasInputFromBlock predicate in BaseSSA. 2025-06-26 07:17:28 +02:00
Anders Schack-Mulligen
a0c849139c Java: Add guards-logic qltest with inline expectation. 2025-06-26 07:17:28 +02:00
Jami
aa65f54b1d Merge pull request #19882 from owen-mc/go/avoid-deprecated-class
Go: Avoid using deprecated class
2025-06-25 21:16:08 -04:00
Owen Mansel-Chan
9663ecad21 Avoid using deprecated class 2025-06-26 01:46:14 +01:00
Owen Mansel-Chan
0f07ab58cf Merge pull request #19654 from owen-mc/go/fix-definedtype-getbasetype
Go: fix `DefinedType.getBaseType`
2025-06-26 00:19:19 +01:00
Owen Mansel-Chan
d7b1d7bef4 Merge pull request #19677 from owen-mc/go/better-class-names-and-helpers
Go: Improve two class names and add some helper predicates
2025-06-26 00:17:32 +01:00
Chris Smowton
2291e10ce6 Fix typo
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-25 21:38:22 +02:00
Tom Hvitved
8c240399c1 Rust: Apply MaD trait models to implementations 2025-06-25 21:32:24 +02:00
Tom Hvitved
3e54c61f52 Rust: Add MaD trait tests 2025-06-25 21:32:22 +02:00
Tom Hvitved
5e265b10c7 Rust: Trait call dispatch in dataflow 2025-06-25 21:32:21 +02:00
Tom Hvitved
a4ed5da50b Rust: Add data flow tests involving traits 2025-06-25 21:32:19 +02:00
REDMOND\brodes
7559c06fdb Merge branch 'operation_step_refactor' of https://github.com/bdrodes/codeql into operation_step_refactor 2025-06-25 15:26:21 -04:00
REDMOND\brodes
7477471bc5 Crypto: Bug fix in output model 2025-06-25 15:25:51 -04:00
Nicolas Will
6571c11eb7 Merge branch 'main' into operation_step_refactor 2025-06-25 20:38:11 +02:00
Nicolas Will
98479ff6c3 Crypto: Update queries to use new type names 2025-06-25 20:34:33 +02:00
Nicolas Will
ad7358ac4f Crypto: Deduplicate "GCM" mapping from OpenSSL modeling 2025-06-25 20:26:38 +02:00
Nicolas Will
8e6031df14 Crypto: Fix further acronym casing and remove unused field 2025-06-25 20:25:33 +02:00
Nicolas Will
b8097501b6 Update cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-25 20:12:51 +02:00
Nicolas Will
14472bf744 Crypto: Refactor type name mapping and fix QL-for-QL alerts 2025-06-25 20:08:14 +02:00
REDMOND\brodes
8280cbcaa1 Crypto: Update JCA model to include new model.qll updates. 2025-06-25 13:55:47 -04:00
Paolo Tranquilli
6803bf3861 Merge pull request #19851 from github/redsun82/rust-emission-trait
Rust: refactor `pre_emit!` and `post_emit!` to a trait
2025-06-25 19:22:29 +02:00
REDMOND\brodes
f9147cfb2b Crypto: Remove experimental qll file 2025-06-25 12:26:41 -04:00
REDMOND\brodes
9cd2241bf6 Crypto: Remove accidentally uploaded temporary file. 2025-06-25 11:36:40 -04:00
REDMOND\brodes
072765abca Crypto: Code scanning warning corrections. 2025-06-25 11:16:49 -04:00
REDMOND\brodes
93bad3c799 Crypto: Misc bug fixes and updated expected files. 2025-06-25 11:02:30 -04:00
Nick Rolfe
867826466e Ruby/QL: unconditionally generate discard predicates 2025-06-25 15:35:58 +01:00
Nick Rolfe
57b866bbe1 Ruby/Rust/QL: move databaseMetadata to prefix.dbscheme
This has no effect on ruby.dbscheme, and adds the relation to
ql.dbscheme and rust.dbscheme. (The relation will be required for
overlay support).
2025-06-25 15:35:08 +01:00
Nick Rolfe
9021168725 QL: fix stats-collection workflow 2025-06-25 15:35:06 +01:00
Nora Dimitrijević
942cfc3bd6 Merge pull request #19842 from d10c/d10c/convert-java-tests-to-qlref
Java: convert remaining `java-code-scanning.qls` query tests to `.qlref`
2025-06-25 16:02:28 +02:00
Nicolas Will
710e08088f Crypto: Refactor casing and documentation 2025-06-25 15:29:03 +02:00
Geoffrey White
006f0e8fcf Merge branch 'main' into lifetime 2025-06-25 14:17:00 +01:00
Michael Nebel
bb85e24121 C#: Convert SQL injection test to use inline expectations. 2025-06-25 14:53:09 +02:00
Michael Nebel
af2ebed395 C#: Add stubs for Microsoft.Data.SqlClient. 2025-06-25 14:53:07 +02:00
Paolo Tranquilli
6a0140d3c9 Rust: fix Const test 2025-06-25 14:42:03 +02:00
Paolo Tranquilli
e4056c0a11 Rust: add change note 2025-06-25 14:39:22 +02:00
Paolo Tranquilli
78ecf1814e Rust: add upgrade/downgrade scripts 2025-06-25 14:36:29 +02:00
Paolo Tranquilli
5d3bdb955c Merge branch 'main' into redsun82/rust-item-reorg 2025-06-25 14:34:48 +02:00
Paolo Tranquilli
ab2e7082f3 Merge branch 'main' into redsun82/codegen-use-one-test-file 2025-06-25 14:31:17 +02:00
Paolo Tranquilli
b8b57365c3 Merge pull request #19876 from github/redsun82/rust-qltest-setup-nightly-toolchain
Rust: fix parallel execution of tests using the nightly toolchain
2025-06-25 14:30:22 +02:00
Paolo Tranquilli
fa006e3ea5 Rust: fix test 2025-06-25 14:17:14 +02:00
Paolo Tranquilli
1f66f902e5 Rust: fix parallel execution of tests using the nightly toolchain
Since we dropped checked in toolchain files for tests requiring nightly,
the `setup.sh` script was not doing its job of setting up the toolchains
and the `rust-src` component, occasionally leading to test failures.
2025-06-25 14:13:43 +02:00
Nick Rolfe
1e68a7e2de Merge pull request #19719 from github/nickrolfe/ruby-discard-predicates
Ruby: generate overlay discard predicates
2025-06-25 07:18:29 -04:00
Michael Nebel
92a1b8971c C#: Add Microsoft.Data.SqlClient to the list of stubs. 2025-06-25 12:52:58 +02:00
Nick Rolfe
a9ddf0026b Ruby: generate overlay discard predicates 2025-06-25 11:47:27 +01:00
Nick Rolfe
1bbba2f664 Merge pull request #19684 from github/nickrolfe/ruby-overlay-extraction
Ruby: add support for extracting overlay databases
2025-06-25 06:39:30 -04:00
Paolo Tranquilli
355fd85c23 Codegen: remove unneeded has|getNumberOf in instance tests 2025-06-25 12:02:13 +02:00
Paolo Tranquilli
99eaaaa830 Rust: fix QL compilation error 2025-06-25 11:50:49 +02:00
Nick Rolfe
c6ff07ad5a Merge branch 'main' into nickrolfe/ruby-overlay-extraction 2025-06-25 05:46:26 -04:00
Paolo Tranquilli
6bbf1e3bc1 Codegen: use one generated test file per directory
This collapses all generated test QL sources into a single one per
directory, using query predicates to run the different tests.

This should improve the time required to run generated tests.
2025-06-25 11:44:54 +02:00
Napalys Klicius
3d9e2f5438 Merge pull request #19858 from Napalys/js/execa
JS: moved `execa` out of experimental
2025-06-25 10:34:52 +02:00
Paolo Tranquilli
bcca47c873 Rust: make AssocItem and ExternItem subclasses of Item 2025-06-25 10:25:24 +02:00
Kasper Svendsen
46ac2fd9f0 Add CI workflow to check overlay annotations 2025-06-25 10:19:25 +02:00
Paolo Tranquilli
792ea10577 Merge branch 'redsun82/codegen-new-parent-child' into redsun82/rust-emission-trait 2025-06-25 10:19:10 +02:00
Kasper Svendsen
7186ea5975 Merge pull request #19871 from github/kaspersv/overlay-script-re
Use regex to match overlay annotations
2025-06-25 09:39:50 +02:00
Kasper Svendsen
869ba0d246 Use regex to match overlay annotations 2025-06-25 09:30:49 +02:00
Napalys Klicius
73126fef9e JS: update change note. 2025-06-25 09:26:26 +02:00
Jeroen Ketema
2f1cd388d1 C++: Update stats file after DCA and extractor changes 2025-06-25 09:21:56 +02:00
Asger F
d39b68cd41 Merge pull request #19849 from asgerf/js/remove-legacy-actions-queries
JS: Remove legacy actions queries
2025-06-25 09:18:33 +02:00
Asger F
853fc1a7cf Merge pull request #19852 from asgerf/js/react-use-server
JS: Model React 'use' and 'use server'
2025-06-25 09:13:56 +02:00
Jeroen Ketema
ddae47118b Merge pull request #16075 from jketema/explicit
C++: Handle explicitly instantiated templates
2025-06-25 08:53:50 +02:00
Jeroen Ketema
fff23040b3 C++: Update test results 2025-06-25 08:14:22 +02:00
REDMOND\brodes
bd0efbe48c Crypto: Overhaul of EVP final/init/update to now use a more general 'OperationStep' mechanic. 2025-06-24 16:03:25 -04:00
Chuan-kai Lin
9a064de86e Merge pull request #19865 from github/cklin/pick-kotlin-version
pick-kotlin-version.py: tolerate warnings
2025-06-24 10:21:13 -07:00
Eric Bickle
b8f8501cf5 Merge pull request #1 from geoffw0/sql
C++: Fix for the SQL query.
2025-06-24 10:13:52 -07:00
Aditya Sharad
1c567b9b71 Merge pull request #19867 from adityasharad/qldoc/opcode-script-regex
QLDoc scripts: Fix overly permissive regex ranges
2025-06-24 10:11:08 -07:00
Aditya Sharad
a79e3cf604 QLDoc scripts: Fix overly permissive regex ranges
The range `A-aa-z` was too permissive and
includes special characters between `Z` and `a`.
Low impact, but fix to address an internally
reported code scanning alert.
2025-06-24 10:00:29 -07:00
Nora Dimitrijević
690446149a Java: add CleartextStorageCookie test
Given that it's a non-path-problem dataflow query, the InlineExpectationsTest is not as useful.
2025-06-24 18:12:19 +02:00
Paolo Tranquilli
1dcd60527c Codegen: improve implementation of generated parent/child relationship
This improves the implementation of the generated parent/child
relationship by adding a new `all_children` field to `ql.Class` which
lists all children (both direct and inherited) of a class, carefully
avoiding duplicating children in case of diamond inheritance. This:
* simplifies the generated code,
* avoid children ambiguities in case of diamond inheritance.

This only comes with some changes in the order of children in the
generated tests (we were previously sorting bases alphabetically there).
For the rest this should be a non-functional change.
2025-06-24 17:26:24 +02:00
Jeroen Ketema
9a83005730 Merge pull request #19862 from jketema/complex
C++: Support more complex 16-bit float types
2025-06-24 17:26:07 +02:00
Chuan-kai Lin
565627847f pick-kotlin-version.py: tolerate warnings
This commit changes pick-kotlin-version.py to use re.search() instead of
re.match(), so that it can better cope with warning messages.
2025-06-24 08:13:43 -07:00
Jeroen Ketema
8f249c77bc C++: Support more complex 16-bit float types 2025-06-24 16:56:34 +02:00
Nora Dimitrijević
a49999dd5d PolynomialReDoS: disable diff-informed support
This is because it was failing the diff-informed consistency check, and like other ReDoS queries (Python?) the query tries to be helpful by showing a substring of a regex, which has a `hasLocation(...)` (intensional) but no corresponding `getLocation()` (extensional). Until the location overrides get updated to support `hasLocation`-based locations, it's probably best to turn off diff-informed support.
2025-06-24 16:42:41 +02:00
Nora Dimitrijević
b2cb585bf2 UnsafeDeserialization: add missing getASelectedSinkLocation override
This fixes the failing diff-informed consistency check.
2025-06-24 16:42:39 +02:00
Nora Dimitrijević
e213e3fc37 Java: convert ImplicitPendingIntents test to .qlref 2025-06-24 16:42:37 +02:00
Nora Dimitrijević
e0311e26c6 Java: convert ImproperIntentVerification test to .qlref
It's a non-path query, so the InlineExpectationsTest postprocessor doesn't do anything.
2025-06-24 16:42:35 +02:00
Nora Dimitrijević
aac4f63e9a Java: convert RequestForgery test to .qlref 2025-06-24 16:42:32 +02:00
Nora Dimitrijević
7f05b72e10 Java: convert OgnlInjection test to .qlref 2025-06-24 16:42:30 +02:00
Nora Dimitrijević
cadfd0dcaa Java: convert RsaWithoutOaep test to .qlref 2025-06-24 16:42:28 +02:00
Nora Dimitrijević
b7e47e2cf3 Java: convert PolynomialReDoS and RegexInjection tests to .qlref
Leaves ReDoS.ql unmodified since it's not a dataflow query; just moves it to its own directory.
2025-06-24 16:42:26 +02:00
Nora Dimitrijević
f5c7ef6ab4 Java: convert XPathInjection test to .qlref 2025-06-24 16:42:23 +02:00
Nora Dimitrijević
162b1c51a9 Java: convert XXE test to .qlref 2025-06-24 16:42:21 +02:00
Nora Dimitrijević
7f33f57c9b Java: convert UrlForward test to .qlref 2025-06-24 16:42:19 +02:00
Nora Dimitrijević
bf1a699982 Java: convert CWE-522 tests to .qlref 2025-06-24 16:42:17 +02:00
Nora Dimitrijević
4412335223 Java: convert UnsafeDeserialization test to .qlref 2025-06-24 16:42:14 +02:00
Nora Dimitrijević
c4b0955045 Java: convert WebviewDebuggingEnabled test to .qlref 2025-06-24 16:42:12 +02:00
Nora Dimitrijević
192f45ed2b Java: convert FragmentInjection test to .qlref 2025-06-24 16:42:10 +02:00
Nora Dimitrijević
2b19cbcd7e Java: convert UnsafeContentUriResolution test to .qlref 2025-06-24 16:42:08 +02:00
Nora Dimitrijević
28694276e2 Java: convert MissingJWTSignatureCheck test to .qlref 2025-06-24 16:42:06 +02:00
Nora Dimitrijević
85c2f72892 Java: convert InsecureRandomness test to .qlref 2025-06-24 16:42:04 +02:00
Nora Dimitrijević
288a938814 Java: convert InsufficientKeySize test to .qlref 2025-06-24 16:42:02 +02:00
Nora Dimitrijević
993b261b63 Java: convert InsecureTrustManager test to .qlref 2025-06-24 16:42:00 +02:00
Nora Dimitrijević
b736e3733c Java: convert IntentUriPermissionManipulation test to .qlref 2025-06-24 16:41:58 +02:00
Nora Dimitrijević
c77875d834 Java: convert TemplateInjection test to .qlref 2025-06-24 16:41:56 +02:00
Nora Dimitrijević
b8c7bd29c3 Java: convert SpelInjection test to .qlref 2025-06-24 16:41:54 +02:00
Nora Dimitrijević
2a837b208b Java: convert MvelInjection test to .qlref 2025-06-24 16:41:52 +02:00
Nora Dimitrijević
1b61cb660a Java: convert JexlInjection test to .qlref 2025-06-24 16:41:50 +02:00
Nora Dimitrijević
1cc91e964d Java: convert GroovyInjection test to .qlref 2025-06-24 16:41:48 +02:00
Nora Dimitrijević
8e53da285f Java: convert XSS test to .qlref 2025-06-24 16:41:46 +02:00
Nora Dimitrijević
199eabdd20 Java: convert XsltInjection test to .qlref
Also, split off into separate directory from JndiInjectionTest because their $Alerts were interfering with each other.
2025-06-24 16:41:43 +02:00
Nora Dimitrijević
3f9e0fee81 Java: convert JndiInjection test to .qlref 2025-06-24 16:41:41 +02:00
Nora Dimitrijević
e1ddce8456 Java: convert PartialPathTraversalFromRemote test to .qlref 2025-06-24 16:41:39 +02:00
Nora Dimitrijević
588efe4b2b Java: Convert TaintedPath test to .qlref 2025-06-24 16:41:35 +02:00
Nora Dimitrijević
c4a385fa6a Merge pull request #19817 from d10c/d10c/convert-tests-to-qlref
Convert remaining `{go,swift,ruby}-code-scanning.qls` query tests to `.qlref`
2025-06-24 16:31:13 +02:00
Arthur Baars
afcd8c3047 Merge pull request #19864 from github/post-release-prep/codeql-cli-2.22.1
Post-release preparation for codeql-cli-2.22.1
2025-06-24 15:45:21 +02:00
Nora Dimitrijević
35a48e7f41 Swift: convert XXE test to .qlref 2025-06-24 14:58:16 +02:00
Nora Dimitrijević
aa3e0116c1 Swift: convert PathInjection test to .qlref 2025-06-24 14:58:12 +02:00
Nora Dimitrijević
895a8fcb0f Swift: convert CleartextLogging test to .qlref 2025-06-24 14:58:08 +02:00
Nora Dimitrijević
7615ec7a24 Swift: convert PredicateInjection test to .qlref 2025-06-24 14:58:03 +02:00
Nora Dimitrijević
92a48cdc2b Ruby: convert InsecureDownload test to .qlref 2025-06-24 14:57:59 +02:00
Nora Dimitrijević
e32982057c Ruby: convert CommandInjection test to .qlref 2025-06-24 14:57:54 +02:00
Nora Dimitrijević
807c7691c6 Ruby: add PrettyPrintModels test postprocessor 2025-06-24 14:57:49 +02:00
Nora Dimitrijević
cf92b0e91b Go: convert IncorrectIntegerConversion test to .qlref 2025-06-24 14:57:48 +02:00
Nora Dimitrijević
76a3306c63 Go: convert UncontrolledAllocationSize test to .qlref 2025-06-24 14:57:44 +02:00
github-actions[bot]
6972c7a872 Post-release preparation for codeql-cli-2.22.1 2025-06-24 12:55:14 +00:00
Geoffrey White
e37979546c Merge pull request #19754 from geoffw0/typeinfer
Rust: Type inference for `for` loops and array expressions
2025-06-24 13:19:37 +01:00
Kasper Svendsen
c380c5f150 Merge pull request #19863 from github/kaspersv/ql4ql-overlay-caller-q
QL4QL: Extend ql/inline-overlay-caller
2025-06-24 13:15:34 +02:00
Napalys Klicius
79a9d7def8 JS: removed execa parts from SystemCommandExecutors and moved it to Execa.qll 2025-06-24 12:41:22 +02:00
Geoffrey White
869c974745 Rust: Change note. 2025-06-24 11:34:54 +01:00
Geoffrey White
96dcdf94af Rust: Change note. 2025-06-24 11:31:38 +01:00
Geoffrey White
21bea7e403 Merge branch 'main' into typeinfer 2025-06-24 11:23:34 +01:00
Paolo Tranquilli
d7f14600b3 Merge pull request #19853 from github/redsun82/rust-enable-change-note-check
Rust: enable change-note check
2025-06-24 12:15:08 +02:00
Kasper Svendsen
e1fc138670 QL4QL: Extend ql/inline-overlay-caller 2025-06-24 11:58:31 +02:00
Napalys Klicius
0902ca0605 JS: address copilot suggestions 2025-06-24 11:37:07 +02:00
Asger F
54bfde9b7a Update javascript/ql/src/change-notes/2025-06-23-remove-legacy-actions-queries.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-24 11:22:37 +02:00
Asger F
bae3e255e4 Merge pull request #19859 from asgerf/js/remote-element-from-docs
JS: Remote mention of Element MaD token
2025-06-24 11:22:24 +02:00
Geoffrey White
6677a81e1c Merge branch 'main' into lifetime 2025-06-24 10:11:21 +01:00
Paolo Tranquilli
d0c7550119 Rust: refactor pre_emit! and post_emit! to a trait 2025-06-24 10:40:33 +02:00
Kasper Svendsen
0ee6a78a4a Java: Allow methods with empty bodies for overlay 2025-06-24 10:38:07 +02:00
Kasper Svendsen
6e92d7e247 Java: Add entity discard predicates 2025-06-24 10:38:06 +02:00
Kasper Svendsen
b6e56f26c7 Java: Add manual overlay annotations 2025-06-24 10:38:05 +02:00
Geoffrey White
c2b317783f C++: Fix for SQL query. 2025-06-24 09:29:46 +01:00
Kasper Svendsen
81b677a2d9 rename overlay[caller] to overlay[caller?] 2025-06-24 10:25:07 +02:00
Kasper Svendsen
2da8d61984 Run config/sync-files.py 2025-06-24 10:25:06 +02:00
Kasper Svendsen
c207cfdeb7 Overlay: Add overlay annotations to Java & shared libraries 2025-06-24 10:25:06 +02:00
Asger F
cb983102e5 JS: Remote mention of Element MaD token 2025-06-24 09:46:20 +02:00
Napalys Klicius
8c345461f0 JS: add change note 2025-06-24 09:08:15 +02:00
Napalys Klicius
d05de1ba4e JS: moved execa test cases outside experimental 2025-06-24 09:08:13 +02:00
Napalys Klicius
d8b5cb5862 JS: moved execa out of experimental 2025-06-24 09:07:43 +02:00
Geoffrey White
11ffb1f86f Merge branch 'main' into lifetime 2025-06-23 17:06:06 +01:00
Paolo Tranquilli
8d4e36f869 Rust: enable change-note check 2025-06-23 17:53:53 +02:00
Geoffrey White
b82a7ab745 Rust: Update variable name in examples. 2025-06-23 16:42:02 +01:00
Asger F
ea0a80a06a JS: Un-deprecate Actions.qll for now as we have some internal queries that use it. 2025-06-23 16:38:04 +02:00
Asger F
4fc5738ded JS: Change note 2025-06-23 16:08:21 +02:00
Asger F
61887beae0 JS: Add test case for false positive 2025-06-23 16:03:41 +02:00
Asger F
cc1a28ac7e JS: Add parameters of server functions as remote flow sources 2025-06-23 16:03:39 +02:00
Asger F
d9f4e4a90d JS: Add tests for functions with "use server" directive 2025-06-23 16:03:38 +02:00
Asger F
7dd7246cd4 JS: Update tests.expected
Mostly noise due to renamed predicates and reordered result sets
2025-06-23 16:03:35 +02:00
Asger F
180b023c7c JS: Add inline expectations to React test 2025-06-23 16:03:33 +02:00
Asger F
1787d4dce8 JS: Enable inline expectations in test
Will update files in next commit
2025-06-23 16:03:32 +02:00
Asger F
1a18e68364 JS: Remove reactLibraryRef
This is not testing anything interesting, and is noisy when adding inline expectations
2025-06-23 16:03:30 +02:00
Asger F
99fb6b62ad JS: Remove test_ prefix from query predicates 2025-06-23 16:03:29 +02:00
Asger F
8ff7182f3a JS: Move React test predicates into one file 2025-06-23 15:37:15 +02:00
Asger F
980d0f46fa JS: Add model for react 'use' 2025-06-23 15:27:21 +02:00
Asger F
768ccc6a54 JS: Add test for react 'use' function 2025-06-23 15:26:08 +02:00
Geoffrey White
530ded18e4 Merge branch 'main' into typeinfer 2025-06-23 14:02:58 +01:00
Asger F
7da2d71a70 JS: Update query suite expectations 2025-06-23 14:57:23 +02:00
Nick Rolfe
45f089fda0 Ruby: skip non-existent files in overlay changes JSON
The previous implementation returned None if any of the paths in the
changes JSON couldn't be canonicalized. This could happen for files that
were deleted in the diff. Now, it just ignores paths for which
canonicalize() fails.
2025-06-23 13:53:18 +01:00
Asger F
b1da23968c JS: Change note 2025-06-23 14:50:09 +02:00
Asger F
76b7228160 JS: Remove js/actions/command-injection
Superseded by actions/command-injection/{medium,critical}
2025-06-23 14:41:26 +02:00
Asger F
9dcb61e771 JS: Remove js/actions/actions-artifact-leak
Superseded by actions/secrets-in-artifacts
2025-06-23 14:39:28 +02:00
Asger F
3a00e8d1c5 JS: Remove js/actions/pull-request-target
Superseded by actions/untrusted-checkout/{medium,high,critical}
2025-06-23 14:37:21 +02:00
Asger F
0d3bb89195 JS: Deprecate Actions.qll 2025-06-23 14:36:15 +02:00
Geoffrey White
4530e85c93 Rust: Repair the test annotations. 2025-06-23 13:12:53 +01:00
Geoffrey White
8c848ac019 Rust: Effects of rustfmt on .expected. 2025-06-23 13:08:42 +01:00
Geoffrey White
d02a7288ff Update rust/ql/lib/codeql/rust/internal/TypeInference.qll
Co-authored-by: Simon Friis Vindum <paldepind@github.com>
2025-06-23 13:04:56 +01:00
Geoffrey White
34cd9766d5 Rust: Run rustfmt --edition 2024 on the test. 2025-06-23 12:59:42 +01:00
Geoffrey White
bfaabab929 Rust: Update more expectations. 2025-06-23 12:58:35 +01:00
Tamas Vajk
60e726bdf2 Java: Add java/javautilconcurrentscheduledthreadpoolexecutor query for zero thread pool size 2025-06-23 12:52:45 +02:00
Eric Bickle
32464a8995 C++: Support SQL Injection sinks for Oracle Call Interface (OCI) 2025-06-20 06:05:24 -07:00
Nick Rolfe
665df4baef Ruby: add minimal path transformer support
Supports only a minimal subset of the project layout specification;
enough to work with the transformers produced by the CLI when building
an overlay database.
2025-06-19 16:34:16 +01:00
Nick Rolfe
1bd7c4f11c Ruby: add databaseMetadata relation to dbscheme
This is required for overlay support.
2025-06-19 16:34:15 +01:00
Nick Rolfe
c4ccc5502d Ruby: add support for extracting overlays 2025-06-19 16:34:14 +01:00
Geoffrey White
7a25596749 Merge branch 'main' into typeinfer 2025-06-19 14:27:35 +01:00
Geoffrey White
26e7b2d5f8 Rust: Accept path resolution consistency changes. 2025-06-19 14:19:13 +01:00
Geoffrey White
d55e8b7010 Rust: Add another test case for ranges. 2025-06-19 13:45:54 +01:00
Geoffrey White
7170e97e22 Rust: Update test expectations format (type=...). 2025-06-19 13:09:28 +01:00
Geoffrey White
f670fcb301 Rust: Add a Vec test case that we actually get (explicit type). 2025-06-19 11:28:17 +01:00
Geoffrey White
1622d08624 Rust: Add inferArrayExprType. 2025-06-19 11:21:37 +01:00
Geoffrey White
639f85a556 Merge branch 'main' into typeinfer 2025-06-19 11:15:52 +01:00
Geoffrey White
36cf4b613e Rust: Accept consistency changes. 2025-06-18 17:32:20 +01:00
REDMOND\brodes
8ee03e48ca Crypto: Fix cpp-specific code scanning alert failure 2025-06-18 11:04:27 -04:00
Geoffrey White
5edd6e85e7 Rust: Restrict results to 'unsafe' blocks. 2025-06-18 15:45:31 +01:00
Geoffrey White
dbde8418bb Rust: Another test case (unsafe function). 2025-06-18 15:29:37 +01:00
Geoffrey White
79cedc2586 Rust: Rename predicate again. 2025-06-18 11:56:04 +01:00
Geoffrey White
5bf799e717 Apply suggestions from code review
Co-authored-by: Simon Friis Vindum <paldepind@github.com>
2025-06-18 11:52:02 +01:00
Geoffrey White
df221ea8f8 Rust: Remove excess 'cached' annotation. 2025-06-17 23:17:58 +01:00
Geoffrey White
dec0deb4d1 Rust: Add some more test cases for type inference on Vecs. 2025-06-17 23:07:32 +01:00
Geoffrey White
4292b03b5c Rust: Add logic for Vecs and slices. 2025-06-17 10:58:26 +01:00
Geoffrey White
66d6770c3f Rust: If we're inferring both ways, it should really be to any element. 2025-06-17 10:47:35 +01:00
Geoffrey White
69da4e7462 Rust: Move inferArrayExprType logic into typeEquality predicate. 2025-06-17 10:45:57 +01:00
Vasco-jofra
e2eca5bbff Update test.expected 2025-06-15 12:12:12 +02:00
Vasco-jofra
6920430073 Improve dependency injection through import function calls 2025-06-15 00:47:34 +02:00
Vasco-jofra
9019879d99 Improve useFactory inter file function detection 2025-06-15 00:32:26 +02:00
Vasco-jofra
477f32c7ff NestJS dependency injection support useValue provider 2025-06-15 00:21:38 +02:00
Vasco-jofra
2b143c86ac NestJS dependency Injection support useFactory provider 2025-06-15 00:09:07 +02:00
Vasco-jofra
baf0d3ef22 Model NestJS middlewares as sources 2025-06-14 23:27:49 +02:00
Vasco-jofra
ddf77a0b72 Remove unnecessary spaces 2025-06-13 15:37:27 +02:00
Vasco-jofra
4ea53773b9 Model the TypeORM Repository API 2025-06-13 15:35:46 +02:00
Geoffrey White
14b75a968b Apply suggestions from code review
Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com>
2025-06-13 14:09:49 +01:00
Geoffrey White
6194676b7d Rust: Accept consistency failures (for now). 2025-06-13 13:51:01 +01:00
Geoffrey White
62e3cc57c6 Merge branch 'main' into typeinfer 2025-06-13 13:45:19 +01:00
Geoffrey White
b89d6d3402 Rust: Implement type inference for ArrayRepeatExprs. 2025-06-13 12:58:51 +01:00
Geoffrey White
51343a5c03 Rust: Implement type inference for ArrayListExprs. 2025-06-13 12:58:49 +01:00
Geoffrey White
f76b56291b Rust: Implement type inference for 'for' loops on arrays. 2025-06-13 12:58:47 +01:00
Geoffrey White
840ef5ce92 Rust: Add test cases for type inference in loops. 2025-06-13 12:37:32 +01:00
Geoffrey White
087e666658 Rust: Exclude sources in macro expansions. 2025-06-11 18:48:23 +01:00
Geoffrey White
168246005c Rust: Extend tests based on cases found in DCA. 2025-06-11 18:33:59 +01:00
Geoffrey White
b29deed919 Rust: Accept changes in an unrelated test reported by CI. 2025-06-11 18:09:22 +01:00
Geoffrey White
ecac0dbe69 Rust: Accept consistency check failures. 2025-06-11 08:52:52 +01:00
Geoffrey White
a9d5d8b2b3 Rust: Accept the new alert message in tests. 2025-06-09 19:14:14 +01:00
Geoffrey White
74ce4e8105 Update rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-09 19:10:39 +01:00
Geoffrey White
e7945e16cb Rust: Accept the query in suite listings. 2025-06-09 19:06:34 +01:00
Geoffrey White
9b0ee8fb9f Rust: Add security-severity tag and reduce precision to medium for now.
precis
2025-06-09 17:58:44 +01:00
Geoffrey White
b3330b5636 Rust: Allow parameter accesses as sources. 2025-06-09 17:58:42 +01:00
Geoffrey White
d3d0a533b5 Rust: Add test showing yet another spurious result. 2025-06-09 17:58:41 +01:00
Geoffrey White
858eec390d Rust: Exclude results where the source is a reference. 2025-06-09 17:58:40 +01:00
Geoffrey White
7bae451af3 Rust: Exclude results in macro invocations. 2025-06-09 17:58:38 +01:00
Geoffrey White
26f85585fd Rust: Add qhelp, examples, and examples as tests. 2025-06-09 17:58:37 +01:00
Geoffrey White
fe20fb403d Rust: More robust fix for closures. 2025-06-09 16:41:31 +01:00
Geoffrey White
21b4baeb42 Rust: Have the alert message cite the variable, so it's easier to understand whether the alert is correct. 2025-06-09 10:28:25 +01:00
Geoffrey White
79f8584efb Rust: Fix spurious results involving closures. 2025-06-09 10:25:48 +01:00
Geoffrey White
bf4ea02dd2 Rust: Implement the query. 2025-06-09 10:25:40 +01:00
Mrigank Pawagi
114b46824a update test 2025-06-05 15:56:35 +00:00
Geoffrey White
526620ca41 Rust: Add some helper predicates for finding enclosing blocks. 2025-06-05 16:30:28 +01:00
Geoffrey White
96dc34e36d Rust: Even more test cases (inspired by real world results). 2025-06-05 16:29:58 +01:00
Mrigank Pawagi
93c485fb13 apply proper formatting in comment 2025-06-05 13:26:38 +00:00
Owen Mansel-Chan
ecd0291b6a Add change note for deprecation 2025-06-05 11:00:00 +01:00
Owen Mansel-Chan
75d9b298b2 Test helper predicates for TypeSpec 2025-06-05 10:52:01 +01:00
Owen Mansel-Chan
c4a8ac4980 Add helper predicates for TypeSpec 2025-06-05 10:51:39 +01:00
Mrigank Pawagi
434973f8e6 Update GlobalUseBeforeInit.ql 2025-06-05 09:48:37 +00:00
Geoffrey White
66c1e2cace Rust: Add test cases for implicit dereferences and more pointer/enum mixes (inspired by early real world results). 2025-06-05 10:37:30 +01:00
Geoffrey White
e2fb1d3892 Rust: Add test cases involving lifetimes + lifetime annotations. 2025-06-05 10:37:29 +01:00
Geoffrey White
ae19ecc674 Rust: Add test cases involving lifetimes + closures and async blocks. 2025-06-05 10:37:28 +01:00
Geoffrey White
43cb98ad15 Rust: Fix some warnings in the existing test. 2025-06-05 10:37:27 +01:00
Geoffrey White
8e8374b9bc Rust: Label source annotations in the test properly. 2025-06-05 10:37:26 +01:00
Geoffrey White
da4fbfb449 Rust: Placeholder new query. 2025-06-05 10:37:25 +01:00
Owen Mansel-Chan
8b9cc99158 Test helper predicates for FieldDecl 2025-06-05 10:35:34 +01:00
Owen Mansel-Chan
d9bc165c72 Add helper predicates for FieldDecl 2025-06-05 10:35:25 +01:00
Owen Mansel-Chan
82e8d3af8d Improve two class names 2025-06-05 10:34:53 +01:00
Owen Mansel-Chan
b2f310cda7 Add change note 2025-06-03 15:36:03 +01:00
Owen Mansel-Chan
4711feb344 Add test for DefinedType.getBaseType 2025-06-03 14:50:05 +01:00
Owen Mansel-Chan
40000840c1 Fix definition of DefinedType.getBaseType 2025-06-03 14:50:03 +01:00
Owen Mansel-Chan
681f9af710 Fix MethodTypes test 2025-06-03 14:50:00 +01:00
Joe Farebrother
38072c7863 Fix typo
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-02 16:42:27 +01:00
Joe Farebrother
57a0c7a1ab Performance fix - Use basic blocks instead of full cfg reachability. 2025-06-02 14:33:52 +01:00
3053 changed files with 191908 additions and 85522 deletions

4
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,4 @@
When reviewing code:
* do not review changes in files with `.expected` extension (they are automatically ensured to be correct).
* in `.ql` and `.qll` files, do not try to review the code itself as you don't understand the programming language
well enough to make comments in these languages. You can still check for typos or comment improvements.

View File

@@ -16,7 +16,6 @@ on:
- "shared/**/*.qll"
- "!**/experimental/**"
- "!ql/**"
- "!rust/**"
- ".github/workflows/check-change-note.yml"
jobs:

View File

@@ -0,0 +1,23 @@
name: Check overlay annotations
on:
push:
branches:
- main
- 'rc/*'
pull_request:
branches:
- main
- 'rc/*'
permissions:
contents: read
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check overlay annotations
run: python config/add-overlay-annotations.py --check java

View File

@@ -53,7 +53,7 @@ jobs:
- name: Create database
run: |
"${CODEQL}" database create \
--search-path "${{ github.workspace }}"
--search-path "${{ github.workspace }}" \
--threads 4 \
--language ql --source-root "${{ github.workspace }}/repo" \
"${{ runner.temp }}/database"

1
Cargo.lock generated
View File

@@ -419,6 +419,7 @@ dependencies = [
"lazy_static",
"rayon",
"regex",
"serde_json",
"tracing",
"tracing-subscriber",
"tree-sitter",

View File

@@ -1,5 +1,5 @@
name: codeql/actions-all
version: 0.4.12
version: 0.4.13-dev
library: true
warnOnImplicitThis: true
dependencies:

View File

@@ -1,6 +1,4 @@
# Environment Path Injection
## Description
## Overview
GitHub Actions allow to define the system PATH variable by writing to a file pointed to by the `GITHUB_PATH` environment variable. Writing to this file appends a directory to the system PATH variable and automatically makes it available to all subsequent actions in the current job.
@@ -12,11 +10,11 @@ echo "$HOME/.local/bin" >> $GITHUB_PATH
If an attacker can control the contents of the system PATH, they are able to influence what commands are run in subsequent steps of the same job.
## Recommendations
## Recommendation
Do not allow untrusted data to influence the system PATH: Avoid using untrusted data sources (e.g., artifact content) to define the system PATH.
## Examples
## Example
### Incorrect Usage
@@ -36,4 +34,4 @@ If an attacker can manipulate the value being set, such as through artifact down
## References
- [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions)
- GitHub Docs: [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions).

View File

@@ -1,6 +1,4 @@
# Environment Path Injection
## Description
## Overview
GitHub Actions allow to define the system PATH variable by writing to a file pointed to by the `GITHUB_PATH` environment variable. Writing to this file appends a directory to the system PATH variable and automatically makes it available to all subsequent actions in the current job.
@@ -12,11 +10,11 @@ echo "$HOME/.local/bin" >> $GITHUB_PATH
If an attacker can control the contents of the system PATH, they are able to influence what commands are run in subsequent steps of the same job.
## Recommendations
## Recommendation
Do not allow untrusted data to influence the system PATH: Avoid using untrusted data sources (e.g., artifact content) to define the system PATH.
## Examples
## Example
### Incorrect Usage
@@ -36,4 +34,4 @@ If an attacker can manipulate the value being set, such as through artifact down
## References
- [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions)
- GitHub Docs: [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions).

View File

@@ -1,6 +1,4 @@
# Environment Variable Injection
## Description
## Overview
GitHub Actions allow to define environment variables by writing to a file pointed to by the `GITHUB_ENV` environment variable:
@@ -37,7 +35,7 @@ steps:
If an attacker can control the values assigned to environment variables and there is no sanitization in place, the attacker will be able to inject additional variables by injecting new lines or `{delimiters}`.
## Recommendations
## Recommendation
1. **Do not allow untrusted data to influence environment variables**:
@@ -64,7 +62,7 @@ If an attacker can control the values assigned to environment variables and ther
} >> "$GITHUB_ENV"
```
## Examples
## Example
### Example of Vulnerability
@@ -113,5 +111,5 @@ An attacker is be able to run arbitrary code by injecting environment variables
## References
- [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions)
- [GitHub Actions Exploitation: Repo Jacking and Environment Manipulation](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation)
- GitHub Docs: [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions).
- Synacktiv: [GitHub Actions Exploitation: Repo Jacking and Environment Manipulation](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation).

View File

@@ -1,6 +1,4 @@
# Environment Variable Injection
## Description
## Overview
GitHub Actions allow to define environment variables by writing to a file pointed to by the `GITHUB_ENV` environment variable:
@@ -37,7 +35,7 @@ steps:
If an attacker can control the values assigned to environment variables and there is no sanitization in place, the attacker will be able to inject additional variables by injecting new lines or `{delimiters}`.
## Recommendations
## Recommendation
1. **Do not allow untrusted data to influence environment variables**:
@@ -64,7 +62,7 @@ If an attacker can control the values assigned to environment variables and ther
} >> "$GITHUB_ENV"
```
## Examples
## Example
### Example of Vulnerability
@@ -113,5 +111,5 @@ An attacker would be able to run arbitrary code by injecting environment variabl
## References
- [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions)
- [GitHub Actions Exploitation: Repo Jacking and Environment Manipulation](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation)
- GitHub Docs: [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions).
- Synacktiv: [GitHub Actions Exploitation: Repo Jacking and Environment Manipulation](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation).

View File

@@ -1,18 +1,16 @@
# Code Injection in GitHub Actions
## Description
## Overview
Using user-controlled input in GitHub Actions may lead to code injection in contexts like _run:_ or _script:_.
Code injection in GitHub Actions may allow an attacker to exfiltrate any secrets used in the workflow and the temporary GitHub repository authorization token. The token may have write access to the repository, allowing an attacker to make changes to the repository.
## Recommendations
## Recommendation
The best practice to avoid code injection vulnerabilities in GitHub workflows is to set the untrusted input value of the expression to an intermediate environment variable and then use the environment variable using the native syntax of the shell/script interpreter (that is, not _${{ env.VAR }}_).
It is also recommended to limit the permissions of any tokens used by a workflow such as the GITHUB_TOKEN.
## Examples
## Example
### Incorrect Usage

View File

@@ -1,18 +1,16 @@
# Code Injection in GitHub Actions
## Description
## Overview
Using user-controlled input in GitHub Actions may lead to code injection in contexts like _run:_ or _script:_.
Code injection in GitHub Actions may allow an attacker to exfiltrate any secrets used in the workflow and the temporary GitHub repository authorization token. The token may have write access to the repository, allowing an attacker to make changes to the repository.
## Recommendations
## Recommendation
The best practice to avoid code injection vulnerabilities in GitHub workflows is to set the untrusted input value of the expression to an intermediate environment variable and then use the environment variable using the native syntax of the shell/script interpreter (that is, not _${{ env.VAR }}_).
It is also recommended to limit the permissions of any tokens used by a workflow such as the GITHUB_TOKEN.
## Examples
## Example
### Incorrect Usage

View File

@@ -1,13 +1,11 @@
# Use of Actions with known vulnerabilities
## Description
## Overview
The security of the workflow and the repository could be compromised by GitHub Actions workflows that utilize GitHub Actions with known vulnerabilities.
## Recommendations
## Recommendation
Either remove the component from the workflow or upgrade it to a version that is not vulnerable.
## References
- [GitHub Docs: Keeping your actions up to date with Dependabot](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot)
- GitHub Docs: [Keeping your actions up to date with Dependabot](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot).

View File

@@ -1,12 +1,21 @@
# Actions Job and Workflow Permissions are not set
## Description
## Overview
If a GitHub Actions job or workflow has no explicit permissions set, then the repository permissions are used. Repositories created under organizations inherit the organization permissions. The organizations or repositories created before February 2023 have the default permissions set to read-write. Often these permissions do not adhere to the principle of least privilege and can be reduced to read-only, leaving the `write` permission only to a specific types as `issues: write` or `pull-requests: write`.
## Recommendations
## Recommendation
Add the `permissions` key to the job or the root of workflow (in this case it is applied to all jobs in the workflow that do not have their own `permissions` key) and assign the least privileges required to complete the task:
Add the `permissions` key to the job or the root of workflow (in this case it is applied to all jobs in the workflow that do not have their own `permissions` key) and assign the least privileges required to complete the task.
## Example
### Incorrect Usage
```yaml
name: "My workflow"
# No permissions block
```
### Correct Usage
```yaml
name: "My workflow"
@@ -27,4 +36,4 @@ jobs:
## References
- [Assigning permissions to jobs](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/assigning-permissions-to-jobs)
- GitHub Docs: [Assigning permissions to jobs](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/assigning-permissions-to-jobs).

View File

@@ -1,14 +1,12 @@
# Improper Access Control
## Description
## Overview
Sometimes labels are used to approve GitHub Actions. An authorization check may not be properly implemented, allowing an attacker to mutate the code after it has been reviewed and approved by label.
## Recommendations
## Recommendation
When using labels, make sure that the code cannot be modified after it has been reviewed and the label has been set.
## Examples
## Example
### Incorrect Usage
@@ -57,4 +55,4 @@ jobs:
## References
- [Events that trigger workflows](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target)
- GitHub Docs: [Events that trigger workflows](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target).

View File

@@ -1,14 +1,12 @@
# Excessive Secrets Exposure
## Description
## Overview
When the workflow runner cannot determine what secrets are needed to run the workflow, it will pass all the available secrets to the runner including organization and repository secrets. This violates the least privileged principle and increases the impact of a potential vulnerability affecting the workflow.
## Recommendations
## Recommendation
Only pass those secrets that are needed by the workflow. Avoid using expressions such as `toJSON(secrets)` or dynamically accessed secrets such as `secrets[format('GH_PAT_%s', matrix.env)]` since the workflow will need to receive all secrets to decide at runtime which one needs to be used.
## Examples
## Example
### Incorrect Usage
@@ -48,5 +46,5 @@ env:
## References
- [Using secrets in GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#using-encrypted-secrets-in-a-workflow)
- [Job uses all secrets](https://github.com/boostsecurityio/poutine/blob/main/docs/content/en/rules/job_all_secrets.md)
- GitHub Docs: [Using secrets in GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#using-encrypted-secrets-in-a-workflow).
- poutine: [Job uses all secrets](https://github.com/boostsecurityio/poutine/blob/main/docs/content/en/rules/job_all_secrets.md).

View File

@@ -1,6 +1,4 @@
# Storage of sensitive information in GitHub Actions artifact
## Description
## Overview
Sensitive information included in a GitHub Actions artifact can allow an attacker to access the sensitive information if the artifact is published.
@@ -10,6 +8,8 @@ Only store information that is meant to be publicly available in a GitHub Action
## Example
### Incorrect Usage
The following example uses `actions/checkout` to checkout code which stores the GITHUB_TOKEN in the \`.git/config\` file and then stores the contents of the \`.git\` repository into the artifact:
```yaml
@@ -28,6 +28,8 @@ jobs:
path: .
```
### Correct Usage
The issue has been fixed below, where the `actions/upload-artifact` uses a version (v4+) which does not include hidden files or directories into the artifact.
```yaml

View File

@@ -1,14 +1,12 @@
# Unmasked Secret Exposure
## Description
## Overview
Secrets derived from other secrets are not known to the workflow runner, and therefore are not masked unless explicitly registered.
## Recommendations
## Recommendation
Avoid defining non-plain secrets. For example, do not define a new secret containing a JSON object and then read properties out of it from the workflow, since these read values will not be masked by the workflow runner.
## Examples
## Example
### Incorrect Usage
@@ -34,4 +32,4 @@ Avoid defining non-plain secrets. For example, do not define a new secret contai
## References
- [Using secrets in GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#using-encrypted-secrets-in-a-workflow)
- GitHub Docs: [Using secrets in GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#using-encrypted-secrets-in-a-workflow).

View File

@@ -1,6 +1,4 @@
# Cache Poisoning in GitHub Actions
## Description
## Overview
GitHub Actions cache poisoning is a technique that allows an attacker to inject malicious content into the Action's cache from unprivileged workflow, potentially leading to code execution in privileged workflows.
@@ -23,7 +21,7 @@ In GitHub Actions, cache scopes are primarily determined by the branch structure
Due to the above design, if something is cached in the context of the default branch (e.g., `main`), it becomes accessible to any feature branch derived from `main`.
## Recommendations
## Recommendation
1. Avoid using caching in workflows that handle sensitive operations like releases.
2. If caching must be used:
@@ -34,7 +32,7 @@ Due to the above design, if something is cached in the context of the default br
4. Never run untrusted code in the context of the default branch.
5. Sign the cache value cryptographically and verify the signature before usage.
## Examples
## Example
### Incorrect Usage
@@ -78,6 +76,6 @@ jobs:
## References
- [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/)
- [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows)
- [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/)
- Adnan Khan's Blog: [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/).
- GitHub Docs: [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows).
- Scribe Security Blog: [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/).

View File

@@ -1,6 +1,4 @@
# Cache Poisoning in GitHub Actions
## Description
## Overview
GitHub Actions cache poisoning is a technique that allows an attacker to inject malicious content into the Action's cache from unprivileged workflow, potentially leading to code execution in privileged workflows.
@@ -23,7 +21,7 @@ In GitHub Actions, cache scopes are primarily determined by the branch structure
Due to the above design, if something is cached in the context of the default branch (e.g., `main`), it becomes accessible to any feature branch derived from `main`.
## Recommendations
## Recommendation
1. Avoid using caching in workflows that handle sensitive operations like releases.
2. If caching must be used:
@@ -34,7 +32,7 @@ Due to the above design, if something is cached in the context of the default br
4. Never run untrusted code in the context of the default branch.
5. Sign the cache value cryptographically and verify the signature before usage.
## Examples
## Example
### Incorrect Usage
@@ -123,6 +121,6 @@ jobs:
## References
- [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/)
- [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows)
- [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/)
- Adnan Khan's Blog: [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/).
- GitHub Docs: [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows).
- Scribe Security Blog: [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/).

View File

@@ -1,6 +1,4 @@
# Cache Poisoning in GitHub Actions
## Description
## Overview
GitHub Actions cache poisoning is a technique that allows an attacker to inject malicious content into the Action's cache from unprivileged workflow, potentially leading to code execution in privileged workflows.
@@ -23,7 +21,7 @@ In GitHub Actions, cache scopes are primarily determined by the branch structure
Due to the above design, if something is cached in the context of the default branch (e.g., `main`), it becomes accessible to any feature branch derived from `main`.
## Recommendations
## Recommendation
1. Avoid using caching in workflows that handle sensitive operations like releases.
2. If caching must be used:
@@ -34,7 +32,7 @@ Due to the above design, if something is cached in the context of the default br
4. Never run untrusted code in the context of the default branch.
5. Sign the cache value cryptographically and verify the signature before usage.
## Examples
## Example
### Incorrect Usage
@@ -80,6 +78,6 @@ jobs:
## References
- [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/)
- [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows)
- [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/)
- Adnan Khan's Blog: [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/).
- GitHub Docs: [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows).
- Scribe Security Blog: [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/).

View File

@@ -1,17 +1,15 @@
# Untrusted Checkout TOCTOU (Time-of-check to time-of-use)
## Description
## Overview
Untrusted Checkout is protected by a security check but the checked-out branch can be changed after the check.
## Recommendations
## Recommendation
Verify that the code has not been modified after the security check. This may be achieved differently depending on the type of check:
- Deployment Environment Approval: Make sure to use a non-mutable reference to the code to be executed. For example use a `sha` instead of a `ref`.
- Label Gates: Make sure to use a non-mutable reference to the code to be executed. For example use a `sha` instead of a `ref`.
## Examples
## Example
### Incorrect Usage (Deployment Environment Approval)
@@ -99,4 +97,4 @@ jobs:
## References
- [ActionsTOCTOU](https://github.com/AdnaneKhan/ActionsTOCTOU)
- [ActionsTOCTOU](https://github.com/AdnaneKhan/ActionsTOCTOU).

View File

@@ -1,17 +1,15 @@
# Untrusted Checkout TOCTOU (Time-of-check to time-of-use)
## Description
## Overview
Untrusted Checkout is protected by a security check but the checked-out branch can be changed after the check.
## Recommendations
## Recommendation
Verify that the code has not been modified after the security check. This may be achieved differently depending on the type of check:
- Deployment Environment Approval: Make sure to use a non-mutable reference to the code to be executed. For example use a `sha` instead of a `ref`.
- Label Gates: Make sure to use a non-mutable reference to the code to be executed. For example use a `sha` instead of a `ref`.
## Examples
## Example
### Incorrect Usage (Deployment Environment Approval)
@@ -99,4 +97,4 @@ jobs:
## References
- [ActionsTOCTOU](https://github.com/AdnaneKhan/ActionsTOCTOU)
- [ActionsTOCTOU](https://github.com/AdnaneKhan/ActionsTOCTOU).

View File

@@ -1,6 +1,4 @@
# If Condition Always Evaluates to True
## Description
## Overview
GitHub Workflow Expressions (`${{ ... }}`) used in the `if` condition of jobs or steps must not contain extra characters or spaces. Otherwise, the condition is invariably evaluated to `true`.
@@ -14,7 +12,7 @@ To avoid the vulnerability where an `if` condition always evaluates to `true`, i
2. Avoid multiline or spaced-out conditional expressions that might inadvertently introduce unwanted characters or formatting.
3. Test the workflow to ensure the `if` conditions behave as expected under different scenarios.
## Examples
## Example
### Correct Usage
@@ -60,4 +58,4 @@ To avoid the vulnerability where an `if` condition always evaluates to `true`, i
## References
- [Expression Always True Github Issue](https://github.com/actions/runner/issues/1173)
- GitHub actions/runner Issues: [Expression Always True](https://github.com/actions/runner/issues/1173).

View File

@@ -1,6 +1,4 @@
# If Condition Always Evaluates to True
## Description
## Overview
GitHub Workflow Expressions (`${{ ... }}`) used in the `if` condition of jobs or steps must not contain extra characters or spaces. Otherwise, the condition is invariably evaluated to `true`.
@@ -14,7 +12,7 @@ To avoid the vulnerability where an `if` condition always evaluates to `true`, i
2. Avoid multiline or spaced-out conditional expressions that might inadvertently introduce unwanted characters or formatting.
3. Test the workflow to ensure the `if` conditions behave as expected under different scenarios.
## Examples
## Example
### Correct Usage
@@ -60,4 +58,4 @@ To avoid the vulnerability where an `if` condition always evaluates to `true`, i
## References
- [Expression Always True Github Issue](https://github.com/actions/runner/issues/1173)
- GitHub actions/runner Issues: [Expression Always True](https://github.com/actions/runner/issues/1173).

View File

@@ -1,16 +1,14 @@
# Artifact poisoning
## Description
## Overview
The workflow downloads artifacts that may be poisoned by an attacker in previously triggered workflows. If the contents of these artifacts are not correctly extracted, stored and verified, they may lead to repository compromise if untrusted code gets executed in a privileged job.
## Recommendations
## Recommendation
- Always consider artifacts content as untrusted.
- Extract the contents of artifacts to a temporary folder so they cannot override existing files.
- Verify the contents of the artifacts downloaded. If an artifact is expected to contain a numeric value, verify it before using it.
## Examples
## Example
### Incorrect Usage
@@ -69,4 +67,4 @@ jobs:
## References
- [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)
- GitHub Security Lab Research: [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/).

View File

@@ -1,16 +1,14 @@
# Artifact poisoning
## Description
## Overview
The workflow downloads artifacts that may be poisoned by an attacker in previously triggered workflows. If the contents of these artifacts are not correctly extracted, stored and verified, they may lead to repository compromise if untrusted code gets executed in a privileged job.
## Recommendations
## Recommendation
- Always consider artifacts content as untrusted.
- Extract the contents of artifacts to a temporary folder so they cannot override existing files.
- Verify the contents of the artifacts downloaded. If an artifact is expected to contain a numeric value, verify it before using it.
## Examples
## Example
### Incorrect Usage
@@ -69,4 +67,4 @@ jobs:
## References
- [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)
- GitHub Security Lab Research: [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/).

View File

@@ -1,14 +1,12 @@
# Unpinned tag for 3rd party Action in workflow
## Description
## Overview
Using a tag for a 3rd party Action that is not pinned to a commit can lead to executing an untrusted Action through a supply chain attack.
## Recommendations
## Recommendation
Pinning an action to a full length commit SHA is currently the only way to use a non-immutable action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload. When selecting a SHA, you should verify it is from the action's repository and not a repository fork.
## Examples
## Example
### Incorrect Usage
@@ -24,4 +22,4 @@ Pinning an action to a full length commit SHA is currently the only way to use a
## References
- [Using third-party actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions)
- GitHub Docs: [Using third-party actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions).

View File

@@ -1,10 +1,8 @@
# Execution of Untrusted Checked-out Code
## Description
## Overview
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.
## Recommendations
## Recommendation
- Avoid using `pull_request_target` unless necessary.
- Employ unprivileged `pull_request` workflows followed by `workflow_run` for privileged operations.
@@ -14,7 +12,7 @@ The best practice is to handle the potentially untrusted pull request via the **
The artifacts downloaded from the first workflow should be considered untrusted and must be verified.
## Examples
## Example
### Incorrect Usage
@@ -134,4 +132,4 @@ jobs:
## References
- [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)
- GitHub Security Lab Research: [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/).

View File

@@ -1,10 +1,8 @@
# Execution of Untrusted Checked-out Code
## Description
## Overview
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.
## Recommendations
## Recommendation
- Avoid using `pull_request_target` unless necessary.
- Employ unprivileged `pull_request` workflows followed by `workflow_run` for privileged operations.
@@ -14,7 +12,7 @@ The best practice is to handle the potentially untrusted pull request via the **
The artifacts downloaded from the first workflow should be considered untrusted and must be verified.
## Examples
## Example
### Incorrect Usage
@@ -134,4 +132,4 @@ jobs:
## References
- [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)
- GitHub Security Lab Research: [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/).

View File

@@ -1,10 +1,8 @@
# Execution of Untrusted Checked-out Code
## Description
## Overview
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.
## Recommendations
## Recommendation
- Avoid using `pull_request_target` unless necessary.
- Employ unprivileged `pull_request` workflows followed by `workflow_run` for privileged operations.
@@ -14,7 +12,7 @@ The best practice is to handle the potentially untrusted pull request via the **
The artifacts downloaded from the first workflow should be considered untrusted and must be verified.
## Examples
## Example
### Incorrect Usage
@@ -134,4 +132,4 @@ jobs:
## References
- [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)
- GitHub Security Lab Research: [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/).

View File

@@ -1,13 +1,11 @@
# Unneccesary use of advanced configuration
## Description
## Overview
The CodeQL workflow does not use any custom settings and could be simplified by switching to the CodeQL default setup.
## Recommendations
## Recommendation
If there is no reason to have a custom configuration switch to the CodeQL default setup.
## References
- [GitHub Docs: Configuring Default Setup for a repository](https://docs.github.com/en/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning#configuring-default-setup-for-a-repository)
- GitHub Docs: [Configuring Default Setup for a repository](https://docs.github.com/en/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning#configuring-default-setup-for-a-repository).

View File

@@ -1,18 +1,16 @@
# Argument Injection in GitHub Actions
## Description
## Overview
Passing user-controlled arguments to certain commands in the context of `Run` steps may lead to arbitrary code execution.
Argument injection in GitHub Actions may allow an attacker to exfiltrate any secrets used in the workflow and the temporary GitHub repository authorization token. The token may have write access to the repository, allowing the attacker to make changes to the repository.
## Recommendations
## Recommendation
When possible avoid passing user-controlled data to commands which may spawn new processes using some of their arguments.
It is also recommended to limit the permissions of any tokens used by a workflow such as the GITHUB_TOKEN.
## Examples
## Example
### Incorrect Usage
@@ -35,7 +33,7 @@ An attacker may set the body of an Issue comment to `BAR/g;1e whoami;#` and the
## References
- [Common Weakness Enumeration: CWE-88](https://cwe.mitre.org/data/definitions/88.html).
- [Argument Injection Explained](https://sonarsource.github.io/argument-injection-vectors/explained/)
- [Argument Injection Vectors](https://sonarsource.github.io/argument-injection-vectors/)
- [GTFOBins](https://gtfobins.github.io/)
- Common Weakness Enumeration: [CWE-88](https://cwe.mitre.org/data/definitions/88.html).
- [Argument Injection Vectors](https://sonarsource.github.io/argument-injection-vectors/).
- Argument Injection Vectors: [Argument Injection Explained](https://sonarsource.github.io/argument-injection-vectors/explained/).
- [GTFOBins](https://gtfobins.github.io/).

View File

@@ -1,18 +1,16 @@
# Argument Injection in GitHub Actions
## Description
## Overview
Passing user-controlled arguments to certain commands in the context of `Run` steps may lead to arbitrary code execution.
Argument injection in GitHub Actions may allow an attacker to exfiltrate any secrets used in the workflow and the temporary GitHub repository authorization token. The token may have write access to the repository, allowing the attacker to make changes to the repository.
## Recommendations
## Recommendation
When possible avoid passing user-controlled data to commands which may spawn new processes using some of their arguments.
It is also recommended to limit the permissions of any tokens used by a workflow such as the GITHUB_TOKEN.
## Examples
## Example
### Incorrect Usage
@@ -35,7 +33,7 @@ An attacker may set the body of an Issue comment to `BAR|g;1e whoami;#` and the
## References
- [Common Weakness Enumeration: CWE-88](https://cwe.mitre.org/data/definitions/88.html).
- [Argument Injection Explained](https://sonarsource.github.io/argument-injection-vectors/explained/)
- [Argument Injection Vectors](https://sonarsource.github.io/argument-injection-vectors/)
- [GTFOBins](https://gtfobins.github.io/)
- Common Weakness Enumeration: [CWE-88](https://cwe.mitre.org/data/definitions/88.html).
- [Argument Injection Vectors](https://sonarsource.github.io/argument-injection-vectors/).
- Argument Injection Vectors: [Argument Injection Explained](https://sonarsource.github.io/argument-injection-vectors/explained/).
- [GTFOBins](https://gtfobins.github.io/).

View File

@@ -1,14 +1,12 @@
# Unversioned Immutable Action
## Description
## Overview
This action is eligible for Immutable Actions, a new GitHub feature that is currently only available for internal users. Immutable Actions are released as packages in the GitHub package registry instead of resolved from a pinned SHA at the repository. The Immutable Action provides the same immutability as pinning the version to a SHA but with improved readability and additional security guarantees.
## Recommendations
## Recommendation
For internal users: when using [immutable actions](https://github.com/github/package-registry-team/blob/main/docs/immutable-actions/immutable-actions-howto.md) use the full semantic version of the action. This will ensure that the action is resolved to the exact version stored in the GitHub package registry.
## Examples
## Example
### Incorrect Usage
@@ -25,4 +23,4 @@ For internal users: when using [immutable actions](https://github.com/github/pac
## References
- [Consuming immutable actions]()
- [Consuming immutable actions]().

View File

@@ -1,5 +1,5 @@
name: codeql/actions-queries
version: 0.6.4
version: 0.6.5-dev
library: false
warnOnImplicitThis: true
groups: [actions, queries]

View File

@@ -17,16 +17,16 @@
#!/usr/bin/python3
import sys
import os
import re
from difflib import context_diff
OVERLAY_PATTERN = re.compile(r'overlay\[[a-zA-Z?_-]+\]')
def has_overlay_annotations(lines):
'''
Check whether the given lines contain any overlay[...] annotations.
'''
overlays = ["local", "local?", "global", "caller", "caller?"]
annotations = [f"overlay[{t}]" for t in overlays]
return any(ann in line for ann in annotations for line in lines)
return any(OVERLAY_PATTERN.search(line) for line in lines)
def is_line_comment(line):

View File

@@ -1,16 +1,19 @@
{
"files": [
"cpp/ql/lib/semmlecode.cpp.dbscheme",
"javascript/ql/lib/semmlecode.javascript.dbscheme",
"python/ql/lib/semmlecode.python.dbscheme",
"ruby/ql/lib/ruby.dbscheme",
"ql/ql/src/ql.dbscheme"
],
"fragments": [
"/*- Compilations -*/",
"/*- External data -*/",
"/*- Files and folders -*/",
"/*- Diagnostic messages -*/",
"/*- Diagnostic messages: severity -*/",
"/*- Source location prefix -*/",
"/*- Database metadata -*/",
"/*- Lines of code -*/",
"/*- Configuration files with key value pairs -*/",
"/*- YAML -*/",
@@ -20,6 +23,7 @@
"/*- DEPRECATED: Snapshot date -*/",
"/*- DEPRECATED: Duplicate code -*/",
"/*- DEPRECATED: Version control data -*/",
"/*- C++ dbscheme -*/",
"/*- JavaScript-specific part -*/",
"/*- Ruby dbscheme -*/",
"/*- Erb dbscheme -*/",
@@ -31,4 +35,4 @@
"/*- Python dbscheme -*/",
"/*- Empty location -*/"
]
}
}

View File

@@ -8,9 +8,9 @@ needs_an_re = re.compile(r'^(?!Unary)[AEIOU]') # Name requiring "an" instead of
start_qldoc_re = re.compile(r'^\s*/\*\*') # Start of a QLDoc comment
end_qldoc_re = re.compile(r'\*/\s*$') # End of a QLDoc comment
blank_qldoc_line_re = re.compile(r'^\s*\*\s*$') # A line in a QLDoc comment with only the '*'
instruction_class_re = re.compile(r'^class (?P<name>[A-aa-z0-9]+)Instruction\s') # Declaration of an `Instruction` class
opcode_base_class_re = re.compile(r'^abstract class (?P<name>[A-aa-z0-9]+)Opcode\s') # Declaration of an `Opcode` base class
opcode_class_re = re.compile(r'^ class (?P<name>[A-aa-z0-9]+)\s') # Declaration of an `Opcode` class
instruction_class_re = re.compile(r'^class (?P<name>[A-Za-z0-9]+)Instruction\s') # Declaration of an `Instruction` class
opcode_base_class_re = re.compile(r'^abstract class (?P<name>[A-Za-z0-9]+)Opcode\s') # Declaration of an `Opcode` base class
opcode_class_re = re.compile(r'^ class (?P<name>[A-Za-z0-9]+)\s') # Declaration of an `Opcode` class
script_dir = path.realpath(path.dirname(__file__))
instruction_path = path.realpath(path.join(script_dir, '../cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll'))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Uncomment cases in dbscheme
compatibility: full

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Remove unused external_package tables from the dbscheme
compatibility: full

View File

@@ -0,0 +1,14 @@
class BuiltinType extends @builtintype {
string toString() { none() }
}
from BuiltinType type, string name, int kind, int kind_new, int size, int sign, int alignment
where
builtintypes(type, name, kind, size, sign, alignment) and
if
type instanceof @complex_fp16 or
type instanceof @complex_std_bfloat16 or
type instanceof @complex_std_float16
then kind_new = 2
else kind_new = kind
select type, name, kind_new, size, sign, alignment

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
description: Introduce new complex 16-bit floating-point types
compatibility: backwards
builtintypes.rel: run builtintypes.qlo

View File

@@ -0,0 +1,9 @@
class Function extends @function {
string toString() { none() }
}
from Function f, string n, int k, int new_k
where
functions(f, n, k) and
if builtin_functions(f) then new_k = 6 else new_k = k
select f, n, new_k

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
description: Move builtin function identification to its own table
compatibility: full
functions.rel: run functions.qlo
builtin_functions.rel: delete

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: sync dbscheme and delete svn tables
compatibility: full

View File

@@ -0,0 +1,161 @@
class Accessible extends @accessible {
string toString() { none() }
}
class Container extends @container {
string toString() { none() }
}
class Expr extends @expr {
string toString() { none() }
}
class Initialiser extends @initialiser {
string toString() { none() }
}
class Location extends @location_default {
string toString() { none() }
}
class Stmt extends @stmt {
string toString() { none() }
}
predicate isLocationDefault(Location l) {
diagnostics(_, _, _, _, _, l)
or
macroinvocations(_, _, l, _)
or
fun_decls(_, _, _, _, l)
or
var_decls(_, _, _, _, l)
or
type_decls(_, _, l)
or
namespace_decls(_, _, l, _)
or
namespace_decls(_, _, _, l)
or
usings(_, _, l, _)
or
static_asserts(_, _, _, l, _)
or
enumconstants(_, _, _, _, _, l)
or
concept_templates(_, _, l)
or
attributes(_, _, _, _, l)
or
attribute_args(_, _, _, _, l)
or
derivations(_, _, _, _, l)
or
frienddecls(_, _, _, l)
or
comments(_, _, l)
or
namequalifiers(_, _, _, l)
or
lambda_capture(_, _, _, _, _, _, l)
or
preprocdirects(_, _, l)
or
xmllocations(_, l)
or
locations_default(l, _, 0, 0, 0, 0) // For containers.
}
predicate isLocationExpr(Location l) {
initialisers(_, _, _, l)
or
exprs(_, _, l)
}
predicate isLocationStmt(Location l) { stmts(_, _, l) }
newtype TExprOrStmtLocation =
TExprLocation(Location l, Container c, int startLine, int startColumn, int endLine, int endColumn) {
isLocationExpr(l) and
(isLocationDefault(l) or isLocationStmt(l)) and
locations_default(l, c, startLine, startColumn, endLine, endColumn)
} or
TStmtLocation(Location l, Container c, int startLine, int startColumn, int endLine, int endColumn) {
isLocationStmt(l) and
(isLocationDefault(l) or isLocationExpr(l)) and
locations_default(l, c, startLine, startColumn, endLine, endColumn)
}
module Fresh = QlBuiltins::NewEntity<TExprOrStmtLocation>;
class NewLocationBase = @location_default or Fresh::EntityId;
class NewLocation extends NewLocationBase {
string toString() { none() }
}
query predicate new_locations_default(
NewLocation l, Container c, int startLine, int startColumn, int endLine, int endColumn
) {
isLocationDefault(l) and
locations_default(l, c, startLine, startColumn, endLine, endColumn)
}
query predicate new_locations_expr(
NewLocation l, Container c, int startLine, int startColumn, int endLine, int endColumn
) {
exists(Location l_old |
isLocationExpr(l_old) and
locations_default(l_old, c, startLine, startColumn, endLine, endColumn)
|
if not isLocationDefault(l_old) and not isLocationStmt(l)
then l = l_old
else l = Fresh::map(TExprLocation(l_old, c, startLine, startColumn, endLine, endColumn))
)
}
query predicate new_locations_stmt(
NewLocation l, Container c, int startLine, int startColumn, int endLine, int endColumn
) {
exists(Location l_old |
isLocationStmt(l_old) and
locations_default(l_old, c, startLine, startColumn, endLine, endColumn)
|
if not isLocationDefault(l_old) and not isLocationExpr(l)
then l = l_old
else l = Fresh::map(TStmtLocation(l_old, c, startLine, startColumn, endLine, endColumn))
)
}
query predicate new_exprs(Expr e, int kind, NewLocation l) {
exists(Location l_old, Container c, int startLine, int startColumn, int endLine, int endColumn |
exprs(e, kind, l_old) and
locations_default(l_old, c, startLine, startColumn, endLine, endColumn)
|
if not isLocationDefault(l_old) and not isLocationStmt(l)
then l = l_old
else l = Fresh::map(TExprLocation(l_old, c, startLine, startColumn, endLine, endColumn))
)
}
query predicate new_initialisers(Initialiser i, Accessible v, Expr e, NewLocation l) {
exists(Location l_old, Container c, int startLine, int startColumn, int endLine, int endColumn |
initialisers(i, v, e, l_old) and
locations_default(l_old, c, startLine, startColumn, endLine, endColumn)
|
if not isLocationDefault(l_old) and not isLocationStmt(l)
then l = l_old
else l = Fresh::map(TExprLocation(l_old, c, startLine, startColumn, endLine, endColumn))
)
}
query predicate new_stmts(Stmt s, int kind, NewLocation l) {
exists(Location l_old, Container c, int startLine, int startColumn, int endLine, int endColumn |
stmts(s, kind, l_old) and
locations_default(l_old, c, startLine, startColumn, endLine, endColumn)
|
if not isLocationDefault(l_old) and not isLocationExpr(l)
then l = l_old
else l = Fresh::map(TStmtLocation(l_old, c, startLine, startColumn, endLine, endColumn))
)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
description: Merge location tables
compatibility: partial
locations_default.rel: run downgrades.ql new_locations_default
locations_expr.rel: run downgrades.ql new_locations_expr
locations_stmt.rel: run downgrades.ql new_locations_stmt
exprs.rel: run downgrades.ql new_exprs
initialisers.rel: run downgrades.ql new_initialisers
stmts.rel: run downgrades.ql new_stmts

View File

@@ -4,4 +4,3 @@
int main() {
return ONE + TWO + THREE + FOUR;
}
// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/a.pch -Iextra_dummy_path

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added `sql-injection` sink models for the Oracle Call Interface (OCI) database library functions `OCIStmtPrepare` and `OCIStmtPrepare2`.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The analysis of C/C++ code targeting 64-bit Arm platforms has been improved. This includes support for the Arm-specific builtin functions, support for the `arm_neon.h` header and Neon vector types, and support for the `fp8` scalar type. The `arm_sve.h` header and scalable vectors are only partially supported at this point.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added support for `__fp16 _Complex` and `__bf16 _Complex` types

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* The `UnknownDefaultLocation`, `UnknownExprLocation`, and `UnknownStmtLocation` classes have been deprecated. Use `UnknownLocation` instead.

View File

@@ -8,7 +8,7 @@ module CryptoInput implements InputSig<Language::Location> {
class LocatableElement = Language::Locatable;
class UnknownLocation = Language::UnknownDefaultLocation;
class UnknownLocation = Language::UnknownLocation;
LocatableElement dfn_to_element(DataFlow::Node node) {
result = node.asExpr() or
@@ -56,7 +56,7 @@ module ArtifactFlowConfig implements DataFlow::ConfigSig {
module ArtifactFlow = DataFlow::Global<ArtifactFlowConfig>;
/**
* Artifact output to node input configuration
* An artifact output to node input configuration
*/
abstract class AdditionalFlowInputStep extends DataFlow::Node {
abstract DataFlow::Node getOutput();
@@ -91,9 +91,8 @@ module GenericDataSourceFlowConfig implements DataFlow::ConfigSig {
module GenericDataSourceFlow = TaintTracking::Global<GenericDataSourceFlowConfig>;
private class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceof Literal {
ConstantDataSource() { this instanceof OpenSslGenericSourceCandidateLiteral }
private class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceof OpenSslGenericSourceCandidateLiteral
{
override DataFlow::Node getOutputNode() { result.asExpr() = this }
override predicate flowsTo(Crypto::FlowAwareElement other) {

View File

@@ -48,7 +48,7 @@ module KnownOpenSslAlgorithmToAlgorithmValueConsumerConfig implements DataFlow::
module KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow =
DataFlow::Global<KnownOpenSslAlgorithmToAlgorithmValueConsumerConfig>;
module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig implements DataFlow::ConfigSig {
module RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof OpenSslPaddingLiteral }
predicate isSink(DataFlow::Node sink) {
@@ -60,8 +60,8 @@ module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig implements DataF
}
}
module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow =
DataFlow::Global<RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig>;
module RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow =
DataFlow::Global<RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig>;
class OpenSslAlgorithmAdditionalFlowStep extends AdditionalFlowInputStep {
OpenSslAlgorithmAdditionalFlowStep() { exists(AlgorithmPassthroughCall c | c.getInNode() = this) }
@@ -114,11 +114,11 @@ class CopyAndDupAlgorithmPassthroughCall extends AlgorithmPassthroughCall {
override DataFlow::Node getOutNode() { result = outNode }
}
class NIDToPointerPassthroughCall extends AlgorithmPassthroughCall {
class NidToPointerPassthroughCall extends AlgorithmPassthroughCall {
DataFlow::Node inNode;
DataFlow::Node outNode;
NIDToPointerPassthroughCall() {
NidToPointerPassthroughCall() {
this.getTarget().getName() in ["OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn"] and
inNode.asExpr() = this.getArgument(0) and
outNode.asExpr() = this
@@ -150,11 +150,11 @@ class PointerToPointerPassthroughCall extends AlgorithmPassthroughCall {
override DataFlow::Node getOutNode() { result = outNode }
}
class PointerToNIDPassthroughCall extends AlgorithmPassthroughCall {
class PointerToNidPassthroughCall extends AlgorithmPassthroughCall {
DataFlow::Node inNode;
DataFlow::Node outNode;
PointerToNIDPassthroughCall() {
PointerToNidPassthroughCall() {
this.getTarget().getName() in ["OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid", "OBJ_txt2nid"] and
(
inNode.asIndirectExpr() = this.getArgument(0)

View File

@@ -5,36 +5,35 @@ private import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmCon
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase
private import AlgToAVCFlow
private import codeql.quantum.experimental.Standardization::Types::KeyOpAlg as KeyOpAlg
/**
* Given a `KnownOpenSslBlockModeAlgorithmExpr`, converts this to a block family type.
* Does not bind if there is no mapping (no mapping to 'unknown' or 'other').
*/
predicate knownOpenSslConstantToBlockModeFamilyType(
KnownOpenSslBlockModeAlgorithmExpr e, Crypto::TBlockCipherModeOfOperationType type
KnownOpenSslBlockModeAlgorithmExpr e, KeyOpAlg::ModeOfOperationType type
) {
exists(string name |
name = e.(KnownOpenSslAlgorithmExpr).getNormalizedName() and
(
name.matches("CBC") and type instanceof Crypto::CBC
name = "CBC" and type instanceof KeyOpAlg::CBC
or
name.matches("CFB%") and type instanceof Crypto::CFB
name = "CFB%" and type instanceof KeyOpAlg::CFB
or
name.matches("CTR") and type instanceof Crypto::CTR
name = "CTR" and type instanceof KeyOpAlg::CTR
or
name.matches("GCM") and type instanceof Crypto::GCM
name = "GCM" and type instanceof KeyOpAlg::GCM
or
name.matches("OFB") and type instanceof Crypto::OFB
name = "OFB" and type instanceof KeyOpAlg::OFB
or
name.matches("XTS") and type instanceof Crypto::XTS
name = "XTS" and type instanceof KeyOpAlg::XTS
or
name.matches("CCM") and type instanceof Crypto::CCM
name = "CCM" and type instanceof KeyOpAlg::CCM
or
name.matches("GCM") and type instanceof Crypto::GCM
name = "CCM" and type instanceof KeyOpAlg::CCM
or
name.matches("CCM") and type instanceof Crypto::CCM
or
name.matches("ECB") and type instanceof Crypto::ECB
name = "ECB" and type instanceof KeyOpAlg::ECB
)
)
}
@@ -64,10 +63,10 @@ class KnownOpenSslBlockModeConstantAlgorithmInstance extends OpenSslAlgorithmIns
getterCall = this
}
override Crypto::TBlockCipherModeOfOperationType getModeType() {
override KeyOpAlg::ModeOfOperationType getModeType() {
knownOpenSslConstantToBlockModeFamilyType(this, result)
or
not knownOpenSslConstantToBlockModeFamilyType(this, _) and result = Crypto::OtherMode()
not knownOpenSslConstantToBlockModeFamilyType(this, _) and result = KeyOpAlg::OtherMode()
}
// NOTE: I'm not going to attempt to parse out the mode specific part, so returning

View File

@@ -33,9 +33,9 @@ predicate knownOpenSslConstantToCipherFamilyType(
or
name.matches("CAST5%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CAST5())
or
name.matches("2DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DoubleDES())
name.matches("2DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DOUBLE_DES())
or
name.matches("3DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::TripleDES())
name.matches("3DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::TRIPLE_DES())
or
name.matches("DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DES())
or
@@ -113,7 +113,7 @@ class KnownOpenSslCipherConstantAlgorithmInstance extends OpenSslAlgorithmInstan
this.(KnownOpenSslCipherAlgorithmExpr).getExplicitKeySize() = result
}
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
override KeyOpAlg::AlgorithmType getAlgorithmType() {
knownOpenSslConstantToCipherFamilyType(this, result)
or
not knownOpenSslConstantToCipherFamilyType(this, _) and

View File

@@ -39,8 +39,14 @@ class KnownOpenSslEllipticCurveConstantAlgorithmInstance extends OpenSslAlgorith
result = this.(Call).getTarget().getName()
}
override Crypto::TEllipticCurveType getEllipticCurveType() {
Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getParsedEllipticCurveName(), _, result)
override Crypto::EllipticCurveFamilyType getEllipticCurveFamilyType() {
if
Crypto::ellipticCurveNameToKnownKeySizeAndFamilyMapping(this.getParsedEllipticCurveName(), _,
_)
then
Crypto::ellipticCurveNameToKnownKeySizeAndFamilyMapping(this.getParsedEllipticCurveName(), _,
result)
else result = Crypto::OtherEllipticCurveType()
}
override string getParsedEllipticCurveName() {
@@ -48,7 +54,7 @@ class KnownOpenSslEllipticCurveConstantAlgorithmInstance extends OpenSslAlgorith
}
override int getKeySize() {
Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.(KnownOpenSslAlgorithmExpr)
Crypto::ellipticCurveNameToKnownKeySizeAndFamilyMapping(this.(KnownOpenSslAlgorithmExpr)
.getNormalizedName(), result, _)
}
}

View File

@@ -11,21 +11,21 @@ predicate knownOpenSslConstantToHashFamilyType(
exists(string name |
name = e.(KnownOpenSslAlgorithmExpr).getNormalizedName() and
(
name.matches("BLAKE2B") and type instanceof Crypto::BLAKE2B
name = "BLAKE2B" and type instanceof Crypto::BLAKE2B
or
name.matches("BLAKE2S") and type instanceof Crypto::BLAKE2S
name = "BLAKE2S" and type instanceof Crypto::BLAKE2S
or
name.matches("GOST%") and type instanceof Crypto::GOSTHash
name.matches("GOST%") and type instanceof Crypto::GOST_HASH
or
name.matches("MD2") and type instanceof Crypto::MD2
name = "MD2" and type instanceof Crypto::MD2
or
name.matches("MD4") and type instanceof Crypto::MD4
name = "MD4" and type instanceof Crypto::MD4
or
name.matches("MD5") and type instanceof Crypto::MD5
name = "MD5" and type instanceof Crypto::MD5
or
name.matches("MDC2") and type instanceof Crypto::MDC2
name = "MDC2" and type instanceof Crypto::MDC2
or
name.matches("POLY1305") and type instanceof Crypto::POLY1305
name = "POLY1305" and type instanceof Crypto::POLY1305
or
name.matches(["SHA", "SHA1"]) and type instanceof Crypto::SHA1
or
@@ -33,13 +33,13 @@ predicate knownOpenSslConstantToHashFamilyType(
or
name.matches("SHA3-%") and type instanceof Crypto::SHA3
or
name.matches(["SHAKE"]) and type instanceof Crypto::SHAKE
name = "SHAKE" and type instanceof Crypto::SHAKE
or
name.matches("SM3") and type instanceof Crypto::SM3
name = "SM3" and type instanceof Crypto::SM3
or
name.matches("RIPEMD160") and type instanceof Crypto::RIPEMD160
name = "RIPEMD160" and type instanceof Crypto::RIPEMD160
or
name.matches("WHIRLPOOL") and type instanceof Crypto::WHIRLPOOL
name = "WHIRLPOOL" and type instanceof Crypto::WHIRLPOOL
)
)
}

View File

@@ -210,7 +210,8 @@ string getAlgorithmAlias(string alias) {
}
/**
* Finds aliases of known alagorithms defined by users (through obj_name_add and various macros pointing to this function)
* Holds for aliases of known algorithms defined by users
* (through obj_name_add and various macros pointing to this function).
*
* The `target` and `alias` are converted to lowercase to be of a standard form.
*/
@@ -222,7 +223,7 @@ predicate customAliases(string target, string alias) {
}
/**
* A hard-coded mapping of known algorithm aliases in OpenSsl.
* Holds for a hard-coded mapping of known algorithm aliases in OpenSsl.
* This was derived by applying the same kind of logic foun din `customAliases` to the
* OpenSsl code base directly.
*

View File

@@ -7,7 +7,7 @@ private import experimental.quantum.OpenSSL.Operations.OpenSSLOperations
private import AlgToAVCFlow
class KnownOpenSslMacConstantAlgorithmInstance extends OpenSslAlgorithmInstance,
Crypto::MACAlgorithmInstance instanceof KnownOpenSslMacAlgorithmExpr
Crypto::MacAlgorithmInstance instanceof KnownOpenSslMacAlgorithmExpr
{
OpenSslAlgorithmValueConsumer getterCall;
@@ -39,14 +39,14 @@ class KnownOpenSslMacConstantAlgorithmInstance extends OpenSslAlgorithmInstance,
result = this.(Call).getTarget().getName()
}
override Crypto::TMACType getMacType() {
this instanceof KnownOpenSslHMacAlgorithmExpr and result instanceof Crypto::THMAC
override Crypto::MacType getMacType() {
this instanceof KnownOpenSslHMacAlgorithmExpr and result = Crypto::HMAC()
or
this instanceof KnownOpenSslCMacAlgorithmExpr and result instanceof Crypto::TCMAC
this instanceof KnownOpenSslCMacAlgorithmExpr and result = Crypto::CMAC()
}
}
class KnownOpenSslHMacConstantAlgorithmInstance extends Crypto::HMACAlgorithmInstance,
class KnownOpenSslHMacConstantAlgorithmInstance extends Crypto::HmacAlgorithmInstance,
KnownOpenSslMacConstantAlgorithmInstance
{
override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() {
@@ -54,13 +54,15 @@ class KnownOpenSslHMacConstantAlgorithmInstance extends Crypto::HMACAlgorithmIns
then
// ASSUMPTION: if there is an explicit hash algorithm, it is already modeled
// and we can simply grab that model's AVC
exists(OpenSslAlgorithmInstance inst | inst.getAvc() = result and inst = this)
this.(OpenSslAlgorithmInstance).getAvc() = result
else
// ASSUMPTION: If no explicit algorithm is given, then it is assumed to be configured by
// a signature operation
exists(Crypto::SignatureOperationInstance s |
s.getHashAlgorithmValueConsumer() = result and
s.getAnAlgorithmValueConsumer() = this.getAvc()
// ASSUMPTION: If no explicit algorithm is given, then find
// where the current AVC traces to a HashAlgorithmIO consuming operation step.
// TODO: need to consider getting reset values, tracing down to the first set for now
exists(OperationStep s, AvcContextCreationStep avc |
avc = this.getAvc() and
avc.flowsToOperationStep(s) and
s.getAlgorithmValueConsumerForInput(HashAlgorithmIO()) = result
)
}
}

View File

@@ -5,6 +5,7 @@ private import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmCon
private import AlgToAVCFlow
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase
private import codeql.quantum.experimental.Standardization::Types::KeyOpAlg as KeyOpAlg
/**
* A class to define padding specific integer values.
@@ -28,18 +29,18 @@ class OpenSslPaddingLiteral extends Literal {
* Does not bind if there is no mapping (no mapping to 'unknown' or 'other').
*/
predicate knownOpenSslConstantToPaddingFamilyType(
KnownOpenSslPaddingAlgorithmExpr e, Crypto::TPaddingType type
KnownOpenSslPaddingAlgorithmExpr e, KeyOpAlg::PaddingSchemeType type
) {
exists(string name |
name = e.(KnownOpenSslAlgorithmExpr).getNormalizedName() and
(
name.matches("OAEP") and type = Crypto::OAEP()
name = "OAEP" and type = KeyOpAlg::OAEP()
or
name.matches("PSS") and type = Crypto::PSS()
name = "PSS" and type = KeyOpAlg::PSS()
or
name.matches("PKCS7") and type = Crypto::PKCS7()
name = "PKCS7" and type = KeyOpAlg::PKCS7()
or
name.matches("PKCS1V15") and type = Crypto::PKCS1_v1_5()
name = "PKCS1V15" and type = KeyOpAlg::PKCS1_V1_5()
)
)
}
@@ -85,7 +86,7 @@ class KnownOpenSslPaddingConstantAlgorithmInstance extends OpenSslAlgorithmInsta
// Source is `this`
src.asExpr() = this and
// This traces to a padding-specific consumer
RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow::flow(src, sink)
RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow::flow(src, sink)
) and
isPaddingSpecificConsumer = true
}
@@ -98,24 +99,24 @@ class KnownOpenSslPaddingConstantAlgorithmInstance extends OpenSslAlgorithmInsta
override OpenSslAlgorithmValueConsumer getAvc() { result = getterCall }
Crypto::TPaddingType getKnownPaddingType() {
this.(Literal).getValue().toInt() in [1, 7, 8] and result = Crypto::PKCS1_v1_5()
KeyOpAlg::PaddingSchemeType getKnownPaddingType() {
this.(Literal).getValue().toInt() in [1, 7, 8] and result = KeyOpAlg::PKCS1_V1_5()
or
this.(Literal).getValue().toInt() = 3 and result = Crypto::NoPadding()
this.(Literal).getValue().toInt() = 3 and result = KeyOpAlg::NoPadding()
or
this.(Literal).getValue().toInt() = 4 and result = Crypto::OAEP()
this.(Literal).getValue().toInt() = 4 and result = KeyOpAlg::OAEP()
or
this.(Literal).getValue().toInt() = 5 and result = Crypto::ANSI_X9_23()
this.(Literal).getValue().toInt() = 5 and result = KeyOpAlg::ANSI_X9_23()
or
this.(Literal).getValue().toInt() = 6 and result = Crypto::PSS()
this.(Literal).getValue().toInt() = 6 and result = KeyOpAlg::PSS()
}
override Crypto::TPaddingType getPaddingType() {
override KeyOpAlg::PaddingSchemeType getPaddingType() {
isPaddingSpecificConsumer = true and
(
result = this.getKnownPaddingType()
or
not exists(this.getKnownPaddingType()) and result = Crypto::OtherPadding()
not exists(this.getKnownPaddingType()) and result = KeyOpAlg::OtherPadding()
)
or
isPaddingSpecificConsumer = false and
@@ -143,7 +144,7 @@ class KnownOpenSslPaddingConstantAlgorithmInstance extends OpenSslAlgorithmInsta
// this instanceof Literal and
// this.getValue().toInt() in [0, 1, 3, 4, 5, 6, 7, 8]
// // TODO: trace to padding-specific consumers
// RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow
// RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow
// }
// override string getRawPaddingAlgorithmName() { result = this.(Literal).getValue().toString() }
// override Crypto::TPaddingType getPaddingType() {
@@ -161,18 +162,18 @@ class KnownOpenSslPaddingConstantAlgorithmInstance extends OpenSslAlgorithmInsta
// else result = Crypto::OtherPadding()
// }
// }
class OAEPPaddingAlgorithmInstance extends Crypto::OAEPPaddingAlgorithmInstance,
class OaepPaddingAlgorithmInstance extends Crypto::OaepPaddingAlgorithmInstance,
KnownOpenSslPaddingConstantAlgorithmInstance
{
OAEPPaddingAlgorithmInstance() {
this.(Crypto::PaddingAlgorithmInstance).getPaddingType() = Crypto::OAEP()
OaepPaddingAlgorithmInstance() {
this.(Crypto::PaddingAlgorithmInstance).getPaddingType() = KeyOpAlg::OAEP()
}
override Crypto::HashAlgorithmInstance getOAEPEncodingHashAlgorithm() {
override Crypto::HashAlgorithmInstance getOaepEncodingHashAlgorithm() {
none() //TODO
}
override Crypto::HashAlgorithmInstance getMGF1HashAlgorithm() {
override Crypto::HashAlgorithmInstance getMgf1HashAlgorithm() {
none() //TODO
}
}

View File

@@ -73,7 +73,7 @@ class KnownOpenSslSignatureConstantAlgorithmInstance extends OpenSslAlgorithmIns
none()
}
override KeyOpAlg::Algorithm getAlgorithmType() {
override KeyOpAlg::AlgorithmType getAlgorithmType() {
knownOpenSslConstantToSignatureFamilyType(this, result)
or
not knownOpenSslConstantToSignatureFamilyType(this, _) and

View File

@@ -4,10 +4,10 @@ private import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmCon
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase
/**
* Cases like EVP_MD5(),
* there is no input, rather it directly gets an algorithm
* and returns it.
* Also includes operations directly using an algorithm
* A call that is considered to inherently 'consume' an algorithm value.
* E.g., cases like EVP_MD5(),
* where there is no input, rather it directly gets an algorithm
* and returns it. Also includes operations directly using an algorithm
* like AES_encrypt().
*/
class DirectAlgorithmValueConsumer extends OpenSslAlgorithmValueConsumer instanceof OpenSslAlgorithmCall

View File

@@ -7,7 +7,7 @@ private import experimental.quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmI
abstract class HashAlgorithmValueConsumer extends OpenSslAlgorithmValueConsumer { }
/**
* EVP_Q_Digest directly consumes algorithm constant values
* An EVP_Q_Digest directly consumes algorithm constant values
*/
class Evp_Q_Digest_Algorithm_Consumer extends HashAlgorithmValueConsumer {
Evp_Q_Digest_Algorithm_Consumer() { this.(Call).getTarget().getName() = "EVP_Q_digest" }

View File

@@ -1,221 +0,0 @@
//TODO: model as data on open APIs should be able to get common flows, and obviate some of this
// e.g., copy/dup calls, need to ingest those models for openSSL and refactor.
/**
* In OpenSSL, flow between 'context' parameters is often used to
* store state/config of how an operation will eventually be performed.
* Tracing algorithms and configurations to operations therefore
* requires tracing context parameters for many OpenSSL apis.
*
* This library provides a dataflow analysis to track context parameters
* between any two functions accepting openssl context parameters.
* The dataflow takes into consideration flowing through duplication and copy calls
* as well as flow through flow killers (free/reset calls).
*
* TODO: we may need to revisit 'free' as a dataflow killer, depending on how
* we want to model use after frees.
*
* This library also provides classes to represent context Types and relevant
* arguments/expressions.
*/
import semmle.code.cpp.dataflow.new.DataFlow
/**
* An openSSL CTX type, which is type for which the stripped underlying type
* matches the pattern 'evp_%ctx_%st'.
* This includes types like:
* - EVP_CIPHER_CTX
* - EVP_MD_CTX
* - EVP_PKEY_CTX
*/
class CtxType extends Type {
CtxType() {
// It is possible for users to use the underlying type of the CTX variables
// these have a name matching 'evp_%ctx_%st
this.getUnspecifiedType().stripType().getName().matches("evp_%ctx_%st")
or
// In principal the above check should be sufficient, but in case of build mode none issues
// i.e., if a typedef cannot be resolved,
// or issues with properly stubbing test cases, we also explicitly check for the wrapping type defs
// i.e., patterns matching 'EVP_%_CTX'
exists(Type base | base = this or base = this.(DerivedType).getBaseType() |
base.getName().matches("EVP_%_CTX")
)
}
}
/**
* A pointer to a CtxType
*/
class CtxPointerExpr extends Expr {
CtxPointerExpr() {
this.getType() instanceof CtxType and
this.getType() instanceof PointerType
}
}
/**
* A call argument of type CtxPointerExpr.
*/
class CtxPointerArgument extends CtxPointerExpr {
CtxPointerArgument() { exists(Call c | c.getAnArgument() = this) }
Call getCall() { result.getAnArgument() = this }
}
/**
* A call returning a CtxPointerExpr.
*/
private class CtxPointerReturn extends CtxPointerExpr instanceof Call {
Call getCall() { result = this }
}
/**
* A call whose target contains 'free' or 'reset' and has an argument of type
* CtxPointerArgument.
*/
private class CtxClearCall extends Call {
CtxClearCall() {
this.getTarget().getName().toLowerCase().matches(["%free%", "%reset%"]) and
this.getAnArgument() instanceof CtxPointerArgument
}
}
abstract private class CtxPassThroughCall extends Call {
abstract DataFlow::Node getNode1();
abstract DataFlow::Node getNode2();
}
/**
* A call whose target contains 'copy' and has an argument of type
* CtxPointerArgument.
*/
private class CtxCopyOutArgCall extends CtxPassThroughCall {
DataFlow::Node n1;
DataFlow::Node n2;
CtxCopyOutArgCall() {
this.getTarget().getName().toLowerCase().matches("%copy%") and
n1.asExpr() = this.getAnArgument() and
n1.getType() instanceof CtxType and
n2.asDefiningArgument() = this.getAnArgument() and
n2.getType() instanceof CtxType and
n1.asDefiningArgument() != n2.asExpr()
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
/**
* A call whose target contains 'dup' and has an argument of type
* CtxPointerArgument.
*/
private class CtxCopyReturnCall extends CtxPassThroughCall, CtxPointerExpr {
DataFlow::Node n1;
CtxCopyReturnCall() {
this.getTarget().getName().toLowerCase().matches("%dup%") and
n1.asExpr() = this.getAnArgument() and
n1.getType() instanceof CtxType
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result.asExpr() = this }
}
/**
* A call to `EVP_PKEY_paramgen` acts as a kind of pass through.
* It's output pkey is eventually used in a new operation generating
* a fresh context pointer (e.g., `EVP_PKEY_CTX_new`).
* It is easier to model this as a pass through
* than to model the flow from the paramgen to the new key generation.
*/
private class CtxParamGenCall extends CtxPassThroughCall {
DataFlow::Node n1;
DataFlow::Node n2;
CtxParamGenCall() {
this.getTarget().getName() = "EVP_PKEY_paramgen" and
n1.asExpr() = this.getArgument(0) and
(
n2.asExpr() = this.getArgument(1)
or
n2.asDefiningArgument() = this.getArgument(1)
)
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
/**
* If the current node gets is an argument to a function
* that returns a pointer type, immediately flow through.
* NOTE: this passthrough is required if we allow
* intermediate steps to go into variables that are not a CTX type.
* See for example `CtxParamGenCall`.
*/
private class CallArgToCtxRet extends CtxPassThroughCall, CtxPointerExpr {
DataFlow::Node n1;
DataFlow::Node n2;
CallArgToCtxRet() {
this.getAnArgument() = n1.asExpr() and
n2.asExpr() = this
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
/**
* A source Ctx of interest is any argument or return of type CtxPointerExpr.
*/
class CtxPointerSource extends CtxPointerExpr {
CtxPointerSource() {
this instanceof CtxPointerReturn or
this instanceof CtxPointerArgument
}
DataFlow::Node asNode() {
result.asExpr() = this
or
result.asDefiningArgument() = this
}
}
/**
* Flow from any CtxPointerSource to other CtxPointerSource.
*/
module OpenSslCtxSourceToSourceFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { exists(CtxPointerSource s | s.asNode() = source) }
predicate isSink(DataFlow::Node sink) { exists(CtxPointerSource s | s.asNode() = sink) }
predicate isBarrier(DataFlow::Node node) {
exists(CtxClearCall c | c.getAnArgument() = node.asExpr())
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2)
}
}
module OpenSslCtxSourceToArgumentFlow = DataFlow::Global<OpenSslCtxSourceToSourceFlowConfig>;
/**
* Holds if there is a context flow from the source to the sink.
*/
predicate ctxSrcToSrcFlow(CtxPointerSource source, CtxPointerSource sink) {
exists(DataFlow::Node a, DataFlow::Node b |
OpenSslCtxSourceToArgumentFlow::flow(a, b) and
a = source.asNode() and
b = sink.asNode()
)
}

View File

@@ -0,0 +1,273 @@
import experimental.quantum.Language
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
import EVPPKeyCtxInitializer
/**
* A base class for all EVP cipher operations.
*/
abstract class EvpCipherInitializer extends OperationStep {
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and
type = PrimaryAlgorithmIO() and
// Constants that are not equal to zero or
// non-constants (e.g., variable accesses, which require data-flow to determine the value)
// A zero (null) value typically indicates use of this operation step to initialize
// other out parameters in a multi-step initialization.
(exists(result.asExpr().getValue()) implies result.asExpr().getValue().toInt() != 0)
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
/**
* A base class for EVP cipher/decrypt/encrypt 'ex' operations.
*/
abstract class EvpEXInitializer extends EvpCipherInitializer {
override DataFlow::Node getInput(IOType type) {
result = super.getInput(type)
or
(
// Constants that are not equal to zero or
// non-constants (e.g., variable accesses, which require data-flow to determine the value)
// A zero (null) value typically indicates use of this operation step to initialize
// other out parameters in a multi-step initialization.
result.asExpr() = this.getArgument(3) and type = KeyIO()
or
result.asExpr() = this.getArgument(4) and type = IVorNonceIO()
) and
(exists(result.asExpr().getValue()) implies result.asExpr().getValue().toInt() != 0)
}
}
/**
* A base class for EVP cipher/decrypt/encrypt 'ex2' operations.
*/
abstract class EvpEX2Initializer extends EvpCipherInitializer {
override DataFlow::Node getInput(IOType type) {
result = super.getInput(type)
or
result.asExpr() = this.getArgument(2) and type = KeyIO()
or
result.asExpr() = this.getArgument(3) and type = IVorNonceIO()
}
}
/**
* A Call to an EVP Cipher/Encrypt/Decrypt initialization operation.
*/
class EvpCipherEXInitCall extends EvpEXInitializer {
EvpCipherEXInitCall() {
this.getTarget().getName() in ["EVP_EncryptInit_ex", "EVP_DecryptInit_ex", "EVP_CipherInit_ex"]
}
override DataFlow::Node getInput(IOType type) {
result = super.getInput(type)
or
// NOTE: for EncryptInit and DecryptInit there is no subtype arg
// the subtype is determined automatically by the initializer based on the operation name
this.getTarget().getName().toLowerCase().matches("%cipherinit%") and
result.asExpr() = this.getArgument(5) and
type = KeyOperationSubtypeIO()
}
}
class Evp_Cipher_EX2_or_Simple_Init_Call extends EvpEX2Initializer {
Evp_Cipher_EX2_or_Simple_Init_Call() {
this.getTarget().getName() in [
"EVP_EncryptInit_ex2", "EVP_DecryptInit_ex2", "EVP_CipherInit_ex2", "EVP_EncryptInit",
"EVP_DecryptInit", "EVP_CipherInit"
]
}
override DataFlow::Node getInput(IOType type) {
result = super.getInput(type)
or
this.getTarget().getName().toLowerCase().matches("%cipherinit%") and
result.asExpr() = this.getArgument(4) and
type = KeyOperationSubtypeIO()
}
}
/**
* A call to EVP_Pkey_encrypt_init, EVP_Pkey_decrypt_init, or their 'ex' variants.
*/
class EvpPkeyEncryptDecryptInit extends OperationStep {
EvpPkeyEncryptDecryptInit() {
this.getTarget().getName() in [
"EVP_PKEY_encrypt_init", "EVP_PKEY_encrypt_init_ex", "EVP_PKEY_decrypt_init",
"EVP_PKEY_decrypt_init_ex"
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = OsslParamIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
class EvpCipherInitSKeyCall extends EvpEX2Initializer {
EvpCipherInitSKeyCall() { this.getTarget().getName() = "EVP_CipherInit_SKEY" }
override DataFlow::Node getInput(IOType type) {
result = super.getInput(type)
or
result.asExpr() = this.getArgument(5) and
type = KeyOperationSubtypeIO()
}
}
//EVP_PKEY_encrypt_init
/**
* A Call to EVP_Cipher/Encrypt/DecryptUpdate.
* https://docs.openssl.org/3.2/man3/EVP_CipherUpdate
*/
class EvpCipherUpdateCall extends OperationStep {
EvpCipherUpdateCall() {
this.getTarget().getName() in ["EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate"]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(3) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(1) and type = CiphertextIO()
or
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = UpdateStep() }
}
/**
* A base configuration for all EVP cipher operations.
*/
abstract class EvpCipherOperationFinalStep extends OperationStep {
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = FinalStep() }
}
/**
* A Call to EVP_Cipher.
*/
class EvpCipherCall extends EvpCipherOperationFinalStep {
EvpCipherCall() { this.getTarget().getName() = "EVP_Cipher" }
override DataFlow::Node getInput(IOType type) {
super.getInput(type) = result
or
result.asExpr() = this.getArgument(2) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
super.getOutput(type) = result
or
result.asExpr() = this.getArgument(1) and type = CiphertextIO()
}
}
/**
* A Call to an EVP Cipher/Encrypt/Decrypt final operation.
*/
class EvpCipherFinalCall extends EvpCipherOperationFinalStep {
EvpCipherFinalCall() {
this.getTarget().getName() in [
"EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal",
"EVP_DecryptFinal", "EVP_CipherFinal"
]
}
override DataFlow::Node getOutput(IOType type) {
super.getOutput(type) = result
or
result.asDefiningArgument() = this.getArgument(1) and
type = CiphertextIO()
// TODO: could indicate text lengths here, as well
}
}
/**
* A call to a PKEY_encrypt or PKEY_decrypt operation.
* https://docs.openssl.org/3.2/man3/EVP_PKEY_decrypt/
* https://docs.openssl.org/3.2/man3/EVP_PKEY_encrypt
*/
class EvpPKeyCipherOperation extends EvpCipherOperationFinalStep {
EvpPKeyCipherOperation() {
this.getTarget().getName() in ["EVP_PKEY_encrypt", "EVP_PKEY_decrypt"]
}
override DataFlow::Node getInput(IOType type) {
super.getInput(type) = result
or
result.asExpr() = this.getArgument(3) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
super.getOutput(type) = result
or
result.asExpr() = this.getArgument(1) and type = CiphertextIO()
// TODO: could indicate text lengths here, as well
}
}
/**
* An EVP cipher operation instance.
* Any operation step that is a final operation step for EVP cipher operation steps.
*/
class EvpCipherOperationInstance extends Crypto::KeyOperationInstance instanceof EvpCipherOperationFinalStep
{
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
super.getPrimaryAlgorithmValueConsumer() = result
}
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
result instanceof Crypto::TEncryptMode and
super.getTarget().getName().toLowerCase().matches("%encrypt%")
or
result instanceof Crypto::TDecryptMode and
super.getTarget().getName().toLowerCase().matches("%decrypt%")
or
super.getTarget().getName().toLowerCase().matches("%cipher%") and
resolveKeyOperationSubTypeOperationStep(super
.getDominatingInitializersToStep(KeyOperationSubtypeIO())) = result
}
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
super.getDominatingInitializersToStep(IVorNonceIO()).getInput(IVorNonceIO()) = result
}
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
super.getDominatingInitializersToStep(KeyIO()).getInput(KeyIO()) = result
}
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
super.getOutputStepFlowingToStep(CiphertextIO()).getOutput(CiphertextIO()) = result
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
super.getDominatingInitializersToStep(PlaintextIO()).getInput(PlaintextIO()) = result
}
}

View File

@@ -1,33 +0,0 @@
private import experimental.quantum.Language
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
private import semmle.code.cpp.dataflow.new.DataFlow
class ECKeyGenOperation extends OpenSslOperation, Crypto::KeyGenerationOperationInstance {
ECKeyGenOperation() { this.(Call).getTarget().getName() = "EC_KEY_generate_key" }
override Expr getAlgorithmArg() { result = this.(Call).getArgument(0) }
override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() }
override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() {
result.asExpr() = this.(Call).getArgument(0)
}
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
none() // no explicit key size, inferred from algorithm
}
override int getKeySizeFixed() {
none()
// TODO: marked as none as the operation itself has no key size, it
// comes from the algorithm source, but note we could grab the
// algorithm source and get the key size (see below).
// We may need to reconsider what is the best approach here.
// result =
// this.getAnAlgorithmValueConsumer()
// .getAKnownAlgorithmSource()
// .(Crypto::EllipticCurveInstance)
// .getKeySize()
}
}

View File

@@ -1,181 +0,0 @@
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.CtxFlow
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
// TODO: need to add key consumer
abstract class Evp_Cipher_Initializer extends EvpKeyOperationSubtypeInitializer,
EvpPrimaryAlgorithmInitializer, EvpKeyInitializer, EvpIVInitializer
{
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
}
abstract class Evp_EX_Initializer extends Evp_Cipher_Initializer {
override Expr getKeyArg() {
// Null key indicates the key is not actually set
// This pattern can occur during a multi-step initialization
// TODO/Note: not flowing 0 to the sink, assuming a direct use of NULL for now
result = this.(Call).getArgument(3) and
(exists(result.getValue()) implies result.getValue().toInt() != 0)
}
override Expr getIVArg() {
// Null IV indicates the IV is not actually set
// This occurs given that setting the IV sometimes requires first setting the IV size.
// TODO/Note: not flowing 0 to the sink, assuming a direct use of NULL for now
result = this.(Call).getArgument(4) and
(exists(result.getValue()) implies result.getValue().toInt() != 0)
}
}
abstract class Evp_EX2_Initializer extends Evp_Cipher_Initializer {
override Expr getKeyArg() { result = this.(Call).getArgument(2) }
override Expr getIVArg() { result = this.(Call).getArgument(3) }
}
class EvpCipherEXInitCall extends Evp_EX_Initializer {
EvpCipherEXInitCall() {
this.(Call).getTarget().getName() in [
"EVP_EncryptInit_ex", "EVP_DecryptInit_ex", "EVP_CipherInit_ex"
]
}
override Expr getKeyOperationSubtypeArg() {
// NOTE: for EncryptInit and DecryptInit there is no subtype arg
// the subtype is determined automatically by the initializer based on the operation name
this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and
result = this.(Call).getArgument(5)
}
}
// if this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%")
// then result instanceof Crypto::TEncryptMode
// else
// if this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%")
// then result instanceof Crypto::TDecryptMode
class Evp_Cipher_EX2_or_Simple_Init_Call extends Evp_EX2_Initializer {
Evp_Cipher_EX2_or_Simple_Init_Call() {
this.(Call).getTarget().getName() in [
"EVP_EncryptInit_ex2", "EVP_DecryptInit_ex2", "EVP_CipherInit_ex2", "EVP_EncryptInit",
"EVP_DecryptInit", "EVP_CipherInit"
]
}
override Expr getKeyOperationSubtypeArg() {
this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and
result = this.(Call).getArgument(4)
}
}
class Evp_CipherInit_SKey_Call extends Evp_EX2_Initializer {
Evp_CipherInit_SKey_Call() { this.(Call).getTarget().getName() = "EVP_CipherInit_SKEY" }
override Expr getKeyOperationSubtypeArg() { result = this.(Call).getArgument(5) }
}
class Evp_Cipher_Update_Call extends EvpUpdate {
Evp_Cipher_Update_Call() {
this.(Call).getTarget().getName() in [
"EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate"
]
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getInputArg() { result = this.(Call).getArgument(3) }
override Expr getOutputArg() { result = this.(Call).getArgument(1) }
}
/**
* see: https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis
* Base configuration for all EVP cipher operations.
*/
abstract class Evp_Cipher_Operation extends EvpOperation, Crypto::KeyOperationInstance {
override Expr getOutputArg() { result = this.(Call).getArgument(1) }
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
result instanceof Crypto::TEncryptMode and
this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%")
or
result instanceof Crypto::TDecryptMode and
this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%")
or
result = this.getInitCall().(EvpKeyOperationSubtypeInitializer).getKeyOperationSubtype() and
this.(Call).getTarget().getName().toLowerCase().matches("%cipher%")
}
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
this.getInitCall().(EvpIVInitializer).getIVArg() = result.asExpr()
}
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
this.getInitCall().(EvpKeyInitializer).getKeyArg() = result.asExpr()
// todo: or track to the EVP_PKEY_CTX_new
}
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = EvpOperation.super.getOutputArtifact()
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = EvpOperation.super.getInputConsumer()
}
}
class Evp_Cipher_Call extends EvpOperation, Evp_Cipher_Operation {
Evp_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" }
override Expr getInputArg() { result = this.(Call).getArgument(2) }
override Expr getAlgorithmArg() {
result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg()
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class Evp_Cipher_Final_Call extends EvpFinal, Evp_Cipher_Operation {
Evp_Cipher_Final_Call() {
this.(Call).getTarget().getName() in [
"EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal",
"EVP_DecryptFinal", "EVP_CipherFinal"
]
}
/**
* Output is both from update calls and from the final call.
*/
override Expr getOutputArg() {
result = EvpFinal.super.getOutputArg()
or
result = Evp_Cipher_Operation.super.getOutputArg()
}
override Expr getAlgorithmArg() {
result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg()
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
/**
* https://docs.openssl.org/3.2/man3/EVP_PKEY_decrypt/
* https://docs.openssl.org/3.2/man3/EVP_PKEY_encrypt
*/
class Evp_PKey_Cipher_Operation extends Evp_Cipher_Operation {
Evp_PKey_Cipher_Operation() {
this.(Call).getTarget().getName() in ["EVP_PKEY_encrypt", "EVP_PKEY_decrypt"]
}
override Expr getInputArg() { result = this.(Call).getArgument(3) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getAlgorithmArg() {
result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg()
}
}

View File

@@ -1,106 +0,0 @@
/**
* https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis
*/
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.CtxFlow
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
class Evp_DigestInit_Variant_Calls extends EvpPrimaryAlgorithmInitializer {
Evp_DigestInit_Variant_Calls() {
this.(Call).getTarget().getName() in [
"EVP_DigestInit", "EVP_DigestInit_ex", "EVP_DigestInit_ex2"
]
}
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class Evp_Digest_Update_Call extends EvpUpdate {
Evp_Digest_Update_Call() { this.(Call).getTarget().getName() = "EVP_DigestUpdate" }
override Expr getInputArg() { result = this.(Call).getArgument(1) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
//https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis
class Evp_Q_Digest_Operation extends EvpOperation, Crypto::HashOperationInstance {
Evp_Q_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Q_digest" }
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
override EvpInitializer getInitCall() {
// This variant of digest does not use an init
// and even if it were used, the init would be ignored/undefined
none()
}
override Expr getInputArg() { result = this.(Call).getArgument(3) }
override Expr getOutputArg() { result = this.(Call).getArgument(5) }
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = EvpOperation.super.getOutputArtifact()
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = EvpOperation.super.getInputConsumer()
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class Evp_Digest_Operation extends EvpOperation, Crypto::HashOperationInstance {
Evp_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Digest" }
// There is no context argument for this function
override CtxPointerSource getContext() { none() }
override Expr getAlgorithmArg() { result = this.(Call).getArgument(4) }
override EvpPrimaryAlgorithmInitializer getInitCall() {
// This variant of digest does not use an init
// and even if it were used, the init would be ignored/undefined
none()
}
override Expr getInputArg() { result = this.(Call).getArgument(0) }
override Expr getOutputArg() { result = this.(Call).getArgument(2) }
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = EvpOperation.super.getOutputArtifact()
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = EvpOperation.super.getInputConsumer()
}
}
class Evp_Digest_Final_Call extends EvpFinal, Crypto::HashOperationInstance {
Evp_Digest_Final_Call() {
this.(Call).getTarget().getName() in [
"EVP_DigestFinal", "EVP_DigestFinal_ex", "EVP_DigestFinalXOF"
]
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getOutputArg() { result = this.(Call).getArgument(1) }
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = EvpFinal.super.getOutputArtifact()
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = EvpFinal.super.getInputConsumer()
}
override Expr getAlgorithmArg() {
result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg()
}
}

View File

@@ -1,96 +0,0 @@
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.CtxFlow
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
class EvpKeyGenInitialize extends EvpPrimaryAlgorithmInitializer {
EvpKeyGenInitialize() {
this.(Call).getTarget().getName() in [
"EVP_PKEY_keygen_init",
"EVP_PKEY_paramgen_init"
]
}
/**
* Gets the algorithm argument.
* In this case the algorithm is encoded through the context argument.
* The context may be directly created from an algorithm consumer,
* or from a new operation off of a prior key. Either way,
* we will treat this argument as the algorithm argument.
*/
override Expr getAlgorithmArg() { result = this.getContext() }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpKeyGenOperation extends EvpOperation, Crypto::KeyGenerationOperationInstance {
DataFlow::Node keyResultNode;
EvpKeyGenOperation() {
this.(Call).getTarget().getName() in ["EVP_RSA_gen", "EVP_PKEY_Q_keygen"] and
keyResultNode.asExpr() = this
or
this.(Call).getTarget().getName() in ["EVP_PKEY_generate", "EVP_PKEY_keygen"] and
keyResultNode.asDefiningArgument() = this.(Call).getArgument(1)
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getAlgorithmArg() {
this.(Call).getTarget().getName() = "EVP_PKEY_Q_keygen" and
result = this.(Call).getArgument(0)
or
result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg()
}
override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() }
override Expr getInputArg() { none() }
override Expr getOutputArg() { result = keyResultNode.asExpr() }
override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result = keyResultNode }
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
this.(Call).getTarget().getName() = "EVP_PKEY_Q_keygen" and
result = DataFlow::exprNode(this.(Call).getArgument(3)) and
// Arg 3 (0 based) is only a key size if the 'type' parameter is RSA, however,
// as a crude approximation, assume that if the type of the argument is not a derived type
// the argument must specify a key size (this is to avoid tracing if "rsa" is in the type parameter)
not this.(Call).getArgument(3).getType().getUnderlyingType() instanceof DerivedType
or
this.(Call).getTarget().getName() = "EVP_RSA_gen" and
result = DataFlow::exprNode(this.(Call).getArgument(0))
or
result = DataFlow::exprNode(this.getInitCall().(EvpKeySizeInitializer).getKeySizeArg())
}
}
/**
* A call to `EVP_PKEY_new_mac_key` that creatse a new generic MAC key.
* Signature: EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *e, const unsigned char *key, int keylen);
*/
class EvpNewMacKey extends EvpOperation, Crypto::KeyGenerationOperationInstance {
DataFlow::Node keyResultNode;
EvpNewMacKey() {
this.(Call).getTarget().getName() = "EVP_PKEY_new_mac_key" and keyResultNode.asExpr() = this
}
override CtxPointerSource getContext() { none() }
override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TSymmetricKeyType() }
override Expr getOutputArg() { result = keyResultNode.asExpr() }
override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result = keyResultNode }
override Expr getInputArg() { none() }
override Expr getAlgorithmArg() { result = this.(Call).getArgument(0) }
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
result = DataFlow::exprNode(this.(Call).getArgument(3))
}
}
/// TODO: https://docs.openssl.org/3.0/man3/EVP_PKEY_new/#synopsis

View File

@@ -6,7 +6,6 @@
*/
import cpp
private import experimental.quantum.OpenSSL.CtxFlow
private import OpenSSLOperations
/**
@@ -14,49 +13,66 @@ private import OpenSSLOperations
* These calls initialize the context from a prior key.
* The key may be generated previously, or merely had it's
* parameters set (e.g., `EVP_PKEY_paramgen`).
* NOTE: for the case of `EVP_PKEY_paramgen`, these calls
* are encoded as context passthroughs, and any operation
* will get all associated initializers for the paramgen
* at the final keygen operation automatically.
*/
class EvpNewKeyCtx extends EvpKeyInitializer {
class EvpNewKeyCtx extends OperationStep instanceof Call {
Expr keyArg;
EvpNewKeyCtx() {
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_new" and
keyArg = this.(Call).getArgument(0)
this.getTarget().getName() = "EVP_PKEY_CTX_new" and
keyArg = this.getArgument(0)
or
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and
keyArg = this.(Call).getArgument(1)
this.getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and
keyArg = this.getArgument(1)
}
/**
* Context is returned
*/
override CtxPointerSource getContext() { result = this }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = keyArg and type = KeyIO()
or
this.getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and
result.asExpr() = this.getArgument(0) and
type = OsslLibContextIO()
}
override Expr getKeyArg() { result = keyArg }
override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = ContextIO() }
override OperationStepType getStepType() { result = ContextCreationStep() }
}
/**
* A call to "EVP_PKEY_CTX_set_ec_paramgen_curve_nid".
* Note that this is a primary algorithm as the pattenr is to specify an "EC" context,
* then set the specific curve later. Although the curve is set later, it is the primary
* algorithm intended for an operation.
*/
class EvpCtxSetPrimaryAlgorithmInitializer extends EvpPrimaryAlgorithmInitializer {
EvpCtxSetPrimaryAlgorithmInitializer() {
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_ec_paramgen_curve_nid"
class EvpCtxSetEcParamgenCurveNidInitializer extends OperationStep {
EvpCtxSetEcParamgenCurveNidInitializer() {
this.getTarget().getName() = "EVP_PKEY_CTX_set_ec_paramgen_curve_nid"
}
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = PrimaryAlgorithmIO()
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
class EvpCtxSetHashAlgorithmInitializer extends EvpHashAlgorithmInitializer {
EvpCtxSetHashAlgorithmInitializer() {
this.(Call).getTarget().getName() in [
/**
* A call to the following:
* - `EVP_PKEY_CTX_set_signature_md`
* - `EVP_PKEY_CTX_set_rsa_mgf1_md_name`
* - `EVP_PKEY_CTX_set_rsa_mgf1_md`
* - `EVP_PKEY_CTX_set_rsa_oaep_md_name`
* - `EVP_PKEY_CTX_set_rsa_oaep_md`
* - `EVP_PKEY_CTX_set_dsa_paramgen_md`
* - `EVP_PKEY_CTX_set_dh_kdf_md`
* - `EVP_PKEY_CTX_set_ecdh_kdf_md`
*/
class EvpCtxSetHashInitializer extends OperationStep {
EvpCtxSetHashInitializer() {
this.getTarget().getName() in [
"EVP_PKEY_CTX_set_signature_md", "EVP_PKEY_CTX_set_rsa_mgf1_md_name",
"EVP_PKEY_CTX_set_rsa_mgf1_md", "EVP_PKEY_CTX_set_rsa_oaep_md_name",
"EVP_PKEY_CTX_set_rsa_oaep_md", "EVP_PKEY_CTX_set_dsa_paramgen_md",
@@ -64,56 +80,95 @@ class EvpCtxSetHashAlgorithmInitializer extends EvpHashAlgorithmInitializer {
]
}
override Expr getHashAlgorithmArg() { result = this.(Call).getArgument(1) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpCtxSetKeySizeInitializer extends EvpKeySizeInitializer {
Expr arg;
EvpCtxSetKeySizeInitializer() {
this.(Call).getTarget().getName() in [
"EVP_PKEY_CTX_set_rsa_keygen_bits", "EVP_PKEY_CTX_set_dsa_paramgen_bits",
"EVP_CIPHER_CTX_set_key_length"
] and
arg = this.(Call).getArgument(1)
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_mac_key" and
arg = this.(Call).getArgument(2)
result.asExpr() = this.getArgument(1) and type = HashAlgorithmIO()
}
override Expr getKeySizeArg() { result = arg }
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override OperationStepType getStepType() { result = InitializerStep() }
}
class EvpCtxSetKeyInitializer extends EvpKeyInitializer {
EvpCtxSetKeyInitializer() { this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_mac_key" }
override Expr getKeyArg() { result = this.(Call).getArgument(1) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpCtxSetPaddingInitializer extends EvpPaddingInitializer {
EvpCtxSetPaddingInitializer() {
this.(Call).getTarget().getName() in [
"EVP_PKEY_CTX_set_rsa_padding", "EVP_CIPHER_CTX_set_padding"
/**
* A call to `EVP_PKEY_CTX_set_rsa_keygen_bits`, `EVP_PKEY_CTX_set_dsa_paramgen_bits`,
* or `EVP_CIPHER_CTX_set_key_length`.
*/
class EvpCtxSetKeySizeInitializer extends OperationStep {
EvpCtxSetKeySizeInitializer() {
this.getTarget().getName() in [
"EVP_PKEY_CTX_set_rsa_keygen_bits", "EVP_PKEY_CTX_set_dsa_paramgen_bits",
"EVP_CIPHER_CTX_set_key_length"
]
}
override Expr getPaddingArg() { result = this.(Call).getArgument(1) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpCtxSetSaltLengthInitializer extends EvpSaltLengthInitializer {
EvpCtxSetSaltLengthInitializer() {
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_rsa_pss_saltlen"
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = KeySizeIO()
}
override Expr getSaltLengthArg() { result = this.(Call).getArgument(1) }
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override OperationStepType getStepType() { result = InitializerStep() }
}
class EvpCtxSetMacKeyInitializer extends OperationStep {
EvpCtxSetMacKeyInitializer() { this.getTarget().getName() = "EVP_PKEY_CTX_set_mac_key" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(2) and type = KeySizeIO()
or
// the raw key that is configured into the output key
result.asExpr() = this.getArgument(1) and type = KeyIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
class EvpCtxSetPaddingInitializer extends OperationStep {
EvpCtxSetPaddingInitializer() {
this.getTarget().getName() in ["EVP_PKEY_CTX_set_rsa_padding", "EVP_CIPHER_CTX_set_padding"]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = PaddingAlgorithmIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
class EvpCtxSetSaltLengthInitializer extends OperationStep {
EvpCtxSetSaltLengthInitializer() {
this.getTarget().getName() = "EVP_PKEY_CTX_set_rsa_pss_saltlen"
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = SaltLengthIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}

View File

@@ -1,200 +0,0 @@
/**
* Provides classes for modeling OpenSSL's EVP signature operations
*/
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.AvcFlow
private import experimental.quantum.OpenSSL.CtxFlow
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
private import experimental.quantum.OpenSSL.Operations.OpenSSLOperations
// TODO: verification functions
class EvpSignatureDigestInitializer extends EvpHashAlgorithmInitializer {
Expr arg;
EvpSignatureDigestInitializer() {
this.(Call).getTarget().getName() in ["EVP_DigestSignInit_ex", "EVP_DigestSignInit"] and
arg = this.(Call).getArgument(2)
or
this.(Call).getTarget().getName() in ["EVP_SignInit", "EVP_SignInit_ex"] and
arg = this.(Call).getArgument(1)
}
override Expr getHashAlgorithmArg() { result = arg }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpSignatureKeyInitializer extends EvpKeyInitializer {
Expr arg;
EvpSignatureKeyInitializer() {
this.(Call).getTarget().getName() = "EVP_DigestSignInit_ex" and
arg = this.(Call).getArgument(5)
or
this.(Call).getTarget().getName() = "EVP_DigestSignInit" and
arg = this.(Call).getArgument(4)
}
override Expr getKeyArg() { result = arg }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpSignaturePrimaryAlgorithmInitializer extends EvpPrimaryAlgorithmInitializer {
Expr arg;
EvpSignaturePrimaryAlgorithmInitializer() {
// signature algorithm
this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] and
arg = this.(Call).getArgument(1)
or
// configuration through the context argument
this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex"] and
arg = this.getContext()
}
override Expr getAlgorithmArg() { result = arg }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class Evp_Signature_Update_Call extends EvpUpdate {
Evp_Signature_Update_Call() {
this.(Call).getTarget().getName() in [
"EVP_DigestSignUpdate", "EVP_SignUpdate", "EVP_PKEY_sign_message_update"
]
}
/**
* Input is the message to sign.
*/
override Expr getInputArg() { result = this.(Call).getArgument(1) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
/**
* We model output explicit output arguments as predicate to use it in constructors.
* The predicate must cover all EVP_Signature_Operation subclasses.
*/
pragma[inline]
private Expr signatureOperationOutputArg(Call call) {
if call.getTarget().getName() = "EVP_SignFinal_ex"
then result = call.getArgument(2)
else result = call.getArgument(1)
}
/**
* The base configuration for all EVP signature operations.
*/
abstract class EvpSignatureOperation extends EvpOperation, Crypto::SignatureOperationInstance {
EvpSignatureOperation() {
this.(Call).getTarget().getName().matches("EVP_%") and
// NULL output argument means the call is to get the size of the signature and such call is not an operation
(
not exists(signatureOperationOutputArg(this).getValue())
or
signatureOperationOutputArg(this).getValue() != "0"
)
}
Expr getHashAlgorithmArg() {
this.getInitCall().(EvpHashAlgorithmInitializer).getHashAlgorithmArg() = result
}
override Expr getAlgorithmArg() {
this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() = result
}
override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() {
AvcToCallArgFlow::flow(result.(OpenSslAlgorithmValueConsumer).getResultNode(),
DataFlow::exprNode(this.getHashAlgorithmArg()))
}
/**
* Signing, verification or unknown.
*/
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
// TODO: if this KeyOperationSubtype does not match initialization call's KeyOperationSubtype then we found a bug
if this.(Call).getTarget().getName().toLowerCase().matches("%sign%")
then result instanceof Crypto::TSignMode
else
if this.(Call).getTarget().getName().toLowerCase().matches("%verify%")
then result instanceof Crypto::TVerifyMode
else result instanceof Crypto::TUnknownKeyOperationMode
}
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
// TODO: some signing operations may have explicit nonce generators
none()
}
/**
* Keys provided in the initialization call or in a context are found by this method.
* Keys in explicit arguments are found by overridden methods in extending classes.
*/
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
result = DataFlow::exprNode(this.getInitCall().(EvpKeyInitializer).getKeyArg())
}
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = EvpOperation.super.getOutputArtifact()
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = EvpOperation.super.getInputConsumer()
}
/**
* TODO: only signing operations for now, change when verificaiton is added
*/
override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { none() }
}
class Evp_Signature_Call extends EvpSignatureOperation {
Evp_Signature_Call() { this.(Call).getTarget().getName() in ["EVP_DigestSign", "EVP_PKEY_sign"] }
/**
* Output is the signature.
*/
override Expr getOutputArg() { result = signatureOperationOutputArg(this) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
/**
* Input is the message to sign.
*/
override Expr getInputArg() { result = this.(Call).getArgument(3) }
}
class Evp_Signature_Final_Call extends EvpFinal, EvpSignatureOperation {
Evp_Signature_Final_Call() {
this.(Call).getTarget().getName() in [
"EVP_DigestSignFinal",
"EVP_SignFinal_ex",
"EVP_SignFinal",
"EVP_PKEY_sign_message_final"
]
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getAlgorithmArg() {
this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() = result
}
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
// key provided as an argument
this.(Call).getTarget().getName() in ["EVP_SignFinal", "EVP_SignFinal_ex"] and
result = DataFlow::exprNode(this.(Call).getArgument(3))
or
// or find key in the initialization call
result = EvpSignatureOperation.super.getKeyConsumer()
}
/**
* Output is the signature.
*/
override Expr getOutputArg() { result = signatureOperationOutputArg(this) }
}

View File

@@ -0,0 +1,134 @@
/**
* https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis
*/
private import experimental.quantum.Language
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
/**
* A call to and EVP digest initializer, such as:
* - `EVP_DigestInit`
* - `EVP_DigestInit_ex`
* - `EVP_DigestInit_ex2`
*/
class EvpDigestInitVariantCalls extends OperationStep instanceof Call {
EvpDigestInitVariantCalls() {
this.getTarget().getName() in ["EVP_DigestInit", "EVP_DigestInit_ex", "EVP_DigestInit_ex2"]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = PrimaryAlgorithmIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and
type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
/**
* A call to `EVP_DigestUpdate`.
*/
class EvpDigestUpdateCall extends OperationStep instanceof Call {
EvpDigestUpdateCall() { this.getTarget().getName() = "EVP_DigestUpdate" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and
type = ContextIO()
}
override OperationStepType getStepType() { result = UpdateStep() }
}
/**
* A base class for final digest operations.
*/
abstract class EvpFinalDigestOperationStep extends OperationStep {
override OperationStepType getStepType() { result = FinalStep() }
}
/**
* A call to `EVP_Q_digest`
* https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis
*/
class EvpQDigestOperation extends EvpFinalDigestOperationStep instanceof Call {
EvpQDigestOperation() { this.getTarget().getName() = "EVP_Q_digest" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(1) and type = PrimaryAlgorithmIO()
or
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(3) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and
type = ContextIO()
or
result.asDefiningArgument() = this.getArgument(5) and type = DigestIO()
}
}
class EvpDigestOperation extends EvpFinalDigestOperationStep instanceof Call {
EvpDigestOperation() { this.getTarget().getName() = "EVP_Digest" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(4) and type = PrimaryAlgorithmIO()
or
result.asExpr() = this.getArgument(0) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asDefiningArgument() = this.getArgument(2) and type = DigestIO()
}
}
/**
* A call to EVP_DigestFinal variants
*/
class EvpDigestFinalCall extends EvpFinalDigestOperationStep instanceof Call {
EvpDigestFinalCall() {
this.getTarget().getName() in ["EVP_DigestFinal", "EVP_DigestFinal_ex", "EVP_DigestFinalXOF"]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and
type = ContextIO()
or
result.asDefiningArgument() = this.getArgument(1) and type = DigestIO()
}
}
/**
* An openssl digest final hash operation instance
*/
class EvpDigestFinalOperationInstance extends Crypto::HashOperationInstance instanceof EvpFinalDigestOperationStep
{
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
super.getPrimaryAlgorithmValueConsumer() = result
}
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
super.getOutputStepFlowingToStep(DigestIO()).getOutput(DigestIO()) = result
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
super.getDominatingInitializersToStep(PlaintextIO()).getInput(PlaintextIO()) = result
}
}

View File

@@ -0,0 +1,204 @@
private import experimental.quantum.Language
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
/**
* A call to EC_KEY_generate_key, which is used to generate an EC key pair.
* Note: this is an operation, though the input parameter is a "EC_KEY*".
* EC_KEY is really an empty context for a key that hasn't been generated, hence
* we consider this an operation generating a key and not accepting a key input.
*/
class ECKeyGen extends OperationStep instanceof Call {
//, Crypto::KeyGenerationOperationInstance {
ECKeyGen() { this.(Call).getTarget().getName() = "EC_KEY_generate_key" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.(Call).getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = KeyIO() }
override OperationStepType getStepType() { result = ContextCreationStep() }
}
/**
* A call to EVP_PKEY_keygen_init or EVP_PKEY_paramgen_init.
*/
class EvpKeyGenInitialize extends OperationStep {
EvpKeyGenInitialize() {
this.getTarget().getName() in [
"EVP_PKEY_keygen_init",
"EVP_PKEY_paramgen_init"
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
abstract class KeyGenFinalOperationStep extends OperationStep {
override OperationStepType getStepType() { result = FinalStep() }
}
/**
* A call to `EVP_PKEY_Q_keygen`
*/
class EvpPKeyQKeyGen extends KeyGenFinalOperationStep instanceof Call {
EvpPKeyQKeyGen() { this.getTarget().getName() = "EVP_PKEY_Q_keygen" }
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this and type = KeyIO()
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
// When arg 3 is a derived type, it is a curve name, otherwise it is a key size for RSA if provided
// and arg 2 is the algorithm type
this.getArgument(3).getType().getUnderlyingType() instanceof DerivedType and
result.asExpr() = this.getArgument(3) and
type = PrimaryAlgorithmIO()
or
not this.getArgument(3).getType().getUnderlyingType() instanceof DerivedType and
result.asExpr() = this.getArgument(2) and
type = PrimaryAlgorithmIO()
or
not this.getArgument(3).getType().getUnderlyingType() instanceof DerivedType and
result.asExpr() = this.getArgument(3) and
type = KeySizeIO()
}
}
/**
* A call to `EVP_RSA_gen`
*/
class EvpRsaGen extends KeyGenFinalOperationStep instanceof Call {
EvpRsaGen() { this.getTarget().getName() = "EVP_RSA_gen" }
override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = KeyIO() }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = KeySizeIO()
}
}
/**
* A call to RSA_generate_key
*/
class RsaGenerateKey extends KeyGenFinalOperationStep instanceof Call {
RsaGenerateKey() { this.getTarget().getName() = "RSA_generate_key" }
override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = KeyIO() }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = KeySizeIO()
}
}
/**
* A call to RSA_generate_key_ex
*/
class RsaGenerateKeyEx extends KeyGenFinalOperationStep instanceof Call {
RsaGenerateKeyEx() { this.getTarget().getName() = "RSA_generate_key_ex" }
override DataFlow::Node getOutput(IOType type) {
result.asDefiningArgument() = this.getArgument(0) and type = KeyIO()
}
override DataFlow::Node getInput(IOType type) {
// arg 0 comes in as a blank RSA key, which we consider a context,
// on output it is considered a key
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
}
/**
* A call to `EVP_PKEY_generate` or `EVP_PKEY_keygen`.
*/
class EvpPkeyGen extends KeyGenFinalOperationStep instanceof Call {
EvpPkeyGen() { this.getTarget().getName() in ["EVP_PKEY_generate", "EVP_PKEY_keygen"] }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asDefiningArgument() = this.getArgument(1) and type = KeyIO()
or
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
}
/**
* A call to `EVP_PKEY_new_mac_key` that creates a new generic MAC key.
* - EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *e, const unsigned char *key, int keylen);
*/
class EvpNewMacKey extends KeyGenFinalOperationStep {
EvpNewMacKey() { this.getTarget().getName() = "EVP_PKEY_new_mac_key" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
// the raw key that is configured into the output key
result.asExpr() = this.getArgument(2) and type = KeyIO()
or
result.asExpr() = this.getArgument(3) and type = KeySizeIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this and type = KeyIO()
or
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
}
/// TODO: https://docs.openssl.org/3.0/man3/EVP_PKEY_new/#synopsis
/**
* An `KeyGenerationOperationInstance` for the for all key gen final operation steps.
*/
class KeyGenOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGenFinalOperationStep
{
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
super.getPrimaryAlgorithmValueConsumer() = result
}
override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() }
override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() {
super.getOutputStepFlowingToStep(KeyIO()).getOutput(KeyIO()) = result
}
override predicate hasKeyValueConsumer() {
exists(OperationStep s | s.flowsToOperationStep(this) and s.setsValue(KeyIO()))
}
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
super.getDominatingInitializersToStep(KeySizeIO()).getInput(KeySizeIO()) = result
}
override int getKeySizeFixed() {
none()
// TODO: marked as none as the operation itself has no key size, it
// comes from the algorithm source, but note we could grab the
// algorithm source and get the key size (see below).
// We may need to reconsider what is the best approach here.
// result =
// this.getAnAlgorithmValueConsumer()
// .getAKnownAlgorithmSource()
// .(Crypto::EllipticCurveInstance)
// .getKeySize()
}
override Crypto::ConsumerInputDataFlowNode getKeyValueConsumer() {
super.getDominatingInitializersToStep(KeyIO()).getInput(KeyIO()) = result
}
}

View File

@@ -1,316 +1,523 @@
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.AvcFlow
private import experimental.quantum.OpenSSL.CtxFlow
private import experimental.quantum.OpenSSL.KeyFlow
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
import semmle.code.cpp.dataflow.new.DataFlow
// Importing these intializers here to ensure the are part of any model that is
// using OpenSslOperationBase. This further ensures that initializers are tied to opeartions
// even if only importing the operation by itself.
import EVPPKeyCtxInitializer
/**
* An openSSL CTX type, which is type for which the stripped underlying type
* matches the pattern 'evp_%ctx_%st'.
* This includes types like:
* - EVP_CIPHER_CTX
* - EVP_MD_CTX
* - EVP_PKEY_CTX
*/
class CtxType extends Type {
CtxType() {
// It is possible for users to use the underlying type of the CTX variables
// these have a name matching 'evp_%ctx_%st
this.getUnspecifiedType().stripType().getName().matches("evp_%ctx_%st")
or
// In principal the above check should be sufficient, but in case of build mode none issues
// i.e., if a typedef cannot be resolved,
// or issues with properly stubbing test cases, we also explicitly check for the wrapping type defs
// i.e., patterns matching 'EVP_%_CTX'
exists(Type base | base = this or base = this.(DerivedType).getBaseType() |
base.getName().matches("EVP_%_CTX")
)
}
}
/**
* A pointer to a CtxType
*/
class CtxPointerExpr extends Expr {
CtxPointerExpr() {
this.getType() instanceof CtxType and
this.getType() instanceof PointerType
}
}
/**
* A call argument of type CtxPointerExpr.
*/
class CtxPointerArgument extends CtxPointerExpr {
CtxPointerArgument() { exists(Call c | c.getAnArgument() = this) }
Call getCall() { result.getAnArgument() = this }
}
/**
* The type of inputs and ouputs for an `OperationStep`.
*/
newtype TIOType =
CiphertextIO() or
// Used for typical CTX types, but not for OSSL_PARAM or OSSL_LIB_CTX
// For OSSL_PARAM and OSSL_LIB_CTX use of OsslParamIO and OsslLibContextIO
ContextIO() or
DigestIO() or
HashAlgorithmIO() or
IVorNonceIO() or
KeyIO() or
KeyOperationSubtypeIO() or
KeySizeIO() or
// Used for OSSL_LIB_CTX
OsslLibContextIO() or
// Used for OSSL_PARAM
OsslParamIO() or
MacIO() or
PaddingAlgorithmIO() or
// Plaintext also includes a message for digest, signature, verification, and mac generation
PlaintextIO() or
PrimaryAlgorithmIO() or
RandomSourceIO() or
SaltLengthIO() or
SeedIO() or
SignatureIO()
private string ioTypeToString(TIOType t) {
t = CiphertextIO() and result = "CiphertextIO"
or
t = ContextIO() and result = "ContextIO"
or
t = DigestIO() and result = "DigestIO"
or
t = HashAlgorithmIO() and result = "HashAlgorithmIO"
or
t = IVorNonceIO() and result = "IVorNonceIO"
or
t = KeyIO() and result = "KeyIO"
or
t = KeyOperationSubtypeIO() and result = "KeyOperationSubtypeIO"
or
t = KeySizeIO() and result = "KeySizeIO"
or
t = OsslLibContextIO() and result = "OsslLibContextIO"
or
t = OsslParamIO() and result = "OsslParamIO"
or
t = MacIO() and result = "MacIO"
or
t = PaddingAlgorithmIO() and result = "PaddingAlgorithmIO"
or
t = PlaintextIO() and result = "PlaintextIO"
or
t = PrimaryAlgorithmIO() and result = "PrimaryAlgorithmIO"
or
t = RandomSourceIO() and result = "RandomSourceIO"
or
t = SaltLengthIO() and result = "SaltLengthIO"
or
t = SeedIO() and result = "SeedIO"
or
t = SignatureIO() and result = "SignatureIO"
}
class IOType extends TIOType {
string toString() {
result = ioTypeToString(this)
or
not exists(ioTypeToString(this)) and result = "UnknownIOType"
}
}
//TODO: add more initializers as needed
/**
* The type of step in an `OperationStep`.
* - `ContextCreationStep`: the creation of a context from an algorithm or key.
* for example `EVP_MD_CTX_create(EVP_sha256())` or `EVP_PKEY_CTX_new(pkey, NULL)`
* - `InitializerStep`: the initialization of an operation through some sort of shared/accumulated context
* for example `EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)`
* - `UpdateStep`: any operation that has and update/final paradigm, the update represents an intermediate step in an operation,
* such as `EVP_DigestUpdate(ctx, data, len)`
* - `FinalStep`: an ultimate operation step. This may be an explicit 'final' in an update/final paradigm, but not necessarily.
* Any operation that does nto operate through an update/final paradigm is considered a final step.
*/
newtype OperationStepType =
// Context creation captures cases where a context is created from an algorithm or key
//
ContextCreationStep() or
InitializerStep() or
UpdateStep() or
FinalStep()
/**
* A step in configuring an operation.
* Captures creation of contexts from algorithms or keys,
* initalization of configurations on contexts,
* update operations (intermediate steps in an operation)
* and the operation itself.
*
* NOTE: if an operation is configured through a means other than a call
* e.g., a pattern like ctx->alg = EVP_sha256()
* then this class will need to be modified to account for that paradigm.
* Currently, this is not a known pattern in OpenSSL.
*/
abstract class OperationStep extends Call {
/**
* Gets the output nodes from the given operation step.
* These are the nodes that flow connecting this step
* to any other step in the operation should follow.
*/
abstract DataFlow::Node getOutput(IOType type);
/**
* Gets any output node from the given operation step.
*/
final DataFlow::Node getAnOutput() { result = this.getOutput(_) }
/**
* Gets the input nodes for the given operation step.
*/
abstract DataFlow::Node getInput(IOType type);
/**
* Gets any input node for the given operation step.
*/
final DataFlow::Node getAnInput() { result = this.getInput(_) }
/**
* Gets the type of the step, e.g., ContextCreationStep, InitializerStep, UpdateStep, FinalStep.
*/
abstract OperationStepType getStepType();
/**
* Holds if this operation step flows to the given `OperationStep` `sink`.
* If `sink` is `this`, then this holds true.
*/
predicate flowsToOperationStep(OperationStep sink) {
sink = this or
OperationStepFlow::flow(this.getAnOutput(), sink.getAnInput())
}
/**
* Holds if this operation step flows from the given `OperationStep` (`source`).
* If `source` is `this`, then this holds true.
*/
predicate flowsFromOperationStep(OperationStep source) {
source = this or
OperationStepFlow::flow(source.getAnOutput(), this.getAnInput())
}
/**
* Holds if this operation step sets a value of the given `IOType`.
*/
predicate setsValue(IOType type) { exists(this.getInput(type)) }
/**
* Gets operation steps that flow to `this` and set the given `IOType`.
* This checks for the last initializers that flow to the `this`,
* i.e., if a value is set then re-set, the last set operation step is returned,
* not both.
* Note: Any 'update' that sets a value is not considered to be 'resetting' an input.
* I.e., there is a difference between changing a configuration before use and
* the operation allows for multiple inputs (like plaintext for cipher update calls before final).
*/
OperationStep getDominatingInitializersToStep(IOType type) {
result.flowsToOperationStep(this) and
result.setsValue(type) and
(
// Do not consider a 'reset' to occur on updates
result.getStepType() = UpdateStep()
or
not exists(OperationStep reset |
result != reset and
reset.setsValue(type) and
reset.flowsToOperationStep(this) and
result.flowsToOperationStep(reset)
)
)
}
/**
* Gets all output of `type` that flow to `this`
* if `this` is a final step and the output is not from
* a separate final step.
*/
OperationStep getOutputStepFlowingToStep(IOType type) {
this.getStepType() = FinalStep() and
result.flowsToOperationStep(this) and
exists(result.getOutput(type)) and
(result = this or result.getStepType() != FinalStep())
}
/**
* Gets an AVC for the primary algorithm for this operation.
* A primary algorithm is an AVC that flows to a ctx input directly or
* an AVC that flows to a primary algorithm input directly.
* See `AvcContextCreationStep` for details about resetting scenarios.
* Gets the first OperationStep an AVC flows to. If a context input,
* the AVC is considered primary.
* If a primary algorithm input, then get the last set primary algorithm
* operation step (dominating operation step, see `getDominatingInitializersToStep`).
*/
Crypto::AlgorithmValueConsumer getPrimaryAlgorithmValueConsumer() {
exists(DataFlow::Node src, DataFlow::Node sink, IOType t, OperationStep avcSucc |
(t = PrimaryAlgorithmIO() or t = ContextIO()) and
avcSucc.flowsToOperationStep(this) and
src.asExpr() = result and
sink = avcSucc.getInput(t) and
AvcToOperationStepFlow::flow(src, sink) and
(
// Case 1: the avcSucc step is a dominating initialization step
t = PrimaryAlgorithmIO() and
avcSucc = this.getDominatingInitializersToStep(PrimaryAlgorithmIO())
or
// Case 2: the succ is a context input (any avcSucc is valid)
t = ContextIO()
)
)
}
/**
* Gets the algorithm value consumer for an input to `this` operation step
* of the given `type`.
* TODO: generalize to use this for `getPrimaryAlgorithmValueConsumer`
*/
Crypto::AlgorithmValueConsumer getAlgorithmValueConsumerForInput(IOType type) {
exists(DataFlow::Node src, DataFlow::Node sink |
AvcToOperationStepFlow::flow(src, sink) and
src.asExpr() = result and
sink = this.getInput(type)
)
}
}
/**
* An AVC is considered to output a 'context type', however,
* each AVC has it's own output types in practice.
* Some output algorithm containers (`EVP_get_cipherbyname`)
* some output explicit contexts (`EVP_PKEY_CTX_new_from_name`).
* The output of an AVC cannot be determined to be a primary algorithm (PrimaryAlgorithmIO), that depends
* on the use of the AVC output.
* The use is assumed to be of two forms:
* - The AVC output flows to a known input that accepts an algorithm
* e.g., `EVP_DigestInit(ctx, type)` the `type` parameter is known to be the primary algorithm.
* `EVP_SignInit(ctx, type)` the `type` parameter is known to be a digest algorithm for the signature.
* - The AVC output flows to a context initialization step
* e.g., `pkey_ctx = EVP_PKEY_CTX_new_from_name(libctx, name, propquery)` this is an AVC call, but the
* API says the output is a context. It is consumed typically by something like:
* `ctx = EVP_PKEY_keygen_init(pkey_ctx)`, but note I cannot consider the `pkey_ctx` parameter to always be a primary algorithm,
* a key gen can be inited by a prior key as well, e.g., `ctx = EVP_PKEY_CTX_new(pkey, NULL)`.
* Hence, these initialization steps take in a context that may have come from an AVC or something else,
* and therefore cannot be considered a primary algorithm.
* Assumption: The first operation step an AVC flows to will be of the above two forms.
* Resetting Algorithm Concerns and Assumptions:
* What if a user resets the algorithm through another AVC call?
* How would we detect that and only look at the 'dominating' (last set) AVC?
* From an AVC, always assess the first operation step it flows to.
* If the first step is to a context input, then we assume that reset is not possible in the same path.
* I.e., a user cannot reset the algorithm without starting an entirely new operation step chain.
* See the use patterns for `pkey_ctx = EVP_PKEY_CTX_new_from_name(...)` mentioned above. A user cannot
* reset the algorithm without calling a new `ctx = EVP_PKEY_keygen_init(pkey_ctx)`,
* i.e., subsequent flow follows the `ctx` output.
* If the first step is to any other input, then we use the `getDominatingInitializersToStep`
* to find the last AVC that set the algorithm for the operation step.
* Domination checks must occur at an operation step (e.g., at a final operation).
* This operation step does not find the dominating AVC.
* If a primary algorithm is explicitly set and and AVC is set through a context input,
* we will use both cases as primary inputs.
*/
class AvcContextCreationStep extends OperationStep instanceof OpenSslAlgorithmValueConsumer {
override DataFlow::Node getOutput(IOType type) {
type = ContextIO() and result = super.getResultNode()
}
override DataFlow::Node getInput(IOType type) { none() }
override OperationStepType getStepType() { result = ContextCreationStep() }
}
abstract private class CtxPassThroughCall extends Call {
abstract DataFlow::Node getNode1();
abstract DataFlow::Node getNode2();
}
/**
* A call whose target contains 'free' or 'reset' and has an argument of type
* CtxPointerArgument.
*/
private class CtxClearCall extends Call {
CtxClearCall() {
this.getTarget().getName().toLowerCase().matches(["%free%", "%reset%"]) and
this.getAnArgument() instanceof CtxPointerArgument
}
}
/**
* A call whose target contains 'copy' and has an argument of type
* CtxPointerArgument.
*/
private class CtxCopyOutArgCall extends CtxPassThroughCall {
DataFlow::Node n1;
DataFlow::Node n2;
CtxCopyOutArgCall() {
this.getTarget().getName().toLowerCase().matches("%copy%") and
n1.asExpr() = this.getAnArgument() and
n1.getType() instanceof CtxType and
n2.asDefiningArgument() = this.getAnArgument() and
n2.getType() instanceof CtxType and
n1.asDefiningArgument() != n2.asExpr()
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
/**
* A call whose target contains 'dup' and has an argument of type
* CtxPointerArgument.
*/
private class CtxCopyReturnCall extends CtxPassThroughCall, CtxPointerExpr {
DataFlow::Node n1;
CtxCopyReturnCall() {
this.getTarget().getName().toLowerCase().matches("%dup%") and
n1.asExpr() = this.getAnArgument() and
n1.getType() instanceof CtxType
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result.asExpr() = this }
}
// TODO: is this still needed?
/**
* A call to `EVP_PKEY_paramgen` acts as a kind of pass through.
* It's output pkey is eventually used in a new operation generating
* a fresh context pointer (e.g., `EVP_PKEY_CTX_new`).
* It is easier to model this as a pass through
* than to model the flow from the paramgen to the new key generation.
*/
private class CtxParamGenCall extends CtxPassThroughCall {
DataFlow::Node n1;
DataFlow::Node n2;
CtxParamGenCall() {
this.getTarget().getName() = "EVP_PKEY_paramgen" and
n1.asExpr() = this.getArgument(0) and
(
n2.asExpr() = this.getArgument(1)
or
n2.asDefiningArgument() = this.getArgument(1)
)
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
//TODO: I am not sure CallArgToCtxRet is needed anymore
/**
* If the current node is an argument to a function
* that returns a pointer type, immediately flow through.
* NOTE: this passthrough is required if we allow
* intermediate steps to go into variables that are not a CTX type.
* See for example `CtxParamGenCall`.
*/
private class CallArgToCtxRet extends CtxPassThroughCall, CtxPointerExpr {
DataFlow::Node n1;
DataFlow::Node n2;
CallArgToCtxRet() {
this.getAnArgument() = n1.asExpr() and
n2.asExpr() = this
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
/**
* A flow configuration from any non-final `OperationStep` to any other `OperationStep`.
*/
module OperationStepFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(OperationStep s |
s.getAnOutput() = source or
s.getAnInput() = source
)
}
predicate isSink(DataFlow::Node sink) {
exists(OperationStep s |
s.getAnInput() = sink or
s.getAnOutput() = sink
)
}
predicate isBarrier(DataFlow::Node node) {
exists(CtxClearCall c | c.getAnArgument() = node.asExpr())
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2)
or
// Flow out through all outputs from an operation step if more than one output
// is defined.
exists(OperationStep s | s.getAnInput() = node1 and s.getAnOutput() = node2)
// TODO: consideration for additional alises defined as follows:
// if an output from an operation step itself flows from the output of another operation step
// then the source of that flow's outputs (all of them) are potential aliases
}
}
module OperationStepFlow = DataFlow::Global<OperationStepFlowConfig>;
/**
* A flow from AVC to the first `OperationStep` the AVC reaches as an input.
*/
module AvcToOperationStepFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(AvcContextCreationStep s | s.getAnOutput() = source)
}
predicate isSink(DataFlow::Node sink) { exists(OperationStep s | s.getAnInput() = sink) }
predicate isBarrier(DataFlow::Node node) {
exists(CtxClearCall c | c.getAnArgument() = node.asExpr())
}
/**
* Only get the first operation step encountered.
*/
predicate isBarrierOut(DataFlow::Node node) { isSink(node) }
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2)
}
}
module AvcToOperationStepFlow = DataFlow::Global<AvcToOperationStepFlowConfig>;
module EncValToInitEncArgConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] }
predicate isSink(DataFlow::Node sink) {
exists(EvpKeyOperationSubtypeInitializer initCall |
sink.asExpr() = initCall.getKeyOperationSubtypeArg()
)
exists(OperationStep s | sink = s.getInput(KeyOperationSubtypeIO()))
}
}
module EncValToInitEncArgFlow = DataFlow::Global<EncValToInitEncArgConfig>;
private predicate argToAvc(Expr arg, Crypto::AlgorithmValueConsumer avc) {
// NOTE: because we trace through keys to their sources we must consider that the arg is an avc
// Consider this example:
// EVP_PKEY *pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, key_len);
// The key may trace into a signing operation. Tracing through the key we will get the arg taking `EVP_PKEY_HMAC`
// as the algorithm value consumer (the input node of the AVC). The output node of this AVC
// is the call return of `EVP_PKEY_new_mac_key`. If we trace from the AVC result to
// the input argument this will not be possible (from the return to the call argument is a backwards flow).
// Therefore, we must consider the input node of the AVC as the argument.
// This should only occur due to tracing through keys to find configuration data.
avc.getInputNode().asExpr() = arg
private Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) {
i = 0 and
result instanceof Crypto::TEncryptMode
or
AvcToCallArgFlow::flow(avc.(OpenSslAlgorithmValueConsumer).getResultNode(),
DataFlow::exprNode(arg))
i = 1 and result instanceof Crypto::TDecryptMode
}
/**
* A class for all OpenSsl operations.
*/
abstract class OpenSslOperation extends Crypto::OperationInstance instanceof Call {
/**
* Gets the argument that specifies the algorithm for the operation.
* This argument might not be immediately present at the specified operation.
* For example, it might be set in an initialization call.
* Modelers of the operation are resonsible for linking the operation to any
* initialization calls, and providing that argument as a returned value here.
*/
abstract Expr getAlgorithmArg();
/**
* Algorithm is specified in initialization call or is implicitly established by the key.
*/
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
argToAvc(this.getAlgorithmArg(), result)
}
}
/**
* A Call to an initialization function for an operation.
* These are not operations in the sense of Crypto::OperationInstance,
* but they are used to initialize the context for the operation.
* There may be multiple initialization calls for the same operation.
* Intended for use with EvPOperation.
*/
abstract class EvpInitializer extends Call {
/**
* Gets the context argument or return that ties together initialization, updates and/or final calls.
* The context is the context coming into the initializer and is the output as well.
* This is assumed to be the same argument.
*/
abstract CtxPointerSource getContext();
}
/**
* A call to initialize a key size.
*/
abstract class EvpKeySizeInitializer extends EvpInitializer {
abstract Expr getKeySizeArg();
}
/**
* A call to initialize a key operation subtype.
*/
abstract class EvpKeyOperationSubtypeInitializer extends EvpInitializer {
abstract Expr getKeyOperationSubtypeArg();
private Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) {
i = 0 and
result instanceof Crypto::TEncryptMode
or
i = 1 and result instanceof Crypto::TDecryptMode
}
Crypto::KeyOperationSubtype getKeyOperationSubtype() {
exists(DataFlow::Node a, DataFlow::Node b |
EncValToInitEncArgFlow::flow(a, b) and
b.asExpr() = this.getKeyOperationSubtypeArg() and
result = this.intToCipherOperationSubtype(a.asExpr().getValue().toInt())
)
or
// Infer the subtype from the initialization call, and ignore the argument
this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%") and
result instanceof Crypto::TEncryptMode
or
this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%") and
result instanceof Crypto::TDecryptMode
}
}
/**
* An primary algorithm initializer initializes the primary algorithm for a given operation.
* For example, for a signing operation, the algorithm initializer may initialize algorithms
* like RSA. Other algorithsm may be initialized on an operation, as part of a larger
* operation/protocol. For example, hashing operations on signing operations; however,
* these are not the primary algorithm. Any other algorithms initialized on an operation
* require a specialized initializer, such as EvpHashAlgorithmInitializer.
*/
abstract class EvpPrimaryAlgorithmInitializer extends EvpInitializer {
abstract Expr getAlgorithmArg();
Crypto::AlgorithmValueConsumer getAlgorithmValueConsumer() {
argToAvc(this.getAlgorithmArg(), result)
}
}
/**
* A call to initialize a key.
*/
abstract class EvpKeyInitializer extends EvpInitializer {
abstract Expr getKeyArg();
}
/**
* A key initializer may initialize the algorithm and the key size through
* the key. Extend any instance of key initializer provide initialization
* of the algorithm and key size from the key.
*/
class EvpInitializerThroughKey extends EvpPrimaryAlgorithmInitializer, EvpKeySizeInitializer,
EvpKeyInitializer
{
Expr arg;
CtxPointerSource context;
EvpInitializerThroughKey() {
exists(EvpKeyInitializer keyInit |
arg = keyInit.getKeyArg() and this = keyInit and context = keyInit.getContext()
)
}
override CtxPointerSource getContext() { result = context }
override Expr getAlgorithmArg() {
result =
getSourceKeyCreationInstanceFromArg(this.getKeyArg()).(OpenSslOperation).getAlgorithmArg()
}
override Expr getKeySizeArg() {
result = getSourceKeyCreationInstanceFromArg(this.getKeyArg()).getKeySizeConsumer().asExpr()
}
override Expr getKeyArg() { result = arg }
}
/**
* A default initializer for any key operation that accepts a key as input.
* A key initializer allows for a mechanic to go backwards to the key creation operation
* and find the algorithm and key size.
* If a user were to stipualte a key consumer for an operation but fail to indicate it as an
* initializer, automatic tracing to the creation operation would not occur.
* USERS SHOULD NOT NEED TO USE OR EXTEND THIS CLASS DIRECTLY.
*
* TODO: re-evaluate this approach
*/
class DefaultKeyInitializer extends EvpKeyInitializer instanceof Crypto::KeyOperationInstance {
Expr arg;
DefaultKeyInitializer() {
exists(Call c |
c.getAChild*() = arg and
arg = this.(Crypto::KeyOperationInstance).getKeyConsumer().asExpr() and
c = this
)
}
override Expr getKeyArg() { result = arg }
override CtxPointerSource getContext() { result = this.(EvpOperation).getContext() }
}
abstract class EvpIVInitializer extends EvpInitializer {
abstract Expr getIVArg();
}
/**
* A call to initialize padding.
*/
abstract class EvpPaddingInitializer extends EvpInitializer {
/**
* Gets the padding mode argument.
* e.g., `EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING)` argument 1 (0-based)
*/
abstract Expr getPaddingArg();
}
/**
* A call to initialize a salt length.
*/
abstract class EvpSaltLengthInitializer extends EvpInitializer {
/**
* Gets the salt length argument.
* e.g., `EVP_PKEY_CTX_set_scrypt_salt_len(ctx, 16)` argument 1 (0-based)
*/
abstract Expr getSaltLengthArg();
}
/**
* A call to initialize a hash algorithm.
*/
abstract class EvpHashAlgorithmInitializer extends EvpInitializer {
abstract Expr getHashAlgorithmArg();
Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() {
argToAvc(this.getHashAlgorithmArg(), result)
}
}
/**
* A Call to an "update" function.
* These are not operations in the sense of Crypto::OperationInstance,
* but produce intermediate results for the operation that are later finalized
* (see EvpFinal).
* Intended for use with EvPOperation.
*/
abstract class EvpUpdate extends Call {
/**
* Gets the context argument that ties together initialization, updates and/or final calls.
*/
abstract CtxPointerSource getContext();
/**
* Update calls always have some input data like plaintext or message digest.
*/
abstract Expr getInputArg();
/**
* Update calls sometimes have some output data like a plaintext.
*/
Expr getOutputArg() { none() }
}
/**
* The base class for all operations of the EVP API.
* This captures one-shot APIs (with and without an initilizer call) and final calls.
* Provides some default methods for Crypto::KeyOperationInstance class.
*/
abstract class EvpOperation extends OpenSslOperation {
/**
* Gets the context argument that ties together initialization, updates and/or final calls.
*/
abstract CtxPointerSource getContext();
/**
* Some input data like plaintext or message digest.
* Either argument provided direcly in the call or all arguments that were provided in update calls.
*/
abstract Expr getInputArg();
/**
* Some output data like ciphertext or signature.
*/
abstract Expr getOutputArg();
/**
* Finds the initialization call, may be none.
*/
EvpInitializer getInitCall() { ctxSrcToSrcFlow(result.getContext(), this.getContext()) }
Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = DataFlow::exprNode(this.getOutputArg())
}
/**
* Input consumer is the input argument of the call.
*/
Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = DataFlow::exprNode(this.getInputArg())
}
}
/**
* An EVP final call,
* which is typicall used in an update/final pattern.
* Final operations are typically identified by "final" in the name,
* e.g., "EVP_DigestFinal", "EVP_EncryptFinal", etc.
* however, this is not a strict rule.
*/
abstract class EvpFinal extends EvpOperation {
/**
* All update calls that were executed before this final call.
*/
EvpUpdate getUpdateCalls() { ctxSrcToSrcFlow(result.getContext(), this.getContext()) }
/**
* Gets the input data provided to all update calls.
* If more input data was provided in the final call, override the method.
*/
override Expr getInputArg() { result = this.getUpdateCalls().getInputArg() }
/**
* Gets the output data provided to all update calls.
* If more output data was provided in the final call, override the method.
*/
override Expr getOutputArg() { result = this.getUpdateCalls().getOutputArg() }
Crypto::KeyOperationSubtype resolveKeyOperationSubTypeOperationStep(OperationStep s) {
exists(DataFlow::Node src |
EncValToInitEncArgFlow::flow(src, s.getInput(KeyOperationSubtypeIO())) and
result = intToCipherOperationSubtype(src.asExpr().getValue().toInt())
)
}

View File

@@ -1,6 +1,5 @@
import OpenSSLOperationBase
import EVPCipherOperation
import EVPHashOperation
import ECKeyGenOperation
import EVPSignatureOperation
import EVPKeyGenOperation
import CipherOperation
import HashOperation
import SignatureOperation
import KeyGenOperation

View File

@@ -0,0 +1,260 @@
/**
* Provides classes for modeling OpenSSL's EVP signature operations
*/
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.AvcFlow
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
private import experimental.quantum.OpenSSL.Operations.OpenSSLOperations
// TODO: verification functions
/**
* A base class for final signature operations.
*/
abstract class EvpSignatureFinalOperation extends OperationStep {
override OperationStepType getStepType() { result = FinalStep() }
}
/**
* A call to EVP_DigestSignInit or EVP_DigestSignInit_ex.
*/
class EvpSignatureDigestInitializer extends OperationStep {
EvpSignatureDigestInitializer() {
this.getTarget().getName() in ["EVP_DigestSignInit_ex", "EVP_DigestSignInit"]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
this.getTarget().getName() = "EVP_DigestSignInit_ex" and
result.asExpr() = this.getArgument(3) and
type = OsslLibContextIO()
or
result.asExpr() = this.getArgument(2) and type = HashAlgorithmIO()
or
this.getTarget().getName() = "EVP_DigestSignInit" and
result.asExpr() = this.getArgument(4) and
type = KeyIO()
or
this.getTarget().getName() = "EVP_DigestSignInit_ex" and
result.asExpr() = this.getArgument(5) and
type = KeyIO()
or
this.getTarget().getName() = "EVP_DigestSignInit_ex" and
result.asExpr() = this.getArgument(6) and
type = OsslParamIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
// EVP_PKEY_CTX
result.asExpr() = this.getArgument(1) and type = ContextIO()
or
this.getTarget().getName() = "EVP_DigestSignInit_ex" and
result.asExpr() = this.getArgument(6) and
type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
/**
* A call to EVP_SignInit or EVP_SignInit_ex.
*/
class EvpSignInit extends OperationStep {
EvpSignInit() { this.getTarget().getName() in ["EVP_SignInit", "EVP_SignInit_ex"] }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = HashAlgorithmIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
/**
* A call to:
* - EVP_PKEY_sign_init_ex
* - EVP_PKEY_sign_init_ex2
* - EVP_PKEY_sign_init
* - EVP_PKEY_sign_message_init
*/
class EvpPkeySignInit extends OperationStep {
EvpPkeySignInit() {
this.getTarget().getName() in [
"EVP_PKEY_sign_init_ex", "EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_init",
"EVP_PKEY_sign_message_init"
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
this.getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] and
result.asExpr() = this.getArgument(1) and
type = PrimaryAlgorithmIO()
or
this.getTarget().getName() = "EVP_PKEY_sign_init_ex" and
result.asExpr() = this.getArgument(1) and
type = OsslParamIO()
or
// Argument 2 (0 based) only exists for EVP_PKEY_sign_init_ex2 and EVP_PKEY_sign_message_init
result.asExpr() = this.getArgument(2) and type = OsslParamIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
/**
* A call to EVP_DIgestSignUpdate, EVP_SignUpdate or EVP_PKEY_sign_message_update.
*/
class EvpSignatureUpdateCall extends OperationStep {
EvpSignatureUpdateCall() {
this.getTarget().getName() in [
"EVP_DigestSignUpdate", "EVP_SignUpdate", "EVP_PKEY_sign_message_update"
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = UpdateStep() }
}
/**
* A call to EVP_SignFinal or EVP_SignFinal_ex.
*/
class EvpSignFinal extends EvpSignatureFinalOperation {
EvpSignFinal() { this.getTarget().getName() in ["EVP_SignFinal_ex", "EVP_SignFinal"] }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(3) and type = KeyIO()
or
// params above 3 (0-based) only exist for EVP_SignFinal_ex
result.asExpr() = this.getArgument(4) and
type = OsslLibContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = SignatureIO()
}
}
/**
* A call to EVP_DigestSign or EVP_PKEY_sign.
*/
class EvpDigestSign extends EvpSignatureFinalOperation {
EvpDigestSign() { this.getTarget().getName() in ["EVP_DigestSign", "EVP_PKEY_sign"] }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(3) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = SignatureIO()
}
}
/**
* A call to EVP_DigestSignFinal or EVP_PKEY_sign_message_final.
*/
class EvpDigestAndPkeySignFinal extends EvpSignatureFinalOperation {
EvpDigestAndPkeySignFinal() {
this.getTarget().getName() in [
"EVP_DigestSignFinal",
"EVP_PKEY_sign_message_final"
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = SignatureIO()
}
override OperationStepType getStepType() { result = FinalStep() }
}
/**
* An EVP signature operation instance.
*/
class EvpSignatureOperationInstance extends Crypto::SignatureOperationInstance instanceof EvpSignatureFinalOperation
{
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
super.getPrimaryAlgorithmValueConsumer() = result
}
/**
* Signing, verification or unknown.
*/
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
// TODO: if this KeyOperationSubtype does not match initialization call's KeyOperationSubtype then we found a bug
if super.getTarget().getName().toLowerCase().matches("%sign%")
then result instanceof Crypto::TSignMode
else
if super.getTarget().getName().toLowerCase().matches("%verify%")
then result instanceof Crypto::TVerifyMode
else result instanceof Crypto::TUnknownKeyOperationMode
}
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
// TODO: some signing operations may have explicit nonce generators
none()
}
/**
* Keys provided in the initialization call or in a context are found by this method.
* Keys in explicit arguments are found by overridden methods in extending classes.
*/
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
super.getDominatingInitializersToStep(KeyIO()).getInput(KeyIO()) = result
}
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
super.getOutputStepFlowingToStep(SignatureIO()).getOutput(SignatureIO()) = result
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
super.getDominatingInitializersToStep(PlaintextIO()).getInput(PlaintextIO()) = result
}
/**
* TODO: only signing operations for now, change when verificaiton is added
*/
override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { none() }
override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() {
super
.getDominatingInitializersToStep(HashAlgorithmIO())
.getAlgorithmValueConsumerForInput(HashAlgorithmIO()) = result
}
}

View File

@@ -0,0 +1,8 @@
# partial model of the Oracle Call Interface (OCI) library
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: sinkModel
data: # namespace, type, subtypes, name, signature, ext, input, kind, provenance
- ["", "", False, "OCIStmtPrepare", "", "", "Argument[*2]", "sql-injection", "manual"]
- ["", "", False, "OCIStmtPrepare2", "", "", "Argument[*3]", "sql-injection", "manual"]

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 5.2.0
version: 5.2.1-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

View File

@@ -282,9 +282,12 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
* definition, if possible.)
*/
override Location getLocation() {
if exists(this.getDefinition())
then result = this.getDefinitionLocation()
else result = this.getADeclarationLocation()
if this instanceof BuiltInFunction
then result instanceof UnknownLocation // a dummy location for the built-in function
else
if exists(this.getDefinition())
then result = this.getDefinitionLocation()
else result = this.getADeclarationLocation()
}
/** Gets a child declaration of this function. */
@@ -896,17 +899,9 @@ class FunctionTemplateSpecialization extends Function {
* A GCC built-in function. For example: `__builtin___memcpy_chk`.
*/
class BuiltInFunction extends Function {
BuiltInFunction() { functions(underlyingElement(this), _, 6) }
/** Gets a dummy location for the built-in function. */
override Location getLocation() {
suppressUnusedThis(this) and
result instanceof UnknownDefaultLocation
}
BuiltInFunction() { builtin_functions(underlyingElement(this)) }
}
private predicate suppressUnusedThis(Function f) { any() }
/**
* A C++ user-defined literal [N4140 13.5.8].
*/

View File

@@ -8,7 +8,7 @@ import semmle.code.cpp.File
/**
* A location of a C/C++ artifact.
*/
class Location extends @location {
class Location extends @location_default {
/** Gets the container corresponding to this location. */
pragma[nomagic]
Container getContainer() { this.fullLocationInfo(result, _, _, _, _) }
@@ -53,9 +53,7 @@ class Location extends @location {
predicate fullLocationInfo(
Container container, int startline, int startcolumn, int endline, int endcolumn
) {
locations_default(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or
locations_expr(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or
locations_stmt(this, unresolveElement(container), startline, startcolumn, endline, endcolumn)
locations_default(this, unresolveElement(container), startline, startcolumn, endline, endcolumn)
}
/**
@@ -146,30 +144,32 @@ class Locatable extends Element { }
* expressions, one for statements and one for other program elements.
*/
class UnknownLocation extends Location {
UnknownLocation() { this.getFile().getAbsolutePath() = "" }
UnknownLocation() {
this.getFile().getAbsolutePath() = "" and locations_default(this, _, 0, 0, 0, 0)
}
}
/**
* A dummy location which is used when something doesn't have a location in
* the source code but needs to have a `Location` associated with it.
*
* DEPRECATED: use `UnknownLocation`
*/
class UnknownDefaultLocation extends UnknownLocation {
UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) }
}
deprecated class UnknownDefaultLocation extends UnknownLocation { }
/**
* A dummy location which is used when an expression doesn't have a
* location in the source code but needs to have a `Location` associated
* with it.
*
* DEPRECATED: use `UnknownLocation`
*/
class UnknownExprLocation extends UnknownLocation {
UnknownExprLocation() { locations_expr(this, _, 0, 0, 0, 0) }
}
deprecated class UnknownExprLocation extends UnknownLocation { }
/**
* A dummy location which is used when a statement doesn't have a location
* in the source code but needs to have a `Location` associated with it.
*
* DEPRECATED: use `UnknownLocation`
*/
class UnknownStmtLocation extends UnknownLocation {
UnknownStmtLocation() { locations_stmt(this, _, 0, 0, 0, 0) }
}
deprecated class UnknownStmtLocation extends UnknownLocation { }

View File

@@ -154,8 +154,9 @@ class MacroInvocation extends MacroAccess {
* well.
*/
Locatable getAnAffectedElement() {
inmacroexpansion(unresolveElement(result), underlyingElement(this)) or
macrolocationbind(underlyingElement(this), result.getLocation())
inmacroexpansion(unresolveElement(result), underlyingElement(this))
or
macrolocationbind(underlyingElement(this), result.getLocation()) and this != result
}
/**
@@ -259,7 +260,8 @@ predicate inMacroExpansion(Locatable element) {
inmacroexpansion(unresolveElement(element), _)
or
macroLocation(element.getLocation()) and
not topLevelMacroAccess(element)
not topLevelMacroAccess(element) and
not element.getLocation() instanceof UnknownLocation
}
/**

View File

@@ -40,7 +40,7 @@ class Namespace extends NameQualifyingElement, @namespace {
override Location getLocation() {
if strictcount(this.getADeclarationEntry()) = 1
then result = this.getADeclarationEntry().getLocation()
else result instanceof UnknownDefaultLocation
else result instanceof UnknownLocation
}
/** Gets the simple name of this namespace. */

View File

@@ -13,7 +13,7 @@ class Specifier extends Element, @specifier {
/** Gets a dummy location for the specifier. */
override Location getLocation() {
exists(this) and
result instanceof UnknownDefaultLocation
result instanceof UnknownLocation
}
override string getAPrimaryQlClass() { result = "Specifier" }

View File

@@ -105,7 +105,7 @@ class AutoType extends TypeTemplateParameter {
override string getAPrimaryQlClass() { result = "AutoType" }
override Location getLocation() { result instanceof UnknownDefaultLocation }
override Location getLocation() { result instanceof UnknownLocation }
}
/**

View File

@@ -290,7 +290,7 @@ class Type extends Locatable, @type {
*/
Type stripType() { result = this }
override Location getLocation() { result instanceof UnknownDefaultLocation }
override Location getLocation() { result instanceof UnknownLocation }
}
/**
@@ -858,6 +858,15 @@ private predicate floatingPointTypeMapping(
or
// __mfp8
kind = 62 and base = 2 and domain = TRealDomain() and realKind = 62 and extended = false
or
// _Complex __fp16
kind = 64 and base = 2 and domain = TComplexDomain() and realKind = 54 and extended = false
or
// _Complex __bf16
kind = 65 and base = 2 and domain = TComplexDomain() and realKind = 55 and extended = false
or
// _Complex std::float16_t
kind = 66 and base = 2 and domain = TComplexDomain() and realKind = 56 and extended = false
}
/**

View File

@@ -229,6 +229,49 @@ private predicate summaryModel0(
)
}
/**
* Holds if the given extension tuple `madId` should pretty-print as `model`.
*
* This predicate should only be used in tests.
*/
predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string output, string kind, string provenance
|
Extensions::sourceModel(namespace, type, subtypes, name, signature, ext, output, kind,
provenance, madId)
|
model =
"Source: " + namespace + "; " + type + "; " + subtypes + "; " + name + "; " + signature + "; "
+ ext + "; " + output + "; " + kind + "; " + provenance
)
or
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string input, string kind, string provenance
|
Extensions::sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance,
madId)
|
model =
"Sink: " + namespace + "; " + type + "; " + subtypes + "; " + name + "; " + signature + "; " +
ext + "; " + input + "; " + kind + "; " + provenance
)
or
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
string input, string output, string kind, string provenance
|
Extensions::summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind,
provenance, madId)
|
model =
"Summary: " + namespace + "; " + type + "; " + subtypes + "; " + name + "; " + signature +
"; " + ext + "; " + input + "; " + output + "; " + kind + "; " + provenance
)
}
/**
* Holds if `input` is `input0`, but with all occurrences of `@` replaced
* by `n` repetitions of `*` (and similarly for `output` and `output0`).

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