From 732e2c9f39228ffffb56a04883dc7efaf6fee5c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 08:09:17 +0000 Subject: [PATCH 01/39] Bump nlohmann_json from 3.11.3 to 3.12.0.bcr.1 Bumps [nlohmann_json](https://github.com/nlohmann/json) from 3.11.3 to 3.12.0.bcr.1. - [Release notes](https://github.com/nlohmann/json/releases) - [Changelog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md) - [Commits](https://github.com/nlohmann/json/commits) --- updated-dependencies: - dependency-name: nlohmann_json dependency-version: 3.12.0.bcr.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- MODULE.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MODULE.bazel b/MODULE.bazel index 16b4a4691f8a..6d93889d4a88 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -24,7 +24,7 @@ bazel_dep(name = "rules_python", version = "1.9.0") bazel_dep(name = "rules_shell", version = "0.7.1") bazel_dep(name = "bazel_skylib", version = "1.9.0") bazel_dep(name = "abseil-cpp", version = "20260107.1", repo_name = "absl") -bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json") +bazel_dep(name = "nlohmann_json", version = "3.12.0.bcr.1", repo_name = "json") bazel_dep(name = "fmt", version = "12.1.0-codeql.1") bazel_dep(name = "rules_kotlin", version = "2.2.2-codeql.1") bazel_dep(name = "gazelle", version = "0.50.0") From 5c2e5bfe8eee8a8e573b5fef1d8d6cbf46c9c4d9 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Sun, 5 Jul 2026 17:20:52 +0100 Subject: [PATCH 02/39] Convert qlref tests to inline expectations --- .../CWE-094-dataURL/CodeInjection.qlref | 3 +- .../Security/CWE-094-dataURL/test.js | 12 +++--- .../EnvValueAndKeyInjection.qlref | 3 +- .../CWE-099/EnvValueAndKeyInjection/test.js | 6 +-- .../EnvValueInjection/EnvValueInjection.qlref | 3 +- .../CWE-099/EnvValueInjection/test.js | 8 ++-- .../CWE-347/localsource/JsonWebToken.js | 10 ++--- ...odeJwtWithoutVerificationLocalSource.qlref | 3 +- .../Security/CWE-347/localsource/jose.js | 4 +- .../Security/CWE-347/localsource/jwtDecode.js | 4 +- .../Security/CWE-347/localsource/jwtSimple.js | 4 +- .../CWE-347/remotesource/JsonWebToken.js | 10 ++--- .../decodeJwtWithoutVerification.qlref | 3 +- .../Security/CWE-347/remotesource/jose.js | 4 +- .../CWE-347/remotesource/jwtDecode.js | 4 +- .../CWE-347/remotesource/jwtSimple.js | 4 +- .../experimental/Security/CWE-918/SSRF.qlref | 3 +- .../SsrfIpv6TransitionIncompleteGuard.qlref | 3 +- .../bad-private-ip-pkg.js | 2 +- .../bad-rfc1918-regex.js | 2 +- .../Security/CWE-918/check-domain.js | 10 ++--- .../Security/CWE-918/check-middleware.js | 2 +- .../Security/CWE-918/check-path.js | 10 ++--- .../Security/CWE-918/check-regex.js | 12 +++--- .../Security/CWE-918/check-validator.js | 14 +++---- .../MultipleArgumentsToSetConstructor.qlref | 3 +- .../MultipleArgumentsToSetConstructorBad.js | 2 +- .../MultipleArgumentsToSetConstructor/tst.js | 2 +- .../UnpromotedRouteHandlerCandidate.qlref | 3 +- .../UnpromotedRouteSetupCandidate.qlref | 3 +- .../frameworks/HTTP-heuristics/src/hapi.js | 2 +- .../frameworks/HTTP-heuristics/src/nodejs.js | 6 +-- .../HTTP-heuristics/src/route-objects.js | 14 +++---- .../frameworks/HTTP-heuristics/src/tst.js | 42 +++++++++---------- .../frameworks/Templating/CodeInjection.qlref | 3 +- .../frameworks/Templating/app.js | 26 ++++++------ .../Templating/views/angularjs_include.ejs | 4 +- .../Templating/views/angularjs_sinks.ejs | 4 +- .../frameworks/Templating/views/ejs_sinks.ejs | 6 +-- .../frameworks/Templating/views/hbs_sinks.hbs | 6 +-- .../frameworks/Templating/views/njk_sinks.njk | 10 ++--- 41 files changed, 145 insertions(+), 134 deletions(-) diff --git a/javascript/ql/test/experimental/Security/CWE-094-dataURL/CodeInjection.qlref b/javascript/ql/test/experimental/Security/CWE-094-dataURL/CodeInjection.qlref index 3caf7ab7b43b..9ed18359d203 100644 --- a/javascript/ql/test/experimental/Security/CWE-094-dataURL/CodeInjection.qlref +++ b/javascript/ql/test/experimental/Security/CWE-094-dataURL/CodeInjection.qlref @@ -1 +1,2 @@ -experimental/Security/CWE-094-dataURL/CodeInjection.ql \ No newline at end of file +query: experimental/Security/CWE-094-dataURL/CodeInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/experimental/Security/CWE-094-dataURL/test.js b/javascript/ql/test/experimental/Security/CWE-094-dataURL/test.js index a5a2e76fa3c8..b8599b5687cc 100644 --- a/javascript/ql/test/experimental/Security/CWE-094-dataURL/test.js +++ b/javascript/ql/test/experimental/Security/CWE-094-dataURL/test.js @@ -2,21 +2,21 @@ const { Worker } = require('node:worker_threads'); var app = require('express')(); app.post('/path', async function (req, res) { - const payload = req.query.queryParameter // like: payload = 'data:text/javascript,console.log("hello!");//' + const payload = req.query.queryParameter // $ Source // like: payload = 'data:text/javascript,console.log("hello!");//' let payloadURL = new URL(payload + sth) // NOT OK - new Worker(payloadURL); + new Worker(payloadURL); // $ Alert payloadURL = new URL(payload + sth) // NOT OK - new Worker(payloadURL); + new Worker(payloadURL); // $ Alert payloadURL = new URL(sth + payload) // OK new Worker(payloadURL); }); app.post('/path2', async function (req, res) { - const payload = req.query.queryParameter // like: payload = 'data:text/javascript,console.log("hello!");//' - await import(payload) // NOT OK - await import(payload + sth) // NOT OK + const payload = req.query.queryParameter // $ Source // like: payload = 'data:text/javascript,console.log("hello!");//' + await import(payload) // $ Alert // NOT OK + await import(payload + sth) // $ Alert // NOT OK await import(sth + payload) // OK }); diff --git a/javascript/ql/test/experimental/Security/CWE-099/EnvValueAndKeyInjection/EnvValueAndKeyInjection.qlref b/javascript/ql/test/experimental/Security/CWE-099/EnvValueAndKeyInjection/EnvValueAndKeyInjection.qlref index fde9a286e5a8..dbd1332e35ae 100644 --- a/javascript/ql/test/experimental/Security/CWE-099/EnvValueAndKeyInjection/EnvValueAndKeyInjection.qlref +++ b/javascript/ql/test/experimental/Security/CWE-099/EnvValueAndKeyInjection/EnvValueAndKeyInjection.qlref @@ -1 +1,2 @@ -experimental/Security/CWE-099/EnvValueAndKeyInjection.ql \ No newline at end of file +query: experimental/Security/CWE-099/EnvValueAndKeyInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/experimental/Security/CWE-099/EnvValueAndKeyInjection/test.js b/javascript/ql/test/experimental/Security/CWE-099/EnvValueAndKeyInjection/test.js index a12377c9cec9..87c151853e79 100644 --- a/javascript/ql/test/experimental/Security/CWE-099/EnvValueAndKeyInjection/test.js +++ b/javascript/ql/test/experimental/Security/CWE-099/EnvValueAndKeyInjection/test.js @@ -2,9 +2,9 @@ const http = require('node:http'); http.createServer((req, res) => { - const { EnvValue, EnvKey } = req.body; - process.env[EnvKey] = EnvValue; // NOT OK - process.env[EnvKey] = EnvValue; // NOT OK + const { EnvValue, EnvKey } = req.body; // $ Source + process.env[EnvKey] = EnvValue; // $ Alert // NOT OK + process.env[EnvKey] = EnvValue; // $ Alert // NOT OK res.end('env has been injected!'); }); diff --git a/javascript/ql/test/experimental/Security/CWE-099/EnvValueInjection/EnvValueInjection.qlref b/javascript/ql/test/experimental/Security/CWE-099/EnvValueInjection/EnvValueInjection.qlref index e03328beda4f..9fc1b79b8107 100644 --- a/javascript/ql/test/experimental/Security/CWE-099/EnvValueInjection/EnvValueInjection.qlref +++ b/javascript/ql/test/experimental/Security/CWE-099/EnvValueInjection/EnvValueInjection.qlref @@ -1 +1,2 @@ -experimental/Security/CWE-099/EnvValueInjection.ql \ No newline at end of file +query: experimental/Security/CWE-099/EnvValueInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/experimental/Security/CWE-099/EnvValueInjection/test.js b/javascript/ql/test/experimental/Security/CWE-099/EnvValueInjection/test.js index cb28f01b88b0..e95ef9cad772 100644 --- a/javascript/ql/test/experimental/Security/CWE-099/EnvValueInjection/test.js +++ b/javascript/ql/test/experimental/Security/CWE-099/EnvValueInjection/test.js @@ -1,10 +1,10 @@ const http = require('node:http'); http.createServer((req, res) => { - const { EnvValue } = req.body; - process.env["A_Critical_Env"] = EnvValue; // NOT OK - process.env[AKey] = EnvValue; // NOT OK - process.env.AKey = EnvValue; // NOT OK + const { EnvValue } = req.body; // $ Source + process.env["A_Critical_Env"] = EnvValue; // $ Alert // NOT OK + process.env[AKey] = EnvValue; // $ Alert // NOT OK + process.env.AKey = EnvValue; // $ Alert // NOT OK res.end('env has been injected!'); }); diff --git a/javascript/ql/test/experimental/Security/CWE-347/localsource/JsonWebToken.js b/javascript/ql/test/experimental/Security/CWE-347/localsource/JsonWebToken.js index 022b0bda11f4..21c5b00e4feb 100644 --- a/javascript/ql/test/experimental/Security/CWE-347/localsource/JsonWebToken.js +++ b/javascript/ql/test/experimental/Security/CWE-347/localsource/JsonWebToken.js @@ -10,18 +10,18 @@ function aJWT() { } (function () { - const UserToken = aJwt() + const UserToken = aJwt() // $ Alert // BAD: no signature verification - jwtJsonwebtoken.decode(UserToken) // NOT OK + jwtJsonwebtoken.decode(UserToken) // $ Sink // NOT OK })(); (function () { - const UserToken = aJwt() + const UserToken = aJwt() // $ Alert // BAD: no signature verification - jwtJsonwebtoken.decode(UserToken) // NOT OK - jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256", "none"] }) // NOT OK + jwtJsonwebtoken.decode(UserToken) // $ Sink // NOT OK + jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256", "none"] }) // $ Sink // NOT OK })(); (function () { diff --git a/javascript/ql/test/experimental/Security/CWE-347/localsource/decodeJwtWithoutVerificationLocalSource.qlref b/javascript/ql/test/experimental/Security/CWE-347/localsource/decodeJwtWithoutVerificationLocalSource.qlref index ee8effa049c5..36743e927596 100644 --- a/javascript/ql/test/experimental/Security/CWE-347/localsource/decodeJwtWithoutVerificationLocalSource.qlref +++ b/javascript/ql/test/experimental/Security/CWE-347/localsource/decodeJwtWithoutVerificationLocalSource.qlref @@ -1 +1,2 @@ -experimental/Security/CWE-347/decodeJwtWithoutVerificationLocalSource.ql \ No newline at end of file +query: experimental/Security/CWE-347/decodeJwtWithoutVerificationLocalSource.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/experimental/Security/CWE-347/localsource/jose.js b/javascript/ql/test/experimental/Security/CWE-347/localsource/jose.js index 625618e194dc..c1e8597147d1 100644 --- a/javascript/ql/test/experimental/Security/CWE-347/localsource/jose.js +++ b/javascript/ql/test/experimental/Security/CWE-347/localsource/jose.js @@ -9,10 +9,10 @@ function aJWT() { } (function () { - const UserToken = aJwt() + const UserToken = aJwt() // $ Alert // no signature verification - jose.decodeJwt(UserToken) // NOT OK + jose.decodeJwt(UserToken) // $ Sink // NOT OK })(); (async function () { diff --git a/javascript/ql/test/experimental/Security/CWE-347/localsource/jwtDecode.js b/javascript/ql/test/experimental/Security/CWE-347/localsource/jwtDecode.js index f3d4a40314c7..21eb58cb40d5 100644 --- a/javascript/ql/test/experimental/Security/CWE-347/localsource/jwtDecode.js +++ b/javascript/ql/test/experimental/Security/CWE-347/localsource/jwtDecode.js @@ -10,9 +10,9 @@ function aJWT() { } (function () { - const UserToken = aJwt() + const UserToken = aJwt() // $ Alert // jwt-decode // no signature verification - jwt_decode(UserToken) // NOT OK + jwt_decode(UserToken) // $ Sink // NOT OK })(); \ No newline at end of file diff --git a/javascript/ql/test/experimental/Security/CWE-347/localsource/jwtSimple.js b/javascript/ql/test/experimental/Security/CWE-347/localsource/jwtSimple.js index 73b79d86d758..320d382c76c6 100644 --- a/javascript/ql/test/experimental/Security/CWE-347/localsource/jwtSimple.js +++ b/javascript/ql/test/experimental/Security/CWE-347/localsource/jwtSimple.js @@ -10,10 +10,10 @@ function aJWT() { } (function () { - const UserToken = aJwt() + const UserToken = aJwt() // $ Alert // BAD: no signature verification - jwt_simple.decode(UserToken, getSecret(), true); // NOT OK + jwt_simple.decode(UserToken, getSecret(), true); // $ Sink // NOT OK })(); (function () { diff --git a/javascript/ql/test/experimental/Security/CWE-347/remotesource/JsonWebToken.js b/javascript/ql/test/experimental/Security/CWE-347/remotesource/JsonWebToken.js index 0e39e95b632d..d1199a226cf1 100644 --- a/javascript/ql/test/experimental/Security/CWE-347/remotesource/JsonWebToken.js +++ b/javascript/ql/test/experimental/Security/CWE-347/remotesource/JsonWebToken.js @@ -7,18 +7,18 @@ function getSecret() { return "A Safe generated random key" } app.get('/jwtJsonwebtoken1', (req, res) => { - const UserToken = req.headers.authorization; + const UserToken = req.headers.authorization; // $ Alert // BAD: no signature verification - jwtJsonwebtoken.decode(UserToken) // NOT OK + jwtJsonwebtoken.decode(UserToken) // $ Sink // NOT OK }) app.get('/jwtJsonwebtoken2', (req, res) => { - const UserToken = req.headers.authorization; + const UserToken = req.headers.authorization; // $ Alert // BAD: no signature verification - jwtJsonwebtoken.decode(UserToken) // NOT OK - jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256", "none"] }) // NOT OK + jwtJsonwebtoken.decode(UserToken) // $ Sink // NOT OK + jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256", "none"] }) // $ Sink // NOT OK }) app.get('/jwtJsonwebtoken3', (req, res) => { diff --git a/javascript/ql/test/experimental/Security/CWE-347/remotesource/decodeJwtWithoutVerification.qlref b/javascript/ql/test/experimental/Security/CWE-347/remotesource/decodeJwtWithoutVerification.qlref index 9e7ea468ee79..d37a36dbcca3 100644 --- a/javascript/ql/test/experimental/Security/CWE-347/remotesource/decodeJwtWithoutVerification.qlref +++ b/javascript/ql/test/experimental/Security/CWE-347/remotesource/decodeJwtWithoutVerification.qlref @@ -1 +1,2 @@ -experimental/Security/CWE-347/decodeJwtWithoutVerification.ql \ No newline at end of file +query: experimental/Security/CWE-347/decodeJwtWithoutVerification.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/experimental/Security/CWE-347/remotesource/jose.js b/javascript/ql/test/experimental/Security/CWE-347/remotesource/jose.js index 30c57650e336..28b7ca95f7e9 100644 --- a/javascript/ql/test/experimental/Security/CWE-347/remotesource/jose.js +++ b/javascript/ql/test/experimental/Security/CWE-347/remotesource/jose.js @@ -8,9 +8,9 @@ function getSecret() { } app.get('/jose1', (req, res) => { - const UserToken = req.headers.authorization; + const UserToken = req.headers.authorization; // $ Alert // no signature verification - jose.decodeJwt(UserToken) // NOT OK + jose.decodeJwt(UserToken) // $ Sink // NOT OK }) diff --git a/javascript/ql/test/experimental/Security/CWE-347/remotesource/jwtDecode.js b/javascript/ql/test/experimental/Security/CWE-347/remotesource/jwtDecode.js index 76a26e0df366..6bdc92ba6260 100644 --- a/javascript/ql/test/experimental/Security/CWE-347/remotesource/jwtDecode.js +++ b/javascript/ql/test/experimental/Security/CWE-347/remotesource/jwtDecode.js @@ -8,11 +8,11 @@ function getSecret() { } app.get('/jwtDecode', (req, res) => { - const UserToken = req.headers.authorization; + const UserToken = req.headers.authorization; // $ Alert // jwt-decode // no signature verification - jwt_decode(UserToken) // NOT OK + jwt_decode(UserToken) // $ Sink // NOT OK }) app.listen(port, () => { diff --git a/javascript/ql/test/experimental/Security/CWE-347/remotesource/jwtSimple.js b/javascript/ql/test/experimental/Security/CWE-347/remotesource/jwtSimple.js index 4803309e3e55..4652ffcc3c68 100644 --- a/javascript/ql/test/experimental/Security/CWE-347/remotesource/jwtSimple.js +++ b/javascript/ql/test/experimental/Security/CWE-347/remotesource/jwtSimple.js @@ -7,10 +7,10 @@ function getSecret() { return "A Safe generated random key" } app.get('/jwtSimple1', (req, res) => { - const UserToken = req.headers.authorization; + const UserToken = req.headers.authorization; // $ Alert // no signature verification - jwt_simple.decode(UserToken, getSecret(), true); // NOT OK + jwt_simple.decode(UserToken, getSecret(), true); // $ Sink // NOT OK }) app.get('/jwtSimple2', (req, res) => { diff --git a/javascript/ql/test/experimental/Security/CWE-918/SSRF.qlref b/javascript/ql/test/experimental/Security/CWE-918/SSRF.qlref index 05a9c8145e60..7819b4827f28 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/SSRF.qlref +++ b/javascript/ql/test/experimental/Security/CWE-918/SSRF.qlref @@ -1 +1,2 @@ -./experimental/Security/CWE-918/SSRF.ql \ No newline at end of file +query: ./experimental/Security/CWE-918/SSRF.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/SsrfIpv6TransitionIncompleteGuard.qlref b/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/SsrfIpv6TransitionIncompleteGuard.qlref index 50159ab72fe1..c26b3d73324a 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/SsrfIpv6TransitionIncompleteGuard.qlref +++ b/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/SsrfIpv6TransitionIncompleteGuard.qlref @@ -1 +1,2 @@ -experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard.ql \ No newline at end of file +query: experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/bad-private-ip-pkg.js b/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/bad-private-ip-pkg.js index 972d7aad9b73..53e3d3660c23 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/bad-private-ip-pkg.js +++ b/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/bad-private-ip-pkg.js @@ -8,6 +8,6 @@ async function validateUrlHost(host) { // NOT OK throw new Error('blocked private host'); } return fetch('http://' + host + '/'); -} +} // $ Alert[javascript/ssrf-ipv6-transition-incomplete-guard] module.exports = { validateUrlHost }; diff --git a/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/bad-rfc1918-regex.js b/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/bad-rfc1918-regex.js index be70a4a5e5dc..4c067666242b 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/bad-rfc1918-regex.js +++ b/javascript/ql/test/experimental/Security/CWE-918/SsrfIpv6TransitionIncompleteGuard/bad-rfc1918-regex.js @@ -13,6 +13,6 @@ function checkTargetHost(host) { // NOT OK throw new Error('blocked internal host'); } return http.get('http://' + host + '/'); -} +} // $ Alert[javascript/ssrf-ipv6-transition-incomplete-guard] module.exports = { checkTargetHost }; diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-domain.js b/javascript/ql/test/experimental/Security/CWE-918/check-domain.js index 0821140ab5fe..e46f4b0bb9e6 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/check-domain.js +++ b/javascript/ql/test/experimental/Security/CWE-918/check-domain.js @@ -13,8 +13,8 @@ const app = express(); app.get('/check-with-axios', req => { // without validation - const url = req.query.url; - axios.get(url); //SSRF + const url = req.query.url; // $ Source + axios.get(url); // $ Alert // SSRF // validating domain only const decodedURI = decodeURIComponent(req.query.url); @@ -22,8 +22,8 @@ app.get('/check-with-axios', req => { const { hostname } = url.parse(decodedURI); - if (isValidDomain(hostname, validDomains)) { - axios.get(req.query.url); //SSRF + if (isValidDomain(hostname, VALID_DOMAINS)) { + axios.get(req.query.url); // $ Alert // SSRF } }); @@ -31,4 +31,4 @@ const isValidDomain = (hostname, validDomains) => ( validDomains.some(domain => ( hostname === domain || hostname.endsWith(`.${domain}`)) ) -); \ No newline at end of file +); diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-middleware.js b/javascript/ql/test/experimental/Security/CWE-918/check-middleware.js index 2a1e6d541665..f79250ae5f70 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/check-middleware.js +++ b/javascript/ql/test/experimental/Security/CWE-918/check-middleware.js @@ -6,7 +6,7 @@ const express = require('express'); const app = express(); app.get('/check-with-axios', validationMiddleware, req => { - axios.get("test.com/" + req.query.tainted); // OK is sanitized by the middleware - False Positive + axios.get("test.com/" + req.query.tainted); // $ Alert // OK is sanitized by the middleware - False Positive }); diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-path.js b/javascript/ql/test/experimental/Security/CWE-918/check-path.js index b26e4924460f..25427ec7f8e9 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/check-path.js +++ b/javascript/ql/test/experimental/Security/CWE-918/check-path.js @@ -16,11 +16,11 @@ app.get('/check-with-axios', req => { const hardcoded = 'hardcodeado'; axios.get('test.com/' + hardcoded); // OK - axios.get('test.com/' + req.query.tainted); // SSRF + axios.get('test.com/' + req.query.tainted); // $ Alert // SSRF axios.get('test.com/' + Number(req.query.tainted)); // OK axios.get('test.com/' + req.user.id); // OK axios.get('test.com/' + encodeURIComponent(req.query.tainted)); // OK - axios.get(`/addresses/${req.query.tainted}`); // SSRF + axios.get(`/addresses/${req.query.tainted}`); // $ Alert // SSRF axios.get(`/addresses/${encodeURIComponent(req.query.tainted)}`); // OK if (Number.isInteger(req.query.tainted)) { @@ -30,11 +30,11 @@ app.get('/check-with-axios', req => { if (isValidInput(req.query.tainted)){ axios.get('test.com/' + req.query.tainted); // OK } else { - axios.get('test.com/' + req.query.tainted); // SSRF + axios.get('test.com/' + req.query.tainted); // $ Alert // SSRF } if (doesntCheckAnything(req.query.tainted)) { - axios.get('test.com/' + req.query.tainted); // SSRF + axios.get('test.com/' + req.query.tainted); // $ Alert // SSRF } if (isValidPath(req.query.tainted, VALID_PATHS)) { @@ -42,7 +42,7 @@ app.get('/check-with-axios', req => { } let baseURL = require('config').base - axios.get(`${baseURL}${req.query.tainted}`); // SSRF + axios.get(`${baseURL}${req.query.tainted}`); // $ Alert // SSRF if(!isValidInput(req.query.tainted)) { return; diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-regex.js b/javascript/ql/test/experimental/Security/CWE-918/check-regex.js index 238aa9068432..9d09d1832ef2 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/check-regex.js +++ b/javascript/ql/test/experimental/Security/CWE-918/check-regex.js @@ -13,7 +13,7 @@ app.get('/check-with-axios', req => { axios.get("test.com/" + req.query.tainted); // OK } if (req.query.tainted.match(/^.*$/)) { // anything - axios.get("test.com/" + req.query.tainted); // SSRF - False Negative + axios.get("test.com/" + req.query.tainted); // $ Alert // SSRF - False Negative } const baseURL = "test.com/" @@ -21,24 +21,24 @@ app.get('/check-with-axios', req => { axios.get(baseURL + req.params.tainted); // OK } if (!isValidPath(req.params.tainted) ) { - axios.get(baseURL + req.params.tainted); // SSRF + axios.get(baseURL + req.params.tainted); // $ Alert // SSRF } else { axios.get(baseURL + req.params.tainted); // OK } // Blacklists are not safe if (!req.query.tainted.match(/^[/\.%]+$/)) { - axios.get("test.com/" + req.query.tainted); // SSRF + axios.get("test.com/" + req.query.tainted); // $ Alert // SSRF } if (!isInBlacklist(req.params.tainted) ) { - axios.get(baseURL + req.params.tainted); // SSRF + axios.get(baseURL + req.params.tainted); // $ Alert // SSRF } if (!isValidPath(req.params.tainted)) { return; } - axios.get("test.com/" + req.query.tainted); // OK - False Positive + axios.get("test.com/" + req.query.tainted); // $ Alert // OK - False Positive if (req.query.tainted.matchAll(/^[0-9a-z]+$/g)) { // letters and numbers axios.get("test.com/" + req.query.tainted); // OK @@ -58,7 +58,7 @@ app.get('/check-with-axios', req => { axios.get(baseURL + req.params.tainted); // OK } if (!isValidPathMatchAll(req.params.tainted) ) { - axios.get(baseURL + req.params.tainted); // NOT OK - SSRF + axios.get(baseURL + req.params.tainted); // $ Alert // NOT OK - SSRF } else { axios.get(baseURL + req.params.tainted); // OK } diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-validator.js b/javascript/ql/test/experimental/Security/CWE-918/check-validator.js index dfe3314b07b3..631f032d9564 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/check-validator.js +++ b/javascript/ql/test/experimental/Security/CWE-918/check-validator.js @@ -12,7 +12,7 @@ app.get("/check-with-axios", req => { axios.get("test.com/" + req.query.tainted); // OK } if (isAlphanumeric(req.query.tainted)) { - axios.get("test.com/" + req.query.tainted); // SSRF + axios.get("test.com/" + req.query.tainted); // $ Alert // SSRF } if (validAlphanumeric(req.query.tainted)) { axios.get("test.com/" + req.query.tainted); // OK @@ -24,7 +24,7 @@ app.get("/check-with-axios", req => { axios.get("test.com/" + req.query.tainted); // OK } if (wrongValidation(req.query.tainted)) { - axios.get("test.com/" + req.query.tainted); // SSRF + axios.get("test.com/" + req.query.tainted); // $ Alert // SSRF } // numbers @@ -47,25 +47,25 @@ app.get("/check-with-axios", req => { axios.get("test.com/" + req.query.tainted); // OK } if (validHexa(req.query.tainted)) { - axios.get("test.com/" + req.query.tainted); // OK. False Positive + axios.get("test.com/" + req.query.tainted); // $ Alert // OK. False Positive } // with simple assignation - const numberURL = req.query.tainted; + const numberURL = req.query.tainted; // $ Source if (validNumber(numberURL)) { axios.get("test.com/" + numberURL); // OK } if (validNumber(numberURL)) { - axios.get("test.com/" + req.query.tainted); // OK. False Positive + axios.get("test.com/" + req.query.tainted); // $ Alert // OK. False Positive } if (validNumber(req.query.tainted)) { - axios.get("test.com/" + numberURL); // OK. False Positive + axios.get("test.com/" + numberURL); // $ Alert // OK. False Positive } if (validHexadecimal(req.query.tainted) || validHexaColor(req.query.tainted) || validDecimal(req.query.tainted) || validFloat(req.query.tainted) || validInt(req.query.tainted) || validNumber(req.query.tainted) || validOctal(req.query.tainted)) { - axios.get("test.com/" + req.query.tainted); // OK. False Positive + axios.get("test.com/" + req.query.tainted); // $ Alert // OK. False Positive } }); diff --git a/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/MultipleArgumentsToSetConstructor.qlref b/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/MultipleArgumentsToSetConstructor.qlref index 3cba54a3a0c5..51cb2f3db8ba 100644 --- a/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/MultipleArgumentsToSetConstructor.qlref +++ b/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/MultipleArgumentsToSetConstructor.qlref @@ -1 +1,2 @@ -experimental/StandardLibrary/MultipleArgumentsToSetConstructor.ql +query: experimental/StandardLibrary/MultipleArgumentsToSetConstructor.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/MultipleArgumentsToSetConstructorBad.js b/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/MultipleArgumentsToSetConstructorBad.js index 4bce4b54c1b4..ea51918032a2 100644 --- a/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/MultipleArgumentsToSetConstructorBad.js +++ b/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/MultipleArgumentsToSetConstructorBad.js @@ -1,4 +1,4 @@ -const vowels = new Set('a', 'e', 'i', 'o', 'u'); +const vowels = new Set('a', 'e', 'i', 'o', 'u'); // $ Alert function isVowel(char) { return vowels.has(char.toLowerCase()); diff --git a/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/tst.js b/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/tst.js index 7f43ae5f9664..1f12916af5b3 100644 --- a/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/tst.js +++ b/javascript/ql/test/experimental/StandardLibrary/MultipleArgumentsToSetConstructor/tst.js @@ -1,6 +1,6 @@ let xs = [1, 2, 3]; let ys = [4, 5, 6]; -new Set(...xs, ...ys); // NOT OK +new Set(...xs, ...ys); // $ Alert // NOT OK new Set([...xs, ...ys]); // OK new Set(xs); // OK new Set(); // OK \ No newline at end of file diff --git a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/UnpromotedRouteHandlerCandidate.qlref b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/UnpromotedRouteHandlerCandidate.qlref index 51fb87eed725..c383d032bc09 100644 --- a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/UnpromotedRouteHandlerCandidate.qlref +++ b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/UnpromotedRouteHandlerCandidate.qlref @@ -1 +1,2 @@ -meta/analysis-quality/UnpromotedRouteHandlerCandidate.ql \ No newline at end of file +query: meta/analysis-quality/UnpromotedRouteHandlerCandidate.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/UnpromotedRouteSetupCandidate.qlref b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/UnpromotedRouteSetupCandidate.qlref index 5ce57dc19ba9..2ef54efcfd0a 100644 --- a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/UnpromotedRouteSetupCandidate.qlref +++ b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/UnpromotedRouteSetupCandidate.qlref @@ -1 +1,2 @@ -meta/analysis-quality/UnpromotedRouteSetupCandidate.ql \ No newline at end of file +query: meta/analysis-quality/UnpromotedRouteSetupCandidate.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/hapi.js b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/hapi.js index 581e2401e05e..9ea46e7ce68f 100644 --- a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/hapi.js +++ b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/hapi.js @@ -1 +1 @@ -function handler(request, h){} +function handler(request, h){} // $ Alert[js/unpromoted-route-handler-candidate] diff --git a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/nodejs.js b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/nodejs.js index 57bcde69d531..315c6dd1379b 100644 --- a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/nodejs.js +++ b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/nodejs.js @@ -2,14 +2,14 @@ var http = require('http'); http.createServer(function(req, res){}); -unknown.createServer(function(req, res){}); +unknown.createServer(function(req, res){}); // $ Alert[js/unpromoted-route-setup-candidate] var createServer = http.createServer; createServer(function(req, res){}); http.createServer().on("request", function(req, res){}); -unknown.on("request", function(req, res){}); -unknown.once("request", function(req, res){}); +unknown.on("request", function(req, res){}); // $ Alert[js/unpromoted-route-setup-candidate] +unknown.once("request", function(req, res){}); // $ Alert[js/unpromoted-route-setup-candidate] function getHandler(){ return function(req, res){}; diff --git a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/route-objects.js b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/route-objects.js index 64dbe4555600..2abb91d0f11b 100644 --- a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/route-objects.js +++ b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/route-objects.js @@ -4,10 +4,10 @@ var app = express(); var route1 = { method: 'post', url: '/foo', - middleWares: [function(req, res){}], + middleWares: [function(req, res){}], // $ Alert[js/unpromoted-route-handler-candidate] handler(req, res) { - } + } // $ Alert[js/unpromoted-route-handler-candidate] }; app[route1.method](route1.url, route1.middleWares, route1.handler); @@ -19,14 +19,14 @@ var routes = [ url: '/foo', handler(req, res) { - } + } // $ Alert[js/unpromoted-route-handler-candidate] }, { method: 'post', url: '/foo', handler(req, res) { - } + } // $ Alert[js/unpromoted-route-handler-candidate] } ]; routes.forEach((route) => { @@ -39,7 +39,7 @@ var route2 = { url: '/foo', handler(req, res) { - } + } // $ Alert[js/unpromoted-route-handler-candidate] }; app[route2.method.toLowerCase()](route2.url, route2.handler); @@ -49,13 +49,13 @@ var route3 = { url: '/foo', handler(req, res) { - } + } // $ Alert[js/unpromoted-route-handler-candidate] }; function wrap(f){ return function(req, res){ f(req); - } + } // $ Alert[js/unpromoted-route-handler-candidate] } app[route3.method](route3.url, wrap(route3.handler)); confuse(wrap); // confuse the type inference diff --git a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/tst.js b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/tst.js index e2d6cfd1ebf0..871ddf9603b7 100644 --- a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/tst.js +++ b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/src/tst.js @@ -3,9 +3,9 @@ var app = express(); app.get('/some/path', function(req, res) {}) -someOtherApp.get('/some/path', function(req, res) {}) +someOtherApp.get('/some/path', function(req, res) {}) // $ Alert[js/unpromoted-route-setup-candidate] -someOtherApp.get('/some/path', function(request, response) {}) +someOtherApp.get('/some/path', function(request, response) {}) // $ Alert[js/unpromoted-route-setup-candidate] someOtherApp.get('/some/path', function(r) { r.acceptsCharsets() @@ -27,23 +27,23 @@ someOtherApp.get('/some/path', function(r, s, n) { n('route') }) -someOtherApp.delete('/some/path', function(req, res) {}) +someOtherApp.delete('/some/path', function(req, res) {}) // $ Alert[js/unpromoted-route-setup-candidate] someOtherApp.get('/some/path', function(req, res) {}, - function(req, res) {}) + function(req, res) {}) // $ Alert[js/unpromoted-route-setup-candidate] someOtherApp.get('/some/path', [ function(req, res) {}, function(req, res) {} -]) +]) // $ Alert[js/unpromoted-route-setup-candidate] someOtherApp.get('/some/path', function() {}, - function(req, res) {}) + function(req, res) {}) // $ Alert[js/unpromoted-route-setup-candidate] -function f(req, res) {} +function f(req, res) {} // $ Alert[js/unpromoted-route-handler-candidate] function f(ctx, next) { ctx.acceptsCharsets() @@ -51,25 +51,25 @@ function f(ctx, next) { function f(req, res) { req() -} +} // $ Alert[js/unpromoted-route-handler-candidate] function called(req,res) { -} +} // $ Alert[js/unpromoted-route-handler-candidate] called() function f(req,res) { return; -} +} // $ Alert[js/unpromoted-route-handler-candidate] function f(req,res) { return x; -} +} // $ Alert[js/unpromoted-route-handler-candidate] function adHocTestsFor_HeuristicRouteHandler() { function rh_dead(req, res) { - } + } // $ Alert[js/unpromoted-route-handler-candidate] function rh_flowToSetup(req, res) { @@ -84,7 +84,7 @@ function adHocTestsFor_HeuristicRouteHandler() { function rh_flowToHeuristicSetup(req, res) { } - unknownApp.get('/some/path', rh_flowToHeuristicSetup) + unknownApp.get('/some/path', rh_flowToHeuristicSetup) // $ Alert[js/unpromoted-route-setup-candidate] } function adHocTestsFor_HeuristicRouteSetups() { @@ -93,22 +93,22 @@ function adHocTestsFor_HeuristicRouteSetups() { } app.get('/some/path', rh); - unknownApp.get('/some/path', rh); + unknownApp.get('/some/path', rh); // $ Alert[js/unpromoted-route-setup-candidate] - unknownApp.get('/some/path', [rh]); + unknownApp.get('/some/path', [rh]); // $ Alert[js/unpromoted-route-setup-candidate] unknownApp.get('/some/path', unknown); unknownApp.get('/some/path', [unknown]); - unknownApp.get('/some/path', unknown, rh); + unknownApp.get('/some/path', unknown, rh); // $ Alert[js/unpromoted-route-setup-candidate] } function adHocTestsFor_HeuristicRouteHandler_withTracking() { function get_rh_dead() { return function rh_dead(req, res) { - } + } // $ Alert[js/unpromoted-route-handler-candidate] } var rh_dead = get_rh_dead(); @@ -134,7 +134,7 @@ function adHocTestsFor_HeuristicRouteHandler_withTracking() { } } var rh_flowToHeuristicSetup = get_rh_flowToHeuristicSetup(); - unknownApp.get('/some/path', rh_flowToHeuristicSetup) + unknownApp.get('/some/path', rh_flowToHeuristicSetup) // $ Alert[js/unpromoted-route-setup-candidate] } function adHocTestsFor_HeuristicRouteSetups_withTracking() { @@ -146,13 +146,13 @@ function adHocTestsFor_HeuristicRouteSetups_withTracking() { var rh = get_rh(); app.get('/some/path', rh); - unknownApp.get('/some/path', rh); + unknownApp.get('/some/path', rh); // $ Alert[js/unpromoted-route-setup-candidate] - unknownApp.get('/some/path', [rh]); + unknownApp.get('/some/path', [rh]); // $ Alert[js/unpromoted-route-setup-candidate] unknownApp.get('/some/path', unknown); unknownApp.get('/some/path', [unknown]); - unknownApp.get('/some/path', unknown, rh); + unknownApp.get('/some/path', unknown, rh); // $ Alert[js/unpromoted-route-setup-candidate] } diff --git a/javascript/ql/test/library-tests/frameworks/Templating/CodeInjection.qlref b/javascript/ql/test/library-tests/frameworks/Templating/CodeInjection.qlref index fe9adbf3b64d..bfeec8aec393 100644 --- a/javascript/ql/test/library-tests/frameworks/Templating/CodeInjection.qlref +++ b/javascript/ql/test/library-tests/frameworks/Templating/CodeInjection.qlref @@ -1 +1,2 @@ -Security/CWE-094/CodeInjection.ql +query: Security/CWE-094/CodeInjection.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/javascript/ql/test/library-tests/frameworks/Templating/app.js b/javascript/ql/test/library-tests/frameworks/Templating/app.js index 8666d79b644f..2822d40be5c1 100644 --- a/javascript/ql/test/library-tests/frameworks/Templating/app.js +++ b/javascript/ql/test/library-tests/frameworks/Templating/app.js @@ -12,11 +12,11 @@ app.get('/ejs', (req, res) => { }, dataInStringLiteral: req.query.dataInStringLiteral, dataInStringLiteralRaw: req.query.dataInStringLiteralRaw, - dataInGeneratedCode: req.query.dataInGeneratedCode, + dataInGeneratedCode: req.query.dataInGeneratedCode, // $ Source dataInGeneratedCodeRaw: req.query.dataInGeneratedCodeRaw, - backslashSink1: req.query.backslashSink1, + backslashSink1: req.query.backslashSink1, // $ Source backslashSink2: req.query.backslashSink2, - dataInEventHandlerString: req.query.dataInEventHandlerString, + dataInEventHandlerString: req.query.dataInEventHandlerString, // $ Source dataInEventHandlerStringRaw: req.query.dataInEventHandlerStringRaw, }); }); @@ -31,11 +31,11 @@ app.get('/hbs', (req, res) => { }, dataInStringLiteral: req.query.dataInStringLiteral, dataInStringLiteralRaw: req.query.dataInStringLiteralRaw, - dataInGeneratedCode: req.query.dataInGeneratedCode, + dataInGeneratedCode: req.query.dataInGeneratedCode, // $ Source dataInGeneratedCodeRaw: req.query.dataInGeneratedCodeRaw, - backslashSink1: req.query.backslashSink1, + backslashSink1: req.query.backslashSink1, // $ Source backslashSink2: req.query.backslashSink2, - dataInEventHandlerString: req.query.dataInEventHandlerString, + dataInEventHandlerString: req.query.dataInEventHandlerString, // $ Source dataInEventHandlerStringRaw: req.query.dataInEventHandlerStringRaw, }); }); @@ -50,20 +50,20 @@ app.get('/njk', (req, res) => { }, dataInStringLiteral: req.query.dataInStringLiteral, dataInStringLiteralRaw: req.query.dataInStringLiteralRaw, - dataInGeneratedCode: req.query.dataInGeneratedCode, - dataInGeneratedCodeRaw: req.query.dataInGeneratedCodeRaw, + dataInGeneratedCode: req.query.dataInGeneratedCode, // $ Source + dataInGeneratedCodeRaw: req.query.dataInGeneratedCodeRaw, // $ Source dataInGeneratedCodeJsonRaw: req.query.dataInGeneratedCodeJsonRaw, - backslashSink1: req.query.backslashSink1, + backslashSink1: req.query.backslashSink1, // $ Source backslashSink2: req.query.backslashSink2, - dataInEventHandlerString: req.query.dataInEventHandlerString, - dataInEventHandlerStringRaw: req.query.dataInEventHandlerStringRaw, + dataInEventHandlerString: req.query.dataInEventHandlerString, // $ Source + dataInEventHandlerStringRaw: req.query.dataInEventHandlerStringRaw, // $ Source }); }); app.get('/angularjs', (req, res) => { res.render('angularjs_sinks', { - escapedHtml: req.query.escapedHtml, - rawHtml: req.query.rawHtml, + escapedHtml: req.query.escapedHtml, // $ Source + rawHtml: req.query.rawHtml, // $ Source }); }); diff --git a/javascript/ql/test/library-tests/frameworks/Templating/views/angularjs_include.ejs b/javascript/ql/test/library-tests/frameworks/Templating/views/angularjs_include.ejs index 2d02e173275f..e9a6f436a8fe 100644 --- a/javascript/ql/test/library-tests/frameworks/Templating/views/angularjs_include.ejs +++ b/javascript/ql/test/library-tests/frameworks/Templating/views/angularjs_include.ejs @@ -1,5 +1,5 @@
- <%= escapedHtml %> - <%- rawHtml %> + <%= escapedHtml %> + <%- rawHtml %>
diff --git a/javascript/ql/test/library-tests/frameworks/Templating/views/angularjs_sinks.ejs b/javascript/ql/test/library-tests/frameworks/Templating/views/angularjs_sinks.ejs index 47105bb360ec..5dc0c0763cf3 100644 --- a/javascript/ql/test/library-tests/frameworks/Templating/views/angularjs_sinks.ejs +++ b/javascript/ql/test/library-tests/frameworks/Templating/views/angularjs_sinks.ejs @@ -1,7 +1,7 @@ - <%= escapedHtml %> - <%- rawHtml %> + <%= escapedHtml %> + <%- rawHtml %> <% include angularjs_include %> diff --git a/javascript/ql/test/library-tests/frameworks/Templating/views/ejs_sinks.ejs b/javascript/ql/test/library-tests/frameworks/Templating/views/ejs_sinks.ejs index 42dfc124a709..b25de673433b 100644 --- a/javascript/ql/test/library-tests/frameworks/Templating/views/ejs_sinks.ejs +++ b/javascript/ql/test/library-tests/frameworks/Templating/views/ejs_sinks.ejs @@ -10,15 +10,15 @@ var dataInStringLiteral = "<%= dataInStringLiteral %>"; var dataInStringLiteralRaw = "<%- dataInStringLiteralRaw %>"; - var dataInGeneratedCode = <%= dataInGeneratedCode %>; + var dataInGeneratedCode = <%= dataInGeneratedCode %>; // $ Alert var dataInGeneratedCodeRaw = <%- dataInGeneratedCodeRaw %>; - init("<%= backslashSink1 %>", "<%= backslashSink2 %>"); + init("<%= backslashSink1 %>", "<%= backslashSink2 %>"); // $ Alert var mustache = "{{ rawHtml }}"; - + <%- include('ejs_include1', { foo: rawHtml }) _%> diff --git a/javascript/ql/test/library-tests/frameworks/Templating/views/hbs_sinks.hbs b/javascript/ql/test/library-tests/frameworks/Templating/views/hbs_sinks.hbs index 198af1cd8d41..a723f95e7745 100644 --- a/javascript/ql/test/library-tests/frameworks/Templating/views/hbs_sinks.hbs +++ b/javascript/ql/test/library-tests/frameworks/Templating/views/hbs_sinks.hbs @@ -22,15 +22,15 @@ var dataInStringLiteral = "{{ dataInStringLiteral }}"; var dataInStringLiteralRaw = "{{{ dataInStringLiteralRaw }}}"; - var dataInGeneratedCode = {{ dataInGeneratedCode }}; + var dataInGeneratedCode = {{ dataInGeneratedCode }}; // $ Alert var dataInGeneratedCodeRaw = {{{ dataInGeneratedCodeRaw }}}; - init("{{ backslashSink1 }}", "{{ backslashSink2 }}"); + init("{{ backslashSink1 }}", "{{ backslashSink2 }}"); // $ Alert var ejs = "<%= rawHtml %>"; - + diff --git a/javascript/ql/test/library-tests/frameworks/Templating/views/njk_sinks.njk b/javascript/ql/test/library-tests/frameworks/Templating/views/njk_sinks.njk index fcfba32a26c8..dc34a5c0bbb1 100644 --- a/javascript/ql/test/library-tests/frameworks/Templating/views/njk_sinks.njk +++ b/javascript/ql/test/library-tests/frameworks/Templating/views/njk_sinks.njk @@ -10,16 +10,16 @@ var dataInStringLiteral = "{{ dataInStringLiteral }}"; var dataInStringLiteralRaw = "{{ dataInStringLiteralRaw | safe }}"; - var dataInGeneratedCode = {{ dataInGeneratedCode }}; - var dataInGeneratedCodeRaw = {{ dataInGeneratedCodeRaw | safe }}; + var dataInGeneratedCode = {{ dataInGeneratedCode }}; // $ Alert + var dataInGeneratedCodeRaw = {{ dataInGeneratedCodeRaw | safe }}; // $ Alert var dataInGeneratedCodeJsonRaw = {{ dataInGeneratedCodeJsonRaw | json | safe }}; - init("{{ backslashSink1 }}", "{{ backslashSink2 }}"); + init("{{ backslashSink1 }}", "{{ backslashSink2 }}"); // $ Alert var ejs = "<%= rawHtml %>"; - - + + From de7723f214b3d63060add7a59f390d3633ac903b Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Sun, 5 Jul 2026 17:07:49 +0100 Subject: [PATCH 03/39] Add SPURIOUS and MISSING tags --- .../Security/CWE-918/check-middleware.js | 2 +- .../Security/CWE-918/check-regex.js | 20 +++++++++---------- .../Security/CWE-918/check-validator.js | 16 +++++++-------- .../missing-explicit-injection.js | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-middleware.js b/javascript/ql/test/experimental/Security/CWE-918/check-middleware.js index f79250ae5f70..5895501e7bfe 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/check-middleware.js +++ b/javascript/ql/test/experimental/Security/CWE-918/check-middleware.js @@ -6,7 +6,7 @@ const express = require('express'); const app = express(); app.get('/check-with-axios', validationMiddleware, req => { - axios.get("test.com/" + req.query.tainted); // $ Alert // OK is sanitized by the middleware - False Positive + axios.get("test.com/" + req.query.tainted); // $ SPURIOUS: Alert // OK is sanitized by the middleware - False Positive }); diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-regex.js b/javascript/ql/test/experimental/Security/CWE-918/check-regex.js index 9d09d1832ef2..710a811857fd 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/check-regex.js +++ b/javascript/ql/test/experimental/Security/CWE-918/check-regex.js @@ -13,14 +13,14 @@ app.get('/check-with-axios', req => { axios.get("test.com/" + req.query.tainted); // OK } if (req.query.tainted.match(/^.*$/)) { // anything - axios.get("test.com/" + req.query.tainted); // $ Alert // SSRF - False Negative + axios.get("test.com/" + req.query.tainted); // $ Alert // SSRF } const baseURL = "test.com/" - if (isValidPath(req.params.tainted) ) { + if (isValidPath(req.params.tainted)) { axios.get(baseURL + req.params.tainted); // OK } - if (!isValidPath(req.params.tainted) ) { + if (!isValidPath(req.params.tainted)) { axios.get(baseURL + req.params.tainted); // $ Alert // SSRF } else { axios.get(baseURL + req.params.tainted); // OK @@ -30,7 +30,7 @@ app.get('/check-with-axios', req => { if (!req.query.tainted.match(/^[/\.%]+$/)) { axios.get("test.com/" + req.query.tainted); // $ Alert // SSRF } - if (!isInBlacklist(req.params.tainted) ) { + if (!isInBlacklist(req.params.tainted)) { axios.get(baseURL + req.params.tainted); // $ Alert // SSRF } @@ -38,7 +38,7 @@ app.get('/check-with-axios', req => { return; } - axios.get("test.com/" + req.query.tainted); // $ Alert // OK - False Positive + axios.get("test.com/" + req.query.tainted); // $ SPURIOUS: Alert // OK - False Positive if (req.query.tainted.matchAll(/^[0-9a-z]+$/g)) { // letters and numbers axios.get("test.com/" + req.query.tainted); // OK @@ -48,20 +48,20 @@ app.get('/check-with-axios', req => { } }); -const isValidPath = path => path.match(/^[0-9a-z]+$/); +const isValidPath = path => path.match(/^[0-9a-z]+$/); -const isInBlackList = path => path.match(/^[/\.%]+$/); +const isInBlackList = path => path.match(/^[/\.%]+$/); app.get('/check-with-axios', req => { const baseURL = "test.com/" - if (isValidPathMatchAll(req.params.tainted) ) { + if (isValidPathMatchAll(req.params.tainted)) { axios.get(baseURL + req.params.tainted); // OK } - if (!isValidPathMatchAll(req.params.tainted) ) { + if (!isValidPathMatchAll(req.params.tainted)) { axios.get(baseURL + req.params.tainted); // $ Alert // NOT OK - SSRF } else { axios.get(baseURL + req.params.tainted); // OK } }); -const isValidPathMatchAll = path => path.matchAll(/^[0-9a-z]+$/g); +const isValidPathMatchAll = path => path.matchAll(/^[0-9a-z]+$/g); diff --git a/javascript/ql/test/experimental/Security/CWE-918/check-validator.js b/javascript/ql/test/experimental/Security/CWE-918/check-validator.js index 631f032d9564..37d9895b24e2 100644 --- a/javascript/ql/test/experimental/Security/CWE-918/check-validator.js +++ b/javascript/ql/test/experimental/Security/CWE-918/check-validator.js @@ -47,7 +47,7 @@ app.get("/check-with-axios", req => { axios.get("test.com/" + req.query.tainted); // OK } if (validHexa(req.query.tainted)) { - axios.get("test.com/" + req.query.tainted); // $ Alert // OK. False Positive + axios.get("test.com/" + req.query.tainted); // $ SPURIOUS: Alert // OK. False Positive } // with simple assignation @@ -56,16 +56,16 @@ app.get("/check-with-axios", req => { axios.get("test.com/" + numberURL); // OK } if (validNumber(numberURL)) { - axios.get("test.com/" + req.query.tainted); // $ Alert // OK. False Positive + axios.get("test.com/" + req.query.tainted); // $ SPURIOUS: Alert // OK. False Positive } if (validNumber(req.query.tainted)) { - axios.get("test.com/" + numberURL); // $ Alert // OK. False Positive + axios.get("test.com/" + numberURL); // $ SPURIOUS: Alert // OK. False Positive } - if (validHexadecimal(req.query.tainted) || validHexaColor(req.query.tainted) || - validDecimal(req.query.tainted) || validFloat(req.query.tainted) || validInt(req.query.tainted) || - validNumber(req.query.tainted) || validOctal(req.query.tainted)) { - axios.get("test.com/" + req.query.tainted); // $ Alert // OK. False Positive + if (validHexadecimal(req.query.tainted) || validHexaColor(req.query.tainted) || + validDecimal(req.query.tainted) || validFloat(req.query.tainted) || validInt(req.query.tainted) || + validNumber(req.query.tainted) || validOctal(req.query.tainted)) { + axios.get("test.com/" + req.query.tainted); // $ SPURIOUS: Alert // OK. False Positive } }); @@ -93,6 +93,6 @@ const validHexaColor = url => validator.isHexColor(url); const validUUID = url => validator.isUUID(url); // unsafe validators -const wrongValidation = url => validator.isByteLength(url, {min:4,max:8}); +const wrongValidation = url => validator.isByteLength(url, { min: 4, max: 8 }); const isAlphanumeric = url => true; diff --git a/javascript/ql/test/query-tests/AngularJS/MissingExplicitInjection/missing-explicit-injection.js b/javascript/ql/test/query-tests/AngularJS/MissingExplicitInjection/missing-explicit-injection.js index 629b62d5b08e..85186dfe8dca 100644 --- a/javascript/ql/test/query-tests/AngularJS/MissingExplicitInjection/missing-explicit-injection.js +++ b/javascript/ql/test/query-tests/AngularJS/MissingExplicitInjection/missing-explicit-injection.js @@ -21,7 +21,7 @@ controller: notInjected7 }; - function injected8(name){} // OK - false negative: we do not track through properties + function injected8(name){} // $ MISSING: Alert // false negative: we do not track through properties var obj8 = { controller: injected8 }; From 662c7b08e8abafda2d3b4d2f75a407ebb481701b Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 2 Jul 2026 14:31:30 +0200 Subject: [PATCH 04/39] Swift: Turn of caching and integrated driver in autobuild * Caching cases compiler invocations to be omitted. You can try this for yourself by turning on caching while building the project from the `xcode-hello` integration test, then cleaning of the build artifacts and building the project again while caching is enabled. This yields a database that is practically empty. * The integrated driver causes compiler invocations to depend on modules shipped with Xcode. Those are unfortunately incompatible with our extractor. --- swift/swift-autobuilder/BuildRunner.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/swift/swift-autobuilder/BuildRunner.cpp b/swift/swift-autobuilder/BuildRunner.cpp index 46108e8259da..2e0045d3c2b6 100644 --- a/swift/swift-autobuilder/BuildRunner.cpp +++ b/swift/swift-autobuilder/BuildRunner.cpp @@ -79,6 +79,9 @@ bool buildXcodeTarget(const XcodeTarget& target, bool dryRun) { argv.push_back(target.name); argv.push_back("CODE_SIGNING_REQUIRED=NO"); argv.push_back("CODE_SIGNING_ALLOWED=NO"); + argv.push_back("COMPILATION_CACHE_ENABLE_CACHING=NO"); + argv.push_back("SWIFT_ENABLE_COMPILE_CACHE=NO"); + argv.push_back("SWIFT_USE_INTEGRATED_DRIVER=NO"); return run_build_command(argv, dryRun); } From 8ecdb2cde0c6dd563edf12adf563aa8935bb2ea0 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 2 Jul 2026 14:36:34 +0200 Subject: [PATCH 05/39] Swift: update integration tests to not use the integrated driver --- swift/ql/integration-tests/osx/hello-ios/test.py | 3 ++- swift/ql/integration-tests/osx/hello-xcode/test.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/swift/ql/integration-tests/osx/hello-ios/test.py b/swift/ql/integration-tests/osx/hello-ios/test.py index b5871f323ccf..ecb4a940d8bd 100644 --- a/swift/ql/integration-tests/osx/hello-ios/test.py +++ b/swift/ql/integration-tests/osx/hello-ios/test.py @@ -7,5 +7,6 @@ def test(codeql, swift, xcode_16): codeql.database.create( command="xcodebuild build " - "CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO", + "CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO " + "SWIFT_USE_INTEGRATED_DRIVER=NO", ) diff --git a/swift/ql/integration-tests/osx/hello-xcode/test.py b/swift/ql/integration-tests/osx/hello-xcode/test.py index 35b0d6dae58e..cf6a17f39084 100644 --- a/swift/ql/integration-tests/osx/hello-xcode/test.py +++ b/swift/ql/integration-tests/osx/hello-xcode/test.py @@ -9,5 +9,6 @@ def test(codeql, swift, xcode_all): command="xcodebuild build " "-project codeql-swift-autobuild-test.xcodeproj " "-target codeql-swift-autobuild-test " - "CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO", + "CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO " + "SWIFT_USE_INTEGRATED_DRIVER=NO", ) From b4db71a38b9938cb9c5828bc096ff6aa48340143 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 2 Jul 2026 14:46:21 +0200 Subject: [PATCH 06/39] Swift: Update autobuild tests --- .../integration-tests/autobuilder/failure/diagnostics.expected | 2 +- .../swift-autobuilder/tests/hello-autobuilder/commands.expected | 2 +- .../tests/hello-targets-with-tests-suffix/commands.expected | 2 +- swift/swift-autobuilder/tests/hello-tests/commands.expected | 2 +- swift/swift-autobuilder/tests/hello-workspace/commands.expected | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/swift/ql/integration-tests/autobuilder/failure/diagnostics.expected b/swift/ql/integration-tests/autobuilder/failure/diagnostics.expected index 2f43e334bc09..248495e3bd2c 100644 --- a/swift/ql/integration-tests/autobuilder/failure/diagnostics.expected +++ b/swift/ql/integration-tests/autobuilder/failure/diagnostics.expected @@ -1,5 +1,5 @@ { - "markdownMessage": "`autobuild` failed to run the build command:\n\n```\n/usr/bin/xcodebuild build -project /hello-failure.xcodeproj -target hello-failure CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO\n```\n\nSet up a [manual build command][1] or [check the logs of the autobuild step][2].\n\n[1]: https://docs.github.com/en/enterprise-server/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages#adding-build-steps-for-a-compiled-language\n[2]: https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/using-workflow-run-logs", + "markdownMessage": "`autobuild` failed to run the build command:\n\n```\n/usr/bin/xcodebuild build -project /hello-failure.xcodeproj -target hello-failure CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO COMPILATION_CACHE_ENABLE_CACHING=NO SWIFT_ENABLE_COMPILE_CACHE=NO SWIFT_USE_INTEGRATED_DRIVER=NO\n```\n\nSet up a [manual build command][1] or [check the logs of the autobuild step][2].\n\n[1]: https://docs.github.com/en/enterprise-server/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages#adding-build-steps-for-a-compiled-language\n[2]: https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/using-workflow-run-logs", "severity": "error", "source": { "extractorName": "swift", diff --git a/swift/swift-autobuilder/tests/hello-autobuilder/commands.expected b/swift/swift-autobuilder/tests/hello-autobuilder/commands.expected index 9d2be19b9c45..dd086268ddbb 100644 --- a/swift/swift-autobuilder/tests/hello-autobuilder/commands.expected +++ b/swift/swift-autobuilder/tests/hello-autobuilder/commands.expected @@ -1 +1 @@ -/usr/bin/xcodebuild build -project ./hello-autobuilder.xcodeproj -target hello-autobuilder CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO +/usr/bin/xcodebuild build -project ./hello-autobuilder.xcodeproj -target hello-autobuilder CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO COMPILATION_CACHE_ENABLE_CACHING=NO SWIFT_ENABLE_COMPILE_CACHE=NO SWIFT_USE_INTEGRATED_DRIVER=NO diff --git a/swift/swift-autobuilder/tests/hello-targets-with-tests-suffix/commands.expected b/swift/swift-autobuilder/tests/hello-targets-with-tests-suffix/commands.expected index 4506ff8aa56e..3d31114ca325 100644 --- a/swift/swift-autobuilder/tests/hello-targets-with-tests-suffix/commands.expected +++ b/swift/swift-autobuilder/tests/hello-targets-with-tests-suffix/commands.expected @@ -1 +1 @@ -/usr/bin/xcodebuild build -project ./Foo.xcodeproj -target FooDemo CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO +/usr/bin/xcodebuild build -project ./Foo.xcodeproj -target FooDemo CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO COMPILATION_CACHE_ENABLE_CACHING=NO SWIFT_ENABLE_COMPILE_CACHE=NO SWIFT_USE_INTEGRATED_DRIVER=NO diff --git a/swift/swift-autobuilder/tests/hello-tests/commands.expected b/swift/swift-autobuilder/tests/hello-tests/commands.expected index a34306fe74c2..46e061896a85 100644 --- a/swift/swift-autobuilder/tests/hello-tests/commands.expected +++ b/swift/swift-autobuilder/tests/hello-tests/commands.expected @@ -1 +1 @@ -/usr/bin/xcodebuild build -project ./hello-tests.xcodeproj -target hello-tests CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO +/usr/bin/xcodebuild build -project ./hello-tests.xcodeproj -target hello-tests CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO COMPILATION_CACHE_ENABLE_CACHING=NO SWIFT_ENABLE_COMPILE_CACHE=NO SWIFT_USE_INTEGRATED_DRIVER=NO diff --git a/swift/swift-autobuilder/tests/hello-workspace/commands.expected b/swift/swift-autobuilder/tests/hello-workspace/commands.expected index ad85eb8c24bb..d9d20e69d087 100644 --- a/swift/swift-autobuilder/tests/hello-workspace/commands.expected +++ b/swift/swift-autobuilder/tests/hello-workspace/commands.expected @@ -1 +1 @@ -/usr/bin/xcodebuild build -workspace ./Hello.xcworkspace -scheme hello-workspace CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO +/usr/bin/xcodebuild build -workspace ./Hello.xcworkspace -scheme hello-workspace CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO COMPILATION_CACHE_ENABLE_CACHING=NO SWIFT_ENABLE_COMPILE_CACHE=NO SWIFT_USE_INTEGRATED_DRIVER=NO From 36af59adbf9771e7911c8dc5310c8fde4f95f217 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 2 Jul 2026 15:34:22 +0200 Subject: [PATCH 07/39] Swift: Update integration test --- .../autobuilder/xcode-fails-spm-works/Files.macos_26.expected | 3 --- .../autobuilder/xcode-fails-spm-works/test.py | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 swift/ql/integration-tests/autobuilder/xcode-fails-spm-works/Files.macos_26.expected diff --git a/swift/ql/integration-tests/autobuilder/xcode-fails-spm-works/Files.macos_26.expected b/swift/ql/integration-tests/autobuilder/xcode-fails-spm-works/Files.macos_26.expected deleted file mode 100644 index 8a3be429106c..000000000000 --- a/swift/ql/integration-tests/autobuilder/xcode-fails-spm-works/Files.macos_26.expected +++ /dev/null @@ -1,3 +0,0 @@ -| Package.swift:0:0:0:0 | Package.swift | -| Sources/hello-world/hello_world.swift:0:0:0:0 | Sources/hello-world/hello_world.swift | -| file://:0:0:0:0 | | diff --git a/swift/ql/integration-tests/autobuilder/xcode-fails-spm-works/test.py b/swift/ql/integration-tests/autobuilder/xcode-fails-spm-works/test.py index 4beed91f233b..298fd2726d0b 100644 --- a/swift/ql/integration-tests/autobuilder/xcode-fails-spm-works/test.py +++ b/swift/ql/integration-tests/autobuilder/xcode-fails-spm-works/test.py @@ -3,7 +3,6 @@ @runs_on.macos -@pytest.mark.ql_test("DB-CHECK", xfail=not runs_on.macos_26) -@pytest.mark.ql_test("*", expected=f"{'.macos_26' if runs_on.macos_26 else ''}.expected") +@pytest.mark.ql_test("DB-CHECK", xfail=True) def test(codeql, swift): codeql.database.create() From 1fb4f9d20850339ddf071df498f3442e9c0c4414 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 19 Jun 2026 14:53:35 +0000 Subject: [PATCH 08/39] yeast: Move schema and YAML loader into yeast-schema crate For type checking rules, we need to be able to load schemas (so we know what to check against). However, since we can't have yeast-macros depending on yeast (where the schema-handling code currently lives) as this would introduce a circular dependency, we instead split the schema-related code into its own yeast-schema crate. --- Cargo.lock | 10 + Cargo.toml | 1 + .../tree_sitter_extractors_deps/defs.bzl | 31 + shared/yeast-schema/BUILD.bazel | 12 + shared/yeast-schema/Cargo.toml | 9 + shared/yeast-schema/src/lib.rs | 33 + shared/yeast-schema/src/node_types_yaml.rs | 762 +++++++++++++++++ shared/yeast-schema/src/schema.rs | 340 ++++++++ shared/yeast/BUILD.bazel | 4 +- shared/yeast/Cargo.toml | 1 + shared/yeast/src/lib.rs | 17 +- shared/yeast/src/node_types_yaml.rs | 777 +----------------- shared/yeast/src/schema.rs | 325 ++------ 13 files changed, 1275 insertions(+), 1047 deletions(-) create mode 100644 shared/yeast-schema/BUILD.bazel create mode 100644 shared/yeast-schema/Cargo.toml create mode 100644 shared/yeast-schema/src/lib.rs create mode 100644 shared/yeast-schema/src/node_types_yaml.rs create mode 100644 shared/yeast-schema/src/schema.rs diff --git a/Cargo.lock b/Cargo.lock index 4fab55a6444f..76043ec0a439 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3724,6 +3724,7 @@ dependencies = [ "tree-sitter-python", "tree-sitter-ruby", "yeast-macros", + "yeast-schema", ] [[package]] @@ -3735,6 +3736,15 @@ dependencies = [ "syn", ] +[[package]] +name = "yeast-schema" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "serde_yaml", +] + [[package]] name = "yoke" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 62eb2e7e920c..9c15b486062b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "shared/tree-sitter-extractor", "shared/yeast", "shared/yeast-macros", + "shared/yeast-schema", "ruby/extractor", "unified/extractor", "unified/extractor/tree-sitter-swift", diff --git a/misc/bazel/3rdparty/tree_sitter_extractors_deps/defs.bzl b/misc/bazel/3rdparty/tree_sitter_extractors_deps/defs.bzl index 11842460638f..7fbdfc4bbd4b 100644 --- a/misc/bazel/3rdparty/tree_sitter_extractors_deps/defs.bzl +++ b/misc/bazel/3rdparty/tree_sitter_extractors_deps/defs.bzl @@ -403,6 +403,13 @@ _NORMAL_DEPENDENCIES = { "syn": Label("@vendor_ts__syn-2.0.106//:syn"), }, }, + "shared/yeast-schema": { + _COMMON_CONDITION: { + "serde": Label("@vendor_ts__serde-1.0.228//:serde"), + "serde_json": Label("@vendor_ts__serde_json-1.0.145//:serde_json"), + "serde_yaml": Label("@vendor_ts__serde_yaml-0.9.34-deprecated//:serde_yaml"), + }, + }, "unified/extractor": { _COMMON_CONDITION: { "clap": Label("@vendor_ts__clap-4.5.48//:clap"), @@ -456,6 +463,10 @@ _NORMAL_ALIASES = { _COMMON_CONDITION: { }, }, + "shared/yeast-schema": { + _COMMON_CONDITION: { + }, + }, "unified/extractor": { _COMMON_CONDITION: { }, @@ -488,6 +499,8 @@ _NORMAL_DEV_DEPENDENCIES = { }, "shared/yeast-macros": { }, + "shared/yeast-schema": { + }, "unified/extractor": { }, "unified/extractor/tree-sitter-swift": { @@ -513,6 +526,8 @@ _NORMAL_DEV_ALIASES = { }, "shared/yeast-macros": { }, + "shared/yeast-schema": { + }, "unified/extractor": { }, "unified/extractor/tree-sitter-swift": { @@ -536,6 +551,8 @@ _PROC_MACRO_DEPENDENCIES = { }, "shared/yeast-macros": { }, + "shared/yeast-schema": { + }, "unified/extractor": { }, "unified/extractor/tree-sitter-swift": { @@ -559,6 +576,8 @@ _PROC_MACRO_ALIASES = { }, "shared/yeast-macros": { }, + "shared/yeast-schema": { + }, "unified/extractor": { }, "unified/extractor/tree-sitter-swift": { @@ -582,6 +601,8 @@ _PROC_MACRO_DEV_DEPENDENCIES = { }, "shared/yeast-macros": { }, + "shared/yeast-schema": { + }, "unified/extractor": { }, "unified/extractor/tree-sitter-swift": { @@ -607,6 +628,8 @@ _PROC_MACRO_DEV_ALIASES = { }, "shared/yeast-macros": { }, + "shared/yeast-schema": { + }, "unified/extractor": { }, "unified/extractor/tree-sitter-swift": { @@ -630,6 +653,8 @@ _BUILD_DEPENDENCIES = { }, "shared/yeast-macros": { }, + "shared/yeast-schema": { + }, "unified/extractor": { }, "unified/extractor/tree-sitter-swift": { @@ -657,6 +682,8 @@ _BUILD_ALIASES = { }, "shared/yeast-macros": { }, + "shared/yeast-schema": { + }, "unified/extractor": { }, "unified/extractor/tree-sitter-swift": { @@ -682,6 +709,8 @@ _BUILD_PROC_MACRO_DEPENDENCIES = { }, "shared/yeast-macros": { }, + "shared/yeast-schema": { + }, "unified/extractor": { }, "unified/extractor/tree-sitter-swift": { @@ -705,6 +734,8 @@ _BUILD_PROC_MACRO_ALIASES = { }, "shared/yeast-macros": { }, + "shared/yeast-schema": { + }, "unified/extractor": { }, "unified/extractor/tree-sitter-swift": { diff --git a/shared/yeast-schema/BUILD.bazel b/shared/yeast-schema/BUILD.bazel new file mode 100644 index 000000000000..85f008a1aa67 --- /dev/null +++ b/shared/yeast-schema/BUILD.bazel @@ -0,0 +1,12 @@ +load("@rules_rust//rust:defs.bzl", "rust_library") +load("//misc/bazel/3rdparty/tree_sitter_extractors_deps:defs.bzl", "aliases", "all_crate_deps") + +exports_files(["Cargo.toml"]) + +rust_library( + name = "yeast-schema", + srcs = glob(["src/**/*.rs"]), + aliases = aliases(), + visibility = ["//visibility:public"], + deps = all_crate_deps(), +) diff --git a/shared/yeast-schema/Cargo.toml b/shared/yeast-schema/Cargo.toml new file mode 100644 index 000000000000..4cf534d4f0ce --- /dev/null +++ b/shared/yeast-schema/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "yeast-schema" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +serde_yaml = "0.9" diff --git a/shared/yeast-schema/src/lib.rs b/shared/yeast-schema/src/lib.rs new file mode 100644 index 000000000000..8e15571c3558 --- /dev/null +++ b/shared/yeast-schema/src/lib.rs @@ -0,0 +1,33 @@ +//! Schema definitions and YAML/JSON node-types loaders for YEAST. +//! +//! This crate carries the parts of the YEAST framework that don't need +//! `tree-sitter`: the [`schema::Schema`] type and its associated +//! [`schema::NodeType`] / [`schema::FieldCardinality`] helpers, plus the +//! YAML and JSON conversion helpers in [`node_types_yaml`]. +//! +//! It exists so that both the runtime crate (`yeast`) and the +//! compile-time `rules!` proc macro (`yeast-macros`) can build against a +//! single source of truth without dragging tree-sitter (a heavy C-backed +//! dep) into the proc-macro toolchain. +//! +//! Tree-sitter-aware adapters — building a `Schema` from a +//! `tree_sitter::Language`, or loading a YAML schema on top of one — +//! live in `yeast::schema` and `yeast::node_types_yaml` respectively. + +pub mod node_types_yaml; +pub mod schema; + +/// Field IDs are stable `u16`s, matching tree-sitter's representation so a +/// schema built from a tree-sitter language can preserve the language's +/// existing IDs. +pub type FieldId = u16; + +/// Kind IDs are stable `u16`s. Like `FieldId`, this matches tree-sitter's +/// representation. +pub type KindId = u16; + +/// Sentinel field id used to mean "the implicit unfielded slot" (what the +/// tree-sitter docs call `children` and what YEAST surfaces in queries as +/// the bare `child:` field). Reserved to avoid clashing with real field +/// IDs allocated by `Schema::register_field`. +pub const CHILD_FIELD: u16 = u16::MAX; diff --git a/shared/yeast-schema/src/node_types_yaml.rs b/shared/yeast-schema/src/node_types_yaml.rs new file mode 100644 index 000000000000..5f6a3906f7cb --- /dev/null +++ b/shared/yeast-schema/src/node_types_yaml.rs @@ -0,0 +1,762 @@ +/// Converts a YAML node-types file to the tree-sitter `node-types.json` format. +/// +/// # YAML format +/// +/// ```yaml +/// supertypes: +/// _expression: +/// - assignment +/// - binary +/// +/// named: +/// assignment: +/// left: _lhs +/// right: _expression +/// identifier: +/// +/// unnamed: +/// - "+" +/// - "end" +/// ``` +/// +/// See the crate-level docs for the full format specification. +use std::collections::{BTreeMap, BTreeSet}; +use std::fmt::Write; + +use crate::CHILD_FIELD; +use serde::Deserialize; +use serde_json::json; + +/// Top-level YAML structure. +#[derive(Deserialize, Default)] +struct YamlNodeTypes { + #[serde(default)] + supertypes: BTreeMap>, + #[serde(default)] + named: BTreeMap>>, + #[serde(default)] + unnamed: Vec, +} + +/// A reference to a node type. Can be: +/// - a plain string (resolved by looking up named vs unnamed) +/// - a map `{unnamed: "name"}` to force unnamed interpretation +#[derive(Deserialize, Debug, Clone)] +#[serde(untagged)] +enum TypeRef { + Name(String), + Explicit { unnamed: String }, +} + +/// A field value: either a single type ref or a list of them. +#[derive(Deserialize, Debug, Clone)] +#[serde(untagged)] +enum TypeRefOrList { + Single(TypeRef), + List(Vec), +} + +impl TypeRefOrList { + fn into_vec(self) -> Vec { + match self { + TypeRefOrList::Single(t) => vec![t], + TypeRefOrList::List(v) => v, + } + } +} + +/// Parsed field name: base name + multiplicity markers. +struct FieldSpec { + name: Option, // None for $children + multiple: bool, + required: bool, +} + +fn parse_field_name(raw: &str) -> FieldSpec { + let is_children = + raw == "$children" || raw == "$children?" || raw == "$children*" || raw == "$children+"; + + let suffix = raw.chars().last().filter(|c| matches!(c, '?' | '*' | '+')); + + let (multiple, required) = match suffix { + Some('?') => (false, false), + Some('*') => (true, false), + Some('+') => (true, true), + _ => (false, true), // bare field name = required, single + }; + + let name = if is_children { + None + } else { + let base = raw.trim_end_matches(['?', '*', '+']); + Some(base.to_string()) + }; + + FieldSpec { + name, + multiple, + required, + } +} + +/// Resolve a TypeRef to a (type, named) pair, given the sets of known named +/// and unnamed types. +fn resolve_type_ref_pair( + type_ref: &TypeRef, + named_types: &BTreeSet, + unnamed_types: &BTreeSet, +) -> (String, bool) { + match type_ref { + TypeRef::Explicit { unnamed } => (unnamed.clone(), false), + TypeRef::Name(name) => { + let is_named = named_types.contains(name); + let is_unnamed = unnamed_types.contains(name); + if is_named && is_unnamed { + (name.clone(), true) + } else if is_unnamed { + (name.clone(), false) + } else { + (name.clone(), true) + } + } + } +} + +/// Resolve a TypeRef to a {type, named} JSON record, given the sets of known named +/// and unnamed types. +fn resolve_type_ref( + type_ref: &TypeRef, + named_types: &BTreeSet, + unnamed_types: &BTreeSet, +) -> serde_json::Value { + let (kind, named) = resolve_type_ref_pair(type_ref, named_types, unnamed_types); + json!({"type": kind, "named": named}) +} + +/// Convert YAML string to node-types JSON string. +pub fn convert(yaml_input: &str) -> Result { + let yaml: YamlNodeTypes = + serde_yaml::from_str(yaml_input).map_err(|e| format!("Failed to parse YAML: {e}"))?; + + // Build the sets of known named and unnamed types for resolution. + let mut named_types = BTreeSet::new(); + for name in yaml.supertypes.keys() { + named_types.insert(name.clone()); + } + for name in yaml.named.keys() { + named_types.insert(name.clone()); + } + let unnamed_types: BTreeSet = yaml.unnamed.iter().cloned().collect(); + + let mut output = Vec::new(); + + // 1. Supertypes + for (name, members) in &yaml.supertypes { + let subtypes: Vec<_> = members + .iter() + .map(|m| resolve_type_ref(m, &named_types, &unnamed_types)) + .collect(); + output.push(json!({ + "type": name, + "named": true, + "subtypes": subtypes, + })); + } + + // 2. Named nodes + for (name, fields_opt) in &yaml.named { + let fields_map = match fields_opt { + None => { + // Leaf token: no fields, no children, no subtypes + output.push(json!({ + "type": name, + "named": true, + "fields": {}, + })); + continue; + } + Some(m) if m.is_empty() => { + output.push(json!({ + "type": name, + "named": true, + "fields": {}, + })); + continue; + } + Some(m) => m, + }; + + let mut json_fields = serde_json::Map::new(); + let mut json_children: Option = None; + + for (raw_field_name, type_refs) in fields_map { + let spec = parse_field_name(raw_field_name); + let types: Vec<_> = type_refs + .clone() + .into_vec() + .iter() + .map(|t| resolve_type_ref(t, &named_types, &unnamed_types)) + .collect(); + + // Cloning to make the borrow checker happy + let field_info = json!({ + "multiple": spec.multiple, + "required": spec.required, + "types": types, + }); + + if spec.name.is_none() { + // $children + json_children = Some(field_info); + } else { + json_fields.insert(spec.name.unwrap(), field_info); + } + } + + let mut entry = json!({ + "type": name, + "named": true, + "fields": json_fields, + }); + + if let Some(children) = json_children { + entry + .as_object_mut() + .unwrap() + .insert("children".to_string(), children); + } + + output.push(entry); + } + + // 3. Unnamed tokens + for name in &yaml.unnamed { + output.push(json!({ + "type": name, + "named": false, + })); + } + + serde_json::to_string_pretty(&output).map_err(|e| format!("Failed to serialize JSON: {e}")) +} + +/// Apply YAML node-type definitions to a mutable Schema. +/// Registers all types, fields, and allowed types from the YAML into the +/// schema. Public so callers can layer YAML node-types onto a Schema that +/// already has fields/kinds preregistered from another source (e.g. a +/// tree-sitter language). +pub fn extend_schema_from_yaml( + schema: &mut crate::schema::Schema, + yaml_input: &str, +) -> Result<(), String> { + let yaml: YamlNodeTypes = + serde_yaml::from_str(yaml_input).map_err(|e| format!("Failed to parse YAML: {e}"))?; + apply_yaml_to_schema(&yaml, schema); + Ok(()) +} + +fn apply_yaml_to_schema( + yaml: &YamlNodeTypes, + schema: &mut crate::schema::Schema, +) { + // Register all supertypes as node kinds + for name in yaml.supertypes.keys() { + schema.register_kind(name); + } + + // Register named node kinds and their fields + for (name, fields_opt) in &yaml.named { + schema.register_kind(name); + if let Some(fields) = fields_opt { + for raw_field_name in fields.keys() { + let spec = parse_field_name(raw_field_name); + if let Some(field_name) = &spec.name { + schema.register_field(field_name); + } + } + } + } + + // Register unnamed tokens as node kinds + for name in &yaml.unnamed { + schema.register_unnamed_kind(name); + } + + let mut named_types = BTreeSet::new(); + for name in yaml.supertypes.keys() { + named_types.insert(name.clone()); + } + for name in yaml.named.keys() { + named_types.insert(name.clone()); + } + let unnamed_types: BTreeSet = yaml.unnamed.iter().cloned().collect(); + + for (supertype, members) in &yaml.supertypes { + let node_types = members + .iter() + .map(|m| { + let (kind, named) = resolve_type_ref_pair(m, &named_types, &unnamed_types); + crate::schema::NodeType { kind, named } + }) + .collect(); + schema.set_supertype_members(supertype, node_types); + } + + // Register allowed field child types for type checking. + for (parent_kind, fields_opt) in &yaml.named { + let Some(fields) = fields_opt else { + continue; + }; + + for (raw_field_name, type_refs) in fields { + let spec = parse_field_name(raw_field_name); + let field_id = match &spec.name { + Some(name) => schema.register_field(name), + None => CHILD_FIELD, + }; + + let mut node_types = type_refs + .clone() + .into_vec() + .into_iter() + .map(|type_ref| { + let (kind, named) = resolve_type_ref_pair(&type_ref, &named_types, &unnamed_types); + crate::schema::NodeType { kind, named } + }) + .collect::>(); + node_types.sort_by(|a, b| a.kind.cmp(&b.kind).then(a.named.cmp(&b.named))); + node_types.dedup_by(|a, b| a.kind == b.kind && a.named == b.named); + schema.set_field_types(parent_kind, field_id, node_types); + schema.set_field_cardinality( + parent_kind, + field_id, + crate::schema::FieldCardinality { + multiple: spec.multiple, + required: spec.required, + }, + ); + } + } +} + +pub fn schema_from_yaml(yaml_input: &str) -> Result { + let mut schema = crate::schema::Schema::new(); + extend_schema_from_yaml(&mut schema, yaml_input)?; + Ok(schema) +} + +// --------------------------------------------------------------------------- +// JSON → YAML conversion +// --------------------------------------------------------------------------- + +/// JSON node-types structures (mirrors tree-sitter's format). +#[derive(Deserialize)] +struct JsonNodeInfo { + #[serde(rename = "type")] + kind: String, + named: bool, + #[serde(default)] + fields: BTreeMap, + children: Option, + #[serde(default)] + subtypes: Vec, +} + +#[derive(Deserialize)] +struct JsonNodeType { + #[serde(rename = "type")] + kind: String, + named: bool, +} + +#[derive(Deserialize)] +struct JsonFieldInfo { + multiple: bool, + required: bool, + types: Vec, +} + +/// Convert a tree-sitter node-types.json string to the YAML format. +pub fn convert_from_json(json_input: &str) -> Result { + let nodes: Vec = + serde_json::from_str(json_input).map_err(|e| format!("Failed to parse JSON: {e}"))?; + + // Collect all named and unnamed types for disambiguation decisions. + let mut all_named: BTreeSet = BTreeSet::new(); + let mut all_unnamed: BTreeSet = BTreeSet::new(); + for node in &nodes { + if node.named { + all_named.insert(node.kind.clone()); + } else { + all_unnamed.insert(node.kind.clone()); + } + } + + let mut supertypes: BTreeMap> = BTreeMap::new(); + let mut named: BTreeMap>> = BTreeMap::new(); + let mut unnamed: Vec = Vec::new(); + + for node in nodes { + if !node.named { + unnamed.push(node.kind); + continue; + } + + if !node.subtypes.is_empty() { + supertypes.insert(node.kind, node.subtypes); + continue; + } + + if node.fields.is_empty() && node.children.is_none() { + // Leaf token + named.insert(node.kind, None); + } else { + let mut fields = BTreeMap::new(); + for (name, info) in node.fields { + fields.insert(name, info); + } + if let Some(children) = node.children { + fields.insert("$children".to_string(), children); + } + named.insert(node.kind, Some(fields)); + } + } + + // Now emit YAML + let mut out = String::new(); + + // Supertypes + if !supertypes.is_empty() { + writeln!(out, "supertypes:").unwrap(); + for (name, members) in &supertypes { + writeln!(out, " {name}:").unwrap(); + for member in members { + let ref_str = format_type_ref(&member.kind, member.named, &all_named, &all_unnamed); + writeln!(out, " - {ref_str}").unwrap(); + } + } + writeln!(out).unwrap(); + } + + // Named + if !named.is_empty() { + writeln!(out, "named:").unwrap(); + for (name, fields_opt) in &named { + match fields_opt { + None => { + writeln!(out, " {name}:").unwrap(); + } + Some(fields) => { + writeln!(out, " {name}:").unwrap(); + for (field_name, info) in fields { + let suffix = field_suffix(info.multiple, info.required); + let yaml_name = if field_name == "$children" { + format!("$children{suffix}") + } else { + format!("{field_name}{suffix}") + }; + + let type_refs: Vec = info + .types + .iter() + .map(|t| format_type_ref(&t.kind, t.named, &all_named, &all_unnamed)) + .collect(); + + if type_refs.len() == 1 { + writeln!(out, " {yaml_name}: {}", type_refs[0]).unwrap(); + } else { + let list = type_refs + .iter() + .map(|s| s.as_str()) + .collect::>() + .join(", "); + writeln!(out, " {yaml_name}: [{list}]").unwrap(); + } + } + } + } + } + writeln!(out).unwrap(); + } + + // Unnamed + if !unnamed.is_empty() { + writeln!(out, "unnamed:").unwrap(); + for name in &unnamed { + writeln!(out, " - {}", force_quote(name)).unwrap(); + } + } + + Ok(out) +} + +fn field_suffix(multiple: bool, required: bool) -> &'static str { + match (multiple, required) { + (false, true) => "", + (false, false) => "?", + (true, true) => "+", + (true, false) => "*", + } +} + +/// Format a type reference for YAML output. Uses the disambiguation rule: +/// plain string if unambiguous, `{unnamed: name}` if the name exists as both +/// named and unnamed and we need the unnamed interpretation. +fn format_type_ref( + kind: &str, + named: bool, + all_named: &BTreeSet, + _all_unnamed: &BTreeSet, +) -> String { + if named { + quote_yaml(kind) + } else { + let is_also_named = all_named.contains(kind); + if is_also_named { + format!("{{unnamed: {}}}", force_quote(kind)) + } else { + force_quote(kind) + } + } +} + +/// Always wrap in double quotes. Used for unnamed node references so they're +/// visually distinct from named ones — YAML treats both forms as equivalent strings. +fn force_quote(s: &str) -> String { + format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")) +} + +/// Quote a YAML string value if it contains special characters or could be +/// misinterpreted. +fn quote_yaml(s: &str) -> String { + let needs_quoting = s.is_empty() + || s.contains(|c: char| { + matches!( + c, + ':' | '{' + | '}' + | '[' + | ']' + | ',' + | '&' + | '*' + | '#' + | '?' + | '|' + | '-' + | '<' + | '>' + | '=' + | '!' + | '%' + | '@' + | '`' + | '"' + | '\'' + ) + }) + || s.starts_with(' ') + || s.ends_with(' ') + || s == "true" + || s == "false" + || s == "null" + || s == "yes" + || s == "no" + || s.parse::().is_ok(); + + if needs_quoting { + format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")) + } else { + s.to_string() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_basic_conversion() { + let yaml = r#" +supertypes: + _expression: + - assignment + - binary + +named: + assignment: + left: _lhs + right: _expression + binary: + left: [_expression, _simple_numeric] + operator: ["!=", "+"] + right: _expression + argument_list: + $children*: [_expression, block_argument] + identifier: + +unnamed: + - "!=" + - "+" + - "end" +"#; + + let json_str = convert(yaml).unwrap(); + let result: Vec = serde_json::from_str(&json_str).unwrap(); + + // Check supertype + let expr = &result[0]; + assert_eq!(expr["type"], "_expression"); + assert_eq!(expr["named"], true); + assert_eq!(expr["subtypes"].as_array().unwrap().len(), 2); + + // Check assignment + let assign = result.iter().find(|n| n["type"] == "assignment").unwrap(); + assert_eq!(assign["fields"]["left"]["required"], true); + assert_eq!(assign["fields"]["left"]["multiple"], false); + assert_eq!(assign["fields"]["left"]["types"][0]["type"], "_lhs"); + assert_eq!(assign["fields"]["left"]["types"][0]["named"], true); + + // Check binary.operator — "!=" and "+" should resolve to unnamed + let binary = result.iter().find(|n| n["type"] == "binary").unwrap(); + let op_types = binary["fields"]["operator"]["types"].as_array().unwrap(); + assert_eq!(op_types[0]["type"], "!="); + assert_eq!(op_types[0]["named"], false); + assert_eq!(op_types[1]["type"], "+"); + assert_eq!(op_types[1]["named"], false); + + // Check argument_list has children, not a field + let arg_list = result + .iter() + .find(|n| n["type"] == "argument_list") + .unwrap(); + assert!(arg_list.get("children").is_some()); + assert_eq!(arg_list["children"]["multiple"], true); + assert_eq!(arg_list["children"]["required"], false); + + // Check identifier is a leaf + let ident = result.iter().find(|n| n["type"] == "identifier").unwrap(); + assert_eq!(ident["fields"].as_object().unwrap().len(), 0); + + // Check unnamed tokens + let end = result.iter().find(|n| n["type"] == "end").unwrap(); + assert_eq!(end["named"], false); + } + + #[test] + fn test_explicit_unnamed_disambiguation() { + let yaml = r#" +named: + foo: + field: [{unnamed: bar}] + +unnamed: + - bar +"#; + + let json_str = convert(yaml).unwrap(); + let result: Vec = serde_json::from_str(&json_str).unwrap(); + let foo = result.iter().find(|n| n["type"] == "foo").unwrap(); + assert_eq!(foo["fields"]["field"]["types"][0]["named"], false); + } + + #[test] + fn test_field_suffixes() { + let yaml = r#" +named: + test_node: + required_single: foo + optional_single?: foo + required_multiple+: foo + optional_multiple*: foo +"#; + + let json_str = convert(yaml).unwrap(); + let result: Vec = serde_json::from_str(&json_str).unwrap(); + let node = result.iter().find(|n| n["type"] == "test_node").unwrap(); + let fields = node["fields"].as_object().unwrap(); + + assert_eq!(fields["required_single"]["required"], true); + assert_eq!(fields["required_single"]["multiple"], false); + + assert_eq!(fields["optional_single"]["required"], false); + assert_eq!(fields["optional_single"]["multiple"], false); + + assert_eq!(fields["required_multiple"]["required"], true); + assert_eq!(fields["required_multiple"]["multiple"], true); + + assert_eq!(fields["optional_multiple"]["required"], false); + assert_eq!(fields["optional_multiple"]["multiple"], true); + } + + #[test] + fn test_json_to_yaml() { + let json = r#"[ + {"type": "_expression", "named": true, "subtypes": [ + {"type": "assignment", "named": true}, + {"type": "identifier", "named": true} + ]}, + {"type": "assignment", "named": true, "fields": { + "left": {"multiple": false, "required": true, "types": [ + {"type": "_expression", "named": true} + ]}, + "right": {"multiple": false, "required": false, "types": [ + {"type": "_expression", "named": true} + ]} + }, "children": { + "multiple": true, "required": false, "types": [ + {"type": "identifier", "named": true} + ] + }}, + {"type": "identifier", "named": true, "fields": {}}, + {"type": "=", "named": false}, + {"type": "end", "named": false} + ]"#; + + let yaml = convert_from_json(json).unwrap(); + + // Verify key structures are present + assert!(yaml.contains("supertypes:")); + assert!(yaml.contains("_expression:")); + assert!(yaml.contains("named:")); + assert!(yaml.contains("assignment:")); + assert!(yaml.contains("left:")); + assert!(yaml.contains("right?:")); + assert!(yaml.contains("$children*:")); + assert!(yaml.contains("identifier:")); + assert!(yaml.contains("unnamed:")); + assert!(yaml.contains("\"=\"")); + assert!(yaml.contains("end")); + } + + #[test] + fn test_round_trip() { + let yaml_input = r#" +supertypes: + _expression: + - assignment + - identifier + +named: + assignment: + left: _expression + right?: _expression + $children*: identifier + identifier: + +unnamed: + - "=" + - end +"#; + + // YAML → JSON → YAML + let json = convert(yaml_input).unwrap(); + let yaml_output = convert_from_json(&json).unwrap(); + // YAML → JSON again (should be identical) + let json2 = convert(&yaml_output).unwrap(); + + let v1: serde_json::Value = serde_json::from_str(&json).unwrap(); + let v2: serde_json::Value = serde_json::from_str(&json2).unwrap(); + assert_eq!(v1, v2); + } +} diff --git a/shared/yeast-schema/src/schema.rs b/shared/yeast-schema/src/schema.rs new file mode 100644 index 000000000000..4acd14377a4d --- /dev/null +++ b/shared/yeast-schema/src/schema.rs @@ -0,0 +1,340 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use crate::{FieldId, KindId, CHILD_FIELD}; + +#[derive(Clone, Debug)] +pub struct NodeType { + pub kind: String, + pub named: bool, +} + +/// Multiplicity/optionality of a field declaration. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct FieldCardinality { + /// Whether the field may hold more than one child. + pub multiple: bool, + /// Whether at least one child must be present. + pub required: bool, +} + +/// A schema defining node kinds and field names for the output AST. +/// Built from a node-types.yml file, independent of any tree-sitter grammar. +/// +/// # Memory management +/// +/// `register_field`/`register_kind`/`register_unnamed_kind` (and their +/// `_with_id` siblings) use `Box::leak` to obtain `&'static str` names. This +/// is intentional: the `&'static str` names appear pervasively in `Node`, +/// `AstCursor`, query patterns, and the extractor's TRAP output, where +/// adding a lifetime would propagate widely. +/// +/// The leak is bounded by the number of distinct kind/field names registered. +/// Schemas are expected to be constructed once per process (e.g. at extractor +/// startup) and reused. Repeated construction in long-running processes will +/// leak memory unboundedly and should be avoided. +#[derive(Clone)] +pub struct Schema { + field_ids: BTreeMap, + field_names: BTreeMap, + next_field_id: FieldId, + kind_ids: BTreeMap, + unnamed_kind_ids: BTreeMap, + kind_names: BTreeMap, + next_kind_id: KindId, + field_types: BTreeMap<(String, FieldId), Vec>, + field_cardinalities: BTreeMap<(String, FieldId), FieldCardinality>, + supertypes: BTreeMap>, +} + +impl Default for Schema { + fn default() -> Self { + Self::new() + } +} + +impl Schema { + pub fn new() -> Self { + Self { + field_ids: BTreeMap::new(), + field_names: BTreeMap::new(), + next_field_id: 1, // 0 is reserved + kind_ids: BTreeMap::new(), + unnamed_kind_ids: BTreeMap::new(), + kind_names: BTreeMap::new(), + next_kind_id: 1, // 0 is reserved + field_types: BTreeMap::new(), + field_cardinalities: BTreeMap::new(), + supertypes: BTreeMap::new(), + } + } + + /// Register a field name, returning its ID. + /// If already registered, returns the existing ID. + pub fn register_field(&mut self, name: &str) -> FieldId { + if name == "child" { + return CHILD_FIELD; + } + if let Some(&id) = self.field_ids.get(name) { + return id; + } + let id = self.next_field_id; + assert!(id < CHILD_FIELD, "too many fields"); + self.next_field_id += 1; + let leaked: &'static str = Box::leak(name.to_string().into_boxed_str()); + self.field_ids.insert(name.to_string(), id); + self.field_names.insert(id, leaked); + id + } + + /// Register a field name with a specific ID, e.g. when importing IDs + /// from an external source like a tree-sitter language. If the name is + /// already registered (with any ID), nothing is changed and the + /// existing ID is returned. + pub fn register_field_with_id(&mut self, name: &str, id: FieldId) -> FieldId { + if name == "child" { + return CHILD_FIELD; + } + if let Some(&existing) = self.field_ids.get(name) { + return existing; + } + assert!(id < CHILD_FIELD, "too many fields"); + let leaked: &'static str = Box::leak(name.to_string().into_boxed_str()); + self.field_ids.insert(name.to_string(), id); + self.field_names.insert(id, leaked); + if id >= self.next_field_id { + self.next_field_id = id + 1; + } + id + } + + /// Register a named node kind name, returning its ID. + /// If already registered, returns the existing ID. + pub fn register_kind(&mut self, name: &str) -> KindId { + if let Some(&id) = self.kind_ids.get(name) { + return id; + } + let id = self.next_kind_id; + self.next_kind_id += 1; + let leaked: &'static str = Box::leak(name.to_string().into_boxed_str()); + self.kind_ids.insert(name.to_string(), id); + self.kind_names.insert(id, leaked); + id + } + + /// Register a named node kind with a specific ID, e.g. when importing + /// IDs from a tree-sitter language. If the name is already registered, + /// nothing is changed and the existing ID is returned. + pub fn register_kind_with_id(&mut self, name: &str, id: KindId) -> KindId { + if let Some(&existing) = self.kind_ids.get(name) { + return existing; + } + let leaked: &'static str = Box::leak(name.to_string().into_boxed_str()); + self.kind_ids.insert(name.to_string(), id); + self.kind_names.insert(id, leaked); + if id >= self.next_kind_id { + self.next_kind_id = id + 1; + } + id + } + + /// Register an unnamed token kind (e.g. `"="`, `"end"`), returning its ID. + /// If already registered, returns the existing ID. + pub fn register_unnamed_kind(&mut self, name: &str) -> KindId { + if let Some(&id) = self.unnamed_kind_ids.get(name) { + return id; + } + let id = self.next_kind_id; + self.next_kind_id += 1; + let leaked: &'static str = Box::leak(name.to_string().into_boxed_str()); + self.unnamed_kind_ids.insert(name.to_string(), id); + self.kind_names.insert(id, leaked); + id + } + + /// Register an unnamed token kind with a specific ID. If the name is + /// already registered as unnamed, nothing is changed and the existing + /// ID is returned. + pub fn register_unnamed_kind_with_id(&mut self, name: &str, id: KindId) -> KindId { + if let Some(&existing) = self.unnamed_kind_ids.get(name) { + return existing; + } + let leaked: &'static str = Box::leak(name.to_string().into_boxed_str()); + self.unnamed_kind_ids.insert(name.to_string(), id); + self.kind_names.insert(id, leaked); + if id >= self.next_kind_id { + self.next_kind_id = id + 1; + } + id + } + + /// Track a name for a kind ID without registering it as named or + /// unnamed. Useful when importing tree-sitter ID tables that may + /// contain duplicate IDs across the named/unnamed split. + pub fn record_kind_name(&mut self, id: KindId, name: &'static str) { + self.kind_names.entry(id).or_insert(name); + if id >= self.next_kind_id { + self.next_kind_id = id + 1; + } + } + + pub fn field_id_for_name(&self, name: &str) -> Option { + if name == "child" { + return Some(CHILD_FIELD); + } + self.field_ids.get(name).copied() + } + + pub fn field_name_for_id(&self, id: FieldId) -> Option<&'static str> { + if id == CHILD_FIELD { + return Some("child"); + } + self.field_names.get(&id).copied() + } + + pub fn id_for_node_kind(&self, kind: &str) -> Option { + self.kind_ids.get(kind).copied() + } + + pub fn id_for_unnamed_node_kind(&self, kind: &str) -> Option { + self.unnamed_kind_ids.get(kind).copied() + } + + /// Has `kind` been registered as a named kind (concrete node or + /// supertype)? + pub fn has_named_kind(&self, kind: &str) -> bool { + self.id_for_node_kind(kind).is_some() + } + + /// Has `kind` been registered as an unnamed token kind? + pub fn has_unnamed_kind(&self, kind: &str) -> bool { + self.id_for_unnamed_node_kind(kind).is_some() + } + + /// Is `field_name` declared as a field on `parent_kind`? + /// `field_name == None` checks for the implicit unfielded slot + /// (`$children`/`CHILD_FIELD`). + pub fn has_field(&self, parent_kind: &str, field_name: Option<&str>) -> bool { + let field_id = match field_name { + Some(name) => match self.field_id_for_name(name) { + Some(id) => id, + None => return false, + }, + None => CHILD_FIELD, + }; + self.field_types(parent_kind, field_id).is_some() + } + + pub fn node_kind_for_id(&self, id: KindId) -> Option<&'static str> { + self.kind_names.get(&id).copied() + } + + pub fn set_field_types( + &mut self, + parent_kind: &str, + field_id: FieldId, + node_types: Vec, + ) { + self.field_types + .insert((parent_kind.to_string(), field_id), node_types); + } + + pub fn field_types( + &self, + parent_kind: &str, + field_id: FieldId, + ) -> Option<&Vec> { + self.field_types + .get(&(parent_kind.to_string(), field_id)) + } + + pub fn set_field_cardinality( + &mut self, + parent_kind: &str, + field_id: FieldId, + cardinality: FieldCardinality, + ) { + self.field_cardinalities + .insert((parent_kind.to_string(), field_id), cardinality); + } + + /// Returns the declared cardinality for a field, if known. + pub fn field_cardinality( + &self, + parent_kind: &str, + field_id: FieldId, + ) -> Option { + self.field_cardinalities + .get(&(parent_kind.to_string(), field_id)) + .copied() + } + + /// Returns an iterator over all `(field_id, field_name)` pairs that are + /// declared as required (`required: true`) for the given `parent_kind`. + pub fn required_fields_for_kind<'a>( + &'a self, + parent_kind: &'a str, + ) -> impl Iterator)> + 'a { + self.field_cardinalities + .iter() + .filter(move |((kind, _), card)| kind == parent_kind && card.required) + .map(move |((_, field_id), _)| { + let name = self.field_name_for_id(*field_id); + (*field_id, name) + }) + } + + pub fn set_supertype_members(&mut self, supertype: &str, node_types: Vec) { + self.supertypes.insert(supertype.to_string(), node_types); + } + + /// Returns the declared members of a supertype, if known. + pub fn supertype_members(&self, supertype: &str) -> Option<&Vec> { + self.supertypes.get(supertype) + } + + /// Is `kind` a known supertype (an abstract grouping)? + pub fn is_supertype(&self, kind: &str) -> bool { + self.supertypes.contains_key(kind) + } + + fn allows_node( + &self, + node_type: &NodeType, + node_kind: &str, + node_named: bool, + active: &mut BTreeSet, + ) -> bool { + if node_type.kind == node_kind && node_type.named == node_named { + return true; + } + + if !node_type.named { + return false; + } + + let Some(members) = self.supertypes.get(&node_type.kind) else { + return false; + }; + + if !active.insert(node_type.kind.clone()) { + return false; + } + + let matched = members + .iter() + .any(|member| self.allows_node(member, node_kind, node_named, active)); + active.remove(&node_type.kind); + matched + } + + pub fn node_matches_types( + &self, + node_kind: &str, + node_named: bool, + node_types: &[NodeType], + ) -> bool { + node_types.iter().any(|node_type| { + self.allows_node(node_type, node_kind, node_named, &mut BTreeSet::new()) + }) + } +} diff --git a/shared/yeast/BUILD.bazel b/shared/yeast/BUILD.bazel index fe0b01bb87bd..5217f20ec67d 100644 --- a/shared/yeast/BUILD.bazel +++ b/shared/yeast/BUILD.bazel @@ -14,5 +14,7 @@ rust_library( "//shared/yeast-macros", ], visibility = ["//visibility:public"], - deps = all_crate_deps(), + deps = all_crate_deps() + [ + "//shared/yeast-schema", + ], ) diff --git a/shared/yeast/Cargo.toml b/shared/yeast/Cargo.toml index 166887c324cf..518a0d1cefc2 100644 --- a/shared/yeast/Cargo.toml +++ b/shared/yeast/Cargo.toml @@ -10,6 +10,7 @@ serde_json = "1.0.108" serde_yaml = "0.9" tree-sitter = ">= 0.23.0" yeast-macros = { path = "../yeast-macros" } +yeast-schema = { path = "../yeast-schema" } tree-sitter-ruby = "0.23" tree-sitter-python = "0.23" diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 2f6da2039d12..c4b448077267 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -57,8 +57,13 @@ impl IntoIterator for Id { } /// Field and Kind ids are provided by tree-sitter -type FieldId = u16; -type KindId = u16; +type FieldId = yeast_schema::FieldId; +type KindId = yeast_schema::KindId; + +/// Sentinel field id used to mean "the implicit unfielded slot". +/// Re-exported from `yeast-schema` so the runtime and the schema share a +/// single value. +pub use yeast_schema::CHILD_FIELD; /// Trait for values that can be appended to a field's id list inside a /// `tree!`/`trees!`/`rule!` template (in `{expr}` placeholders). @@ -157,8 +162,6 @@ impl YeastSourceRange for &T { } } -pub const CHILD_FIELD: u16 = u16::MAX; - #[derive(Debug)] pub struct AstCursor<'a> { ast: &'a Ast, @@ -304,7 +307,7 @@ impl std::fmt::Debug for Ast { impl Ast { /// Construct an AST from a TS tree pub fn from_tree(language: tree_sitter::Language, tree: &tree_sitter::Tree) -> Self { - let schema = schema::Schema::from_language(&language); + let schema = schema::from_language(&language); Self::from_tree_with_schema(schema, tree, &language) } @@ -1251,7 +1254,7 @@ impl DesugaringConfig { pub fn build_schema(&self, language: &tree_sitter::Language) -> Result { match self.output_node_types_yaml { Some(yaml) => node_types_yaml::schema_from_yaml_with_language(yaml, language), - None => Ok(schema::Schema::from_language(language)), + None => Ok(schema::from_language(language)), } } } @@ -1265,7 +1268,7 @@ pub struct Runner<'a, C = ()> { impl<'a, C> Runner<'a, C> { /// Create a runner using the input grammar's schema for output. pub fn new(language: tree_sitter::Language, phases: &'a [Phase]) -> Self { - let schema = schema::Schema::from_language(&language); + let schema = schema::from_language(&language); Self { language, schema, diff --git a/shared/yeast/src/node_types_yaml.rs b/shared/yeast/src/node_types_yaml.rs index f4d9f2a1c427..7beb4bb25bed 100644 --- a/shared/yeast/src/node_types_yaml.rs +++ b/shared/yeast/src/node_types_yaml.rs @@ -1,767 +1,22 @@ -/// Converts a YAML node-types file to the tree-sitter `node-types.json` format. -/// -/// # YAML format -/// -/// ```yaml -/// supertypes: -/// _expression: -/// - assignment -/// - binary -/// -/// named: -/// assignment: -/// left: _lhs -/// right: _expression -/// identifier: -/// -/// unnamed: -/// - "+" -/// - "end" -/// ``` -/// -/// See the crate-level docs for the full format specification. -use std::collections::{BTreeMap, BTreeSet}; -use std::fmt::Write; - -use crate::CHILD_FIELD; -use serde::Deserialize; -use serde_json::json; - -/// Top-level YAML structure. -#[derive(Deserialize, Default)] -struct YamlNodeTypes { - #[serde(default)] - supertypes: BTreeMap>, - #[serde(default)] - named: BTreeMap>>, - #[serde(default)] - unnamed: Vec, -} - -/// A reference to a node type. Can be: -/// - a plain string (resolved by looking up named vs unnamed) -/// - a map `{unnamed: "name"}` to force unnamed interpretation -#[derive(Deserialize, Debug, Clone)] -#[serde(untagged)] -enum TypeRef { - Name(String), - Explicit { unnamed: String }, -} - -/// A field value: either a single type ref or a list of them. -#[derive(Deserialize, Debug, Clone)] -#[serde(untagged)] -enum TypeRefOrList { - Single(TypeRef), - List(Vec), -} - -impl TypeRefOrList { - fn into_vec(self) -> Vec { - match self { - TypeRefOrList::Single(t) => vec![t], - TypeRefOrList::List(v) => v, - } - } -} - -/// Parsed field name: base name + multiplicity markers. -struct FieldSpec { - name: Option, // None for $children - multiple: bool, - required: bool, -} - -fn parse_field_name(raw: &str) -> FieldSpec { - let is_children = - raw == "$children" || raw == "$children?" || raw == "$children*" || raw == "$children+"; - - let suffix = raw.chars().last().filter(|c| matches!(c, '?' | '*' | '+')); - - let (multiple, required) = match suffix { - Some('?') => (false, false), - Some('*') => (true, false), - Some('+') => (true, true), - _ => (false, true), // bare field name = required, single - }; - - let name = if is_children { - None - } else { - let base = raw.trim_end_matches(['?', '*', '+']); - Some(base.to_string()) - }; - - FieldSpec { - name, - multiple, - required, - } -} - -/// Resolve a TypeRef to a (type, named) pair, given the sets of known named -/// and unnamed types. -fn resolve_type_ref_pair( - type_ref: &TypeRef, - named_types: &BTreeSet, - unnamed_types: &BTreeSet, -) -> (String, bool) { - match type_ref { - TypeRef::Explicit { unnamed } => (unnamed.clone(), false), - TypeRef::Name(name) => { - let is_named = named_types.contains(name); - let is_unnamed = unnamed_types.contains(name); - if is_named && is_unnamed { - (name.clone(), true) - } else if is_unnamed { - (name.clone(), false) - } else { - (name.clone(), true) - } - } - } -} - -/// Resolve a TypeRef to a {type, named} JSON record, given the sets of known named -/// and unnamed types. -fn resolve_type_ref( - type_ref: &TypeRef, - named_types: &BTreeSet, - unnamed_types: &BTreeSet, -) -> serde_json::Value { - let (kind, named) = resolve_type_ref_pair(type_ref, named_types, unnamed_types); - json!({"type": kind, "named": named}) -} - -/// Convert YAML string to node-types JSON string. -pub fn convert(yaml_input: &str) -> Result { - let yaml: YamlNodeTypes = - serde_yaml::from_str(yaml_input).map_err(|e| format!("Failed to parse YAML: {e}"))?; - - // Build the sets of known named and unnamed types for resolution. - let mut named_types = BTreeSet::new(); - for name in yaml.supertypes.keys() { - named_types.insert(name.clone()); - } - for name in yaml.named.keys() { - named_types.insert(name.clone()); - } - let unnamed_types: BTreeSet = yaml.unnamed.iter().cloned().collect(); - - let mut output = Vec::new(); - - // 1. Supertypes - for (name, members) in &yaml.supertypes { - let subtypes: Vec<_> = members - .iter() - .map(|m| resolve_type_ref(m, &named_types, &unnamed_types)) - .collect(); - output.push(json!({ - "type": name, - "named": true, - "subtypes": subtypes, - })); - } - - // 2. Named nodes - for (name, fields_opt) in &yaml.named { - let fields_map = match fields_opt { - None => { - // Leaf token: no fields, no children, no subtypes - output.push(json!({ - "type": name, - "named": true, - "fields": {}, - })); - continue; - } - Some(m) if m.is_empty() => { - output.push(json!({ - "type": name, - "named": true, - "fields": {}, - })); - continue; - } - Some(m) => m, - }; - - let mut json_fields = serde_json::Map::new(); - let mut json_children: Option = None; - - for (raw_field_name, type_refs) in fields_map { - let spec = parse_field_name(raw_field_name); - let types: Vec<_> = type_refs - .clone() - .into_vec() - .iter() - .map(|t| resolve_type_ref(t, &named_types, &unnamed_types)) - .collect(); - - // Cloning to make the borrow checker happy - let field_info = json!({ - "multiple": spec.multiple, - "required": spec.required, - "types": types, - }); - - if spec.name.is_none() { - // $children - json_children = Some(field_info); - } else { - json_fields.insert(spec.name.unwrap(), field_info); - } - } - - let mut entry = json!({ - "type": name, - "named": true, - "fields": json_fields, - }); - - if let Some(children) = json_children { - entry - .as_object_mut() - .unwrap() - .insert("children".to_string(), children); - } - - output.push(entry); - } - - // 3. Unnamed tokens - for name in &yaml.unnamed { - output.push(json!({ - "type": name, - "named": false, - })); - } - - serde_json::to_string_pretty(&output).map_err(|e| format!("Failed to serialize JSON: {e}")) -} - -/// Apply YAML node-type definitions to a mutable Schema. -/// Registers all types, fields, and allowed types from the YAML into the schema. -fn apply_yaml_to_schema(yaml: &YamlNodeTypes, schema: &mut crate::schema::Schema) { - // Register all supertypes as node kinds - for name in yaml.supertypes.keys() { - schema.register_kind(name); - } - - // Register named node kinds and their fields - for (name, fields_opt) in &yaml.named { - schema.register_kind(name); - if let Some(fields) = fields_opt { - for raw_field_name in fields.keys() { - let spec = parse_field_name(raw_field_name); - if let Some(field_name) = &spec.name { - schema.register_field(field_name); - } - } - } - } - - // Register unnamed tokens as node kinds - for name in &yaml.unnamed { - schema.register_unnamed_kind(name); - } - - let mut named_types = BTreeSet::new(); - for name in yaml.supertypes.keys() { - named_types.insert(name.clone()); - } - for name in yaml.named.keys() { - named_types.insert(name.clone()); - } - let unnamed_types: BTreeSet = yaml.unnamed.iter().cloned().collect(); - - for (supertype, members) in &yaml.supertypes { - let node_types = members - .iter() - .map(|m| { - let (kind, named) = resolve_type_ref_pair(m, &named_types, &unnamed_types); - crate::schema::NodeType { kind, named } - }) - .collect(); - schema.set_supertype_members(supertype, node_types); - } - - // Register allowed field child types for type checking. - for (parent_kind, fields_opt) in &yaml.named { - let Some(fields) = fields_opt else { - continue; - }; - - for (raw_field_name, type_refs) in fields { - let spec = parse_field_name(raw_field_name); - let field_id = match &spec.name { - Some(name) => schema.register_field(name), - None => CHILD_FIELD, - }; - - let mut node_types = type_refs - .clone() - .into_vec() - .into_iter() - .map(|type_ref| { - let (kind, named) = - resolve_type_ref_pair(&type_ref, &named_types, &unnamed_types); - crate::schema::NodeType { kind, named } - }) - .collect::>(); - node_types.sort_by(|a, b| a.kind.cmp(&b.kind).then(a.named.cmp(&b.named))); - node_types.dedup_by(|a, b| a.kind == b.kind && a.named == b.named); - schema.set_field_types(parent_kind, field_id, node_types); - schema.set_field_cardinality( - parent_kind, - field_id, - crate::schema::FieldCardinality { - multiple: spec.multiple, - required: spec.required, - }, - ); - } - } -} - -pub fn schema_from_yaml(yaml_input: &str) -> Result { - let yaml: YamlNodeTypes = - serde_yaml::from_str(yaml_input).map_err(|e| format!("Failed to parse YAML: {e}"))?; - - let mut schema = crate::schema::Schema::new(); - apply_yaml_to_schema(&yaml, &mut schema); - - Ok(schema) -} - -/// Build a Schema from a YAML string, extending a tree-sitter Language. -/// The Schema inherits all field/kind names from the Language, plus any -/// additional ones defined in the YAML. +//! YAML/JSON node-types loaders for YEAST. +//! +//! The pure YAML/JSON conversion routines live in [`yeast_schema::node_types_yaml`]. +//! This module re-exports them and adds the tree-sitter-aware adapter +//! [`schema_from_yaml_with_language`]. + +pub use yeast_schema::node_types_yaml::{ + convert, convert_from_json, extend_schema_from_yaml, schema_from_yaml, +}; + +/// Build a Schema from a YAML string, layered on top of a tree-sitter +/// `Language`. The Schema inherits all field/kind names from the language +/// (preserving the language's IDs), plus any additional ones defined in +/// the YAML. pub fn schema_from_yaml_with_language( yaml_input: &str, language: &tree_sitter::Language, ) -> Result { - let yaml: YamlNodeTypes = - serde_yaml::from_str(yaml_input).map_err(|e| format!("Failed to parse YAML: {e}"))?; - - let mut schema = crate::schema::Schema::from_language(language); - apply_yaml_to_schema(&yaml, &mut schema); - + let mut schema = crate::schema::from_language(language); + extend_schema_from_yaml(&mut schema, yaml_input)?; Ok(schema) } - -// --------------------------------------------------------------------------- -// JSON → YAML conversion -// --------------------------------------------------------------------------- - -/// JSON node-types structures (mirrors tree-sitter's format). -#[derive(Deserialize)] -struct JsonNodeInfo { - #[serde(rename = "type")] - kind: String, - named: bool, - #[serde(default)] - fields: BTreeMap, - children: Option, - #[serde(default)] - subtypes: Vec, -} - -#[derive(Deserialize)] -struct JsonNodeType { - #[serde(rename = "type")] - kind: String, - named: bool, -} - -#[derive(Deserialize)] -struct JsonFieldInfo { - multiple: bool, - required: bool, - types: Vec, -} - -/// Convert a tree-sitter node-types.json string to the YAML format. -pub fn convert_from_json(json_input: &str) -> Result { - let nodes: Vec = - serde_json::from_str(json_input).map_err(|e| format!("Failed to parse JSON: {e}"))?; - - // Collect all named and unnamed types for disambiguation decisions. - let mut all_named: BTreeSet = BTreeSet::new(); - let mut all_unnamed: BTreeSet = BTreeSet::new(); - for node in &nodes { - if node.named { - all_named.insert(node.kind.clone()); - } else { - all_unnamed.insert(node.kind.clone()); - } - } - - let mut supertypes: BTreeMap> = BTreeMap::new(); - let mut named: BTreeMap>> = BTreeMap::new(); - let mut unnamed: Vec = Vec::new(); - - for node in nodes { - if !node.named { - unnamed.push(node.kind); - continue; - } - - if !node.subtypes.is_empty() { - supertypes.insert(node.kind, node.subtypes); - continue; - } - - if node.fields.is_empty() && node.children.is_none() { - // Leaf token - named.insert(node.kind, None); - } else { - let mut fields = BTreeMap::new(); - for (name, info) in node.fields { - fields.insert(name, info); - } - if let Some(children) = node.children { - fields.insert("$children".to_string(), children); - } - named.insert(node.kind, Some(fields)); - } - } - - // Now emit YAML - let mut out = String::new(); - - // Supertypes - if !supertypes.is_empty() { - writeln!(out, "supertypes:").unwrap(); - for (name, members) in &supertypes { - writeln!(out, " {name}:").unwrap(); - for member in members { - let ref_str = format_type_ref(&member.kind, member.named, &all_named, &all_unnamed); - writeln!(out, " - {ref_str}").unwrap(); - } - } - writeln!(out).unwrap(); - } - - // Named - if !named.is_empty() { - writeln!(out, "named:").unwrap(); - for (name, fields_opt) in &named { - match fields_opt { - None => { - writeln!(out, " {name}:").unwrap(); - } - Some(fields) => { - writeln!(out, " {name}:").unwrap(); - for (field_name, info) in fields { - let suffix = field_suffix(info.multiple, info.required); - let yaml_name = if field_name == "$children" { - format!("$children{suffix}") - } else { - format!("{field_name}{suffix}") - }; - - let type_refs: Vec = info - .types - .iter() - .map(|t| format_type_ref(&t.kind, t.named, &all_named, &all_unnamed)) - .collect(); - - if type_refs.len() == 1 { - writeln!(out, " {yaml_name}: {}", type_refs[0]).unwrap(); - } else { - let list = type_refs - .iter() - .map(|s| s.as_str()) - .collect::>() - .join(", "); - writeln!(out, " {yaml_name}: [{list}]").unwrap(); - } - } - } - } - } - writeln!(out).unwrap(); - } - - // Unnamed - if !unnamed.is_empty() { - writeln!(out, "unnamed:").unwrap(); - for name in &unnamed { - writeln!(out, " - {}", force_quote(name)).unwrap(); - } - } - - Ok(out) -} - -fn field_suffix(multiple: bool, required: bool) -> &'static str { - match (multiple, required) { - (false, true) => "", - (false, false) => "?", - (true, true) => "+", - (true, false) => "*", - } -} - -/// Format a type reference for YAML output. Uses the disambiguation rule: -/// plain string if unambiguous, `{unnamed: name}` if the name exists as both -/// named and unnamed and we need the unnamed interpretation. -fn format_type_ref( - kind: &str, - named: bool, - all_named: &BTreeSet, - _all_unnamed: &BTreeSet, -) -> String { - if named { - quote_yaml(kind) - } else { - let is_also_named = all_named.contains(kind); - if is_also_named { - format!("{{unnamed: {}}}", force_quote(kind)) - } else { - force_quote(kind) - } - } -} - -/// Always wrap in double quotes. Used for unnamed node references so they're -/// visually distinct from named ones — YAML treats both forms as equivalent strings. -fn force_quote(s: &str) -> String { - format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")) -} - -/// Quote a YAML string value if it contains special characters or could be -/// misinterpreted. -fn quote_yaml(s: &str) -> String { - let needs_quoting = s.is_empty() - || s.contains(|c: char| { - matches!( - c, - ':' | '{' - | '}' - | '[' - | ']' - | ',' - | '&' - | '*' - | '#' - | '?' - | '|' - | '-' - | '<' - | '>' - | '=' - | '!' - | '%' - | '@' - | '`' - | '"' - | '\'' - ) - }) - || s.starts_with(' ') - || s.ends_with(' ') - || s == "true" - || s == "false" - || s == "null" - || s == "yes" - || s == "no" - || s.parse::().is_ok(); - - if needs_quoting { - format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")) - } else { - s.to_string() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_basic_conversion() { - let yaml = r#" -supertypes: - _expression: - - assignment - - binary - -named: - assignment: - left: _lhs - right: _expression - binary: - left: [_expression, _simple_numeric] - operator: ["!=", "+"] - right: _expression - argument_list: - $children*: [_expression, block_argument] - identifier: - -unnamed: - - "!=" - - "+" - - "end" -"#; - - let json_str = convert(yaml).unwrap(); - let result: Vec = serde_json::from_str(&json_str).unwrap(); - - // Check supertype - let expr = &result[0]; - assert_eq!(expr["type"], "_expression"); - assert_eq!(expr["named"], true); - assert_eq!(expr["subtypes"].as_array().unwrap().len(), 2); - - // Check assignment - let assign = result.iter().find(|n| n["type"] == "assignment").unwrap(); - assert_eq!(assign["fields"]["left"]["required"], true); - assert_eq!(assign["fields"]["left"]["multiple"], false); - assert_eq!(assign["fields"]["left"]["types"][0]["type"], "_lhs"); - assert_eq!(assign["fields"]["left"]["types"][0]["named"], true); - - // Check binary.operator — "!=" and "+" should resolve to unnamed - let binary = result.iter().find(|n| n["type"] == "binary").unwrap(); - let op_types = binary["fields"]["operator"]["types"].as_array().unwrap(); - assert_eq!(op_types[0]["type"], "!="); - assert_eq!(op_types[0]["named"], false); - assert_eq!(op_types[1]["type"], "+"); - assert_eq!(op_types[1]["named"], false); - - // Check argument_list has children, not a field - let arg_list = result - .iter() - .find(|n| n["type"] == "argument_list") - .unwrap(); - assert!(arg_list.get("children").is_some()); - assert_eq!(arg_list["children"]["multiple"], true); - assert_eq!(arg_list["children"]["required"], false); - - // Check identifier is a leaf - let ident = result.iter().find(|n| n["type"] == "identifier").unwrap(); - assert_eq!(ident["fields"].as_object().unwrap().len(), 0); - - // Check unnamed tokens - let end = result.iter().find(|n| n["type"] == "end").unwrap(); - assert_eq!(end["named"], false); - } - - #[test] - fn test_explicit_unnamed_disambiguation() { - let yaml = r#" -named: - foo: - field: [{unnamed: bar}] - -unnamed: - - bar -"#; - - let json_str = convert(yaml).unwrap(); - let result: Vec = serde_json::from_str(&json_str).unwrap(); - let foo = result.iter().find(|n| n["type"] == "foo").unwrap(); - assert_eq!(foo["fields"]["field"]["types"][0]["named"], false); - } - - #[test] - fn test_field_suffixes() { - let yaml = r#" -named: - test_node: - required_single: foo - optional_single?: foo - required_multiple+: foo - optional_multiple*: foo -"#; - - let json_str = convert(yaml).unwrap(); - let result: Vec = serde_json::from_str(&json_str).unwrap(); - let node = result.iter().find(|n| n["type"] == "test_node").unwrap(); - let fields = node["fields"].as_object().unwrap(); - - assert_eq!(fields["required_single"]["required"], true); - assert_eq!(fields["required_single"]["multiple"], false); - - assert_eq!(fields["optional_single"]["required"], false); - assert_eq!(fields["optional_single"]["multiple"], false); - - assert_eq!(fields["required_multiple"]["required"], true); - assert_eq!(fields["required_multiple"]["multiple"], true); - - assert_eq!(fields["optional_multiple"]["required"], false); - assert_eq!(fields["optional_multiple"]["multiple"], true); - } - - #[test] - fn test_json_to_yaml() { - let json = r#"[ - {"type": "_expression", "named": true, "subtypes": [ - {"type": "assignment", "named": true}, - {"type": "identifier", "named": true} - ]}, - {"type": "assignment", "named": true, "fields": { - "left": {"multiple": false, "required": true, "types": [ - {"type": "_expression", "named": true} - ]}, - "right": {"multiple": false, "required": false, "types": [ - {"type": "_expression", "named": true} - ]} - }, "children": { - "multiple": true, "required": false, "types": [ - {"type": "identifier", "named": true} - ] - }}, - {"type": "identifier", "named": true, "fields": {}}, - {"type": "=", "named": false}, - {"type": "end", "named": false} - ]"#; - - let yaml = convert_from_json(json).unwrap(); - - // Verify key structures are present - assert!(yaml.contains("supertypes:")); - assert!(yaml.contains("_expression:")); - assert!(yaml.contains("named:")); - assert!(yaml.contains("assignment:")); - assert!(yaml.contains("left:")); - assert!(yaml.contains("right?:")); - assert!(yaml.contains("$children*:")); - assert!(yaml.contains("identifier:")); - assert!(yaml.contains("unnamed:")); - assert!(yaml.contains("\"=\"")); - assert!(yaml.contains("end")); - } - - #[test] - fn test_round_trip() { - let yaml_input = r#" -supertypes: - _expression: - - assignment - - identifier - -named: - assignment: - left: _expression - right?: _expression - $children*: identifier - identifier: - -unnamed: - - "=" - - end -"#; - - // YAML → JSON → YAML - let json = convert(yaml_input).unwrap(); - let yaml_output = convert_from_json(&json).unwrap(); - // YAML → JSON again (should be identical) - let json2 = convert(&yaml_output).unwrap(); - - let v1: serde_json::Value = serde_json::from_str(&json).unwrap(); - let v2: serde_json::Value = serde_json::from_str(&json2).unwrap(); - assert_eq!(v1, v2); - } -} diff --git a/shared/yeast/src/schema.rs b/shared/yeast/src/schema.rs index da13bb8b6b70..daa8ad98eb5b 100644 --- a/shared/yeast/src/schema.rs +++ b/shared/yeast/src/schema.rs @@ -1,285 +1,54 @@ -use std::collections::{BTreeMap, BTreeSet}; - -use crate::{FieldId, KindId, CHILD_FIELD}; - -#[derive(Clone, Debug)] -pub struct NodeType { - pub kind: String, - pub named: bool, -} - -/// Multiplicity/optionality of a field declaration. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct FieldCardinality { - /// Whether the field may hold more than one child. - pub multiple: bool, - /// Whether at least one child must be present. - pub required: bool, -} - -/// A schema defining node kinds and field names for the output AST. -/// Built from a node-types.yml file, independent of any tree-sitter grammar. -/// -/// # Memory management -/// -/// `register_field`/`register_kind`/`register_unnamed_kind` use `Box::leak` -/// to obtain `&'static str` names. This is intentional: the `&'static str` -/// names appear pervasively in `Node`, `AstCursor`, query patterns, and the -/// extractor's TRAP output, where adding a lifetime would propagate widely. -/// -/// The leak is bounded by the number of distinct kind/field names registered. -/// Schemas are expected to be constructed once per process (e.g. at extractor -/// startup) and reused. Repeated construction in long-running processes will -/// leak memory unboundedly and should be avoided. -#[derive(Clone)] -pub struct Schema { - field_ids: BTreeMap, - field_names: BTreeMap, - next_field_id: FieldId, - kind_ids: BTreeMap, - unnamed_kind_ids: BTreeMap, - kind_names: BTreeMap, - next_kind_id: KindId, - field_types: BTreeMap<(String, FieldId), Vec>, - field_cardinalities: BTreeMap<(String, FieldId), FieldCardinality>, - supertypes: BTreeMap>, -} - -impl Default for Schema { - fn default() -> Self { - Self::new() - } -} - -impl Schema { - pub fn new() -> Self { - Self { - field_ids: BTreeMap::new(), - field_names: BTreeMap::new(), - next_field_id: 1, // 0 is reserved - kind_ids: BTreeMap::new(), - unnamed_kind_ids: BTreeMap::new(), - kind_names: BTreeMap::new(), - next_kind_id: 1, // 0 is reserved - field_types: BTreeMap::new(), - field_cardinalities: BTreeMap::new(), - supertypes: BTreeMap::new(), - } - } - - /// Create a schema from a tree-sitter language, importing all its - /// known field and kind names. - pub fn from_language(language: &tree_sitter::Language) -> Self { - let mut schema = Self::new(); - // Import all field names, preserving tree-sitter's IDs - for id in 1..=language.field_count() as u16 { - if let Some(name) = language.field_name_for_id(id) { - schema.field_ids.insert(name.to_string(), id); - schema.field_names.insert(id, name); - if id >= schema.next_field_id { - schema.next_field_id = id + 1; - } +//! YEAST schema types. +//! +//! The schema struct itself lives in the [`yeast_schema`] crate (so it can +//! be shared with the `yeast-macros` proc-macro crate without dragging +//! tree-sitter into proc-macro compiles). This module re-exports its +//! public API and supplies the one tree-sitter-aware adapter the runtime +//! needs: [`from_language`]. + +pub use yeast_schema::schema::{FieldCardinality, NodeType, Schema}; + +/// Build a [`Schema`] from a tree-sitter language, importing all its +/// known field and kind names so the resulting schema's IDs line up with +/// the language's own IDs (i.e. `field_name_for_id` agrees). +pub fn from_language(language: &tree_sitter::Language) -> Schema { + let mut schema = Schema::new(); + + // Import all field names, preserving tree-sitter's IDs. + for id in 1..=language.field_count() as u16 { + if let Some(name) = language.field_name_for_id(id) { + schema.register_field_with_id(name, id); + } + } + + // Import all node kind names, preserving tree-sitter's IDs. + // Track named and unnamed variants separately. For both, prefer the + // canonical ID returned by `id_for_node_kind`, since some languages + // have multiple IDs for the same name (e.g. the reserved error token + // at ID 0 may share a name with a real token). + for id in 0..language.node_kind_count() as u16 { + if let Some(name) = language.node_kind_for_id(id) { + if name.is_empty() { + continue; } - } - // Import all node kind names, preserving tree-sitter's IDs. - // Track named and unnamed variants separately. For both named and - // unnamed kinds, use the canonical ID from id_for_node_kind, since - // some languages have multiple IDs for the same name (e.g., the - // reserved error token at ID 0 may share a name with a real token). - for id in 0..language.node_kind_count() as u16 { - if let Some(name) = language.node_kind_for_id(id) { - if !name.is_empty() { - let is_named = language.node_kind_is_named(id); - if is_named { - let canonical_id = language.id_for_node_kind(name, true); - if canonical_id != 0 && !schema.kind_ids.contains_key(name) { - schema.kind_ids.insert(name.to_string(), canonical_id); - schema.kind_names.insert(canonical_id, name); - } - } else { - let canonical_id = language.id_for_node_kind(name, false); - if canonical_id != 0 && !schema.unnamed_kind_ids.contains_key(name) { - schema - .unnamed_kind_ids - .insert(name.to_string(), canonical_id); - schema.kind_names.insert(canonical_id, name); - } - } - // Always track the name for any ID we encounter - schema.kind_names.entry(id).or_insert(name); - if id >= schema.next_kind_id { - schema.next_kind_id = id + 1; - } + let is_named = language.node_kind_is_named(id); + if is_named { + let canonical_id = language.id_for_node_kind(name, true); + if canonical_id != 0 && schema.id_for_node_kind(name).is_none() { + schema.register_kind_with_id(name, canonical_id); + } + } else { + let canonical_id = language.id_for_node_kind(name, false); + if canonical_id != 0 && schema.id_for_unnamed_node_kind(name).is_none() { + schema.register_unnamed_kind_with_id(name, canonical_id); } } + // Always track the name for any ID we encounter (so + // `node_kind_for_id` works for the literal `id` we saw, even + // when it isn't the canonical one). + schema.record_kind_name(id, name); } - schema - } - - /// Register a field name, returning its ID. - /// If already registered, returns the existing ID. - pub fn register_field(&mut self, name: &str) -> FieldId { - if name == "child" { - return CHILD_FIELD; - } - if let Some(&id) = self.field_ids.get(name) { - return id; - } - let id = self.next_field_id; - assert!(id < CHILD_FIELD, "too many fields"); - self.next_field_id += 1; - let leaked: &'static str = Box::leak(name.to_string().into_boxed_str()); - self.field_ids.insert(name.to_string(), id); - self.field_names.insert(id, leaked); - id - } - - /// Register a named node kind name, returning its ID. - /// If already registered, returns the existing ID. - pub fn register_kind(&mut self, name: &str) -> KindId { - if let Some(&id) = self.kind_ids.get(name) { - return id; - } - let id = self.next_kind_id; - self.next_kind_id += 1; - let leaked: &'static str = Box::leak(name.to_string().into_boxed_str()); - self.kind_ids.insert(name.to_string(), id); - self.kind_names.insert(id, leaked); - id } - /// Register an unnamed token kind (e.g. `"="`, `"end"`), returning its ID. - /// If already registered, returns the existing ID. - pub fn register_unnamed_kind(&mut self, name: &str) -> KindId { - if let Some(&id) = self.unnamed_kind_ids.get(name) { - return id; - } - let id = self.next_kind_id; - self.next_kind_id += 1; - let leaked: &'static str = Box::leak(name.to_string().into_boxed_str()); - self.unnamed_kind_ids.insert(name.to_string(), id); - self.kind_names.insert(id, leaked); - id - } - - pub fn field_id_for_name(&self, name: &str) -> Option { - if name == "child" { - return Some(CHILD_FIELD); - } - self.field_ids.get(name).copied() - } - - pub fn field_name_for_id(&self, id: FieldId) -> Option<&'static str> { - if id == CHILD_FIELD { - return Some("child"); - } - self.field_names.get(&id).copied() - } - - pub fn id_for_node_kind(&self, kind: &str) -> Option { - self.kind_ids.get(kind).copied() - } - - pub fn id_for_unnamed_node_kind(&self, kind: &str) -> Option { - self.unnamed_kind_ids.get(kind).copied() - } - - pub fn node_kind_for_id(&self, id: KindId) -> Option<&'static str> { - self.kind_names.get(&id).copied() - } - - pub fn set_field_types( - &mut self, - parent_kind: &str, - field_id: FieldId, - node_types: Vec, - ) { - self.field_types - .insert((parent_kind.to_string(), field_id), node_types); - } - - pub fn field_types(&self, parent_kind: &str, field_id: FieldId) -> Option<&Vec> { - self.field_types.get(&(parent_kind.to_string(), field_id)) - } - - pub fn set_field_cardinality( - &mut self, - parent_kind: &str, - field_id: FieldId, - cardinality: FieldCardinality, - ) { - self.field_cardinalities - .insert((parent_kind.to_string(), field_id), cardinality); - } - - /// Returns the declared cardinality for a field, if known. - pub fn field_cardinality( - &self, - parent_kind: &str, - field_id: FieldId, - ) -> Option { - self.field_cardinalities - .get(&(parent_kind.to_string(), field_id)) - .copied() - } - - /// Returns an iterator over all `(field_id, field_name)` pairs that are - /// declared as required (`required: true`) for the given `parent_kind`. - pub fn required_fields_for_kind<'a>( - &'a self, - parent_kind: &'a str, - ) -> impl Iterator)> + 'a { - self.field_cardinalities - .iter() - .filter(move |((kind, _), card)| kind == parent_kind && card.required) - .map(move |((_, field_id), _)| { - let name = self.field_name_for_id(*field_id); - (*field_id, name) - }) - } - - pub fn set_supertype_members(&mut self, supertype: &str, node_types: Vec) { - self.supertypes.insert(supertype.to_string(), node_types); - } - - fn allows_node( - &self, - node_type: &NodeType, - node_kind: &str, - node_named: bool, - active: &mut BTreeSet, - ) -> bool { - if node_type.kind == node_kind && node_type.named == node_named { - return true; - } - - if !node_type.named { - return false; - } - - let Some(members) = self.supertypes.get(&node_type.kind) else { - return false; - }; - - if !active.insert(node_type.kind.clone()) { - return false; - } - - let matched = members - .iter() - .any(|member| self.allows_node(member, node_kind, node_named, active)); - active.remove(&node_type.kind); - matched - } - - pub fn node_matches_types( - &self, - node_kind: &str, - node_named: bool, - node_types: &[NodeType], - ) -> bool { - node_types.iter().any(|node_type| { - self.allows_node(node_type, node_kind, node_named, &mut BTreeSet::new()) - }) - } + schema } From ea36f2d7f8fbfccae9d05fa5addd4a1ab4d97d06 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 19 Jun 2026 15:31:02 +0000 Subject: [PATCH 09/39] yeast: add `rules!` macro This macro allows the easy addition of multiple rules at the same time. In addition, it also accepts an input and output schema, which eventually will be used to check the validity of the rewrite rules. --- shared/yeast-macros/src/lib.rs | 40 +++ shared/yeast-macros/src/parse.rs | 243 +++++++++++++++++++ shared/yeast/doc/yeast.md | 41 ++++ shared/yeast/src/lib.rs | 2 +- shared/yeast/tests/input-types.yml | 40 +++ shared/yeast/tests/test.rs | 120 +++++++++ unified/extractor/tests/rules_macro_smoke.rs | 25 ++ 7 files changed, 510 insertions(+), 1 deletion(-) create mode 100644 shared/yeast/tests/input-types.yml create mode 100644 unified/extractor/tests/rules_macro_smoke.rs diff --git a/shared/yeast-macros/src/lib.rs b/shared/yeast-macros/src/lib.rs index 7db97f9fb709..0df96c91d26b 100644 --- a/shared/yeast-macros/src/lib.rs +++ b/shared/yeast-macros/src/lib.rs @@ -113,3 +113,43 @@ pub fn rule(input: TokenStream) -> TokenStream { Err(err) => err.to_compile_error().into(), } } + +/// Bundle a list of YEAST rewrite rules with input/output node-types +/// schema paths. Returns a `Vec`; substitutable for +/// `vec![rule!(...), ...]`. +/// +/// Each comma-separated item in the bracketed list may be: +/// +/// 1. A **bare rule body** `(query) => (template)` — the `rule!(...)` +/// wrapper is implicit. +/// 2. An explicit `rule!(...)` invocation, possibly chained as +/// `rule!(...).repeated()` or path-prefixed as `yeast::rule!(...)`. +/// 3. Any other expression returning a `Rule` (helper-function calls, +/// conditionals). +/// +/// ```ignore +/// let translation_rules: Vec = yeast::rules! { +/// input: "tree-sitter-swift/node-types.yml", +/// output: "ast_types.yml", +/// [ +/// (source_file (_)* @cs) => (top_level body: {..cs}), +/// (simple_identifier) @id => (name_expr identifier: (identifier #{id})), +/// rule!((integer_literal) @lit => (int_literal #{lit})).repeated(), +/// helper_fn(), +/// ] +/// }; +/// ``` +/// +/// Paths are resolved relative to the consuming crate's `CARGO_MANIFEST_DIR` +/// (the same convention `include_str!` uses for relative paths). The +/// resolved paths are also emitted as `include_str!` references so the +/// consuming crate gets invalidated when a schema YAML changes, prepping +/// the ground for compile-time type-checking against those schemas. +#[proc_macro] +pub fn rules(input: TokenStream) -> TokenStream { + let input2: TokenStream2 = input.into(); + match parse::parse_rules_top(input2) { + Ok(output) => output.into(), + Err(err) => err.to_compile_error().into(), + } +} diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 2ab6236fdac9..01070c74bbce 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -897,6 +897,219 @@ fn expect_repetition(tokens: &mut Tokens) -> Result { } } +// --------------------------------------------------------------------------- +// rules! parsing — bundle a list of rules with input/output schema paths. +// +// The macro accepts both bare rule bodies (`(query) => (template)`) and +// explicit `rule!(...)` invocations. The schema paths are recorded but +// not yet consumed; a later change layers compile-time type-checking on +// top, using these paths to load the input/output schemas. +// --------------------------------------------------------------------------- + +/// Parse `rules! { input: "path", output: "path", [ items, ... ] }`. +/// +/// Each item in the bracketed list can be: +/// * a **bare rule body** `(query) => (template)` — wrapped implicitly +/// in `yeast::rule! { ... }` for codegen; +/// * an explicit `rule!(...)` (or `rule!(...).repeated()`, +/// `yeast::rule!(...)`, etc.) — passed through verbatim; +/// * any other expression returning a `Rule` (helper-function calls, +/// conditionals) — passed through verbatim. +/// +/// Returns a `Vec` containing the items in order. The expansion +/// also emits `include_str!` references to the resolved schema paths so +/// Cargo treats them as inputs to the consuming crate; this validates +/// path existence at compile time and prepares the ground for later +/// schema-aware checks. +pub fn parse_rules_top(input: TokenStream) -> Result { + let mut tokens = input.into_iter().peekable(); + + let input_path = parse_named_string_arg(&mut tokens, "input")?; + expect_punct(&mut tokens, ',', "expected `,` after input path")?; + let output_path = parse_named_string_arg(&mut tokens, "output")?; + expect_punct(&mut tokens, ',', "expected `,` after output path")?; + + // Resolve paths relative to the consuming crate's CARGO_MANIFEST_DIR + // so callers can write paths like "tree-sitter-swift/node-types.yml" + // alongside their other workspace-relative includes (e.g. include_str!). + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").map_err(|_| { + syn::Error::new( + Span::call_site(), + "rules!: CARGO_MANIFEST_DIR is not set; cannot resolve schema paths", + ) + })?; + let resolve_path = |raw: &str| -> std::path::PathBuf { + let p = std::path::PathBuf::from(raw); + if p.is_absolute() { + p + } else { + std::path::PathBuf::from(&manifest_dir).join(p) + } + }; + let input_abs = resolve_path(&input_path.value); + let output_abs = resolve_path(&output_path.value); + + let list = expect_group(&mut tokens, Delimiter::Bracket)?; + if let Some(tok) = tokens.next() { + return Err(syn::Error::new_spanned( + tok, + "unexpected token after `rules!` list", + )); + } + + let items = split_top_level_commas(list.stream()); + let emitted_items: Vec = items + .into_iter() + .map(|item| { + // Bare rule body — wrap in `yeast::rule! { ... }` so the + // existing rule-construction macro handles codegen. Other + // items pass through unchanged. + if has_top_level_arrow(&item) { + quote! { yeast::rule! { #item } } + } else { + item + } + }) + .collect(); + + // Emit `include_str!` references to both schema files so Cargo + // treats them as inputs to the consuming crate's compilation. The + // `const _` bindings are unused; rustc/LLVM drop them after the + // file-input dependency edge is recorded. Absolute paths are used + // because `include_str!` resolves relative paths against the source + // file, while `rules!`'s own paths are relative to + // `CARGO_MANIFEST_DIR`. + let input_abs_str = input_abs.to_string_lossy().into_owned(); + let output_abs_str = output_abs.to_string_lossy().into_owned(); + let input_lit = proc_macro2::Literal::string(&input_abs_str); + let output_lit = proc_macro2::Literal::string(&output_abs_str); + + Ok(quote! { + { + const _: &::core::primitive::str = ::core::include_str!(#input_lit); + const _: &::core::primitive::str = ::core::include_str!(#output_lit); + vec![ #(#emitted_items),* ] + } + }) +} + +/// True iff `item` contains a `=>` operator at the top level (not nested +/// inside any group). Used to detect bare rule bodies inside `rules!`. +fn has_top_level_arrow(item: &TokenStream) -> bool { + let toks: Vec = item.clone().into_iter().collect(); + find_top_level_arrow(&toks).is_some() +} + +/// Find the index of the first token of a top-level `=>` operator (the +/// `=`), ignoring `=>` inside any group. Returns `None` if not present. +fn find_top_level_arrow(toks: &[TokenTree]) -> Option { + let mut i = 0; + while i + 1 < toks.len() { + if let (TokenTree::Punct(p1), TokenTree::Punct(p2)) = (&toks[i], &toks[i + 1]) { + if p1.as_char() == '=' + && p1.spacing() == proc_macro2::Spacing::Joint + && p2.as_char() == '>' + { + return Some(i); + } + } + i += 1; + } + None +} + +/// A string literal argument named `expected_name` parsed from `name: "value"`. +struct NamedString { + value: String, + #[allow(dead_code)] + span: Span, +} + +fn parse_named_string_arg(tokens: &mut Tokens, expected_name: &str) -> Result { + let name = expect_ident( + tokens, + &format!("expected `{expected_name}:` argument"), + )?; + if name != expected_name { + return Err(syn::Error::new_spanned( + name, + format!("expected `{expected_name}:` argument"), + )); + } + expect_punct( + tokens, + ':', + &format!("expected `:` after `{expected_name}`"), + )?; + let lit = expect_literal(tokens)?; + let span = lit.span(); + let value = string_literal_value(&lit).ok_or_else(|| { + syn::Error::new( + span, + format!("`{expected_name}` must be a string literal path"), + ) + })?; + Ok(NamedString { value, span }) +} + +/// Read a literal as a plain Rust string, stripping the surrounding quotes +/// and unescaping. Falls back to `None` if the literal isn't a string. +fn string_literal_value(lit: &Literal) -> Option { + let raw = lit.to_string(); + let bytes = raw.as_bytes(); + // Match plain `"..."` literals; reject byte strings, raw strings (for + // simplicity), char literals, numbers, etc. + if bytes.first() != Some(&b'"') || bytes.last() != Some(&b'"') { + return None; + } + let mut out = String::with_capacity(raw.len()); + let mut chars = raw[1..raw.len() - 1].chars(); + while let Some(c) = chars.next() { + if c != '\\' { + out.push(c); + continue; + } + match chars.next()? { + 'n' => out.push('\n'), + 't' => out.push('\t'), + 'r' => out.push('\r'), + '\\' => out.push('\\'), + '\'' => out.push('\''), + '"' => out.push('"'), + '0' => out.push('\0'), + other => { + // Unknown escape — give up rather than silently mis-parse. + out.push('\\'); + out.push(other); + } + } + } + Some(out) +} + +/// Split a token stream into top-level comma-separated items. Commas inside +/// any group token (parens, brackets, braces) are ignored so that things +/// like `rule!(a, b)` aren't accidentally split. +fn split_top_level_commas(stream: TokenStream) -> Vec { + let mut items = Vec::new(); + let mut current: Vec = Vec::new(); + for tt in stream { + if let TokenTree::Punct(p) = &tt { + if p.as_char() == ',' && p.spacing() == proc_macro2::Spacing::Alone { + if !current.is_empty() { + items.push(current.drain(..).collect()); + } + continue; + } + } + current.push(tt); + } + if !current.is_empty() { + items.push(current.into_iter().collect()); + } + items +} + fn maybe_wrap_capture(tokens: &mut Tokens, base: TokenStream) -> Result { if peek_is_at(tokens) { let name = consume_capture_marker(tokens)?; @@ -970,3 +1183,33 @@ fn maybe_wrap_list_capture(tokens: &mut Tokens, elem: TokenStream) -> Result` is present. + let toks = quote! { (a) => (b) }; + assert!(has_top_level_arrow(&toks)); + // `rule!((a) => (b))`: the `=>` is INSIDE the macro group, so + // it's not at top level. Must NOT be detected as a bare body. + let toks = quote! { rule!((a) => (b)) }; + assert!(!has_top_level_arrow(&toks)); + // Helper call: no `=>` anywhere. + let toks = quote! { make_rule() }; + assert!(!has_top_level_arrow(&toks)); + // Match expressions inside a block: `=>` is inside braces. + let toks = quote! { { match x { 1 => 2, _ => 3 } } }; + assert!(!has_top_level_arrow(&toks)); + // Bare shorthand form: top-level `=>` followed by a bare ident. + let toks = quote! { (a) => kind }; + assert!(has_top_level_arrow(&toks)); + } +} diff --git a/shared/yeast/doc/yeast.md b/shared/yeast/doc/yeast.md index 8aa050592f6b..3427597be2e6 100644 --- a/shared/yeast/doc/yeast.md +++ b/shared/yeast/doc/yeast.md @@ -437,3 +437,44 @@ For the dbscheme/QL code generator, set `Language::desugar` to a `DesugaringConfig` carrying the same YAML; the generator converts it to JSON for downstream code generation. The `phases` field of the config is unused at code-generation time. + +## The `rules!` macro + +The [`rules!`] macro bundles a list of rewrite rules with the input and +output node-types schema paths. It's a drop-in replacement for the +hand-written `vec![rule!(...), rule!(...), ...]` form and accepts a +slightly looser syntax: bare rule bodies don't need an explicit +`rule!(...)` wrapper. + +```rust +let translation_rules: Vec = yeast::rules! { + input: "tree-sitter-swift/node-types.yml", + output: "ast_types.yml", + [ + (simple_identifier) @name + => + (name_expr identifier: (identifier #{name})), + + (integer_literal) @lit + => + (int_literal #{lit}), + ] +}; +``` + +Each comma-separated item in the bracketed list may be: + +- A **bare rule body** `(query) => (template)` — no `rule!(...)` wrapper. +- An explicit `rule!(...)` invocation, with optional postfix calls such + as `rule!(...).repeated()`. +- Any other expression returning a `Rule` (helper functions, etc.). + +Schema paths are resolved relative to the consuming crate's +`CARGO_MANIFEST_DIR` (the same convention `include_str!` uses for +relative paths). The resolved paths are emitted as `include_str!` +references in the expansion so the consuming crate's incremental cache +invalidates when a schema YAML changes — laying the groundwork for +schema-aware compile-time checks on the rule bodies. + +The `Vec` produced by `rules!` flows into `add_phase` exactly as +before. \ No newline at end of file diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index c4b448077267..e6759f1ddb92 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -15,7 +15,7 @@ pub mod schema; pub mod tree_builder; mod visitor; -pub use yeast_macros::{query, rule, tree, trees}; +pub use yeast_macros::{query, rule, rules, tree, trees}; use captures::Captures; use query::QueryNode; diff --git a/shared/yeast/tests/input-types.yml b/shared/yeast/tests/input-types.yml new file mode 100644 index 000000000000..6bc184ec6470 --- /dev/null +++ b/shared/yeast/tests/input-types.yml @@ -0,0 +1,40 @@ +# Test input schema for yeast rules! macro tests. Covers a small subset of +# tree-sitter-ruby kinds used by the test rules. Kept deliberately small so +# the macro's compile-time loader can be exercised over a known surface. + +named: + program: + $children*: [assignment, call, identifier, for] + + assignment: + left: [identifier, left_assignment_list] + right: [identifier, integer, call] + + left_assignment_list: + $children*: identifier + + for: + pattern: [identifier, left_assignment_list] + value: in + body: do + + in: + $children: [identifier, call] + + do: + $children*: [identifier, assignment, call] + + call: + receiver: [identifier, call] + method: identifier + + identifier: + integer: + +unnamed: + - "=" + - "," + - "for" + - "in" + - "do" + - "end" diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 57a9e17dbd4c..4a2fb26f4fe3 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -1322,3 +1322,123 @@ fn test_hash_brace_uses_capture_location_for_leaf() { assert_eq!(bar.start_byte(), 4); assert_eq!(bar.end_byte(), 7); } + +// ---- `rules!` macro tests (compile-time type-checking) ---- + +/// `rules!` should accept well-typed rules using the bare-rule-body +/// syntax (no inner `rule!` invocations) and produce a `Vec` that +/// behaves identically to a plain `vec![rule!(...)]` list. +#[test] +fn test_rules_macro_accepts_bare_rule_body() { + let rules: Vec = yeast::rules! { + input: "tests/input-types.yml", + output: "tests/node-types.yml", + [ + (assignment + left: (_) @left + right: (_) @right + ) + => + (assignment + left: {right} + right: {left} + ), + ] + }; + + let dump = run_and_dump("x = 1", rules); + assert_dump_eq( + &dump, + r#" + program + assignment + left: integer "1" + right: identifier "x" + "#, + ); +} + +/// The bare-rule-body shorthand `=> output_kind` should also be accepted. +#[test] +fn test_rules_macro_accepts_bare_shorthand_form() { + let rules: Vec = yeast::rules! { + input: "tests/input-types.yml", + output: "tests/node-types.yml", + [ + (assignment + left: (_) @method + right: (_) @receiver + ) + => call, + ] + }; + + let dump = run_and_dump("x = 1", rules); + assert_dump_eq( + &dump, + r#" + program + call + method: identifier "x" + receiver: integer "1" + "#, + ); +} + +/// Backwards-compat: explicit `rule!(...)` invocations inside `rules!` +/// should still type-check and behave the same as the bare form. +#[test] +fn test_rules_macro_accepts_explicit_rule_macro() { + let rules: Vec = yeast::rules! { + input: "tests/input-types.yml", + output: "tests/node-types.yml", + [ + rule!( + (assignment + left: (_) @left + right: (_) @right + ) + => + (assignment + left: {right} + right: {left} + ) + ), + ] + }; + assert_eq!(rules.len(), 1); +} + +/// `rules!` should pass through items that aren't bare rule bodies or +/// `rule!(...)` calls (e.g. helper-function calls returning a `Rule`), +/// without type-checking them. Bare and explicit rules in the same list +/// still get checked. +#[test] +fn test_rules_macro_allows_non_rule_items() { + fn extra() -> yeast::Rule { + rule!((identifier) => (identifier "extra")) + } + let rules: Vec = yeast::rules! { + input: "tests/input-types.yml", + output: "tests/node-types.yml", + [ + (integer) => (integer "checked"), + extra(), + ] + }; + assert_eq!(rules.len(), 2); +} + +/// `rules!` should accept lists that mix bare-rule and explicit-rule items. +#[test] +fn test_rules_macro_mixes_bare_and_explicit_forms() { + let rules: Vec = yeast::rules! { + input: "tests/input-types.yml", + output: "tests/node-types.yml", + [ + (integer) => (integer "I"), + rule!((identifier) => (identifier "S")), + ] + }; + assert_eq!(rules.len(), 2); +} diff --git a/unified/extractor/tests/rules_macro_smoke.rs b/unified/extractor/tests/rules_macro_smoke.rs new file mode 100644 index 000000000000..cde8ae3ca4ab --- /dev/null +++ b/unified/extractor/tests/rules_macro_smoke.rs @@ -0,0 +1,25 @@ +/// Smoke test: load a few real Swift translation rules through the new +/// `yeast::rules!` macro using the bare-rule-body syntax, and confirm the +/// input + output schemas accept them. Compiles only — any type-checking +/// error surfaces as a compile-time error. +#[test] +fn rules_macro_compiles_against_real_swift_schemas() { + let _rules: Vec = yeast::rules! { + input: "tree-sitter-swift/node-types.yml", + output: "ast_types.yml", + [ + (simple_identifier) @name + => + (name_expr + identifier: (identifier #{name})), + + (integer_literal) @lit + => + (int_literal #{lit}), + + (line_string_literal) @lit + => + (string_literal #{lit}), + ] + }; +} From ee04938dedea3e1a83b4dd38ecc68c90b48abe10 Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 2 Jul 2026 12:05:23 +0000 Subject: [PATCH 10/39] yeast: Require type annotations on root-level Rust interpolations In order to facilitate static type checking of rules (and to make it easier for human readers as well), rust blocks at the root level (i.e. rules of the form `... => { ... }`) must now have a type annotation in front. All other forms are unaffected: if the right hand side of a rule is a tree, we can read the type of the root node directly. For interpolations that happen inside of such a tree, we can recover the type by looking at what field we're interpolating into, and consulting the output schema. All existing uses have been updated to have the appropriate type annotations, though these are of course not checked yet (and so could be wrong). Finally, this commit also removes the final catch-all rule `_ @node => {node}`. Because of the preceding rule that matches `(_) @node`, this rule would only ever match unnamed nodes, and I think in practice it did not match at all (at least not in our current set of tests). To give it a proper type we would have to add some notion of an "any" type, which I would like to avoid. If it _does_ turn out to be needed, we can easily add it back (ideally with a test-case that shows why it's still needed). --- shared/yeast-macros/src/parse.rs | 143 +++++++++++++++++- shared/yeast/doc/yeast.md | 65 +++++++- shared/yeast/tests/test.rs | 116 +++++++++++++- .../extractor/src/languages/swift/swift.rs | 59 ++++---- 4 files changed, 337 insertions(+), 46 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 01070c74bbce..986a9bac641a 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -617,6 +617,76 @@ fn extract_captures_inner( } } +/// A rule's return-type annotation, when the body is a Rust block. Written +/// between `=>` and the block body using the schema's own vocabulary: +/// +/// ```text +/// => kind { … } // single node of that kind +/// => kind? { … } // Option (0 or 1) +/// => kind* { … } // Vec (0+) +/// ``` +/// +/// Template bodies (`=> (kind …)`) never carry an annotation — the +/// output kind is the template root. The shorthand `=> kind` (no +/// body) also carries no annotation. See `parse_rule_top` for dispatch. +#[derive(Clone, Debug)] +struct ReturnAnnotation { + kind: Ident, + multiplicity: AnnotationMultiplicity, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum AnnotationMultiplicity { + Single, + Optional, + Repeated, +} + +/// Peek at the token stream to decide whether the transform following +/// `=>` is a **new** annotation form (`kind [? | *] { … }`). If so, +/// consume the annotation and return it, leaving the `{ … }` body in +/// the stream for the caller to parse. Otherwise leave the stream +/// untouched and return `None`. +/// +/// The lookahead distinguishes: +/// `kind {` → annotation (single) +/// `kind? {` → annotation (optional) +/// `kind* {` → annotation (repeated) +/// `kind` → shorthand form (no `{` follows) — NOT an annotation +/// anything else → template or bare block — NOT an annotation +fn try_consume_return_annotation(tokens: &mut Tokens) -> Result> { + // Must start with an identifier (the kind name). + let mut lookahead = tokens.clone(); + let Some(TokenTree::Ident(_)) = lookahead.next() else { + return Ok(None); + }; + // Then optionally `?` or `*`, then a `{` group. + let after_suffix = match lookahead.peek() { + Some(TokenTree::Punct(p)) if p.as_char() == '?' || p.as_char() == '*' => { + lookahead.next(); + lookahead.peek() + } + other => other, + }; + if !matches!(after_suffix, Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace) { + return Ok(None); + } + // Commit: consume the ident + suffix from the real stream. + let kind = expect_ident(tokens, "expected output-kind name in annotation")?; + let multiplicity = match tokens.peek() { + Some(TokenTree::Punct(p)) if p.as_char() == '?' => { + tokens.next(); + AnnotationMultiplicity::Optional + } + Some(TokenTree::Punct(p)) if p.as_char() == '*' => { + tokens.next(); + AnnotationMultiplicity::Repeated + } + _ => AnnotationMultiplicity::Single, + }; + Ok(Some(ReturnAnnotation { kind, multiplicity })) +} + /// Parse `rule!( query => transform )`. pub fn parse_rule_top(input: TokenStream) -> Result { let mut tokens = input.into_iter().peekable(); @@ -688,8 +758,52 @@ pub fn parse_rule_top(input: TokenStream) -> Result { }) .collect(); - // Parse transform: either shorthand `=> kind_name` or full `=> (template ...)` - let transform_body = if peek_is_field(&mut tokens) && { + // Parse transform: the token(s) after `=>` fall into one of three + // shapes, dispatched in order: + // + // 1. `kind [? | *] { rust_body }` — annotated Rust body (NEW). + // Static-analysis-ready: the annotation declares the output + // kind and multiplicity in the schema's own vocabulary. + // 2. `kind` alone — shorthand: emit `(kind field: {@cap})…` from + // the query's captures. + // 3. anything else — full template form (`(kind …)` or bare + // `{ … }` splice via `parse_direct_list`). + let annotation = try_consume_return_annotation(&mut tokens)?; + + let transform_body = if let Some(annotation) = annotation { + // Annotation form: `=> kind [? | *] { rust_body }`. + let body_group = expect_group(&mut tokens, Delimiter::Brace)?; + if let Some(tok) = tokens.next() { + return Err(syn::Error::new_spanned( + tok, + "unexpected token after annotated rule body", + )); + } + let body = body_group.stream(); + // The annotation is not yet consumed by codegen — it will drive + // typed handles once the schema-driven codegen lands. For now, + // emit a self-documenting reference to the annotated kind and + // preserve today's `Vec` closure return so behavior + // is unchanged. + let kind_str = annotation.kind.to_string(); + let mult_str = match annotation.multiplicity { + AnnotationMultiplicity::Single => "single", + AnnotationMultiplicity::Optional => "optional", + AnnotationMultiplicity::Repeated => "repeated", + }; + let _ = (kind_str, mult_str); // silence unused warnings until wired + + // For now, adapt the user's typed return value to the framework's + // `Vec` closure result. This uses `IntoFieldIds`, which + // already accepts a bare `Id`, an iterable of ids, or `Option` + // — matching the three annotation multiplicities. + quote! { + let __value = { #body }; + let mut __ids: Vec = Vec::new(); + yeast::IntoFieldIds::extend_into(__value, &mut __ids); + __ids + } + } else if peek_is_field(&mut tokens) && { // Shorthand form: bare identifier = output node kind. // Auto-generate template from captures. let mut lookahead = tokens.clone(); @@ -749,6 +863,26 @@ pub fn parse_rule_top(input: TokenStream) -> Result { vec![__id] } } else { + // Reject bare `{ ... }` transforms — they used to be accepted + // as either a Rust body producing a `Vec` or a template + // consisting of a single `{cap}` splice. Both patterns lost + // static-analysis information (no visible output kind), so we + // now require rules with block bodies to use the annotation + // form `=> kind [? | *] { ... }`. Templates must start with a + // parenthesized node (e.g. `(if_expr ...)`). + if let Some(TokenTree::Group(g)) = tokens.peek() { + if g.delimiter() == Delimiter::Brace { + let span = g.span(); + return Err(syn::Error::new( + span, + "bare `{...}` rule bodies are no longer accepted; \ + use the annotation form `=> kind [? | *] { ... }` \ + (where the kind names the output node's schema kind, \ + optionally suffixed with `?` or `*` for multiplicity)", + )); + } + } + // Full template form let transform_items = parse_direct_list(&mut tokens, &ctx_ident)?; @@ -1026,10 +1160,7 @@ struct NamedString { } fn parse_named_string_arg(tokens: &mut Tokens, expected_name: &str) -> Result { - let name = expect_ident( - tokens, - &format!("expected `{expected_name}:` argument"), - )?; + let name = expect_ident(tokens, &format!("expected `{expected_name}:` argument"))?; if name != expected_name { return Err(syn::Error::new_spanned( name, diff --git a/shared/yeast/doc/yeast.md b/shared/yeast/doc/yeast.md index 3427597be2e6..90edb510c1a1 100644 --- a/shared/yeast/doc/yeast.md +++ b/shared/yeast/doc/yeast.md @@ -312,13 +312,15 @@ already conforms to the output schema. For rules that need the raw (input-schema) capture — typically to read its source text or to translate it explicitly with mutable context state between calls — use `@@name` instead. The body sees the original -input-schema `Id`: +input-schema `Id`. Because these rules always have a Rust block body, +they use the annotation form (see [the `rule!` macro +section](#the-rule-macro) for the full grammar): ```rust yeast::rule!( (assignment left: (_) @@raw_lhs right: (_) @rhs) => - { + call { // raw_lhs is untranslated: read its original source text. let text = ctx.ast.source_text(raw_lhs); // rhs is already translated by the auto-translate prefix. @@ -372,26 +374,79 @@ automatically: single captures bind as `Id`, repeated captures (after ## The `rule!` macro -`rule!` combines a query and a transform into a single declaration: +`rule!` combines a query and a transform into a single declaration. +There are three transform forms, each suited to a different level of +rule complexity: ```rust -// Full template form +// 1. Template form — a tree literal describing the output. yeast::rule!( (query_pattern field: (_) @capture) => (output_template field: {capture}) ) -// Shorthand form — captures become fields on the output node +// 2. Shorthand form — captures become fields on a bare output kind. yeast::rule!( (query_pattern field: (_) @capture) => output_kind ) + +// 3. Annotation form — a Rust block body preceded by the output kind. +yeast::rule!( + (query_pattern child: (_)+ @@children) + => + output_kind* { + // arbitrary Rust; must evaluate to a value compatible with the + // declared multiplicity (see below). + let mut result = Vec::new(); + for child in children { + result.extend(ctx.translate(child)?); + } + result + } +) ``` The shorthand `=> kind` form auto-generates the template, mapping each capture name to a field of the same name on the output node. +### Annotation form + +Rules that need imperative logic — mutating [`BuildCtx`] state per +iteration, computing intermediate values, or looping over captures — +use the annotation form. It has three shapes distinguished by a suffix +on the output-kind identifier: + +| Syntax | Body must evaluate to | Meaning | +|---------------------|-------------------------------------|--------------------------------| +| `=> kind { ... }` | a single node id of `kind` | Emit exactly one node. | +| `=> kind? { ... }` | an `Option` of a node id of `kind` | Emit 0 or 1 nodes (`None`/`Some`). | +| `=> kind* { ... }` | an iterable of node ids of `kind` | Emit 0+ nodes; flattens into the enclosing splice slot. | + +The suffix mirrors the `?` / `*` markers used elsewhere in the schema +DSL (see [`ast_types.yml`](../../../unified/extractor/ast_types.yml)): +bare identifier = required single, `?` = optional single, `*` = +repeated. + +The annotation names the schema kind of the output, giving the macro +enough information for future static analysis (e.g. computing the +static output type of translated captures at their consumer sites). + +**Bare `=> { ... }` block bodies are rejected** — every Rust-block body +must carry an annotation, so the output kind is always visible without +having to inspect the block's expression. + +### Choosing between the forms + +Prefer the simplest form that fits: + +- If the whole transform is a tree literal, use the **template form**. +- If the transform is a template whose root matches a query capture + 1:1, use the **shorthand form**. +- If the transform needs Rust logic (loops, `let` bindings, calls to + `ctx.translate`, etc.), use the **annotation form**. + ## Integration with the extractor A YEAST desugaring pass is configured with a [`DesugaringConfig`], which diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 4a2fb26f4fe3..3a24709dd9ff 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -989,7 +989,7 @@ fn test_one_shot_recurses_into_returned_capture() { yeast::rule!( (assignment left: (_) @left right: (_) @right) => - {left} + identifier { left } ), yeast::rule!((identifier) => (identifier "ID")), yeast::rule!((integer) => (integer "INT")), @@ -1084,7 +1084,7 @@ fn test_raw_capture_marker() { yeast::rule!( (assignment left: (_) @@raw_lhs right: (_) @rhs) => - { + call { let text = ctx.ast.source_text(raw_lhs); tree!((call method: (identifier #{text.as_str()}) @@ -1139,7 +1139,7 @@ fn test_raw_capture_marker_explicit_translate() { yeast::rule!( (assignment left: (_) @@raw_lhs right: (_) @rhs) => - { + call { let translated_lhs = ctx.translate(raw_lhs)?; tree!((call method: {translated_lhs} @@ -1442,3 +1442,113 @@ fn test_rules_macro_mixes_bare_and_explicit_forms() { }; assert_eq!(rules.len(), 2); } + +// ---- Rule-body return-type annotation tests ---- +// +// The annotation form `=> kind [? | *] { rust_body }` is the future +// interface for Rust-bodied rules: the schema-vocabulary annotation +// declares the rule's output kind for static analysis. Today's codegen +// does NOT yet consume the annotation (it just adapts the returned +// value to `Vec` via `IntoFieldIds`); these tests only exercise +// the parser + the runtime-equivalence property. + +/// Annotation form with `*` (repeated): the rule body returns a +/// `Vec` and the annotation says the outputs are `assignment`s. +#[test] +fn test_rule_annotation_repeated() { + // Behaviourally equivalent to a two-node splice template. + let r: Rule = rule!( + (assignment left: (_) @l right: (_) @r) + => + assignment* { + let a1 = tree!((assignment left: {l} right: {r})); + let a2 = tree!((assignment left: {r} right: {l})); + vec![a1, a2] + } + ); + let ast = run_and_ast("x = 1", vec![r]); + // Just verify the run completes without a schema error; two + // top-level `assignment` nodes should appear as siblings. + let mut count = 0usize; + for id in ast.reachable_node_ids() { + if let Some(n) = ast.get_node(id) { + if n.kind_name() == "assignment" { + count += 1; + } + } + } + assert!( + count >= 2, + "expected at least two assignment nodes, got {count}" + ); +} + +/// Annotation form with `?` (optional): the rule body returns +/// `Option`. This uses `None` so the rule effectively deletes the +/// node. +#[test] +fn test_rule_annotation_optional_none() { + // Delete every `integer` (returning None yields no output nodes). + let r: Rule = rule!( + (integer) @lit + => + integer? { + let _ = lit; + None:: + } + ); + let ast = run_and_ast("42", vec![r]); + // No integer node should survive. + for id in ast.reachable_node_ids() { + if let Some(n) = ast.get_node(id) { + assert_ne!(n.kind_name(), "integer", "integer should have been deleted"); + } + } +} + +/// Annotation form (single): the rule body returns a bare `Id`. +#[test] +fn test_rule_annotation_single() { + // Identity on assignment nodes, expressed with the annotation form. + let r: Rule = rule!( + (assignment left: (_) @l right: (_) @r) + => + assignment { + tree!((assignment left: {l} right: {r})) + } + ); + let ast = run_and_ast("x = 1", vec![r]); + let mut has_assignment = false; + for id in ast.reachable_node_ids() { + if let Some(n) = ast.get_node(id) { + if n.kind_name() == "assignment" { + has_assignment = true; + } + } + } + assert!(has_assignment, "expected an assignment node"); +} + +/// The shorthand `=> kind` form (no body, no annotation) must still be +/// distinguished from the annotation form and continue to work. +#[test] +fn test_shorthand_still_works_alongside_annotation_syntax() { + let r: Rule = rule!( + (assignment left: (_) @method right: (_) @receiver) + => + call + ); + let ast = run_and_ast("x = 1", vec![r]); + let mut has_call = false; + for id in ast.reachable_node_ids() { + if let Some(n) = ast.get_node(id) { + if n.kind_name() == "call" { + has_call = true; + } + } + } + assert!( + has_call, + "shorthand form should still produce a `call` node" + ); +} diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 54cce1a7fc4c..cb3fdff88753 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -133,8 +133,8 @@ fn translation_rules() -> Vec> { ) ), // Declarations may be wrapped in local/global wrapper nodes. - rule!((global_declaration _ @inner) => {inner}), - rule!((local_declaration _ @inner) => {inner}), + rule!((global_declaration _ @inner) => stmt { inner }), + rule!((local_declaration _ @inner) => stmt { inner }), // ---- Literals ---- rule!((integer_literal) => (int_literal)), rule!((hex_literal) => (int_literal)), @@ -227,7 +227,7 @@ fn translation_rules() -> Vec> { type: _? @ty computed_value: (computed_property accessor: _+ @@accessors)) => - {{ + accessor_declaration* { ctx.property_name = Some(tree!((identifier #{pattern}))); ctx.property_type = ty; @@ -239,7 +239,7 @@ fn translation_rules() -> Vec> { result.extend(ctx.translate(acc)?); } result - }} + } ), // Computed property: shorthand getter (no explicit get/set, just // statements) → a single accessor_declaration with kind "get". @@ -277,7 +277,7 @@ fn translation_rules() -> Vec> { value: _? @@val observers: (willset_didset_block willset: _? @@ws didset: _? @@ds)) => - {{ + member* { // The initializer value must not inherit the binding // context (it may contain patterns, e.g. a switch // expression), so translate it inside a `ctx.scoped` @@ -313,7 +313,7 @@ fn translation_rules() -> Vec> { result.extend(ctx.translate(obs)?); } result - }} + } ), // property_binding with any pattern name (identifier or // destructuring). Reads outer modifiers / chained tag from `ctx`. @@ -351,7 +351,7 @@ fn translation_rules() -> Vec> { declarator: _* @@decls (modifiers)* @mods) => - {{ + member* { let binding_text = ctx.ast.source_text(binding_kind); let binding = ctx.literal("modifier", &binding_text); // The `let`/`var` binding modifier leads the declaration's @@ -365,7 +365,7 @@ fn translation_rules() -> Vec> { result.extend(ctx.translate(decl)?); } result - }} + } ), // ---- Enums ---- // enum_type_parameter → parameter (with optional name as pattern). @@ -425,7 +425,7 @@ fn translation_rules() -> Vec> { rule!( (enum_entry case: _+ @@cases (modifiers)* @mods) => - {{ + member* { ctx.outer_modifiers = mods; let mut result = Vec::new(); @@ -434,7 +434,7 @@ fn translation_rules() -> Vec> { result.extend(ctx.translate(case)?); } result - }} + } ), // Plain assignment: `x = expr` rule!( @@ -449,9 +449,9 @@ fn translation_rules() -> Vec> { (compound_assign_expr target: {target} operator: (infix_operator #{op}) value: {value}) ), // Unwrap `type` wrapper node - rule!((type name: @inner) => {inner}), + rule!((type name: @inner) => type_expr { inner }), // `directly_assignable_expression` is just a wrapper; unwrap it - rule!((directly_assignable_expression expr: @inner) => {inner}), + rule!((directly_assignable_expression expr: @inner) => expr { inner }), // Pattern with bound_identifier → name_pattern. rule!( (pattern bound_identifier: @name) @@ -463,12 +463,12 @@ fn translation_rules() -> Vec> { rule!( (pattern kind: (binding_pattern binding: (value_binding_pattern mutability: @@binding_kind) pattern: @@pattern)) => - {{ + pattern* { let binding_text = ctx.ast.source_text(binding_kind); let binding = ctx.literal("modifier", &binding_text); ctx.outer_modifiers = vec![binding]; ctx.translate(pattern)? - }} + } ), // case T.foo(x,y) pattern rule!( @@ -501,7 +501,7 @@ fn translation_rules() -> Vec> { rule!( (pattern kind: (simple_identifier) @name) => - { + pattern { if ctx.in_binding_pattern() { tree!((name_pattern identifier: (identifier #{name}))) } else { @@ -537,10 +537,10 @@ fn translation_rules() -> Vec> { rule!( (function_parameter parameter: @@p default_value: _? @def) => - {{ + parameter* { ctx.default_value = def; ctx.translate(p)? - }} + } ), // Parameter with external name and type rule!( @@ -763,7 +763,7 @@ fn translation_rules() -> Vec> { element: (pattern_element pattern: (name_pattern identifier: (identifier #{name}))))) ), // If-condition — unwrap (pass through the inner expression/pattern) - rule!((if_condition kind: @inner) => {inner}), + rule!((if_condition kind: @inner) => expr_or_pattern { inner }), // ---- Loops ---- // For-in loop with optional where-clause guard. rule!( @@ -796,7 +796,7 @@ fn translation_rules() -> Vec> { body: (block stmt: {body})) ), // Labeled statement (e.g. `outer: for ...`). Strip the trailing ':' from the label token. - rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => { + rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => labeled_stmt { let text = ctx.ast.source_text(lbl); let name = &text[..text.len() - 1]; tree!((labeled_stmt label: (identifier #{name}) stmt: {stmt})) @@ -818,7 +818,7 @@ fn translation_rules() -> Vec> { rule!((dictionary_literal_item key: @k value: @v) => (key_value_pair key: {k} value: {v})), // ---- Optionals and errors ---- // Optional chaining — unwrap the marker - rule!((optional_chain_marker expr: @inner) => {inner}), + rule!((optional_chain_marker expr: @inner) => expr { inner }), // try/try?/try! expr → unary_expr with operator "try", "try?" or "try!" rule!((try_expression (try_operator) @op expr: @inner) => (unary_expr operator: (prefix_operator #{op}) operand: {inner})), rule!((try_expression operator: (try_operator) @op expr: @inner) => (unary_expr operator: (prefix_operator #{op}) operand: {inner})), @@ -874,7 +874,7 @@ fn translation_rules() -> Vec> { rule!( (identifier part: _+ @parts) => - {member_chain(&mut ctx, parts)} + expr { member_chain(&mut ctx, parts) } ), // Scoped import declaration (for example `import struct Foo.Bar`): // flatten the identifier parts into a member_access_expr and bind the @@ -905,7 +905,7 @@ fn translation_rules() -> Vec> { // Super expression → super_expr rule!((super_expression) => (super_expr)), // Modifiers — unwrap to individual modifier children - rule!((modifiers _* @mods) => {mods}), + rule!((modifiers _* @mods) => modifier* { mods }), rule!((attribute) @m => (modifier #{m})), rule!((visibility_modifier) @m => (modifier #{m})), rule!((function_modifier) @m => (modifier #{m})), @@ -917,7 +917,7 @@ fn translation_rules() -> Vec> { rule!((inheritance_modifier) @m => (modifier #{m})), rule!((property_behavior_modifier) @m => (modifier #{m})), // Type annotations — unwrap - rule!((type_annotation type: @inner) => {inner}), + rule!((type_annotation type: @inner) => type_expr { inner }), // user_type is split into simple_user_type parts. // Keep a conservative textual fallback to avoid dropping type information. rule!((user_type) @ty => (named_type_expr name: (identifier #{ty}))), @@ -1092,7 +1092,7 @@ fn translation_rules() -> Vec> { type: _? @ty (modifiers)* @mods) => - {{ + accessor_declaration* { ctx.property_name = Some(tree!((identifier #{name}))); ctx.property_type = ty; ctx.outer_modifiers = mods; @@ -1103,7 +1103,7 @@ fn translation_rules() -> Vec> { result.extend(ctx.translate(acc)?); } result - }} + } ), // getter_specifier / setter_specifier → bodyless accessor_declaration // getter_specifier / setter_specifier → bodyless @@ -1130,7 +1130,7 @@ fn translation_rules() -> Vec> { modifier: {chained_modifier(&mut ctx)}) ), // protocol_property_requirements wrapper — should be consumed by above; fallback - rule!((protocol_property_requirements accessor: _* @accs) => {accs}), + rule!((protocol_property_requirements accessor: _* @accs) => accessor_declaration* { accs }), // Computed getter → accessor_declaration (body optional). // Reads property name/type from the outer property_binding rule // and binding/outer modifiers + chained tag from the outer @@ -1186,7 +1186,7 @@ fn translation_rules() -> Vec> { // willset/didset block — spread to children (only reachable as a // fallback; the outer property_binding manual rule normally // captures the willset/didset clauses directly). - rule!((willset_didset_block _* @clauses) => {clauses}), + rule!((willset_didset_block _* @clauses) => accessor_declaration* { clauses }), // willset clause → accessor_declaration (body optional). Reads // `ctx.property_name` set by the outer property_binding rule and // binding/outer modifiers + chained tag from the outer @@ -1220,11 +1220,6 @@ fn translation_rules() -> Vec> { => (unsupported_node) ), - rule!( - _ @node - => - {node} - ), ] } From 11afcce8b32be1ef6624e61665bc60955f8bc329 Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 2 Jul 2026 12:57:43 +0000 Subject: [PATCH 11/39] yeast: Fix bug in matching `(_)` Turns out, `(_)` would match both named and unnamed nodes, as we never checked the value of the `match_unnamed` field. This is the real reason why the final catch-all rule we removed in the last commit was superfluous -- unnamed nodes were being caught by the penultimate rule instead (and mapped to `unsupported_node`). Having fixed the bug, we now (correctly) get errors due to unmatched unnamed nodes in the input. To fix this, we change the catch-all rule to match unnamed nodes as well. This restores the previous behaviour exactly. At some point, we should find a better way to handle unnamed nodes, as it seems wasteful to map these to `unsupported_node` (since we in practice only use them for their string content). Perhaps we should not attempt to translate unnamed nodes at all? --- shared/yeast/src/query.rs | 12 +++++++++++- unified/extractor/src/languages/swift/swift.rs | 11 +++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/shared/yeast/src/query.rs b/shared/yeast/src/query.rs index bcf0f7facab1..3e61ac60b2be 100644 --- a/shared/yeast/src/query.rs +++ b/shared/yeast/src/query.rs @@ -66,7 +66,17 @@ impl QueryNode { pub fn do_match(&self, ast: &Ast, node: Id, matches: &mut Captures) -> Result { match self { - QueryNode::Any { .. } => Ok(true), + QueryNode::Any { match_unnamed } => { + if *match_unnamed { + Ok(true) + } else { + // `(_)` only matches named nodes, matching tree-sitter + // semantics. Bare `_` (with `match_unnamed = true`) + // matches any node. + let n = ast.get_node(node).unwrap(); + Ok(n.is_named()) + } + } QueryNode::Node { kind, children } => { let node = ast.get_node(node).unwrap(); let target_kind = ast diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index cb3fdff88753..74809ea18e41 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1215,8 +1215,15 @@ fn translation_rules() -> Vec> { // Preprocessor conditionals — unsupported rule!((diagnostic) => (unsupported_node)), // ---- Fallbacks ---- - rule!( - (_) + // Bare `_` (rather than `(_)`) so this matches both named nodes + // and unnamed tokens. Any unnamed token that escapes the + // input-schema-specific rules (e.g. captured operators in + // `additive_expression op: @op`) has its auto-translated value + // replaced with an `unsupported_node` whose source range is + // inherited from the original token, so `#{op}` still reads the + // original text. + rule!( + _ => (unsupported_node) ), From d609680083f078bdfec7f1acbed55ed9c79c27d2 Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 2 Jul 2026 21:03:59 +0000 Subject: [PATCH 12/39] yeast: Fix escaping bug in yeast-macros Happily, it turned out that there was already a library function for handling this case. --- shared/yeast-macros/src/parse.rs | 37 +++++--------------------------- 1 file changed, 5 insertions(+), 32 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 986a9bac641a..01c0b574b1cf 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -1183,39 +1183,12 @@ fn parse_named_string_arg(tokens: &mut Tokens, expected_name: &str) -> Result Option { - let raw = lit.to_string(); - let bytes = raw.as_bytes(); - // Match plain `"..."` literals; reject byte strings, raw strings (for - // simplicity), char literals, numbers, etc. - if bytes.first() != Some(&b'"') || bytes.last() != Some(&b'"') { - return None; - } - let mut out = String::with_capacity(raw.len()); - let mut chars = raw[1..raw.len() - 1].chars(); - while let Some(c) = chars.next() { - if c != '\\' { - out.push(c); - continue; - } - match chars.next()? { - 'n' => out.push('\n'), - 't' => out.push('\t'), - 'r' => out.push('\r'), - '\\' => out.push('\\'), - '\'' => out.push('\''), - '"' => out.push('"'), - '0' => out.push('\0'), - other => { - // Unknown escape — give up rather than silently mis-parse. - out.push('\\'); - out.push(other); - } - } - } - Some(out) + let tokens = TokenStream::from(TokenTree::Literal(lit.clone())); + syn::parse2::(tokens).ok().map(|s| s.value()) } /// Split a token stream into top-level comma-separated items. Commas inside From 233c3ce30d70aba4d450ee280adc95b8cfe6f64d Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 9 Jul 2026 16:47:49 +0100 Subject: [PATCH 13/39] Fix incorrect sanitizers --- ...6-07-09-remove-incorrect-path-sanitizer.md | 4 ++ go/ql/lib/ext/path.filepath.model.yml | 5 --- .../go/security/TaintedPathCustomizations.qll | 37 ------------------- .../Security/CWE-022/TaintedPath.expected | 15 +++++++- .../Security/CWE-022/TaintedPath.go | 8 ++-- .../Security/CWE-022/ZipSlip.expected | 17 ++++++++- .../test/query-tests/Security/CWE-022/tst.go | 8 ++-- 7 files changed, 40 insertions(+), 54 deletions(-) create mode 100644 go/ql/lib/change-notes/2026-07-09-remove-incorrect-path-sanitizer.md diff --git a/go/ql/lib/change-notes/2026-07-09-remove-incorrect-path-sanitizer.md b/go/ql/lib/change-notes/2026-07-09-remove-incorrect-path-sanitizer.md new file mode 100644 index 000000000000..4e7a4be2cafe --- /dev/null +++ b/go/ql/lib/change-notes/2026-07-09-remove-incorrect-path-sanitizer.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The function `Rel` in `path/filepath` was incorrectly considered a sanitizer for `go/path-injection` and `go/zipslip`. This has now been fixed, which may lead to more results for those queries. diff --git a/go/ql/lib/ext/path.filepath.model.yml b/go/ql/lib/ext/path.filepath.model.yml index d450e2bbc568..15bcb7d386d8 100644 --- a/go/ql/lib/ext/path.filepath.model.yml +++ b/go/ql/lib/ext/path.filepath.model.yml @@ -1,9 +1,4 @@ extensions: - - addsTo: - pack: codeql/go-all - extensible: barrierModel - data: - - ["path/filepath", "", False, "Rel", "", "", "ReturnValue", "path-injection", "manual"] - addsTo: pack: codeql/go-all extensible: summaryModel diff --git a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll index 20341159c64c..d49365537373 100644 --- a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll +++ b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll @@ -129,43 +129,6 @@ module TaintedPath { DotDotReplaceAll() { this.getReplacedString() = ["..", "."] } } - /** - * A node `nd` guarded by a check that ensures it is contained within some root folder, - * considered as a sanitizer for path traversal. - * - * We currently recognize checks of the following form: - * - * ``` - * ..., err := filepath.Rel(base, path) - * if err == nil { - * // path is known to be contained in base - * } - * ``` - */ - class PathContainmentCheck extends SanitizerGuard, DataFlow::EqualityTestNode { - DataFlow::Node path; - boolean outcome; - - PathContainmentCheck() { - exists(Function f, FunctionInput inp, FunctionOutput outp, DataFlow::Property p | - f.hasQualifiedName("path/filepath", "Rel") and - inp.isParameter(1) and - outp.isResult(1) and - p.isNil() - | - exists(DataFlow::Node call, DataFlow::Node res | - call = f.getACall() and - DataFlow::localFlow(outp.getNode(call), res) - | - p.checkOn(this, outcome, res) and - path = inp.getNode(call) - ) - ) - } - - override predicate checks(Expr e, boolean branch) { e = path.asExpr() and branch = outcome } - } - /** * A call of the form `strings.HasPrefix(path, ...)` considered as a sanitizer guard * for `path`. diff --git a/go/ql/test/query-tests/Security/CWE-022/TaintedPath.expected b/go/ql/test/query-tests/Security/CWE-022/TaintedPath.expected index c95fa5e7af62..851ed3533ae9 100644 --- a/go/ql/test/query-tests/Security/CWE-022/TaintedPath.expected +++ b/go/ql/test/query-tests/Security/CWE-022/TaintedPath.expected @@ -1,25 +1,36 @@ #select | TaintedPath.go:18:29:18:40 | tainted_path | TaintedPath.go:15:18:15:22 | selection of URL | TaintedPath.go:18:29:18:40 | tainted_path | This path depends on a $@. | TaintedPath.go:15:18:15:22 | selection of URL | user-provided value | | TaintedPath.go:22:28:22:69 | call to Join | TaintedPath.go:15:18:15:22 | selection of URL | TaintedPath.go:22:28:22:69 | call to Join | This path depends on a $@. | TaintedPath.go:15:18:15:22 | selection of URL | user-provided value | +| TaintedPath.go:27:28:27:45 | sanitized_filepath | TaintedPath.go:15:18:15:22 | selection of URL | TaintedPath.go:27:28:27:45 | sanitized_filepath | This path depends on a $@. | TaintedPath.go:15:18:15:22 | selection of URL | user-provided value | +| TaintedPath.go:43:29:43:40 | tainted_path | TaintedPath.go:15:18:15:22 | selection of URL | TaintedPath.go:43:29:43:40 | tainted_path | This path depends on a $@. | TaintedPath.go:15:18:15:22 | selection of URL | user-provided value | | TaintedPath.go:74:28:74:57 | call to Clean | TaintedPath.go:15:18:15:22 | selection of URL | TaintedPath.go:74:28:74:57 | call to Clean | This path depends on a $@. | TaintedPath.go:15:18:15:22 | selection of URL | user-provided value | edges | TaintedPath.go:15:18:15:22 | selection of URL | TaintedPath.go:15:18:15:30 | call to Query | provenance | Src:MaD:2 MaD:3 | | TaintedPath.go:15:18:15:30 | call to Query | TaintedPath.go:18:29:18:40 | tainted_path | provenance | Sink:MaD:1 | | TaintedPath.go:15:18:15:30 | call to Query | TaintedPath.go:22:57:22:68 | tainted_path | provenance | | | TaintedPath.go:22:57:22:68 | tainted_path | TaintedPath.go:22:28:22:69 | call to Join | provenance | FunctionModel Sink:MaD:1 | +| TaintedPath.go:22:57:22:68 | tainted_path | TaintedPath.go:26:63:26:74 | tainted_path | provenance | | +| TaintedPath.go:22:57:22:68 | tainted_path | TaintedPath.go:43:29:43:40 | tainted_path | provenance | Sink:MaD:1 | | TaintedPath.go:22:57:22:68 | tainted_path | TaintedPath.go:74:39:74:56 | ...+... | provenance | | -| TaintedPath.go:74:39:74:56 | ...+... | TaintedPath.go:74:28:74:57 | call to Clean | provenance | MaD:4 Sink:MaD:1 | +| TaintedPath.go:26:2:26:75 | ... := ...[0] | TaintedPath.go:27:28:27:45 | sanitized_filepath | provenance | Sink:MaD:1 | +| TaintedPath.go:26:63:26:74 | tainted_path | TaintedPath.go:26:2:26:75 | ... := ...[0] | provenance | MaD:4 | +| TaintedPath.go:74:39:74:56 | ...+... | TaintedPath.go:74:28:74:57 | call to Clean | provenance | MaD:5 Sink:MaD:1 | models | 1 | Sink: io/ioutil; ; false; ReadFile; ; ; Argument[0]; path-injection; manual | | 2 | Source: net/http; Request; true; URL; ; ; ; remote; manual | | 3 | Summary: net/url; URL; true; Query; ; ; Argument[receiver]; ReturnValue; taint; manual | -| 4 | Summary: path; ; false; Clean; ; ; Argument[0]; ReturnValue; taint; manual | +| 4 | Summary: path/filepath; ; false; Rel; ; ; Argument[0..1]; ReturnValue[0]; taint; manual | +| 5 | Summary: path; ; false; Clean; ; ; Argument[0]; ReturnValue; taint; manual | nodes | TaintedPath.go:15:18:15:22 | selection of URL | semmle.label | selection of URL | | TaintedPath.go:15:18:15:30 | call to Query | semmle.label | call to Query | | TaintedPath.go:18:29:18:40 | tainted_path | semmle.label | tainted_path | | TaintedPath.go:22:28:22:69 | call to Join | semmle.label | call to Join | | TaintedPath.go:22:57:22:68 | tainted_path | semmle.label | tainted_path | +| TaintedPath.go:26:2:26:75 | ... := ...[0] | semmle.label | ... := ...[0] | +| TaintedPath.go:26:63:26:74 | tainted_path | semmle.label | tainted_path | +| TaintedPath.go:27:28:27:45 | sanitized_filepath | semmle.label | sanitized_filepath | +| TaintedPath.go:43:29:43:40 | tainted_path | semmle.label | tainted_path | | TaintedPath.go:74:28:74:57 | call to Clean | semmle.label | call to Clean | | TaintedPath.go:74:39:74:56 | ...+... | semmle.label | ...+... | subpaths diff --git a/go/ql/test/query-tests/Security/CWE-022/TaintedPath.go b/go/ql/test/query-tests/Security/CWE-022/TaintedPath.go index 812b56f7c94b..680c108d0562 100644 --- a/go/ql/test/query-tests/Security/CWE-022/TaintedPath.go +++ b/go/ql/test/query-tests/Security/CWE-022/TaintedPath.go @@ -22,9 +22,9 @@ func handler(w http.ResponseWriter, r *http.Request) { data, _ = ioutil.ReadFile(filepath.Join("/home/user/", tainted_path)) // $ Alert[go/path-injection] w.Write(data) - // GOOD: This can only read inside the provided safe path + // BAD: This could still read any file on the file system sanitized_filepath, _ := filepath.Rel("/home/user/safepath", tainted_path) - data, _ = ioutil.ReadFile(sanitized_filepath) + data, _ = ioutil.ReadFile(sanitized_filepath) // $ Alert[go/path-injection] w.Write(data) // GOOD: This can only read inside the provided safe path @@ -37,10 +37,10 @@ func handler(w http.ResponseWriter, r *http.Request) { data, _ = ioutil.ReadFile(strings.ReplaceAll(tainted_path, "..", "")) w.Write(data) - // GOOD: This can only read inside the provided safe path + // BAD: This could still read any file on the file system _, err := filepath.Rel("/home/user/safepath", tainted_path) if err == nil { - data, _ = ioutil.ReadFile(tainted_path) + data, _ = ioutil.ReadFile(tainted_path) // $ Alert[go/path-injection] w.Write(data) } diff --git a/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected b/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected index 3bfd80a120ca..88f1666af3b9 100644 --- a/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected +++ b/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected @@ -2,7 +2,9 @@ | UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | Unsanitized archive entry, which may contain '..', is used in a $@. | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | file system operation | | ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:14:20:14:20 | p | Unsanitized archive entry, which may contain '..', is used in a $@. | ZipSlip.go:14:20:14:20 | p | file system operation | | tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:16:14:16:34 | call to Dir | Unsanitized archive entry, which may contain '..', is used in a $@. | tarslip.go:16:14:16:34 | call to Dir | file system operation | +| tst.go:23:2:43:2 | range statement[1] | tst.go:23:2:43:2 | range statement[1] | tst.go:27:21:27:48 | call to Join | Unsanitized archive entry, which may contain '..', is used in a $@. | tst.go:27:21:27:48 | call to Join | file system operation | | tst.go:23:2:43:2 | range statement[1] | tst.go:23:2:43:2 | range statement[1] | tst.go:29:20:29:23 | path | Unsanitized archive entry, which may contain '..', is used in a $@. | tst.go:29:20:29:23 | path | file system operation | +| tst.go:23:2:43:2 | range statement[1] | tst.go:23:2:43:2 | range statement[1] | tst.go:31:21:31:24 | path | Unsanitized archive entry, which may contain '..', is used in a $@. | tst.go:31:21:31:24 | path | file system operation | edges | UnsafeUnzipSymlinkGood.go:52:24:52:32 | SSA def(candidate) | UnsafeUnzipSymlinkGood.go:61:53:61:61 | candidate | provenance | | | UnsafeUnzipSymlinkGood.go:61:53:61:61 | candidate | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | provenance | FunctionModel Sink:MaD:3 | @@ -14,14 +16,20 @@ edges | ZipSlip.go:12:3:12:30 | ... := ...[0] | ZipSlip.go:14:20:14:20 | p | provenance | Sink:MaD:1 | | ZipSlip.go:12:24:12:29 | selection of Name | ZipSlip.go:12:3:12:30 | ... := ...[0] | provenance | MaD:4 | | tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:16:23:16:33 | selection of Name | provenance | | -| tarslip.go:16:23:16:33 | selection of Name | tarslip.go:16:14:16:34 | call to Dir | provenance | MaD:5 Sink:MaD:2 | +| tarslip.go:16:23:16:33 | selection of Name | tarslip.go:16:14:16:34 | call to Dir | provenance | MaD:6 Sink:MaD:2 | +| tst.go:23:2:43:2 | range statement[1] | tst.go:25:38:25:41 | path | provenance | | | tst.go:23:2:43:2 | range statement[1] | tst.go:29:20:29:23 | path | provenance | Sink:MaD:1 | +| tst.go:23:2:43:2 | range statement[1] | tst.go:31:21:31:24 | path | provenance | Sink:MaD:1 | +| tst.go:25:3:25:42 | ... := ...[0] | tst.go:27:41:27:47 | relpath | provenance | | +| tst.go:25:38:25:41 | path | tst.go:25:3:25:42 | ... := ...[0] | provenance | MaD:5 | +| tst.go:27:41:27:47 | relpath | tst.go:27:21:27:48 | call to Join | provenance | FunctionModel Sink:MaD:1 | models | 1 | Sink: io/ioutil; ; false; WriteFile; ; ; Argument[0]; path-injection; manual | | 2 | Sink: os; ; false; MkdirAll; ; ; Argument[0]; path-injection; manual | | 3 | Sink: os; ; false; Readlink; ; ; Argument[0]; path-injection; manual | | 4 | Summary: path/filepath; ; false; Abs; ; ; Argument[0]; ReturnValue[0]; taint; manual | -| 5 | Summary: path; ; false; Dir; ; ; Argument[0]; ReturnValue; taint; manual | +| 5 | Summary: path/filepath; ; false; Rel; ; ; Argument[0..1]; ReturnValue[0]; taint; manual | +| 6 | Summary: path; ; false; Dir; ; ; Argument[0]; ReturnValue; taint; manual | nodes | UnsafeUnzipSymlinkGood.go:52:24:52:32 | SSA def(candidate) | semmle.label | SSA def(candidate) | | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | semmle.label | call to Join | @@ -37,5 +45,10 @@ nodes | tarslip.go:16:14:16:34 | call to Dir | semmle.label | call to Dir | | tarslip.go:16:23:16:33 | selection of Name | semmle.label | selection of Name | | tst.go:23:2:43:2 | range statement[1] | semmle.label | range statement[1] | +| tst.go:25:3:25:42 | ... := ...[0] | semmle.label | ... := ...[0] | +| tst.go:25:38:25:41 | path | semmle.label | path | +| tst.go:27:21:27:48 | call to Join | semmle.label | call to Join | +| tst.go:27:41:27:47 | relpath | semmle.label | relpath | | tst.go:29:20:29:23 | path | semmle.label | path | +| tst.go:31:21:31:24 | path | semmle.label | path | subpaths diff --git a/go/ql/test/query-tests/Security/CWE-022/tst.go b/go/ql/test/query-tests/Security/CWE-022/tst.go index 4cf3a77c4c8d..33b2aa072c0d 100644 --- a/go/ql/test/query-tests/Security/CWE-022/tst.go +++ b/go/ql/test/query-tests/Security/CWE-022/tst.go @@ -14,7 +14,7 @@ func uploadFile(w http.ResponseWriter, r *http.Request) { file, handler, _ := r.FormFile("file") // err handling defer file.Close() - tempFile, _ := ioutil.TempFile("/tmp", handler.Filename) // NOT OK + tempFile, _ := ioutil.TempFile("/tmp", handler.Filename) // OK use(tempFile) } @@ -24,11 +24,11 @@ func unzip2(f string, root string) { path := f.Name relpath, err := filepath.Rel(root, path) if err == nil { - ioutil.WriteFile(filepath.Join(root, relpath), []byte("present"), 0666) // OK + ioutil.WriteFile(filepath.Join(root, relpath), []byte("present"), 0666) // $ Sink[go/zipslip] } - ioutil.WriteFile(path, []byte("present"), 0666) // $ Sink[go/zipslip] // NOT OK + ioutil.WriteFile(path, []byte("present"), 0666) // $ Sink[go/zipslip] if containedIn(path, root) { - ioutil.WriteFile(path, []byte("present"), 0666) // OK + ioutil.WriteFile(path, []byte("present"), 0666) // $ Sink[go/zipslip] } if ok, _ := regexp.MatchString("^[a-z]*$", path); ok { ioutil.WriteFile(path, []byte("present"), 0666) // OK From ec83894d48760db35e3f6cc47c239880fcaedc5d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 9 Jul 2026 17:25:35 +0000 Subject: [PATCH 14/39] Post-release preparation for codeql-cli-2.26.1 --- actions/ql/lib/qlpack.yml | 2 +- actions/ql/src/qlpack.yml | 2 +- cpp/ql/lib/qlpack.yml | 2 +- cpp/ql/src/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/lib/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/src/qlpack.yml | 2 +- csharp/ql/lib/qlpack.yml | 2 +- csharp/ql/src/qlpack.yml | 2 +- go/ql/consistency-queries/qlpack.yml | 2 +- go/ql/lib/qlpack.yml | 2 +- go/ql/src/qlpack.yml | 2 +- java/ql/lib/qlpack.yml | 2 +- java/ql/src/qlpack.yml | 2 +- javascript/ql/lib/qlpack.yml | 2 +- javascript/ql/src/qlpack.yml | 2 +- misc/suite-helpers/qlpack.yml | 2 +- python/ql/lib/qlpack.yml | 2 +- python/ql/src/qlpack.yml | 2 +- ruby/ql/lib/qlpack.yml | 2 +- ruby/ql/src/qlpack.yml | 2 +- rust/ql/lib/qlpack.yml | 2 +- rust/ql/src/qlpack.yml | 2 +- shared/concepts/qlpack.yml | 2 +- shared/controlflow/qlpack.yml | 2 +- shared/dataflow/qlpack.yml | 2 +- shared/mad/qlpack.yml | 2 +- shared/namebinding/qlpack.yml | 2 +- shared/quantum/qlpack.yml | 2 +- shared/rangeanalysis/qlpack.yml | 2 +- shared/regex/qlpack.yml | 2 +- shared/ssa/qlpack.yml | 2 +- shared/threat-models/qlpack.yml | 2 +- shared/tutorial/qlpack.yml | 2 +- shared/typeflow/qlpack.yml | 2 +- shared/typeinference/qlpack.yml | 2 +- shared/typetracking/qlpack.yml | 2 +- shared/typos/qlpack.yml | 2 +- shared/util/qlpack.yml | 2 +- shared/xml/qlpack.yml | 2 +- shared/yaml/qlpack.yml | 2 +- swift/ql/lib/qlpack.yml | 2 +- swift/ql/src/qlpack.yml | 2 +- 42 files changed, 42 insertions(+), 42 deletions(-) diff --git a/actions/ql/lib/qlpack.yml b/actions/ql/lib/qlpack.yml index 77ecbfaa8f17..fec6a9446d0d 100644 --- a/actions/ql/lib/qlpack.yml +++ b/actions/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/actions-all -version: 0.4.39 +version: 0.4.40-dev library: true warnOnImplicitThis: true dependencies: diff --git a/actions/ql/src/qlpack.yml b/actions/ql/src/qlpack.yml index b623ff9b903b..2d53972a2eaa 100644 --- a/actions/ql/src/qlpack.yml +++ b/actions/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/actions-queries -version: 0.6.31 +version: 0.6.32-dev library: false warnOnImplicitThis: true groups: [actions, queries] diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index 5f4b92a191fc..73226a267c9e 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-all -version: 12.0.0 +version: 12.0.1-dev groups: cpp dbscheme: semmlecode.cpp.dbscheme extractor: cpp diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml index 1d1a9206a28e..b193e3ea3603 100644 --- a/cpp/ql/src/qlpack.yml +++ b/cpp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-queries -version: 1.7.0 +version: 1.7.1-dev groups: - cpp - queries diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index 9fb3427e685c..e33ebfaa5cc6 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-all -version: 1.7.70 +version: 1.7.71-dev groups: - csharp - solorigate diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index 4b55c9412078..b25077c16670 100644 --- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-queries -version: 1.7.70 +version: 1.7.71-dev groups: - csharp - solorigate diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index 336a7c6be731..0a034f90073c 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-all -version: 7.1.0 +version: 7.1.1-dev groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index b243e8fb928d..ee88fc20f703 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-queries -version: 1.8.0 +version: 1.8.1-dev groups: - csharp - queries diff --git a/go/ql/consistency-queries/qlpack.yml b/go/ql/consistency-queries/qlpack.yml index d7752bb1bf64..d5e7e5af81e5 100644 --- a/go/ql/consistency-queries/qlpack.yml +++ b/go/ql/consistency-queries/qlpack.yml @@ -1,5 +1,5 @@ name: codeql-go-consistency-queries -version: 1.0.53 +version: 1.0.54-dev groups: - go - queries diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index 1573f5b2ad8f..dd21ea23eec0 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-all -version: 7.2.1 +version: 7.2.2-dev groups: go dbscheme: go.dbscheme extractor: go diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml index 98b0c2cf12c0..68359b55c923 100644 --- a/go/ql/src/qlpack.yml +++ b/go/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-queries -version: 1.6.6 +version: 1.6.7-dev groups: - go - queries diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index a3664696b9da..09b4eb797c77 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-all -version: 9.2.1 +version: 9.2.2-dev groups: java dbscheme: config/semmlecode.dbscheme extractor: java diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index 2c49d49c6172..4dc88f1fa1c8 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-queries -version: 1.11.6 +version: 1.11.7-dev groups: - java - queries diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index 3ffde4f1d31a..9ffe3dc1a055 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-all -version: 2.8.1 +version: 2.8.2-dev groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index 3a88e0c6b139..56c7b6675e1b 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-queries -version: 2.4.1 +version: 2.4.2-dev groups: - javascript - queries diff --git a/misc/suite-helpers/qlpack.yml b/misc/suite-helpers/qlpack.yml index 248b9c7207cf..38df6eb9512a 100644 --- a/misc/suite-helpers/qlpack.yml +++ b/misc/suite-helpers/qlpack.yml @@ -1,4 +1,4 @@ name: codeql/suite-helpers -version: 1.0.53 +version: 1.0.54-dev groups: shared warnOnImplicitThis: true diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index 837f6c437dd5..415f397df570 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-all -version: 7.2.1 +version: 7.2.2-dev groups: python dbscheme: semmlecode.python.dbscheme extractor: python diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml index 5e0040f834fb..703a6ecb6f51 100644 --- a/python/ql/src/qlpack.yml +++ b/python/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-queries -version: 1.8.6 +version: 1.8.7-dev groups: - python - queries diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index ed80e6ead32c..4ec86b3f63b6 100644 --- a/ruby/ql/lib/qlpack.yml +++ b/ruby/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-all -version: 6.0.1 +version: 6.0.2-dev groups: ruby extractor: ruby dbscheme: ruby.dbscheme diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml index 1016e0294afc..2b01f60bc656 100644 --- a/ruby/ql/src/qlpack.yml +++ b/ruby/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-queries -version: 1.6.6 +version: 1.6.7-dev groups: - ruby - queries diff --git a/rust/ql/lib/qlpack.yml b/rust/ql/lib/qlpack.yml index 74edd9e3549d..94135af275f7 100644 --- a/rust/ql/lib/qlpack.yml +++ b/rust/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/rust-all -version: 0.2.17 +version: 0.2.18-dev groups: rust extractor: rust dbscheme: rust.dbscheme diff --git a/rust/ql/src/qlpack.yml b/rust/ql/src/qlpack.yml index ecc76c64f3bb..7fdf25e71220 100644 --- a/rust/ql/src/qlpack.yml +++ b/rust/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/rust-queries -version: 0.1.38 +version: 0.1.39-dev groups: - rust - queries diff --git a/shared/concepts/qlpack.yml b/shared/concepts/qlpack.yml index b7f9407b10b0..fae2ee681789 100644 --- a/shared/concepts/qlpack.yml +++ b/shared/concepts/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/concepts -version: 0.0.27 +version: 0.0.28-dev groups: shared library: true dependencies: diff --git a/shared/controlflow/qlpack.yml b/shared/controlflow/qlpack.yml index 4fdac9e19299..f18fc3f099b3 100644 --- a/shared/controlflow/qlpack.yml +++ b/shared/controlflow/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/controlflow -version: 2.0.37 +version: 2.0.38-dev groups: shared library: true dependencies: diff --git a/shared/dataflow/qlpack.yml b/shared/dataflow/qlpack.yml index 24de3405f909..4c5e24dafcb7 100644 --- a/shared/dataflow/qlpack.yml +++ b/shared/dataflow/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/dataflow -version: 2.1.9 +version: 2.1.10-dev groups: shared library: true dependencies: diff --git a/shared/mad/qlpack.yml b/shared/mad/qlpack.yml index 44bf6c5c6a6d..79f075ca1f0c 100644 --- a/shared/mad/qlpack.yml +++ b/shared/mad/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/mad -version: 1.0.53 +version: 1.0.54-dev groups: shared library: true dependencies: diff --git a/shared/namebinding/qlpack.yml b/shared/namebinding/qlpack.yml index 9bd12561910a..2e7acee2ef87 100644 --- a/shared/namebinding/qlpack.yml +++ b/shared/namebinding/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/namebinding -version: 0.0.2 +version: 0.0.3-dev groups: shared library: true dependencies: diff --git a/shared/quantum/qlpack.yml b/shared/quantum/qlpack.yml index 9f8995a2ca64..2b3c84caceea 100644 --- a/shared/quantum/qlpack.yml +++ b/shared/quantum/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/quantum -version: 0.0.31 +version: 0.0.32-dev groups: shared library: true dependencies: diff --git a/shared/rangeanalysis/qlpack.yml b/shared/rangeanalysis/qlpack.yml index b934c84900c1..e1368aa2345e 100644 --- a/shared/rangeanalysis/qlpack.yml +++ b/shared/rangeanalysis/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/rangeanalysis -version: 1.0.53 +version: 1.0.54-dev groups: shared library: true dependencies: diff --git a/shared/regex/qlpack.yml b/shared/regex/qlpack.yml index c64f4833d95f..3de28f64f281 100644 --- a/shared/regex/qlpack.yml +++ b/shared/regex/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/regex -version: 1.0.53 +version: 1.0.54-dev groups: shared library: true dependencies: diff --git a/shared/ssa/qlpack.yml b/shared/ssa/qlpack.yml index d92b095c581f..be9a88a001af 100644 --- a/shared/ssa/qlpack.yml +++ b/shared/ssa/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ssa -version: 2.0.29 +version: 2.0.30-dev groups: shared library: true dependencies: diff --git a/shared/threat-models/qlpack.yml b/shared/threat-models/qlpack.yml index 782bde4ee5cc..b63415957428 100644 --- a/shared/threat-models/qlpack.yml +++ b/shared/threat-models/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/threat-models -version: 1.0.53 +version: 1.0.54-dev library: true groups: shared dataExtensions: diff --git a/shared/tutorial/qlpack.yml b/shared/tutorial/qlpack.yml index 44f487b4b396..f1cc9f375cf1 100644 --- a/shared/tutorial/qlpack.yml +++ b/shared/tutorial/qlpack.yml @@ -1,7 +1,7 @@ name: codeql/tutorial description: Library for the CodeQL detective tutorials, helping new users learn to write CodeQL queries. -version: 1.0.53 +version: 1.0.54-dev groups: shared library: true warnOnImplicitThis: true diff --git a/shared/typeflow/qlpack.yml b/shared/typeflow/qlpack.yml index 5545bfb44220..c69f8203a95a 100644 --- a/shared/typeflow/qlpack.yml +++ b/shared/typeflow/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typeflow -version: 1.0.53 +version: 1.0.54-dev groups: shared library: true dependencies: diff --git a/shared/typeinference/qlpack.yml b/shared/typeinference/qlpack.yml index b9625bdbc6da..0d181eacd022 100644 --- a/shared/typeinference/qlpack.yml +++ b/shared/typeinference/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typeinference -version: 0.0.34 +version: 0.0.35-dev groups: shared library: true dependencies: diff --git a/shared/typetracking/qlpack.yml b/shared/typetracking/qlpack.yml index 2f007b6795d2..fe58d4175564 100644 --- a/shared/typetracking/qlpack.yml +++ b/shared/typetracking/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typetracking -version: 2.0.37 +version: 2.0.38-dev groups: shared library: true dependencies: diff --git a/shared/typos/qlpack.yml b/shared/typos/qlpack.yml index 3b6940512a64..e6465e717341 100644 --- a/shared/typos/qlpack.yml +++ b/shared/typos/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typos -version: 1.0.53 +version: 1.0.54-dev groups: shared library: true warnOnImplicitThis: true diff --git a/shared/util/qlpack.yml b/shared/util/qlpack.yml index 44ea768668f8..7c1589ce7c60 100644 --- a/shared/util/qlpack.yml +++ b/shared/util/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/util -version: 2.0.40 +version: 2.0.41-dev groups: shared library: true dependencies: null diff --git a/shared/xml/qlpack.yml b/shared/xml/qlpack.yml index 6c506e429871..bb3f29d17187 100644 --- a/shared/xml/qlpack.yml +++ b/shared/xml/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/xml -version: 1.0.53 +version: 1.0.54-dev groups: shared library: true dependencies: diff --git a/shared/yaml/qlpack.yml b/shared/yaml/qlpack.yml index a9b004ad8ff0..e169087c6244 100644 --- a/shared/yaml/qlpack.yml +++ b/shared/yaml/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/yaml -version: 1.0.53 +version: 1.0.54-dev groups: shared library: true warnOnImplicitThis: true diff --git a/swift/ql/lib/qlpack.yml b/swift/ql/lib/qlpack.yml index f4160734f511..807adcb196fc 100644 --- a/swift/ql/lib/qlpack.yml +++ b/swift/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/swift-all -version: 6.7.2 +version: 6.7.3-dev groups: swift extractor: swift dbscheme: swift.dbscheme diff --git a/swift/ql/src/qlpack.yml b/swift/ql/src/qlpack.yml index b1fd127caaa4..050ce125a8e8 100644 --- a/swift/ql/src/qlpack.yml +++ b/swift/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/swift-queries -version: 1.3.6 +version: 1.3.7-dev groups: - swift - queries From 8ff671f99ddbc6bb6d0ad27c998b75cf80468035 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 00:44:25 +0000 Subject: [PATCH 15/39] Add changed framework coverage reports --- .../library-coverage/coverage.csv | 599 +++++++++--------- .../library-coverage/coverage.rst | 4 +- 2 files changed, 302 insertions(+), 301 deletions(-) diff --git a/java/documentation/library-coverage/coverage.csv b/java/documentation/library-coverage/coverage.csv index 75c6ca1da780..39bb4b37e593 100644 --- a/java/documentation/library-coverage/coverage.csv +++ b/java/documentation/library-coverage/coverage.csv @@ -1,299 +1,300 @@ -package,sink,source,summary,sink:bean-validation,sink:command-injection,sink:credentials-key,sink:credentials-password,sink:credentials-username,sink:encryption-iv,sink:encryption-salt,sink:environment-injection,sink:file-content-store,sink:fragment-injection,sink:groovy-injection,sink:hostname-verification,sink:html-injection,sink:information-leak,sink:intent-redirection,sink:jexl-injection,sink:jndi-injection,sink:js-injection,sink:ldap-injection,sink:log-injection,sink:mvel-injection,sink:notification,sink:ognl-injection,sink:path-injection,sink:path-injection[read],sink:pending-intents,sink:regex-use,sink:regex-use[-1],sink:regex-use[0],sink:regex-use[],sink:regex-use[f-1],sink:regex-use[f1],sink:regex-use[f],sink:request-forgery,sink:response-splitting,sink:sql-injection,sink:template-injection,sink:trust-boundary-violation,sink:unsafe-deserialization,sink:url-forward,sink:url-redirection,sink:xpath-injection,sink:xslt-injection,source:android-external-storage-dir,source:commandargs,source:contentprovider,source:database,source:environment,source:file,source:remote,summary:taint,summary:value -actions.osgi,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6, -android.app,77,,103,,,,,,,,,,11,,,,,7,,,,,,,42,,,,17,,,,,,,,,,,,,,,,,,,,,,,,,18,85 -android.content,24,31,154,,,,,,,,,,,,,,,16,,,,,,,,,,,,,,,,,,,,,8,,,,,,,,4,,27,,,,,63,91 -android.database,59,,41,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,59,,,,,,,,,,,,,,,41, -android.net,,,60,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,45,15 -android.os,1,2,122,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,2,,,,,,,41,81 -android.support.v4.app,11,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -android.util,6,16,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,16,, -android.webkit,3,2,,,,,,,,,,,,,,2,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,, -android.widget,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,1, -androidx.core.app,47,,95,,,,,,,,,,,,,,,,,,,,,,41,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,12,83 -androidx.fragment.app,11,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -androidx.slice,2,5,88,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,5,,,,,27,61 -antlr,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -ch.ethz.ssh2,2,,,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -cn.hutool.core.codec,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -com.alibaba.com.caucho.hessian.io,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,, -com.alibaba.druid.sql,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,1, -com.alibaba.fastjson2,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -com.amazonaws.auth,2,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.auth0.jwt.algorithms,6,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.azure.identity,3,,,,,1,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.caucho.burlap.io,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,, -com.caucho.hessian.io,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,, -com.cedarsoftware.util.io,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,, -com.couchbase.client.core.env,15,,1,,,,9,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -com.couchbase.client.java,10,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,, -com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -com.esotericsoftware.yamlbeans,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,, -com.fasterxml.jackson.core,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -com.fasterxml.jackson.databind,2,,8,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,8, -com.google.common.base,4,,87,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,1,,,,,,,,,,,,,,,,,,,,,63,24 -com.google.common.cache,,,17,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17 -com.google.common.collect,,,553,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,551 -com.google.common.flogger,29,,,,,,,,,,,,,,,,,,,,,,29,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.google.common.io,10,,73,,,,,,,,,1,,,,,,,,,,,,,,,4,5,,,,,,,,,,,,,,,,,,,,,,,,,,72,1 -com.google.gson,,,52,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,38,14 -com.hubspot.jinjava,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,, -com.jcraft.jsch,5,,1,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,1, -com.microsoft.sqlserver.jdbc,4,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.mitchellbosecke.pebble,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,, -com.mongodb,10,,,,,,4,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.opensymphony.xwork2,56,,961,,,,,,,,,,,,,,,,,,,,,,,56,,,,,,,,,,,,,,,,,,,,,,,,,,,,867,94 -com.rabbitmq.client,,21,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,21,7, -com.sshtools.j2ssh.authentication,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.sun.crypto.provider,19,,,,,17,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.sun.jndi.ldap,4,,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.sun.net.httpserver,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.sun.net.ssl,3,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.sun.rowset,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.sun.security.auth.module,2,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.sun.security.ntlm,5,,,,,,3,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.sun.security.sasl.digest,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.thoughtworks.xstream,1,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.trilead.ssh2,13,,,,,2,4,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.unboundid.ldap.sdk,17,,,,,,,,,,,,,,,,,,,,,17,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -com.zaxxer.hikari,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,, -flexjson,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 -freemarker.cache,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,, -freemarker.template,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,,,,,,,,,,,,,, -groovy.lang,26,,,,,,,,,,,,,26,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -groovy.text,1,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -groovy.util,5,,,,,,,,,,,,,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -hudson,75,9,2648,,4,,,,,,3,2,,,,4,,,,,,,,,,,39,17,,,,,,,,,6,,,,,,,,,,,,,,,5,4,2572,76 -io.jsonwebtoken,,2,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,4, -io.netty.bootstrap,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,,,, -io.netty.buffer,,,207,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,130,77 -io.netty.channel,9,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,,,,,,,,,,,,,,,2,, -io.netty.handler.codec,4,13,259,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,3,,,,,,,,,,,,,,,,13,143,116 -io.netty.handler.ssl,4,,,,,,,,,,,,,,,,,,,,,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,, -io.netty.handler.stream,1,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,, -io.netty.resolver,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -io.netty.util,2,,23,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,1,,,,,,,,,,,,,,,,,21,2 -io.undertow.server.handlers.resource,1,,3,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,3, -jakarta.activation,2,,2,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,1,,,,,,,,,,,,,,,,,2, -jakarta.faces.context,4,7,,,,,,,,,,,,,,2,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,7,, -jakarta.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23 -jakarta.persistence,2,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,1, -jakarta.servlet,2,26,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,26,, -jakarta.ws.rs.client,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,, -jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,, -jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,94,55 -jakarta.xml.bind.attachment,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,, -java.applet,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,11, -java.awt,1,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,3 -java.beans,1,,177,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,82,95 -java.io,66,1,225,,,,,,,,,22,,,,,,,,,,,,,,,29,15,,,,,,,,,,,,,,,,,,,,,,,,1,,202,23 -java.lang,38,3,790,,13,,,,,,1,,,,,,,,,,,,8,,,,2,9,,,4,,,1,,,,,,,,,,,,,,,,,3,,,510,280 -java.math,,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9 -java.net,23,3,347,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,21,,,,,,,,,,,,,,,,3,248,99 -java.nio,47,,499,,,,,,,,,5,,,,,,,,,,,,,,,25,16,,,,,,,,,1,,,,,,,,,,,,,,,,,302,197 -java.rmi,,,68,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,45,23 -java.security,21,,583,,,11,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,285,298 -java.sql,15,1,292,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,9,,,,,,,,,,,1,,,,274,18 -java.text,,,154,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,72,82 -java.time,,,131,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,27,104 -java.util,48,2,1340,,,,,,,,,1,,,,,,,,,,,34,,,,3,,,,,5,2,,1,2,,,,,,,,,,,,,,,2,,,558,782 -javafx.scene.web,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,, -javax.accessibility,,,63,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,28,35 -javax.activation,2,,7,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,1,,,,,,,,,,,,,,,,,7, -javax.annotation.processing,,,28,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,25,3 -javax.crypto,19,,140,,,12,3,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,76,64 -javax.faces.context,4,7,,,,,,,,,,,,,,2,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,7,, -javax.imageio,1,,304,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,138,166 -javax.jms,,9,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,57, -javax.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23 -javax.lang.model,,,277,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,217,60 -javax.management,2,,766,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,363,403 -javax.naming,7,,341,,,,,,,,,,,,,,,,,6,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,191,150 -javax.net,4,,136,,,,2,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,87,49 -javax.portlet,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,, -javax.print,2,,133,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,102,31 -javax.rmi.ssl,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6 -javax.script,1,,50,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,14,36 -javax.security.auth,7,,147,,,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,50,97 -javax.security.cert,,,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5, -javax.security.sasl,,,49,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,42,7 -javax.servlet,10,29,3,,,,,,,,,,,,,,1,,,,,,,,,,,2,,,,,,,,,,3,,,2,,2,,,,,,,,,,29,3, -javax.smartcardio,,,34,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,24,10 -javax.sound.midi,,,60,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,51,9 -javax.sound.sampled,,,90,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,53,37 -javax.sql,7,,126,,,,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,68,58 -javax.tools,,,66,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,62,4 -javax.transaction.xa,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -javax.validation,1,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,, -javax.ws.rs.client,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,, -javax.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,, -javax.ws.rs.core,3,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,2,,,,,,,,,,94,55 -javax.xml.bind.attachment,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,, -javax.xml.catalog,,,12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,11,1 -javax.xml.crypto,,,269,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,172,97 -javax.xml.datatype,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5,1 -javax.xml.namespace,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5,10 -javax.xml.parsers,,,37,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35,2 -javax.xml.stream,,,221,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,201,20 -javax.xml.transform,2,,134,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,72,62 -javax.xml.validation,,,29,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,29, -javax.xml.xpath,3,,26,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,26, -jenkins,,,523,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,500,23 -jodd.json,,,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10 -kotlin,16,,1849,,,,,,,,,,,,,,,,,,,,,,,,11,3,,,,,,,,,2,,,,,,,,,,,,,,,,,1836,13 -liquibase.database.jvm,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,, -liquibase.statement.core,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,, -net.lingala.zip4j,2,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,, -net.schmizz.sshj,4,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -net.sf.json,2,,338,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,321,17 -net.sf.saxon.s9api,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5,,,,,,,,, -ognl,6,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -okhttp3,4,,50,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,,,23,27 -org.acegisecurity,,,49,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,49, -org.antlr.runtime,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.avro,18,19,,,,,,,,,,,,,,,,,,,,,,,,,17,,,,,,,,,,1,,,,,,,,,,,1,,,,17,1,, -org.apache.commons.codec,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6, -org.apache.commons.collections,,,800,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,783 -org.apache.commons.collections4,,,806,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,789 -org.apache.commons.compress.archivers.tar,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4, -org.apache.commons.exec,10,,,,6,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.commons.fileupload,,11,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,11,4, -org.apache.commons.httpclient.util,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -org.apache.commons.io,124,,570,,,,,,,,,4,,,,,,,,,,,,,,,102,3,,,,,,,,,15,,,,,,,,,,,,,,,,,556,14 -org.apache.commons.jelly,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,, -org.apache.commons.jexl2,15,,,,,,,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.commons.jexl3,15,,,,,,,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.commons.lang,1,,767,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,596,171 -org.apache.commons.lang3,7,,425,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,1,,,,,,,,,,,,294,131 -org.apache.commons.logging,6,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.commons.net,13,12,,,,,2,2,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,6,,,,,,,,,,,,,,,,12,, -org.apache.commons.ognl,6,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.commons.text,,,272,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,220,52 -org.apache.cxf.catalog,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,, -org.apache.cxf.common.classloader,3,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,2,,,,,,,,,,,,,,,,,, -org.apache.cxf.common.jaxb,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.cxf.common.logging,6,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.cxf.configuration.jsse,2,,,,,,,,,,,,,,1,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.cxf.helpers,10,,,,,,,,,,,,,,,,,,,,,,,,,,5,,,,,,,,,,,,,,,,,,5,,,,,,,,,, -org.apache.cxf.resource,9,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,5,,,,,,,,,,,,,,,,,, -org.apache.cxf.staxutils,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.cxf.tools.corba.utils,4,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.cxf.tools.util,10,,,,,,,,,,,,,,,,,,,,,,,,,,10,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.cxf.transform,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,, -org.apache.directory.ldap.client.api,1,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.hadoop.fs,3,,11,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,11, -org.apache.hadoop.hive.metastore,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,, -org.apache.hadoop.hive.ql.exec,1,,1,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -org.apache.hadoop.hive.ql.metadata,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.hc.client5.http.async.methods,84,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,84,,,,,,,,,,,,,,,,,, -org.apache.hc.client5.http.classic.methods,37,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,37,,,,,,,,,,,,,,,,,, -org.apache.hc.client5.http.fluent,19,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,19,,,,,,,,,,,,,,,,,, -org.apache.hc.client5.http.protocol,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 -org.apache.hc.client5.http.utils,,,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7, -org.apache.hc.core5.benchmark,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,, -org.apache.hc.core5.function,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -org.apache.hc.core5.http,73,2,45,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,72,,,,,,,,,,,,,,,,2,45, -org.apache.hc.core5.net,,,18,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,18, -org.apache.hc.core5.util,,,24,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,18,6 -org.apache.hive.hcatalog.templeton,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,, -org.apache.http,53,3,117,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,51,,,,,,,,,,,,,,,,3,108,9 -org.apache.ibatis.jdbc,6,,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,57, -org.apache.ibatis.mapping,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -org.apache.log4j,11,,,,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.logging.log4j,359,,8,,,,,,,,,,,,,,,,,,,,359,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,4 -org.apache.shiro.authc,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,, -org.apache.shiro.codec,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -org.apache.shiro.jndi,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.shiro.mgt,1,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.sshd.client.session,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.struts.beanvalidation.validation.interceptor,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4, -org.apache.struts2,14,,3873,,,,,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,3,,,,,,,,,,,,,3839,34 -org.apache.tools.ant,14,,,,1,,,,,,,,,,,,,,,,,,,,,,5,8,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.apache.tools.zip,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -org.apache.velocity.app,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,, -org.apache.velocity.runtime,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,, -org.codehaus.cargo.container.installer,3,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,1,,,,,,,,,,,,,,,,,, -org.codehaus.groovy.control,1,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.dom4j,20,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,20,,,,,,,,,, -org.eclipse.jetty.client,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,, -org.exolab.castor.xml,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,, -org.fusesource.leveldbjni,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.geogebra.web.full.main,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,, -org.gradle.api.file,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3, -org.hibernate,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,,,,,,,,,,,,,,,, -org.ho.yaml,8,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,,,,,,,,,,,,, -org.influxdb,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,, -org.jabsorb,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,, -org.jboss.logging,324,,,,,,,,,,,,,,,,,,,,,,324,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.jboss.vfs,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.jdbi.v3.core,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,, -org.jenkins.ui.icon,,,49,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,48,1 -org.jenkins.ui.symbol,,,33,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,25,8 -org.jooq,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,, -org.json,,,236,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,198,38 -org.keycloak.models.map.storage,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,, -org.kohsuke.stapler,20,24,363,,,,,,,,,,,,,2,,,,,,,,,,,8,1,,,,,,,,,3,,,,,,1,5,,,,,,,,,24,352,11 -org.lastaflute.web,,1,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,4, -org.mvel2,16,,,,,,,,,,,,,,,,,,,,,,,16,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.openjdk.jmh.runner.options,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.owasp.esapi,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -org.pac4j.jwt.config.encryption,4,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.pac4j.jwt.config.signature,4,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.scijava.log,13,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.slf4j,55,,6,,,,,,,,,,,,,,,,,,,,55,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,4 -org.springframework.beans,,,30,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,30 -org.springframework.boot.jdbc,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,, -org.springframework.cache,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13 -org.springframework.context,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3, -org.springframework.core.io,17,,6,,,,,,,,,,,,,,,,,,,,,,,,16,,,,,,,,,,1,,,,,,,,,,,,,,,,,6, -org.springframework.data.repository,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 -org.springframework.http,14,,77,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,14,,,,,,,,,,,,,,,,,67,10 -org.springframework.jdbc.core,19,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,19,,,,,,,,,,,,,,,, -org.springframework.jdbc.datasource,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,,,, -org.springframework.jdbc.object,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,,,,,,,,,,,,,,, -org.springframework.jndi,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.springframework.ldap,47,,,,,,,,,,,,,,,,,,,33,,14,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.springframework.security.core.userdetails,2,,,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -org.springframework.security.web.savedrequest,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,, -org.springframework.ui,,,32,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,32 -org.springframework.util,10,,142,,,,,,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,,,,,,,,,,,90,52 -org.springframework.validation,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13, -org.springframework.web.client,13,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,,,,,,,,3,, -org.springframework.web.context.request,,8,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,, -org.springframework.web.multipart,,12,12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,12,12, -org.springframework.web.portlet,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,, -org.springframework.web.reactive.function.client,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,,,, -org.springframework.web.servlet,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,, -org.springframework.web.socket,,8,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,6, -org.springframework.web.util,,9,159,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,134,25 -org.thymeleaf,2,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,2, -org.xml.sax,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -org.xmlpull.v1,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,, -org.yaml.snakeyaml,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, -play.libs.ws,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,, -play.mvc,1,13,24,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,13,24, -ratpack.core.form,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3, -ratpack.core.handling,,6,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,4, -ratpack.core.http,,10,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,10, -ratpack.exec,,,48,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,48 -ratpack.form,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3, -ratpack.func,,,35,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35 -ratpack.handling,,6,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,4, -ratpack.http,,10,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,10, -ratpack.util,,,35,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35 -retrofit2,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,1, -software.amazon.awssdk.transfer.s3.model,8,,,,,,,,,,,,,,,,,,,,,,,,,,8,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.jvmstat.perfdata.monitor.protocol.local,3,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.jvmstat.perfdata.monitor.protocol.rmi,1,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.misc,3,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.net.ftp,5,,,,,,2,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.net.www.protocol.http,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.security.acl,1,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.security.jgss.krb5,2,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.security.krb5,9,,,,,3,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.security.pkcs,4,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.security.pkcs11,3,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.security.provider,2,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.security.ssl,3,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.security.x509,1,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -sun.tools.jconsole,28,,,,,,13,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +package,sink,source,summary,sink:bean-validation,sink:command-injection,sink:credentials-key,sink:credentials-password,sink:credentials-username,sink:encryption-iv,sink:encryption-salt,sink:environment-injection,sink:file-content-store,sink:fragment-injection,sink:groovy-injection,sink:hostname-verification,sink:html-injection,sink:information-leak,sink:intent-redirection,sink:jexl-injection,sink:jndi-injection,sink:js-injection,sink:ldap-injection,sink:log-injection,sink:mvel-injection,sink:notification,sink:ognl-injection,sink:path-injection,sink:path-injection[read],sink:pending-intents,sink:regex-use,sink:regex-use[-1],sink:regex-use[0],sink:regex-use[],sink:regex-use[f-1],sink:regex-use[f1],sink:regex-use[f],sink:request-forgery,sink:response-splitting,sink:sql-injection,sink:template-injection,sink:trust-boundary-violation,sink:unsafe-deserialization,sink:url-forward,sink:url-redirection,sink:xpath-injection,sink:xslt-injection,sink:xxe,source:android-external-storage-dir,source:commandargs,source:contentprovider,source:database,source:environment,source:file,source:remote,summary:taint,summary:value +actions.osgi,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6, +android.app,77,,103,,,,,,,,,,11,,,,,7,,,,,,,42,,,,17,,,,,,,,,,,,,,,,,,,,,,,,,,18,85 +android.content,24,31,154,,,,,,,,,,,,,,,16,,,,,,,,,,,,,,,,,,,,,8,,,,,,,,,4,,27,,,,,63,91 +android.database,59,,41,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,59,,,,,,,,,,,,,,,,41, +android.net,,,60,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,45,15 +android.os,1,2,122,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,2,,,,,,,41,81 +android.support.v4.app,11,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +android.util,6,16,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,16,, +android.webkit,3,2,,,,,,,,,,,,,,2,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,, +android.widget,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,1, +androidx.core.app,47,,95,,,,,,,,,,,,,,,,,,,,,,41,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,12,83 +androidx.fragment.app,11,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +androidx.slice,2,5,88,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,5,,,,,27,61 +antlr,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +ch.ethz.ssh2,2,,,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +cn.hutool.core.codec,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +com.alibaba.com.caucho.hessian.io,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,, +com.alibaba.druid.sql,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,1, +com.alibaba.fastjson2,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +com.amazonaws.auth,2,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.auth0.jwt.algorithms,6,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.azure.identity,3,,,,,1,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.caucho.burlap.io,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,, +com.caucho.hessian.io,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,, +com.cedarsoftware.util.io,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,, +com.couchbase.client.core.env,15,,1,,,,9,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +com.couchbase.client.java,10,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,, +com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +com.esotericsoftware.yamlbeans,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,, +com.fasterxml.jackson.core,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +com.fasterxml.jackson.databind,2,,8,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,8, +com.google.common.base,4,,87,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,1,,,,,,,,,,,,,,,,,,,,,,63,24 +com.google.common.cache,,,17,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17 +com.google.common.collect,,,553,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,551 +com.google.common.flogger,29,,,,,,,,,,,,,,,,,,,,,,29,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.google.common.io,10,,73,,,,,,,,,1,,,,,,,,,,,,,,,4,5,,,,,,,,,,,,,,,,,,,,,,,,,,,72,1 +com.google.gson,,,52,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,38,14 +com.hubspot.jinjava,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,, +com.jcraft.jsch,5,,1,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,1, +com.microsoft.sqlserver.jdbc,4,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.mitchellbosecke.pebble,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,, +com.mongodb,10,,,,,,4,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.opensymphony.xwork2,56,,961,,,,,,,,,,,,,,,,,,,,,,,56,,,,,,,,,,,,,,,,,,,,,,,,,,,,,867,94 +com.rabbitmq.client,,21,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,21,7, +com.sshtools.j2ssh.authentication,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.sun.crypto.provider,19,,,,,17,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.sun.jndi.ldap,4,,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.sun.net.httpserver,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.sun.net.ssl,3,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.sun.rowset,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.sun.security.auth.module,2,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.sun.security.ntlm,5,,,,,,3,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.sun.security.sasl.digest,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.thoughtworks.xstream,1,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.trilead.ssh2,13,,,,,2,4,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.unboundid.ldap.sdk,17,,,,,,,,,,,,,,,,,,,,,17,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +com.zaxxer.hikari,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,, +flexjson,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 +freemarker.cache,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,, +freemarker.template,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,,,,,,,,,,,,,,, +groovy.lang,26,,,,,,,,,,,,,26,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +groovy.text,1,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +groovy.util,5,,,,,,,,,,,,,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +hudson,75,9,2648,,4,,,,,,3,2,,,,4,,,,,,,,,,,39,17,,,,,,,,,6,,,,,,,,,,,,,,,,5,4,2572,76 +io.jsonwebtoken,,2,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,4, +io.netty.bootstrap,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,,,,, +io.netty.buffer,,,207,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,130,77 +io.netty.channel,9,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,,,,,,,,,,,,,,,,2,, +io.netty.handler.codec,4,13,259,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,3,,,,,,,,,,,,,,,,,13,143,116 +io.netty.handler.ssl,4,,,,,,,,,,,,,,,,,,,,,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,, +io.netty.handler.stream,1,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,, +io.netty.resolver,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +io.netty.util,2,,23,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,1,,,,,,,,,,,,,,,,,,21,2 +io.undertow.server.handlers.resource,1,,3,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,3, +jakarta.activation,2,,2,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,1,,,,,,,,,,,,,,,,,,2, +jakarta.faces.context,4,7,,,,,,,,,,,,,,2,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,7,, +jakarta.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23 +jakarta.persistence,2,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,1, +jakarta.servlet,2,26,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,26,, +jakarta.ws.rs.client,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,, +jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,, +jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,94,55 +jakarta.xml.bind.attachment,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,, +java.applet,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,11, +java.awt,1,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,3 +java.beans,1,,177,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,82,95 +java.io,66,1,225,,,,,,,,,22,,,,,,,,,,,,,,,29,15,,,,,,,,,,,,,,,,,,,,,,,,,1,,202,23 +java.lang,38,3,790,,13,,,,,,1,,,,,,,,,,,,8,,,,2,9,,,4,,,1,,,,,,,,,,,,,,,,,,3,,,510,280 +java.math,,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9 +java.net,23,3,347,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,21,,,,,,,,,,,,,,,,,3,248,99 +java.nio,47,,499,,,,,,,,,5,,,,,,,,,,,,,,,25,16,,,,,,,,,1,,,,,,,,,,,,,,,,,,302,197 +java.rmi,,,68,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,45,23 +java.security,21,,583,,,11,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,285,298 +java.sql,15,1,292,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,9,,,,,,,,,,,,1,,,,274,18 +java.text,,,154,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,72,82 +java.time,,,131,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,27,104 +java.util,48,2,1340,,,,,,,,,1,,,,,,,,,,,34,,,,3,,,,,5,2,,1,2,,,,,,,,,,,,,,,,2,,,558,782 +javafx.scene.web,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,, +javax.accessibility,,,63,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,28,35 +javax.activation,2,,7,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,1,,,,,,,,,,,,,,,,,,7, +javax.annotation.processing,,,28,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,25,3 +javax.crypto,19,,140,,,12,3,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,76,64 +javax.faces.context,4,7,,,,,,,,,,,,,,2,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,7,, +javax.imageio,1,,304,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,138,166 +javax.jms,,9,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,57, +javax.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23 +javax.lang.model,,,277,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,217,60 +javax.management,2,,766,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,363,403 +javax.naming,7,,341,,,,,,,,,,,,,,,,,6,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,191,150 +javax.net,4,,136,,,,2,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,87,49 +javax.portlet,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,, +javax.print,2,,133,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,102,31 +javax.rmi.ssl,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6 +javax.script,1,,50,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,14,36 +javax.security.auth,7,,147,,,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,50,97 +javax.security.cert,,,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5, +javax.security.sasl,,,49,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,42,7 +javax.servlet,10,29,3,,,,,,,,,,,,,,1,,,,,,,,,,,2,,,,,,,,,,3,,,2,,2,,,,,,,,,,,29,3, +javax.smartcardio,,,34,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,24,10 +javax.sound.midi,,,60,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,51,9 +javax.sound.sampled,,,90,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,53,37 +javax.sql,7,,126,,,,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,68,58 +javax.tools,,,66,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,62,4 +javax.transaction.xa,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +javax.validation,1,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,, +javax.ws.rs.client,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,, +javax.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,, +javax.ws.rs.core,3,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,2,,,,,,,,,,,94,55 +javax.xml.bind.attachment,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,, +javax.xml.catalog,,,12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,11,1 +javax.xml.crypto,,,269,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,172,97 +javax.xml.datatype,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5,1 +javax.xml.namespace,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5,10 +javax.xml.parsers,,,37,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35,2 +javax.xml.stream,,,221,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,201,20 +javax.xml.transform,2,,134,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,72,62 +javax.xml.validation,,,29,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,29, +javax.xml.xpath,3,,26,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,26, +jenkins,,,523,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,500,23 +jodd.json,,,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10 +kotlin,16,,1849,,,,,,,,,,,,,,,,,,,,,,,,11,3,,,,,,,,,2,,,,,,,,,,,,,,,,,,1836,13 +liquibase.database.jvm,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,, +liquibase.statement.core,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,, +net.lingala.zip4j,2,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +net.schmizz.sshj,4,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +net.sf.json,2,,338,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,321,17 +net.sf.saxon.s9api,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5,,,,,,,,,, +ognl,6,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +okhttp3,4,,50,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,,,,23,27 +org.acegisecurity,,,49,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,49, +org.antlr.runtime,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.avro,18,19,,,,,,,,,,,,,,,,,,,,,,,,,17,,,,,,,,,,1,,,,,,,,,,,,1,,,,17,1,, +org.apache.commons.codec,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6, +org.apache.commons.collections,,,800,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,783 +org.apache.commons.collections4,,,806,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,789 +org.apache.commons.compress.archivers.tar,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4, +org.apache.commons.exec,10,,,,6,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.commons.fileupload,,11,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,11,4, +org.apache.commons.httpclient.util,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +org.apache.commons.io,124,,570,,,,,,,,,4,,,,,,,,,,,,,,,102,3,,,,,,,,,15,,,,,,,,,,,,,,,,,,556,14 +org.apache.commons.jelly,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,, +org.apache.commons.jexl2,15,,,,,,,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.commons.jexl3,15,,,,,,,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.commons.lang,1,,767,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,596,171 +org.apache.commons.lang3,7,,425,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,1,,,,,,,,,,,,,294,131 +org.apache.commons.logging,6,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.commons.net,13,12,,,,,2,2,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,6,,,,,,,,,,,,,,,,,12,, +org.apache.commons.ognl,6,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.commons.text,,,272,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,220,52 +org.apache.cxf.catalog,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,, +org.apache.cxf.common.classloader,3,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,2,,,,,,,,,,,,,,,,,,, +org.apache.cxf.common.jaxb,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.cxf.common.logging,6,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.cxf.configuration.jsse,2,,,,,,,,,,,,,,1,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.cxf.helpers,10,,,,,,,,,,,,,,,,,,,,,,,,,,5,,,,,,,,,,,,,,,,,,5,,,,,,,,,,, +org.apache.cxf.resource,9,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,5,,,,,,,,,,,,,,,,,,, +org.apache.cxf.staxutils,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.cxf.tools.corba.utils,4,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.cxf.tools.util,10,,,,,,,,,,,,,,,,,,,,,,,,,,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.cxf.transform,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,, +org.apache.directory.ldap.client.api,1,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.hadoop.fs,3,,11,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,11, +org.apache.hadoop.hive.metastore,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,,, +org.apache.hadoop.hive.ql.exec,1,,1,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +org.apache.hadoop.hive.ql.metadata,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.hc.client5.http.async.methods,84,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,84,,,,,,,,,,,,,,,,,,, +org.apache.hc.client5.http.classic.methods,37,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,37,,,,,,,,,,,,,,,,,,, +org.apache.hc.client5.http.fluent,19,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,19,,,,,,,,,,,,,,,,,,, +org.apache.hc.client5.http.protocol,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 +org.apache.hc.client5.http.utils,,,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7, +org.apache.hc.core5.benchmark,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,, +org.apache.hc.core5.function,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +org.apache.hc.core5.http,73,2,45,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,72,,,,,,,,,,,,,,,,,2,45, +org.apache.hc.core5.net,,,18,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,18, +org.apache.hc.core5.util,,,24,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,18,6 +org.apache.hive.hcatalog.templeton,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,, +org.apache.http,53,3,117,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,51,,,,,,,,,,,,,,,,,3,108,9 +org.apache.ibatis.jdbc,6,,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,57, +org.apache.ibatis.mapping,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +org.apache.log4j,11,,,,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.logging.log4j,359,,8,,,,,,,,,,,,,,,,,,,,359,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,4 +org.apache.poi,43,24,2216,,,,,,,,,,,,,,,,,,,,,,,,40,,,,,,,,,,,,,,,,,,,,3,,,,,1,23,,1160,1056 +org.apache.shiro.authc,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,, +org.apache.shiro.codec,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +org.apache.shiro.jndi,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.shiro.mgt,1,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.sshd.client.session,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.struts.beanvalidation.validation.interceptor,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4, +org.apache.struts2,14,,3873,,,,,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,3839,34 +org.apache.tools.ant,14,,,,1,,,,,,,,,,,,,,,,,,,,,,5,8,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.tools.zip,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +org.apache.velocity.app,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,, +org.apache.velocity.runtime,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,, +org.codehaus.cargo.container.installer,3,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,1,,,,,,,,,,,,,,,,,,, +org.codehaus.groovy.control,1,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.dom4j,20,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,20,,,,,,,,,,, +org.eclipse.jetty.client,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,, +org.exolab.castor.xml,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,, +org.fusesource.leveldbjni,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.geogebra.web.full.main,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,, +org.gradle.api.file,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3, +org.hibernate,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,,,,,,,,,,,,,,,,, +org.ho.yaml,8,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,,,,,,,,,,,,,, +org.influxdb,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,, +org.jabsorb,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,, +org.jboss.logging,324,,,,,,,,,,,,,,,,,,,,,,324,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.jboss.vfs,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.jdbi.v3.core,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,, +org.jenkins.ui.icon,,,49,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,48,1 +org.jenkins.ui.symbol,,,33,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,25,8 +org.jooq,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,, +org.json,,,236,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,198,38 +org.keycloak.models.map.storage,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,, +org.kohsuke.stapler,20,24,363,,,,,,,,,,,,,2,,,,,,,,,,,8,1,,,,,,,,,3,,,,,,1,5,,,,,,,,,,24,352,11 +org.lastaflute.web,,1,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,4, +org.mvel2,16,,,,,,,,,,,,,,,,,,,,,,,16,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.openjdk.jmh.runner.options,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.owasp.esapi,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +org.pac4j.jwt.config.encryption,4,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.pac4j.jwt.config.signature,4,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.scijava.log,13,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.slf4j,55,,6,,,,,,,,,,,,,,,,,,,,55,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,4 +org.springframework.beans,,,30,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,30 +org.springframework.boot.jdbc,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,, +org.springframework.cache,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13 +org.springframework.context,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3, +org.springframework.core.io,17,,6,,,,,,,,,,,,,,,,,,,,,,,,16,,,,,,,,,,1,,,,,,,,,,,,,,,,,,6, +org.springframework.data.repository,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 +org.springframework.http,14,,77,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,14,,,,,,,,,,,,,,,,,,67,10 +org.springframework.jdbc.core,19,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,19,,,,,,,,,,,,,,,,, +org.springframework.jdbc.datasource,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,,,,, +org.springframework.jdbc.object,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,,,,,,,,,,,,,,,, +org.springframework.jndi,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.springframework.ldap,47,,,,,,,,,,,,,,,,,,,33,,14,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.springframework.security.core.userdetails,2,,,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.springframework.security.web.savedrequest,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,, +org.springframework.ui,,,32,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,32 +org.springframework.util,10,,142,,,,,,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,,,,,,,,,,,,90,52 +org.springframework.validation,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13, +org.springframework.web.client,13,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,,,,,,,,,3,, +org.springframework.web.context.request,,8,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,, +org.springframework.web.multipart,,12,12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,12,12, +org.springframework.web.portlet,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,, +org.springframework.web.reactive.function.client,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,,,,, +org.springframework.web.servlet,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,, +org.springframework.web.socket,,8,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,6, +org.springframework.web.util,,9,159,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,134,25 +org.thymeleaf,2,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,2, +org.xml.sax,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +org.xmlpull.v1,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,, +org.yaml.snakeyaml,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, +play.libs.ws,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,, +play.mvc,1,13,24,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,13,24, +ratpack.core.form,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3, +ratpack.core.handling,,6,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,4, +ratpack.core.http,,10,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,10, +ratpack.exec,,,48,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,48 +ratpack.form,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3, +ratpack.func,,,35,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35 +ratpack.handling,,6,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,4, +ratpack.http,,10,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,10, +ratpack.util,,,35,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35 +retrofit2,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,1, +software.amazon.awssdk.transfer.s3.model,8,,,,,,,,,,,,,,,,,,,,,,,,,,8,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.jvmstat.perfdata.monitor.protocol.local,3,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.jvmstat.perfdata.monitor.protocol.rmi,1,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.misc,3,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.net.ftp,5,,,,,,2,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.net.www.protocol.http,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.security.acl,1,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.security.jgss.krb5,2,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.security.krb5,9,,,,,3,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.security.pkcs,4,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.security.pkcs11,3,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.security.provider,2,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.security.ssl,3,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.security.x509,1,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +sun.tools.jconsole,28,,,,,,13,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/java/documentation/library-coverage/coverage.rst b/java/documentation/library-coverage/coverage.rst index 28f862d87432..cc3da4594a66 100644 --- a/java/documentation/library-coverage/coverage.rst +++ b/java/documentation/library-coverage/coverage.rst @@ -40,6 +40,6 @@ Java framework & library support `Spring `_,``org.springframework.*``,46,494,144,26,,28,14,,36 `Thymeleaf `_,``org.thymeleaf``,,2,2,,,,,, `jOOQ `_,``org.jooq``,,,1,,,1,,, - Others,"``actions.osgi``, ``antlr``, ``ch.ethz.ssh2``, ``cn.hutool.core.codec``, ``com.alibaba.com.caucho.hessian.io``, ``com.alibaba.druid.sql``, ``com.alibaba.fastjson2``, ``com.amazonaws.auth``, ``com.auth0.jwt.algorithms``, ``com.azure.identity``, ``com.caucho.burlap.io``, ``com.caucho.hessian.io``, ``com.cedarsoftware.util.io``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.esotericsoftware.yamlbeans``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.microsoft.sqlserver.jdbc``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2``, ``com.sshtools.j2ssh.authentication``, ``com.sun.crypto.provider``, ``com.sun.jndi.ldap``, ``com.sun.net.httpserver``, ``com.sun.net.ssl``, ``com.sun.rowset``, ``com.sun.security.auth.module``, ``com.sun.security.ntlm``, ``com.sun.security.sasl.digest``, ``com.thoughtworks.xstream``, ``com.trilead.ssh2``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``hudson``, ``io.jsonwebtoken``, ``io.undertow.server.handlers.resource``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``liquibase.database.jvm``, ``liquibase.statement.core``, ``net.lingala.zip4j``, ``net.schmizz.sshj``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.avro``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.fileupload``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.cxf.catalog``, ``org.apache.cxf.common.classloader``, ``org.apache.cxf.common.jaxb``, ``org.apache.cxf.common.logging``, ``org.apache.cxf.configuration.jsse``, ``org.apache.cxf.helpers``, ``org.apache.cxf.resource``, ``org.apache.cxf.staxutils``, ``org.apache.cxf.tools.corba.utils``, ``org.apache.cxf.tools.util``, ``org.apache.cxf.transform``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hadoop.hive.ql.exec``, ``org.apache.hadoop.hive.ql.metadata``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hc.client5.http.protocol``, ``org.apache.hc.client5.http.utils``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.ibatis.mapping``, ``org.apache.log4j``, ``org.apache.shiro.authc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.shiro.mgt``, ``org.apache.sshd.client.session``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.codehaus.cargo.container.installer``, ``org.dom4j``, ``org.exolab.castor.xml``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.ho.yaml``, ``org.influxdb``, ``org.jabsorb``, ``org.jboss.vfs``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.keycloak.models.map.storage``, ``org.kohsuke.stapler``, ``org.lastaflute.web``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.owasp.esapi``, ``org.pac4j.jwt.config.encryption``, ``org.pac4j.jwt.config.signature``, ``org.scijava.log``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``software.amazon.awssdk.transfer.s3.model``, ``sun.jvmstat.perfdata.monitor.protocol.local``, ``sun.jvmstat.perfdata.monitor.protocol.rmi``, ``sun.misc``, ``sun.net.ftp``, ``sun.net.www.protocol.http``, ``sun.security.acl``, ``sun.security.jgss.krb5``, ``sun.security.krb5``, ``sun.security.pkcs``, ``sun.security.pkcs11``, ``sun.security.provider``, ``sun.security.ssl``, ``sun.security.x509``, ``sun.tools.jconsole``",127,6042,775,148,6,14,18,,186 - Totals,,382,26411,2708,421,16,137,33,1,416 + Others,"``actions.osgi``, ``antlr``, ``ch.ethz.ssh2``, ``cn.hutool.core.codec``, ``com.alibaba.com.caucho.hessian.io``, ``com.alibaba.druid.sql``, ``com.alibaba.fastjson2``, ``com.amazonaws.auth``, ``com.auth0.jwt.algorithms``, ``com.azure.identity``, ``com.caucho.burlap.io``, ``com.caucho.hessian.io``, ``com.cedarsoftware.util.io``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.esotericsoftware.yamlbeans``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.microsoft.sqlserver.jdbc``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2``, ``com.sshtools.j2ssh.authentication``, ``com.sun.crypto.provider``, ``com.sun.jndi.ldap``, ``com.sun.net.httpserver``, ``com.sun.net.ssl``, ``com.sun.rowset``, ``com.sun.security.auth.module``, ``com.sun.security.ntlm``, ``com.sun.security.sasl.digest``, ``com.thoughtworks.xstream``, ``com.trilead.ssh2``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``hudson``, ``io.jsonwebtoken``, ``io.undertow.server.handlers.resource``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``liquibase.database.jvm``, ``liquibase.statement.core``, ``net.lingala.zip4j``, ``net.schmizz.sshj``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.avro``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.fileupload``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.cxf.catalog``, ``org.apache.cxf.common.classloader``, ``org.apache.cxf.common.jaxb``, ``org.apache.cxf.common.logging``, ``org.apache.cxf.configuration.jsse``, ``org.apache.cxf.helpers``, ``org.apache.cxf.resource``, ``org.apache.cxf.staxutils``, ``org.apache.cxf.tools.corba.utils``, ``org.apache.cxf.tools.util``, ``org.apache.cxf.transform``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hadoop.hive.ql.exec``, ``org.apache.hadoop.hive.ql.metadata``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hc.client5.http.protocol``, ``org.apache.hc.client5.http.utils``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.ibatis.mapping``, ``org.apache.log4j``, ``org.apache.poi``, ``org.apache.shiro.authc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.shiro.mgt``, ``org.apache.sshd.client.session``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.codehaus.cargo.container.installer``, ``org.dom4j``, ``org.exolab.castor.xml``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.ho.yaml``, ``org.influxdb``, ``org.jabsorb``, ``org.jboss.vfs``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.keycloak.models.map.storage``, ``org.kohsuke.stapler``, ``org.lastaflute.web``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.owasp.esapi``, ``org.pac4j.jwt.config.encryption``, ``org.pac4j.jwt.config.signature``, ``org.scijava.log``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``software.amazon.awssdk.transfer.s3.model``, ``sun.jvmstat.perfdata.monitor.protocol.local``, ``sun.jvmstat.perfdata.monitor.protocol.rmi``, ``sun.misc``, ``sun.net.ftp``, ``sun.net.www.protocol.http``, ``sun.security.acl``, ``sun.security.jgss.krb5``, ``sun.security.krb5``, ``sun.security.pkcs``, ``sun.security.pkcs11``, ``sun.security.provider``, ``sun.security.ssl``, ``sun.security.x509``, ``sun.tools.jconsole``",151,8258,818,188,6,14,18,,186 + Totals,,406,28627,2751,461,16,137,33,1,416 From 985a79a45e4f76093a704d948763cc1c64992dae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 03:03:43 +0000 Subject: [PATCH 16/39] Bump golang.org/x/tools Bumps the extractor-dependencies group in /go/extractor with 1 update: [golang.org/x/tools](https://github.com/golang/tools). Updates `golang.org/x/tools` from 0.47.0 to 0.48.0 - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.47.0...v0.48.0) --- updated-dependencies: - dependency-name: golang.org/x/tools dependency-version: 0.48.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: extractor-dependencies ... Signed-off-by: dependabot[bot] --- go/extractor/go.mod | 4 ++-- go/extractor/go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go/extractor/go.mod b/go/extractor/go.mod index b95ec78da344..e121c4a98a8a 100644 --- a/go/extractor/go.mod +++ b/go/extractor/go.mod @@ -10,7 +10,7 @@ toolchain go1.26.4 // bazel mod tidy require ( golang.org/x/mod v0.38.0 - golang.org/x/tools v0.47.0 + golang.org/x/tools v0.48.0 ) require github.com/stretchr/testify v1.11.1 @@ -18,6 +18,6 @@ require github.com/stretchr/testify v1.11.1 require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sync v0.21.0 // indirect + golang.org/x/sync v0.22.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go/extractor/go.sum b/go/extractor/go.sum index de0d26d035b8..76fbec137b47 100644 --- a/go/extractor/go.sum +++ b/go/extractor/go.sum @@ -8,10 +8,10 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= -golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= -golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= -golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From 33261f441e1ed81de8841cde84c072b32adcd132 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 10 Jul 2026 15:32:44 +0200 Subject: [PATCH 17/39] Java: Improve join order. Before: ``` [2026-07-10 15:15:58] Evaluated non-recursive predicate ControlFlowGraph::NonReturningCalls::EffectivelyNonVirtualMethod.getAnAccess/0#dispred#efac59ed@090459qt in 4303ms (size: 186002). Evaluated relational algebra for predicate ControlFlowGraph::NonReturningCalls::EffectivelyNonVirtualMethod.getAnAccess/0#dispred#efac59ed@090459qt with tuple counts: 790592 ~0% {2} r1 = JOIN `Expr::MethodCall.getMethod/0#dispred#41989dc9_10#join_rhs` WITH `Member::Method.getSourceDeclaration/0#dispred#93e6cdf8` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 539755681 ~2% {2} | JOIN WITH `Member::SrcMethod.getAPossibleImplementationOfSrcMethod/0#dispred#4f4317e6#bf` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 186002 ~2% {2} | JOIN WITH ControlFlowGraph::NonReturningCalls::EffectivelyNonVirtualMethod#86c19e07 ON FIRST 1 OUTPUT Lhs.0, Lhs.1 return r1 ``` After: ``` [2026-07-10 15:29:39] Evaluated non-recursive predicate ControlFlowGraph::NonReturningCalls::EffectivelyNonVirtualMethod.getAnAccess/0#dispred#efac59ed@32fb2e61 in 10ms (size: 186002). Evaluated relational algebra for predicate ControlFlowGraph::NonReturningCalls::EffectivelyNonVirtualMethod.getAnAccess/0#dispred#efac59ed@32fb2e61 with tuple counts: 122765 ~0% {2} r1 = SCAN ControlFlowGraph::NonReturningCalls::EffectivelyNonVirtualMethod#86c19e07 OUTPUT In.0, In.0 122766 ~0% {2} | JOIN WITH `Member::SrcMethod.getAPossibleImplementationOfSrcMethod/0#dispred#4f4317e6_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 126911 ~0% {2} | JOIN WITH `Member::Method.getSourceDeclaration/0#dispred#93e6cdf8_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 186002 ~2% {2} | JOIN WITH `Expr::MethodCall.getMethod/0#dispred#41989dc9_10#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1 return r1 ``` --- java/ql/lib/semmle/code/java/ControlFlowGraph.qll | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index 7390be9cb3cf..c4f0c44b3813 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -406,7 +406,9 @@ private module NonReturningCalls { } /** Gets a `MethodCall` that calls this method. */ - MethodCall getAnAccess() { result.getMethod().getAPossibleImplementation() = this } + MethodCall getAnAccess() { + result.getMethod().getAPossibleImplementation() = pragma[only_bind_out](this) + } } /** Holds if a call to `m` indicates that `m` is expected to return. */ From 6057b4b6e9c187732b2f985366d2dc081e8dadbb Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Sat, 11 Jul 2026 07:28:14 +0100 Subject: [PATCH 18/39] Fix path sanitizer --- .../2026-07-11-file-getname-path-sanitizer.md | 4 +++ java/ql/lib/ext/java.io.model.yml | 5 ---- .../code/java/security/PathSanitizer.qll | 20 ++++++++++--- .../CWE-022/semmle/tests/TaintedPath.expected | 26 +++++++++++++++-- .../CWE-022/semmle/tests/TaintedPath.java | 28 ++++++++++++++++--- 5 files changed, 68 insertions(+), 15 deletions(-) create mode 100644 java/ql/lib/change-notes/2026-07-11-file-getname-path-sanitizer.md diff --git a/java/ql/lib/change-notes/2026-07-11-file-getname-path-sanitizer.md b/java/ql/lib/change-notes/2026-07-11-file-getname-path-sanitizer.md new file mode 100644 index 000000000000..dddcae521ff3 --- /dev/null +++ b/java/ql/lib/change-notes/2026-07-11-file-getname-path-sanitizer.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* `java.io.File.getName()` is no longer treated as a complete sanitizer for `java/path-injection`, since it does not remove a `..` path component (for example `new File("..").getName()` returns `".."`). It is now only recognized as a sanitizer when combined with a subsequent check for `..` components, which may result in new alerts. diff --git a/java/ql/lib/ext/java.io.model.yml b/java/ql/lib/ext/java.io.model.yml index dd47342d590a..f7c236de4eeb 100644 --- a/java/ql/lib/ext/java.io.model.yml +++ b/java/ql/lib/ext/java.io.model.yml @@ -162,8 +162,3 @@ extensions: extensible: sourceModel data: - ["java.io", "FileInputStream", True, "FileInputStream", "", "", "Argument[this]", "file", "manual"] - - addsTo: - pack: codeql/java-all - extensible: barrierModel - data: - - ["java.io", "File", True, "getName", "()", "", "ReturnValue", "path-injection", "manual"] diff --git a/java/ql/lib/semmle/code/java/security/PathSanitizer.qll b/java/ql/lib/semmle/code/java/security/PathSanitizer.qll index 42304bb72dc9..7c428a6691cd 100644 --- a/java/ql/lib/semmle/code/java/security/PathSanitizer.qll +++ b/java/ql/lib/semmle/code/java/security/PathSanitizer.qll @@ -97,16 +97,28 @@ private class AllowedPrefixSanitizer extends PathInjectionSanitizer { } } +/** A call to `java.io.File.getName`, which returns the final component of a path. */ +private class FileGetNameCall extends MethodCall { + FileGetNameCall() { this.getMethod().hasQualifiedName("java.io", "File", "getName") } +} + /** * Holds if `g` is a guard that considers a path safe because it is checked for `..` components, having previously - * been checked for a trusted prefix. + * been checked for a trusted prefix or been reduced to its final path component by `File.getName`. */ private predicate dotDotCheckGuard(Guard g, Expr e, boolean branch) { pathTraversalGuard(g, e, branch) and - exists(Guard previousGuard | - previousGuard.(AllowedPrefixGuard).controls(g.getBasicBlock(), true) + ( + exists(Guard previousGuard | + previousGuard.(AllowedPrefixGuard).controls(g.getBasicBlock(), true) + or + previousGuard.(BlockListGuard).controls(g.getBasicBlock(), false) + ) or - previousGuard.(BlockListGuard).controls(g.getBasicBlock(), false) + // `File.getName` strips any directory prefix, returning only the final path + // component. The only remaining path traversal risk is when that component is + // itself `..`, so a check for `..` components completes the sanitization. + exists(FileGetNameCall getName | localTaintFlowToPathGuard(getName, g)) ) } diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected index bfeb4d730c25..b44b294edc47 100644 --- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected +++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected @@ -1,6 +1,7 @@ #select | SanitizationTests2.java:16:59:16:62 | path | SanitizationTests2.java:14:59:14:91 | path : String | SanitizationTests2.java:16:59:16:62 | path | This path depends on a $@. | SanitizationTests2.java:14:59:14:91 | path | user-provided value | | TaintedPath.java:16:71:16:78 | filename | TaintedPath.java:13:58:13:78 | getInputStream(...) : InputStream | TaintedPath.java:16:71:16:78 | filename | This path depends on a $@. | TaintedPath.java:13:58:13:78 | getInputStream(...) | user-provided value | +| TaintedPath.java:105:71:105:78 | baseName | TaintedPath.java:98:58:98:78 | getInputStream(...) : InputStream | TaintedPath.java:105:71:105:78 | baseName | This path depends on a $@. | TaintedPath.java:98:58:98:78 | getInputStream(...) | user-provided value | | Test.java:37:52:37:68 | (...)... | Test.java:32:16:32:45 | getParameter(...) : String | Test.java:37:52:37:68 | (...)... | This path depends on a $@. | Test.java:32:16:32:45 | getParameter(...) | user-provided value | | Test.java:39:32:39:48 | (...)... | Test.java:32:16:32:45 | getParameter(...) : String | Test.java:39:32:39:48 | (...)... | This path depends on a $@. | Test.java:32:16:32:45 | getParameter(...) | user-provided value | | Test.java:41:47:41:63 | (...)... | Test.java:32:16:32:45 | getParameter(...) : String | Test.java:41:47:41:63 | (...)... | This path depends on a $@. | Test.java:32:16:32:45 | getParameter(...) | user-provided value | @@ -81,9 +82,18 @@ edges | SanitizationTests2.java:14:59:14:91 | path : String | SanitizationTests2.java:16:59:16:62 | path | provenance | Sink:MaD:23 | | TaintedPath.java:13:17:13:89 | new BufferedReader(...) : BufferedReader | TaintedPath.java:14:27:14:40 | filenameReader : BufferedReader | provenance | | | TaintedPath.java:13:36:13:88 | new InputStreamReader(...) : InputStreamReader | TaintedPath.java:13:17:13:89 | new BufferedReader(...) : BufferedReader | provenance | MaD:74 | -| TaintedPath.java:13:58:13:78 | getInputStream(...) : InputStream | TaintedPath.java:13:36:13:88 | new InputStreamReader(...) : InputStreamReader | provenance | Src:MaD:72 MaD:76 | +| TaintedPath.java:13:58:13:78 | getInputStream(...) : InputStream | TaintedPath.java:13:36:13:88 | new InputStreamReader(...) : InputStreamReader | provenance | Src:MaD:72 MaD:78 | | TaintedPath.java:14:27:14:40 | filenameReader : BufferedReader | TaintedPath.java:14:27:14:51 | readLine(...) : String | provenance | MaD:75 | | TaintedPath.java:14:27:14:51 | readLine(...) : String | TaintedPath.java:16:71:16:78 | filename | provenance | Sink:MaD:27 | +| TaintedPath.java:98:17:98:89 | new BufferedReader(...) : BufferedReader | TaintedPath.java:99:27:99:40 | filenameReader : BufferedReader | provenance | | +| TaintedPath.java:98:36:98:88 | new InputStreamReader(...) : InputStreamReader | TaintedPath.java:98:17:98:89 | new BufferedReader(...) : BufferedReader | provenance | MaD:74 | +| TaintedPath.java:98:58:98:78 | getInputStream(...) : InputStream | TaintedPath.java:98:36:98:88 | new InputStreamReader(...) : InputStreamReader | provenance | Src:MaD:72 MaD:78 | +| TaintedPath.java:99:27:99:40 | filenameReader : BufferedReader | TaintedPath.java:99:27:99:51 | readLine(...) : String | provenance | MaD:75 | +| TaintedPath.java:99:27:99:51 | readLine(...) : String | TaintedPath.java:100:30:100:37 | filename : String | provenance | | +| TaintedPath.java:100:21:100:38 | new File(...) : File | TaintedPath.java:101:27:101:30 | file : File | provenance | | +| TaintedPath.java:100:30:100:37 | filename : String | TaintedPath.java:100:21:100:38 | new File(...) : File | provenance | MaD:76 | +| TaintedPath.java:101:27:101:30 | file : File | TaintedPath.java:101:27:101:40 | getName(...) : String | provenance | MaD:77 | +| TaintedPath.java:101:27:101:40 | getName(...) : String | TaintedPath.java:105:71:105:78 | baseName | provenance | Sink:MaD:27 | | Test.java:32:16:32:45 | getParameter(...) : String | Test.java:37:61:37:68 | source(...) : String | provenance | Src:MaD:73 | | Test.java:32:16:32:45 | getParameter(...) : String | Test.java:39:41:39:48 | source(...) : String | provenance | Src:MaD:73 | | Test.java:32:16:32:45 | getParameter(...) : String | Test.java:41:56:41:63 | source(...) : String | provenance | Src:MaD:73 | @@ -312,7 +322,9 @@ models | 73 | Source: javax.servlet; ServletRequest; false; getParameter; (String); ; ReturnValue; remote; manual | | 74 | Summary: java.io; BufferedReader; false; BufferedReader; ; ; Argument[0]; Argument[this]; taint; manual | | 75 | Summary: java.io; BufferedReader; true; readLine; ; ; Argument[this]; ReturnValue; taint; manual | -| 76 | Summary: java.io; InputStreamReader; false; InputStreamReader; ; ; Argument[0]; Argument[this]; taint; manual | +| 76 | Summary: java.io; File; false; File; ; ; Argument[0]; Argument[this]; taint; manual | +| 77 | Summary: java.io; File; true; getName; (); ; Argument[this]; ReturnValue; taint; manual | +| 78 | Summary: java.io; InputStreamReader; false; InputStreamReader; ; ; Argument[0]; Argument[this]; taint; manual | nodes | SanitizationTests2.java:14:59:14:91 | path : String | semmle.label | path : String | | SanitizationTests2.java:16:59:16:62 | path | semmle.label | path | @@ -322,6 +334,16 @@ nodes | TaintedPath.java:14:27:14:40 | filenameReader : BufferedReader | semmle.label | filenameReader : BufferedReader | | TaintedPath.java:14:27:14:51 | readLine(...) : String | semmle.label | readLine(...) : String | | TaintedPath.java:16:71:16:78 | filename | semmle.label | filename | +| TaintedPath.java:98:17:98:89 | new BufferedReader(...) : BufferedReader | semmle.label | new BufferedReader(...) : BufferedReader | +| TaintedPath.java:98:36:98:88 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader | +| TaintedPath.java:98:58:98:78 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | +| TaintedPath.java:99:27:99:40 | filenameReader : BufferedReader | semmle.label | filenameReader : BufferedReader | +| TaintedPath.java:99:27:99:51 | readLine(...) : String | semmle.label | readLine(...) : String | +| TaintedPath.java:100:21:100:38 | new File(...) : File | semmle.label | new File(...) : File | +| TaintedPath.java:100:30:100:37 | filename : String | semmle.label | filename : String | +| TaintedPath.java:101:27:101:30 | file : File | semmle.label | file : File | +| TaintedPath.java:101:27:101:40 | getName(...) : String | semmle.label | getName(...) : String | +| TaintedPath.java:105:71:105:78 | baseName | semmle.label | baseName | | Test.java:32:16:32:45 | getParameter(...) : String | semmle.label | getParameter(...) : String | | Test.java:37:52:37:68 | (...)... | semmle.label | (...)... | | Test.java:37:61:37:68 | source(...) : String | semmle.label | source(...) : String | diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.java b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.java index fffb93c62916..fc01e1d23568 100644 --- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.java +++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.java @@ -93,18 +93,38 @@ public void sendUserFileGood5(Socket sock, String user) throws Exception { } } - public void sendUserFileGood4(Socket sock, String user) throws IOException { + public void sendUserFileBad2(Socket sock, String user) throws IOException { BufferedReader filenameReader = - new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8")); + new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8")); // $ Source[java/path-injection] String filename = filenameReader.readLine(); File file = new File(filename); String baseName = file.getName(); - // GOOD: only use the final component of the user provided path - BufferedReader fileReader = new BufferedReader(new FileReader(baseName)); + // BAD: `getName()` strips directory separators but does not remove a `..` + // component (`new File("..").getName()` returns ".."), so it is not a + // complete path injection sanitizer. + BufferedReader fileReader = new BufferedReader(new FileReader(baseName)); // $ Alert[java/path-injection] String fileLine = fileReader.readLine(); while (fileLine != null) { sock.getOutputStream().write(fileLine.getBytes()); fileLine = fileReader.readLine(); } } + + public void sendUserFileGood4(Socket sock, String user) throws IOException { + BufferedReader filenameReader = + new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8")); + String filename = filenameReader.readLine(); + File file = new File(filename); + String baseName = file.getName(); + // GOOD: `getName()` strips directory separators and the `..` check removes + // the only remaining traversal possibility. + if (!baseName.contains("..")) { + BufferedReader fileReader = new BufferedReader(new FileReader(baseName)); + String fileLine = fileReader.readLine(); + while (fileLine != null) { + sock.getOutputStream().write(fileLine.getBytes()); + fileLine = fileReader.readLine(); + } + } + } } From 63ff22abd6cad01854c0c087456489c82acb0dd5 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 13 Jul 2026 00:08:51 +0200 Subject: [PATCH 19/39] Go: Remove dead `ObjectsOverride` --- go/extractor/extractor.go | 31 ------------------------------- go/extractor/trap/trapwriter.go | 20 +++++++++----------- 2 files changed, 9 insertions(+), 42 deletions(-) diff --git a/go/extractor/extractor.go b/go/extractor/extractor.go index 158f0029704d..f4188cfb6816 100644 --- a/go/extractor/extractor.go +++ b/go/extractor/extractor.go @@ -1644,7 +1644,6 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { dbscheme.TypeNameTable.Emit(tw, lbl, origintp.Obj().Name()) underlying := origintp.Underlying() extractUnderlyingType(tw, lbl, underlying) - trackInstantiatedStructFields(tw, tp, origintp) entitylbl, exists := tw.Labeler.LookupObjectID(origintp.Obj(), lbl) if entitylbl == trap.InvalidLabel { @@ -2035,36 +2034,6 @@ func getObjectBeingUsed(tw *trap.Writer, ident *ast.Ident) types.Object { } } -// trackInstantiatedStructFields tries to give the fields of an instantiated -// struct type underlying `tp` the same labels as the corresponding fields of -// the generic struct type. This is so that when we come across the -// instantiated field in `tw.Package.TypesInfo.Uses` we will get the label for -// the generic field instead. -func trackInstantiatedStructFields(tw *trap.Writer, tp, origintp *types.Named) { - if tp == origintp { - return - } - - if instantiatedStruct, ok := tp.Underlying().(*types.Struct); ok { - genericStruct, ok2 := origintp.Underlying().(*types.Struct) - if !ok2 { - log.Fatalf( - "Error: underlying type of instantiated type is a struct but underlying type of generic type is %s", - origintp.Underlying()) - } - - if instantiatedStruct.NumFields() != genericStruct.NumFields() { - log.Fatalf( - "Error: instantiated struct %s has different number of fields than the generic version %s (%d != %d)", - instantiatedStruct, genericStruct, instantiatedStruct.NumFields(), genericStruct.NumFields()) - } - - for i := 0; i < instantiatedStruct.NumFields(); i++ { - tw.ObjectsOverride[instantiatedStruct.Field(i)] = genericStruct.Field(i) - } - } -} - func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) trap.Label { parent, exists := typeParamParent[tp] if !exists { diff --git a/go/extractor/trap/trapwriter.go b/go/extractor/trap/trapwriter.go index 50763719f50c..c565f343665b 100644 --- a/go/extractor/trap/trapwriter.go +++ b/go/extractor/trap/trapwriter.go @@ -17,16 +17,15 @@ import ( // A Writer provides methods for writing data to a TRAP file type Writer struct { - zip *gzip.Writer - wzip *bufio.Writer - wfile *bufio.Writer - file *os.File - Labeler *Labeler - path string - trapFilePath string - Package *packages.Package - TypesOverride map[ast.Expr]types.Type - ObjectsOverride map[types.Object]types.Object + zip *gzip.Writer + wzip *bufio.Writer + wfile *bufio.Writer + file *os.File + Labeler *Labeler + path string + trapFilePath string + Package *packages.Package + TypesOverride map[ast.Expr]types.Type } func FileFor(path string) (string, error) { @@ -67,7 +66,6 @@ func NewWriter(path string, pkg *packages.Package) (*Writer, error) { trapFilePath, pkg, make(map[ast.Expr]types.Type), - make(map[types.Object]types.Object), } tw.Labeler = newLabeler(tw) return tw, nil From 559f9f38500dbaff9ba091e1af725e2b14592450 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 13 Jul 2026 00:38:58 +0200 Subject: [PATCH 20/39] Go: Remove dead `isAlias` --- go/extractor/extractor.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/go/extractor/extractor.go b/go/extractor/extractor.go index f4188cfb6816..0e7fd199e7ce 100644 --- a/go/extractor/extractor.go +++ b/go/extractor/extractor.go @@ -1529,12 +1529,6 @@ func extractSpec(tw *trap.Writer, spec ast.Spec, parent trap.Label, idx int) { extractNodeLocation(tw, spec, lbl) } -// Determines whether the given type is an alias. -func isAlias(tp types.Type) bool { - _, ok := tp.(*types.Alias) - return ok -} - // extractType extracts type information for `tp` and returns its associated label; // types are only extracted once, so the second time `extractType` is invoked it simply returns the label func extractType(tw *trap.Writer, tp types.Type) trap.Label { From 0584ca09dca8dfde7f7fd309be033f95b51a5429 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 04:12:46 +0000 Subject: [PATCH 21/39] Go: Update to 1.26.5 --- MODULE.bazel | 2 +- go/actions/test/action.yml | 2 +- go/extractor/go.mod | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index e6f1ddb893dd..9f9a2125da92 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -276,7 +276,7 @@ use_repo( ) go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk") -go_sdk.download(version = "1.26.4") +go_sdk.download(version = "1.26.5") go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") go_deps.from_file(go_mod = "//go/extractor:go.mod") diff --git a/go/actions/test/action.yml b/go/actions/test/action.yml index 3cc3334d39ed..d4c391cd0103 100644 --- a/go/actions/test/action.yml +++ b/go/actions/test/action.yml @@ -4,7 +4,7 @@ inputs: go-test-version: description: Which Go version to use for running the tests required: false - default: "~1.26.4" + default: "~1.26.5" run-code-checks: description: Whether to run formatting, code and qhelp generation checks required: false diff --git a/go/extractor/go.mod b/go/extractor/go.mod index e121c4a98a8a..b45815abbc95 100644 --- a/go/extractor/go.mod +++ b/go/extractor/go.mod @@ -2,7 +2,7 @@ module github.com/github/codeql-go/extractor go 1.26 -toolchain go1.26.4 +toolchain go1.26.5 // when updating this, run // bazel run @rules_go//go -- mod tidy From 032636cf347366b163fc3a8b4c60ffb5ba65fea3 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Sun, 12 Jul 2026 21:22:53 +0200 Subject: [PATCH 22/39] Go: Track whether a type parameter was defined as part of a receiver --- go/extractor/dbscheme/dbscheme.go | 5 +++ go/extractor/dbscheme/tables.go | 3 +- go/extractor/extractor.go | 59 +++++++++++++++++++------------ go/extractor/trap/trapwriter.go | 2 ++ go/ql/lib/go.dbscheme | 6 ++-- go/ql/lib/go.dbscheme.stats | 4 +++ go/ql/lib/semmle/go/Types.qll | 8 ++--- 7 files changed, 56 insertions(+), 31 deletions(-) diff --git a/go/extractor/dbscheme/dbscheme.go b/go/extractor/dbscheme/dbscheme.go index 550c7920c711..45aea39b4e54 100644 --- a/go/extractor/dbscheme/dbscheme.go +++ b/go/extractor/dbscheme/dbscheme.go @@ -277,6 +277,11 @@ func FloatColumn(columnName string) Column { return Column{columnName, FLOAT, false, true} } +// BooleanColumn constructs a column with name `columnName` holding boolean values +func BooleanColumn(columnName string) Column { + return Column{columnName, BOOLEAN, false, true} +} + // A Table represents a database table type Table struct { name string diff --git a/go/extractor/dbscheme/tables.go b/go/extractor/dbscheme/tables.go index 9c537fbaf89f..b72c33795182 100644 --- a/go/extractor/dbscheme/tables.go +++ b/go/extractor/dbscheme/tables.go @@ -1241,4 +1241,5 @@ var TypeParamTable = NewTable("typeparam", EntityColumn(CompositeType, "bound"), EntityColumn(TypeParamParentObjectType, "parent"), IntColumn("idx"), -).KeySet("parent", "idx") + BooleanColumn("is_from_recv"), +).KeySet("parent", "idx", "is_from_recv") diff --git a/go/extractor/extractor.go b/go/extractor/extractor.go index 0e7fd199e7ce..f421688e52ad 100644 --- a/go/extractor/extractor.go +++ b/go/extractor/extractor.go @@ -32,7 +32,13 @@ import ( ) var MaxGoRoutines int -var typeParamParent map[*types.TypeParam]types.Object = make(map[*types.TypeParam]types.Object) + +type typeParamParentEntry struct { + parent types.Object + isFromReceiver bool +} + +var typeParamParent map[*types.TypeParam]typeParamParentEntry = make(map[*types.TypeParam]typeParamParentEntry) func init() { // this sets the number of threads that the Go runtime will spawn; this is separate @@ -530,8 +536,7 @@ func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label) // do not appear as objects in any scope, so they have to be dealt // with separately in extractMethods. if funcObj, ok := obj.(*types.Func); ok { - populateTypeParamParents(funcObj.Type().(*types.Signature).TypeParams(), obj) - populateTypeParamParents(funcObj.Type().(*types.Signature).RecvTypeParams(), obj) + populateTypeParamParentsFromFunction(funcObj) } // Populate type parameter parents for defined types and alias types. if typeNameObj, ok := obj.(*types.TypeName); ok { @@ -542,9 +547,9 @@ func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label) // careful with alias types because before Go 1.24 they would // return the underlying type. if tp, ok := typeNameObj.Type().(*types.Named); ok && !typeNameObj.IsAlias() { - populateTypeParamParents(tp.TypeParams(), obj) + populateTypeParamParents(tp.TypeParams(), obj, false) } else if tp, ok := typeNameObj.Type().(*types.Alias); ok { - populateTypeParamParents(tp.TypeParams(), obj) + populateTypeParamParents(tp.TypeParams(), obj, false) } } extractObject(tw, obj, lbl) @@ -570,8 +575,7 @@ func extractMethod(tw *trap.Writer, meth *types.Func) trap.Label { if !exists { // Populate type parameter parents for methods. They do not appear as // objects in any scope, so they have to be dealt with separately here. - populateTypeParamParents(meth.Type().(*types.Signature).TypeParams(), meth) - populateTypeParamParents(meth.Type().(*types.Signature).RecvTypeParams(), meth) + populateTypeParamParentsFromFunction(meth) extractObject(tw, meth, methlbl) } @@ -1677,9 +1681,9 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { } case *types.TypeParam: kind = dbscheme.TypeParamType.Index() - parentlbl := getTypeParamParentLabel(tw, tp) + parentlbl, isReceiverChild := getTypeParamParentLabel(tw, tp) constraintLabel := extractType(tw, tp.Constraint()) - dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), constraintLabel, parentlbl, tp.Index()) + dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), constraintLabel, parentlbl, tp.Index(), isReceiverChild) case *types.Union: kind = dbscheme.TypeSetLiteral.Index() for i := 0; i < tp.Len(); i++ { @@ -1819,9 +1823,9 @@ func getTypeLabel(tw *trap.Writer, tp types.Type) (trap.Label, bool) { } lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s};definedtype", entitylbl)) case *types.TypeParam: - parentlbl := getTypeParamParentLabel(tw, tp) + parentlbl, isReceiverChild := getTypeParamParentLabel(tw, tp) idx := tp.Index() - lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%v},%d,%s;typeparamtype", parentlbl, idx, tp.Obj().Name())) + lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%v},%d,%t,%s;typeparamtype", parentlbl, idx, isReceiverChild, tp.Obj().Name())) case *types.Union: var b strings.Builder for i := 0; i < tp.Len(); i++ { @@ -2005,11 +2009,20 @@ func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.L } } +func populateTypeParamParentsFromFunction(funcObj *types.Func) { + recvTypeParams := funcObj.Type().(*types.Signature).RecvTypeParams() + populateTypeParamParents(recvTypeParams, funcObj, true) + typeParams := funcObj.Type().(*types.Signature).TypeParams() + populateTypeParamParents(typeParams, funcObj, false) + +} + // populateTypeParamParents sets `parent` as the parent of the elements of `typeparams` -func populateTypeParamParents(typeparams *types.TypeParamList, parent types.Object) { +// and records whether elements are defined in a receiver. +func populateTypeParamParents(typeparams *types.TypeParamList, parent types.Object, isFromReceiver bool) { if typeparams != nil { - for idx := 0; idx < typeparams.Len(); idx++ { - setTypeParamParent(typeparams.At(idx), parent) + for tparam := range typeparams.TypeParams() { + setTypeParamParent(tparam, parent, isFromReceiver) } } } @@ -2028,24 +2041,24 @@ func getObjectBeingUsed(tw *trap.Writer, ident *ast.Ident) types.Object { } } -func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) trap.Label { - parent, exists := typeParamParent[tp] +func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) (trap.Label, bool) { + entry, exists := typeParamParent[tp] if !exists { log.Fatalf("Parent of type parameter does not exist: %s %s", tp.String(), tp.Constraint().String()) } - parentlbl, _ := tw.Labeler.ScopedObjectID(parent, func() trap.Label { + parentlbl, _ := tw.Labeler.ScopedObjectID(entry.parent, func() trap.Label { log.Fatalf("getTypeLabel() called for parent of type parameter %s", tp.String()) return trap.InvalidLabel }) - return parentlbl + return parentlbl, entry.isFromReceiver } -func setTypeParamParent(tp *types.TypeParam, newobj types.Object) { - obj, exists := typeParamParent[tp] +func setTypeParamParent(tp *types.TypeParam, newobj types.Object, isFromReceiver bool) { + entry, exists := typeParamParent[tp] if !exists { - typeParamParent[tp] = newobj - } else if newobj != obj { - log.Fatalf("Parent of type parameter '%s %s' being set to a different value: '%s' vs '%s'", tp.String(), tp.Constraint().String(), obj, newobj) + typeParamParent[tp] = typeParamParentEntry{newobj, isFromReceiver} + } else if entry.parent != newobj { + log.Fatalf("Parent of type parameter '%s %s' being set to a different value: '%s' vs '%s'", tp.String(), tp.Constraint().String(), entry.parent, newobj) } } diff --git a/go/extractor/trap/trapwriter.go b/go/extractor/trap/trapwriter.go index c565f343665b..fa7436be48bf 100644 --- a/go/extractor/trap/trapwriter.go +++ b/go/extractor/trap/trapwriter.go @@ -165,6 +165,8 @@ func (tw *Writer) Emit(table string, values []interface{}) error { fmt.Fprintf(tw.wzip, "%d", value) case float64: fmt.Fprintf(tw.wzip, "%e", value) + case bool: + fmt.Fprintf(tw.wzip, "%t", value) default: return errors.New("Cannot emit value") } diff --git a/go/ql/lib/go.dbscheme b/go/ql/lib/go.dbscheme index b1341734d687..5ff5325d274a 100644 --- a/go/ql/lib/go.dbscheme +++ b/go/ql/lib/go.dbscheme @@ -244,9 +244,9 @@ has_ellipsis(int id: @callorconversionexpr ref); variadic(int id: @signaturetype ref); -#keyset[parent, idx] -typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @compositetype ref, - int parent: @typeparamparentobject ref, int idx: int ref); +#keyset[parent, idx, is_from_recv] +typeparam(unique int tp: @typeparamtype ref, string name: string ref, + int bound: @compositetype ref, int parent: @typeparamparentobject ref, int idx: int ref, boolean is_from_recv: boolean ref); @container = @file | @folder; diff --git a/go/ql/lib/go.dbscheme.stats b/go/ql/lib/go.dbscheme.stats index 126bbff00f7f..dd9440b09982 100644 --- a/go/ql/lib/go.dbscheme.stats +++ b/go/ql/lib/go.dbscheme.stats @@ -17791,6 +17791,10 @@ idx 3126 + + is_from_recv + 0 + diff --git a/go/ql/lib/semmle/go/Types.qll b/go/ql/lib/semmle/go/Types.qll index 574d75685439..217ac5d211c8 100644 --- a/go/ql/lib/semmle/go/Types.qll +++ b/go/ql/lib/semmle/go/Types.qll @@ -382,18 +382,18 @@ class CompositeType extends @compositetype, Type { } /** A type that comes from a type parameter. */ class TypeParamType extends @typeparamtype, CompositeType { /** Gets the name of this type parameter type. */ - string getParamName() { typeparam(this, result, _, _, _) } + string getParamName() { typeparam(this, result, _, _, _, _) } /** Gets the constraint of this type parameter type. */ - Type getConstraint() { typeparam(this, _, result, _, _) } + Type getConstraint() { typeparam(this, _, result, _, _, _) } override InterfaceType getUnderlyingType() { result = this.getConstraint().getUnderlyingType() } /** Gets the parent object of this type parameter type. */ - TypeParamParentEntity getParent() { typeparam(this, _, _, result, _) } + TypeParamParentEntity getParent() { typeparam(this, _, _, result, _, _) } /** Gets the index of this type parameter type. */ - int getIndex() { typeparam(this, _, _, _, result) } + int getIndex() { typeparam(this, _, _, _, result, _) } override string pp() { result = this.getParamName() } From 37454ab5f647692fee2c6dde530d64c464b7a8df Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 13 Jul 2026 11:53:26 +0200 Subject: [PATCH 23/39] Go: Add upgrade and downgrade scripts --- .../go.dbscheme | 563 ++++++++++++++++++ .../old.dbscheme | 563 ++++++++++++++++++ .../typeparam.ql | 15 + .../upgrade.properties | 3 + .../go.dbscheme | 563 ++++++++++++++++++ .../old.dbscheme | 563 ++++++++++++++++++ .../typeparam.ql | 15 + .../upgrade.properties | 3 + 8 files changed, 2288 insertions(+) create mode 100644 go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/go.dbscheme create mode 100644 go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/old.dbscheme create mode 100644 go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/typeparam.ql create mode 100644 go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/upgrade.properties create mode 100644 go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/go.dbscheme create mode 100644 go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/old.dbscheme create mode 100644 go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/typeparam.ql create mode 100644 go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/upgrade.properties diff --git a/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/go.dbscheme b/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/go.dbscheme new file mode 100644 index 000000000000..b1341734d687 --- /dev/null +++ b/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/go.dbscheme @@ -0,0 +1,563 @@ +/** Auto-generated dbscheme; do not edit. Run `make gen` in directory `go/` to regenerate. */ + + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +/** Overlay support **/ + +databaseMetadata( + string metadataKey: string ref, + string value: string ref +); + +overlayChangedFiles( + string path: string ref +); + + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +compilations(unique int id: @compilation, string cwd: string ref); + +#keyset[id, num] +compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); + +#keyset[id, num, kind] +compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); + +diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); + +compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); + +#keyset[id, num] +compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); + +diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, + string full_error_message: string ref, int location: @location ref); + +locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, + int endLine: int ref, int endColumn: int ref); + +numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); + +files(unique int id: @file, string name: string ref); + +folders(unique int id: @folder, string name: string ref); + +containerparent(int parent: @container ref, unique int child: @container ref); + +has_location(unique int locatable: @locatable ref, int location: @location ref); + +#keyset[parent, idx] +comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); + +comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); + +doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); + +#keyset[parent, idx] +exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); + +literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); + +constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); + +fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); + +typeparamdecls(unique int id: @typeparamdecl, int parent: @typeparamdeclparent ref, int idx: int ref); + +#keyset[parent, idx] +stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); + +#keyset[parent, idx] +decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); + +#keyset[parent, idx] +specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); + +scopes(unique int id: @scope, int kind: int ref); + +scopenesting(unique int inner: @scope ref, int outer: @scope ref); + +scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); + +objects(unique int id: @object, int kind: int ref, string name: string ref); + +objectscopes(unique int object: @object ref, int scope: @scope ref); + +objecttypes(unique int object: @object ref, int tp: @type ref); + +methodreceivers(unique int method: @object ref, int receiver: @object ref); + +fieldstructs(unique int field: @object ref, int struct: @structtype ref); + +methodhosts(int method: @object ref, int host: @definedtype ref); + +defs(int ident: @ident ref, int object: @object ref); + +uses(int ident: @ident ref, int object: @object ref); + +types(unique int id: @type, int kind: int ref); + +type_of(unique int expr: @expr ref, int tp: @type ref); + +typename(unique int tp: @type ref, string name: string ref); + +key_type(unique int map: @maptype ref, int tp: @type ref); + +element_type(unique int container: @containertype ref, int tp: @type ref); + +base_type(unique int ptr: @pointertype ref, int tp: @type ref); + +underlying_type(unique int defined: @definedtype ref, int tp: @type ref); + +#keyset[parent, index] +component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); + +#keyset[parent, index] +struct_tags(int parent: @structtype ref, int index: int ref, string tag: string ref); + +#keyset[interface, index] +interface_private_method_ids(int interface: @interfacetype ref, int index: int ref, string id: string ref); + +array_length(unique int tp: @arraytype ref, string len: string ref); + +type_objects(unique int tp: @type ref, int object: @object ref); + +packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); + +#keyset[parent, idx] +modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); + +#keyset[parent, idx] +modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); + +#keyset[package, idx] +errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, + string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); + +has_ellipsis(int id: @callorconversionexpr ref); + +variadic(int id: @signaturetype ref); + +#keyset[parent, idx] +typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @compositetype ref, + int parent: @typeparamparentobject ref, int idx: int ref); + +@container = @file | @folder; + +@locatable = @xmllocatable | @node | @localscope; + +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @typeparamdeclparent + | @scopenode | @comment_group | @comment; + +@documentable = @file | @field | @typeparamdecl | @spec | @gendecl | @funcdecl | @modexpr; + +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @typeparamdecl | @spec; + +@modexprparent = @file | @modexpr; + +@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; + +@stmtparent = @funcdef | @stmt | @decl; + +@declparent = @file | @declstmt; + +@typeparamdeclparent = @funcdecl | @typespec; + +@funcdef = @funclit | @funcdecl; + +@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; + +@location = @location_default; + +@sourceline = @locatable; + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment; + +case @expr.kind of + 0 = @badexpr +| 1 = @ident +| 2 = @ellipsis +| 3 = @intlit +| 4 = @floatlit +| 5 = @imaglit +| 6 = @charlit +| 7 = @stringlit +| 8 = @funclit +| 9 = @compositelit +| 10 = @parenexpr +| 11 = @selectorexpr +| 12 = @indexexpr +| 13 = @genericfunctioninstantiationexpr +| 14 = @generictypeinstantiationexpr +| 15 = @sliceexpr +| 16 = @typeassertexpr +| 17 = @callorconversionexpr +| 18 = @starexpr +| 19 = @keyvalueexpr +| 20 = @arraytypeexpr +| 21 = @structtypeexpr +| 22 = @functypeexpr +| 23 = @interfacetypeexpr +| 24 = @maptypeexpr +| 25 = @typesetliteralexpr +| 26 = @plusexpr +| 27 = @minusexpr +| 28 = @notexpr +| 29 = @complementexpr +| 30 = @derefexpr +| 31 = @addressexpr +| 32 = @arrowexpr +| 33 = @lorexpr +| 34 = @landexpr +| 35 = @eqlexpr +| 36 = @neqexpr +| 37 = @lssexpr +| 38 = @leqexpr +| 39 = @gtrexpr +| 40 = @geqexpr +| 41 = @addexpr +| 42 = @subexpr +| 43 = @orexpr +| 44 = @xorexpr +| 45 = @mulexpr +| 46 = @quoexpr +| 47 = @remexpr +| 48 = @shlexpr +| 49 = @shrexpr +| 50 = @andexpr +| 51 = @andnotexpr +| 52 = @sendchantypeexpr +| 53 = @recvchantypeexpr +| 54 = @sendrcvchantypeexpr; + +@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; + +@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; + +@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; + +@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; + +@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; + +@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; + +@logicalunaryexpr = @notexpr; + +@bitwiseunaryexpr = @complementexpr; + +@arithmeticunaryexpr = @plusexpr | @minusexpr; + +@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; + +@logicalbinaryexpr = @lorexpr | @landexpr; + +@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; + +@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; + +@shiftexpr = @shlexpr | @shrexpr; + +@comparison = @equalitytest | @relationalcomparison; + +@equalitytest = @eqlexpr | @neqexpr; + +@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; + +@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; + +case @stmt.kind of + 0 = @badstmt +| 1 = @declstmt +| 2 = @emptystmt +| 3 = @labeledstmt +| 4 = @exprstmt +| 5 = @sendstmt +| 6 = @incstmt +| 7 = @decstmt +| 8 = @gostmt +| 9 = @deferstmt +| 10 = @returnstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @gotostmt +| 14 = @fallthroughstmt +| 15 = @blockstmt +| 16 = @ifstmt +| 17 = @caseclause +| 18 = @exprswitchstmt +| 19 = @typeswitchstmt +| 20 = @commclause +| 21 = @selectstmt +| 22 = @forstmt +| 23 = @rangestmt +| 24 = @assignstmt +| 25 = @definestmt +| 26 = @addassignstmt +| 27 = @subassignstmt +| 28 = @mulassignstmt +| 29 = @quoassignstmt +| 30 = @remassignstmt +| 31 = @andassignstmt +| 32 = @orassignstmt +| 33 = @xorassignstmt +| 34 = @shlassignstmt +| 35 = @shrassignstmt +| 36 = @andnotassignstmt; + +@incdecstmt = @incstmt | @decstmt; + +@assignment = @simpleassignstmt | @compoundassignstmt; + +@simpleassignstmt = @assignstmt | @definestmt; + +@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt + | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; + +@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; + +@switchstmt = @exprswitchstmt | @typeswitchstmt; + +@loopstmt = @forstmt | @rangestmt; + +case @decl.kind of + 0 = @baddecl +| 1 = @importdecl +| 2 = @constdecl +| 3 = @typedecl +| 4 = @vardecl +| 5 = @funcdecl; + +@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; + +case @spec.kind of + 0 = @importspec +| 1 = @valuespec +| 2 = @typedefspec +| 3 = @aliasspec; + +@typespec = @typedefspec | @aliasspec; + +case @object.kind of + 0 = @pkgobject +| 1 = @decltypeobject +| 2 = @builtintypeobject +| 3 = @declconstobject +| 4 = @builtinconstobject +| 5 = @declvarobject +| 6 = @declfunctionobject +| 7 = @builtinfunctionobject +| 8 = @labelobject; + +@typeparamparentobject = @decltypeobject | @declfunctionobject; + +@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; + +@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; + +@typeobject = @decltypeobject | @builtintypeobject; + +@valueobject = @constobject | @varobject | @functionobject; + +@constobject = @declconstobject | @builtinconstobject; + +@varobject = @declvarobject; + +@functionobject = @declfunctionobject | @builtinfunctionobject; + +case @scope.kind of + 0 = @universescope +| 1 = @packagescope +| 2 = @localscope; + +case @type.kind of + 0 = @invalidtype +| 1 = @boolexprtype +| 2 = @inttype +| 3 = @int8type +| 4 = @int16type +| 5 = @int32type +| 6 = @int64type +| 7 = @uinttype +| 8 = @uint8type +| 9 = @uint16type +| 10 = @uint32type +| 11 = @uint64type +| 12 = @uintptrtype +| 13 = @float32type +| 14 = @float64type +| 15 = @complex64type +| 16 = @complex128type +| 17 = @stringexprtype +| 18 = @unsafepointertype +| 19 = @boolliteraltype +| 20 = @intliteraltype +| 21 = @runeliteraltype +| 22 = @floatliteraltype +| 23 = @complexliteraltype +| 24 = @stringliteraltype +| 25 = @nilliteraltype +| 26 = @typeparamtype +| 27 = @arraytype +| 28 = @slicetype +| 29 = @structtype +| 30 = @pointertype +| 31 = @interfacetype +| 32 = @tupletype +| 33 = @signaturetype +| 34 = @maptype +| 35 = @sendchantype +| 36 = @recvchantype +| 37 = @sendrcvchantype +| 38 = @definedtype +| 39 = @typesetliteraltype; + +@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; + +@booltype = @boolexprtype | @boolliteraltype; + +@numerictype = @integertype | @floattype | @complextype; + +@integertype = @signedintegertype | @unsignedintegertype; + +@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; + +@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; + +@floattype = @float32type | @float64type | @floatliteraltype; + +@complextype = @complex64type | @complex128type | @complexliteraltype; + +@stringtype = @stringexprtype | @stringliteraltype; + +@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype + | @stringliteraltype | @nilliteraltype; + +@compositetype = @typeparamtype | @containertype | @structtype | @pointertype | @interfacetype | @tupletype + | @signaturetype | @definedtype | @typesetliteraltype; + +@containertype = @arraytype | @slicetype | @maptype | @chantype; + +@chantype = @sendchantype | @recvchantype | @sendrcvchantype; + +case @modexpr.kind of + 0 = @modcommentblock +| 1 = @modline +| 2 = @modlineblock +| 3 = @modlparen +| 4 = @modrparen; + +case @error.kind of + 0 = @unknownerror +| 1 = @listerror +| 2 = @parseerror +| 3 = @typeerror; + diff --git a/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/old.dbscheme b/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/old.dbscheme new file mode 100644 index 000000000000..5ff5325d274a --- /dev/null +++ b/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/old.dbscheme @@ -0,0 +1,563 @@ +/** Auto-generated dbscheme; do not edit. Run `make gen` in directory `go/` to regenerate. */ + + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +/** Overlay support **/ + +databaseMetadata( + string metadataKey: string ref, + string value: string ref +); + +overlayChangedFiles( + string path: string ref +); + + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +compilations(unique int id: @compilation, string cwd: string ref); + +#keyset[id, num] +compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); + +#keyset[id, num, kind] +compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); + +diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); + +compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); + +#keyset[id, num] +compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); + +diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, + string full_error_message: string ref, int location: @location ref); + +locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, + int endLine: int ref, int endColumn: int ref); + +numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); + +files(unique int id: @file, string name: string ref); + +folders(unique int id: @folder, string name: string ref); + +containerparent(int parent: @container ref, unique int child: @container ref); + +has_location(unique int locatable: @locatable ref, int location: @location ref); + +#keyset[parent, idx] +comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); + +comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); + +doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); + +#keyset[parent, idx] +exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); + +literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); + +constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); + +fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); + +typeparamdecls(unique int id: @typeparamdecl, int parent: @typeparamdeclparent ref, int idx: int ref); + +#keyset[parent, idx] +stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); + +#keyset[parent, idx] +decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); + +#keyset[parent, idx] +specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); + +scopes(unique int id: @scope, int kind: int ref); + +scopenesting(unique int inner: @scope ref, int outer: @scope ref); + +scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); + +objects(unique int id: @object, int kind: int ref, string name: string ref); + +objectscopes(unique int object: @object ref, int scope: @scope ref); + +objecttypes(unique int object: @object ref, int tp: @type ref); + +methodreceivers(unique int method: @object ref, int receiver: @object ref); + +fieldstructs(unique int field: @object ref, int struct: @structtype ref); + +methodhosts(int method: @object ref, int host: @definedtype ref); + +defs(int ident: @ident ref, int object: @object ref); + +uses(int ident: @ident ref, int object: @object ref); + +types(unique int id: @type, int kind: int ref); + +type_of(unique int expr: @expr ref, int tp: @type ref); + +typename(unique int tp: @type ref, string name: string ref); + +key_type(unique int map: @maptype ref, int tp: @type ref); + +element_type(unique int container: @containertype ref, int tp: @type ref); + +base_type(unique int ptr: @pointertype ref, int tp: @type ref); + +underlying_type(unique int defined: @definedtype ref, int tp: @type ref); + +#keyset[parent, index] +component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); + +#keyset[parent, index] +struct_tags(int parent: @structtype ref, int index: int ref, string tag: string ref); + +#keyset[interface, index] +interface_private_method_ids(int interface: @interfacetype ref, int index: int ref, string id: string ref); + +array_length(unique int tp: @arraytype ref, string len: string ref); + +type_objects(unique int tp: @type ref, int object: @object ref); + +packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); + +#keyset[parent, idx] +modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); + +#keyset[parent, idx] +modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); + +#keyset[package, idx] +errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, + string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); + +has_ellipsis(int id: @callorconversionexpr ref); + +variadic(int id: @signaturetype ref); + +#keyset[parent, idx, is_from_recv] +typeparam(unique int tp: @typeparamtype ref, string name: string ref, + int bound: @compositetype ref, int parent: @typeparamparentobject ref, int idx: int ref, boolean is_from_recv: boolean ref); + +@container = @file | @folder; + +@locatable = @xmllocatable | @node | @localscope; + +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @typeparamdeclparent + | @scopenode | @comment_group | @comment; + +@documentable = @file | @field | @typeparamdecl | @spec | @gendecl | @funcdecl | @modexpr; + +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @typeparamdecl | @spec; + +@modexprparent = @file | @modexpr; + +@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; + +@stmtparent = @funcdef | @stmt | @decl; + +@declparent = @file | @declstmt; + +@typeparamdeclparent = @funcdecl | @typespec; + +@funcdef = @funclit | @funcdecl; + +@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; + +@location = @location_default; + +@sourceline = @locatable; + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment; + +case @expr.kind of + 0 = @badexpr +| 1 = @ident +| 2 = @ellipsis +| 3 = @intlit +| 4 = @floatlit +| 5 = @imaglit +| 6 = @charlit +| 7 = @stringlit +| 8 = @funclit +| 9 = @compositelit +| 10 = @parenexpr +| 11 = @selectorexpr +| 12 = @indexexpr +| 13 = @genericfunctioninstantiationexpr +| 14 = @generictypeinstantiationexpr +| 15 = @sliceexpr +| 16 = @typeassertexpr +| 17 = @callorconversionexpr +| 18 = @starexpr +| 19 = @keyvalueexpr +| 20 = @arraytypeexpr +| 21 = @structtypeexpr +| 22 = @functypeexpr +| 23 = @interfacetypeexpr +| 24 = @maptypeexpr +| 25 = @typesetliteralexpr +| 26 = @plusexpr +| 27 = @minusexpr +| 28 = @notexpr +| 29 = @complementexpr +| 30 = @derefexpr +| 31 = @addressexpr +| 32 = @arrowexpr +| 33 = @lorexpr +| 34 = @landexpr +| 35 = @eqlexpr +| 36 = @neqexpr +| 37 = @lssexpr +| 38 = @leqexpr +| 39 = @gtrexpr +| 40 = @geqexpr +| 41 = @addexpr +| 42 = @subexpr +| 43 = @orexpr +| 44 = @xorexpr +| 45 = @mulexpr +| 46 = @quoexpr +| 47 = @remexpr +| 48 = @shlexpr +| 49 = @shrexpr +| 50 = @andexpr +| 51 = @andnotexpr +| 52 = @sendchantypeexpr +| 53 = @recvchantypeexpr +| 54 = @sendrcvchantypeexpr; + +@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; + +@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; + +@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; + +@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; + +@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; + +@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; + +@logicalunaryexpr = @notexpr; + +@bitwiseunaryexpr = @complementexpr; + +@arithmeticunaryexpr = @plusexpr | @minusexpr; + +@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; + +@logicalbinaryexpr = @lorexpr | @landexpr; + +@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; + +@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; + +@shiftexpr = @shlexpr | @shrexpr; + +@comparison = @equalitytest | @relationalcomparison; + +@equalitytest = @eqlexpr | @neqexpr; + +@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; + +@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; + +case @stmt.kind of + 0 = @badstmt +| 1 = @declstmt +| 2 = @emptystmt +| 3 = @labeledstmt +| 4 = @exprstmt +| 5 = @sendstmt +| 6 = @incstmt +| 7 = @decstmt +| 8 = @gostmt +| 9 = @deferstmt +| 10 = @returnstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @gotostmt +| 14 = @fallthroughstmt +| 15 = @blockstmt +| 16 = @ifstmt +| 17 = @caseclause +| 18 = @exprswitchstmt +| 19 = @typeswitchstmt +| 20 = @commclause +| 21 = @selectstmt +| 22 = @forstmt +| 23 = @rangestmt +| 24 = @assignstmt +| 25 = @definestmt +| 26 = @addassignstmt +| 27 = @subassignstmt +| 28 = @mulassignstmt +| 29 = @quoassignstmt +| 30 = @remassignstmt +| 31 = @andassignstmt +| 32 = @orassignstmt +| 33 = @xorassignstmt +| 34 = @shlassignstmt +| 35 = @shrassignstmt +| 36 = @andnotassignstmt; + +@incdecstmt = @incstmt | @decstmt; + +@assignment = @simpleassignstmt | @compoundassignstmt; + +@simpleassignstmt = @assignstmt | @definestmt; + +@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt + | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; + +@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; + +@switchstmt = @exprswitchstmt | @typeswitchstmt; + +@loopstmt = @forstmt | @rangestmt; + +case @decl.kind of + 0 = @baddecl +| 1 = @importdecl +| 2 = @constdecl +| 3 = @typedecl +| 4 = @vardecl +| 5 = @funcdecl; + +@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; + +case @spec.kind of + 0 = @importspec +| 1 = @valuespec +| 2 = @typedefspec +| 3 = @aliasspec; + +@typespec = @typedefspec | @aliasspec; + +case @object.kind of + 0 = @pkgobject +| 1 = @decltypeobject +| 2 = @builtintypeobject +| 3 = @declconstobject +| 4 = @builtinconstobject +| 5 = @declvarobject +| 6 = @declfunctionobject +| 7 = @builtinfunctionobject +| 8 = @labelobject; + +@typeparamparentobject = @decltypeobject | @declfunctionobject; + +@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; + +@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; + +@typeobject = @decltypeobject | @builtintypeobject; + +@valueobject = @constobject | @varobject | @functionobject; + +@constobject = @declconstobject | @builtinconstobject; + +@varobject = @declvarobject; + +@functionobject = @declfunctionobject | @builtinfunctionobject; + +case @scope.kind of + 0 = @universescope +| 1 = @packagescope +| 2 = @localscope; + +case @type.kind of + 0 = @invalidtype +| 1 = @boolexprtype +| 2 = @inttype +| 3 = @int8type +| 4 = @int16type +| 5 = @int32type +| 6 = @int64type +| 7 = @uinttype +| 8 = @uint8type +| 9 = @uint16type +| 10 = @uint32type +| 11 = @uint64type +| 12 = @uintptrtype +| 13 = @float32type +| 14 = @float64type +| 15 = @complex64type +| 16 = @complex128type +| 17 = @stringexprtype +| 18 = @unsafepointertype +| 19 = @boolliteraltype +| 20 = @intliteraltype +| 21 = @runeliteraltype +| 22 = @floatliteraltype +| 23 = @complexliteraltype +| 24 = @stringliteraltype +| 25 = @nilliteraltype +| 26 = @typeparamtype +| 27 = @arraytype +| 28 = @slicetype +| 29 = @structtype +| 30 = @pointertype +| 31 = @interfacetype +| 32 = @tupletype +| 33 = @signaturetype +| 34 = @maptype +| 35 = @sendchantype +| 36 = @recvchantype +| 37 = @sendrcvchantype +| 38 = @definedtype +| 39 = @typesetliteraltype; + +@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; + +@booltype = @boolexprtype | @boolliteraltype; + +@numerictype = @integertype | @floattype | @complextype; + +@integertype = @signedintegertype | @unsignedintegertype; + +@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; + +@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; + +@floattype = @float32type | @float64type | @floatliteraltype; + +@complextype = @complex64type | @complex128type | @complexliteraltype; + +@stringtype = @stringexprtype | @stringliteraltype; + +@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype + | @stringliteraltype | @nilliteraltype; + +@compositetype = @typeparamtype | @containertype | @structtype | @pointertype | @interfacetype | @tupletype + | @signaturetype | @definedtype | @typesetliteraltype; + +@containertype = @arraytype | @slicetype | @maptype | @chantype; + +@chantype = @sendchantype | @recvchantype | @sendrcvchantype; + +case @modexpr.kind of + 0 = @modcommentblock +| 1 = @modline +| 2 = @modlineblock +| 3 = @modlparen +| 4 = @modrparen; + +case @error.kind of + 0 = @unknownerror +| 1 = @listerror +| 2 = @parseerror +| 3 = @typeerror; + diff --git a/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/typeparam.ql b/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/typeparam.ql new file mode 100644 index 000000000000..00c17208e6ea --- /dev/null +++ b/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/typeparam.ql @@ -0,0 +1,15 @@ +class TypeParamType extends @typeparamtype { + string toString() { none() } +} + +class CompositeType extends @compositetype { + string toString() { none() } +} + +class TypeParamParentObject extends @typeparamparentobject { + string toString() { none() } +} + +from TypeParamType tp, string name, CompositeType bound, TypeParamParentObject parent, int idx, boolean is_from_recv +where typeparam(tp, name, bound, parent, idx, is_from_recv) +select tp, name, bound, parent, idx diff --git a/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/upgrade.properties b/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/upgrade.properties new file mode 100644 index 000000000000..d383bdf4ad30 --- /dev/null +++ b/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/upgrade.properties @@ -0,0 +1,3 @@ +description: Track whether a type parameter is from a receiver +compatibility: partial +typeparam.rel: run typeparam.qlo diff --git a/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/go.dbscheme b/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/go.dbscheme new file mode 100644 index 000000000000..5ff5325d274a --- /dev/null +++ b/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/go.dbscheme @@ -0,0 +1,563 @@ +/** Auto-generated dbscheme; do not edit. Run `make gen` in directory `go/` to regenerate. */ + + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +/** Overlay support **/ + +databaseMetadata( + string metadataKey: string ref, + string value: string ref +); + +overlayChangedFiles( + string path: string ref +); + + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +compilations(unique int id: @compilation, string cwd: string ref); + +#keyset[id, num] +compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); + +#keyset[id, num, kind] +compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); + +diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); + +compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); + +#keyset[id, num] +compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); + +diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, + string full_error_message: string ref, int location: @location ref); + +locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, + int endLine: int ref, int endColumn: int ref); + +numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); + +files(unique int id: @file, string name: string ref); + +folders(unique int id: @folder, string name: string ref); + +containerparent(int parent: @container ref, unique int child: @container ref); + +has_location(unique int locatable: @locatable ref, int location: @location ref); + +#keyset[parent, idx] +comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); + +comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); + +doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); + +#keyset[parent, idx] +exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); + +literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); + +constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); + +fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); + +typeparamdecls(unique int id: @typeparamdecl, int parent: @typeparamdeclparent ref, int idx: int ref); + +#keyset[parent, idx] +stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); + +#keyset[parent, idx] +decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); + +#keyset[parent, idx] +specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); + +scopes(unique int id: @scope, int kind: int ref); + +scopenesting(unique int inner: @scope ref, int outer: @scope ref); + +scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); + +objects(unique int id: @object, int kind: int ref, string name: string ref); + +objectscopes(unique int object: @object ref, int scope: @scope ref); + +objecttypes(unique int object: @object ref, int tp: @type ref); + +methodreceivers(unique int method: @object ref, int receiver: @object ref); + +fieldstructs(unique int field: @object ref, int struct: @structtype ref); + +methodhosts(int method: @object ref, int host: @definedtype ref); + +defs(int ident: @ident ref, int object: @object ref); + +uses(int ident: @ident ref, int object: @object ref); + +types(unique int id: @type, int kind: int ref); + +type_of(unique int expr: @expr ref, int tp: @type ref); + +typename(unique int tp: @type ref, string name: string ref); + +key_type(unique int map: @maptype ref, int tp: @type ref); + +element_type(unique int container: @containertype ref, int tp: @type ref); + +base_type(unique int ptr: @pointertype ref, int tp: @type ref); + +underlying_type(unique int defined: @definedtype ref, int tp: @type ref); + +#keyset[parent, index] +component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); + +#keyset[parent, index] +struct_tags(int parent: @structtype ref, int index: int ref, string tag: string ref); + +#keyset[interface, index] +interface_private_method_ids(int interface: @interfacetype ref, int index: int ref, string id: string ref); + +array_length(unique int tp: @arraytype ref, string len: string ref); + +type_objects(unique int tp: @type ref, int object: @object ref); + +packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); + +#keyset[parent, idx] +modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); + +#keyset[parent, idx] +modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); + +#keyset[package, idx] +errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, + string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); + +has_ellipsis(int id: @callorconversionexpr ref); + +variadic(int id: @signaturetype ref); + +#keyset[parent, idx, is_from_recv] +typeparam(unique int tp: @typeparamtype ref, string name: string ref, + int bound: @compositetype ref, int parent: @typeparamparentobject ref, int idx: int ref, boolean is_from_recv: boolean ref); + +@container = @file | @folder; + +@locatable = @xmllocatable | @node | @localscope; + +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @typeparamdeclparent + | @scopenode | @comment_group | @comment; + +@documentable = @file | @field | @typeparamdecl | @spec | @gendecl | @funcdecl | @modexpr; + +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @typeparamdecl | @spec; + +@modexprparent = @file | @modexpr; + +@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; + +@stmtparent = @funcdef | @stmt | @decl; + +@declparent = @file | @declstmt; + +@typeparamdeclparent = @funcdecl | @typespec; + +@funcdef = @funclit | @funcdecl; + +@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; + +@location = @location_default; + +@sourceline = @locatable; + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment; + +case @expr.kind of + 0 = @badexpr +| 1 = @ident +| 2 = @ellipsis +| 3 = @intlit +| 4 = @floatlit +| 5 = @imaglit +| 6 = @charlit +| 7 = @stringlit +| 8 = @funclit +| 9 = @compositelit +| 10 = @parenexpr +| 11 = @selectorexpr +| 12 = @indexexpr +| 13 = @genericfunctioninstantiationexpr +| 14 = @generictypeinstantiationexpr +| 15 = @sliceexpr +| 16 = @typeassertexpr +| 17 = @callorconversionexpr +| 18 = @starexpr +| 19 = @keyvalueexpr +| 20 = @arraytypeexpr +| 21 = @structtypeexpr +| 22 = @functypeexpr +| 23 = @interfacetypeexpr +| 24 = @maptypeexpr +| 25 = @typesetliteralexpr +| 26 = @plusexpr +| 27 = @minusexpr +| 28 = @notexpr +| 29 = @complementexpr +| 30 = @derefexpr +| 31 = @addressexpr +| 32 = @arrowexpr +| 33 = @lorexpr +| 34 = @landexpr +| 35 = @eqlexpr +| 36 = @neqexpr +| 37 = @lssexpr +| 38 = @leqexpr +| 39 = @gtrexpr +| 40 = @geqexpr +| 41 = @addexpr +| 42 = @subexpr +| 43 = @orexpr +| 44 = @xorexpr +| 45 = @mulexpr +| 46 = @quoexpr +| 47 = @remexpr +| 48 = @shlexpr +| 49 = @shrexpr +| 50 = @andexpr +| 51 = @andnotexpr +| 52 = @sendchantypeexpr +| 53 = @recvchantypeexpr +| 54 = @sendrcvchantypeexpr; + +@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; + +@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; + +@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; + +@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; + +@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; + +@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; + +@logicalunaryexpr = @notexpr; + +@bitwiseunaryexpr = @complementexpr; + +@arithmeticunaryexpr = @plusexpr | @minusexpr; + +@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; + +@logicalbinaryexpr = @lorexpr | @landexpr; + +@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; + +@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; + +@shiftexpr = @shlexpr | @shrexpr; + +@comparison = @equalitytest | @relationalcomparison; + +@equalitytest = @eqlexpr | @neqexpr; + +@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; + +@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; + +case @stmt.kind of + 0 = @badstmt +| 1 = @declstmt +| 2 = @emptystmt +| 3 = @labeledstmt +| 4 = @exprstmt +| 5 = @sendstmt +| 6 = @incstmt +| 7 = @decstmt +| 8 = @gostmt +| 9 = @deferstmt +| 10 = @returnstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @gotostmt +| 14 = @fallthroughstmt +| 15 = @blockstmt +| 16 = @ifstmt +| 17 = @caseclause +| 18 = @exprswitchstmt +| 19 = @typeswitchstmt +| 20 = @commclause +| 21 = @selectstmt +| 22 = @forstmt +| 23 = @rangestmt +| 24 = @assignstmt +| 25 = @definestmt +| 26 = @addassignstmt +| 27 = @subassignstmt +| 28 = @mulassignstmt +| 29 = @quoassignstmt +| 30 = @remassignstmt +| 31 = @andassignstmt +| 32 = @orassignstmt +| 33 = @xorassignstmt +| 34 = @shlassignstmt +| 35 = @shrassignstmt +| 36 = @andnotassignstmt; + +@incdecstmt = @incstmt | @decstmt; + +@assignment = @simpleassignstmt | @compoundassignstmt; + +@simpleassignstmt = @assignstmt | @definestmt; + +@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt + | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; + +@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; + +@switchstmt = @exprswitchstmt | @typeswitchstmt; + +@loopstmt = @forstmt | @rangestmt; + +case @decl.kind of + 0 = @baddecl +| 1 = @importdecl +| 2 = @constdecl +| 3 = @typedecl +| 4 = @vardecl +| 5 = @funcdecl; + +@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; + +case @spec.kind of + 0 = @importspec +| 1 = @valuespec +| 2 = @typedefspec +| 3 = @aliasspec; + +@typespec = @typedefspec | @aliasspec; + +case @object.kind of + 0 = @pkgobject +| 1 = @decltypeobject +| 2 = @builtintypeobject +| 3 = @declconstobject +| 4 = @builtinconstobject +| 5 = @declvarobject +| 6 = @declfunctionobject +| 7 = @builtinfunctionobject +| 8 = @labelobject; + +@typeparamparentobject = @decltypeobject | @declfunctionobject; + +@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; + +@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; + +@typeobject = @decltypeobject | @builtintypeobject; + +@valueobject = @constobject | @varobject | @functionobject; + +@constobject = @declconstobject | @builtinconstobject; + +@varobject = @declvarobject; + +@functionobject = @declfunctionobject | @builtinfunctionobject; + +case @scope.kind of + 0 = @universescope +| 1 = @packagescope +| 2 = @localscope; + +case @type.kind of + 0 = @invalidtype +| 1 = @boolexprtype +| 2 = @inttype +| 3 = @int8type +| 4 = @int16type +| 5 = @int32type +| 6 = @int64type +| 7 = @uinttype +| 8 = @uint8type +| 9 = @uint16type +| 10 = @uint32type +| 11 = @uint64type +| 12 = @uintptrtype +| 13 = @float32type +| 14 = @float64type +| 15 = @complex64type +| 16 = @complex128type +| 17 = @stringexprtype +| 18 = @unsafepointertype +| 19 = @boolliteraltype +| 20 = @intliteraltype +| 21 = @runeliteraltype +| 22 = @floatliteraltype +| 23 = @complexliteraltype +| 24 = @stringliteraltype +| 25 = @nilliteraltype +| 26 = @typeparamtype +| 27 = @arraytype +| 28 = @slicetype +| 29 = @structtype +| 30 = @pointertype +| 31 = @interfacetype +| 32 = @tupletype +| 33 = @signaturetype +| 34 = @maptype +| 35 = @sendchantype +| 36 = @recvchantype +| 37 = @sendrcvchantype +| 38 = @definedtype +| 39 = @typesetliteraltype; + +@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; + +@booltype = @boolexprtype | @boolliteraltype; + +@numerictype = @integertype | @floattype | @complextype; + +@integertype = @signedintegertype | @unsignedintegertype; + +@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; + +@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; + +@floattype = @float32type | @float64type | @floatliteraltype; + +@complextype = @complex64type | @complex128type | @complexliteraltype; + +@stringtype = @stringexprtype | @stringliteraltype; + +@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype + | @stringliteraltype | @nilliteraltype; + +@compositetype = @typeparamtype | @containertype | @structtype | @pointertype | @interfacetype | @tupletype + | @signaturetype | @definedtype | @typesetliteraltype; + +@containertype = @arraytype | @slicetype | @maptype | @chantype; + +@chantype = @sendchantype | @recvchantype | @sendrcvchantype; + +case @modexpr.kind of + 0 = @modcommentblock +| 1 = @modline +| 2 = @modlineblock +| 3 = @modlparen +| 4 = @modrparen; + +case @error.kind of + 0 = @unknownerror +| 1 = @listerror +| 2 = @parseerror +| 3 = @typeerror; + diff --git a/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/old.dbscheme b/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/old.dbscheme new file mode 100644 index 000000000000..b1341734d687 --- /dev/null +++ b/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/old.dbscheme @@ -0,0 +1,563 @@ +/** Auto-generated dbscheme; do not edit. Run `make gen` in directory `go/` to regenerate. */ + + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +/** Overlay support **/ + +databaseMetadata( + string metadataKey: string ref, + string value: string ref +); + +overlayChangedFiles( + string path: string ref +); + + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +compilations(unique int id: @compilation, string cwd: string ref); + +#keyset[id, num] +compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); + +#keyset[id, num, kind] +compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); + +diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); + +compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); + +#keyset[id, num] +compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); + +diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, + string full_error_message: string ref, int location: @location ref); + +locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, + int endLine: int ref, int endColumn: int ref); + +numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); + +files(unique int id: @file, string name: string ref); + +folders(unique int id: @folder, string name: string ref); + +containerparent(int parent: @container ref, unique int child: @container ref); + +has_location(unique int locatable: @locatable ref, int location: @location ref); + +#keyset[parent, idx] +comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); + +comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); + +doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); + +#keyset[parent, idx] +exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); + +literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); + +constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); + +fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); + +typeparamdecls(unique int id: @typeparamdecl, int parent: @typeparamdeclparent ref, int idx: int ref); + +#keyset[parent, idx] +stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); + +#keyset[parent, idx] +decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); + +#keyset[parent, idx] +specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); + +scopes(unique int id: @scope, int kind: int ref); + +scopenesting(unique int inner: @scope ref, int outer: @scope ref); + +scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); + +objects(unique int id: @object, int kind: int ref, string name: string ref); + +objectscopes(unique int object: @object ref, int scope: @scope ref); + +objecttypes(unique int object: @object ref, int tp: @type ref); + +methodreceivers(unique int method: @object ref, int receiver: @object ref); + +fieldstructs(unique int field: @object ref, int struct: @structtype ref); + +methodhosts(int method: @object ref, int host: @definedtype ref); + +defs(int ident: @ident ref, int object: @object ref); + +uses(int ident: @ident ref, int object: @object ref); + +types(unique int id: @type, int kind: int ref); + +type_of(unique int expr: @expr ref, int tp: @type ref); + +typename(unique int tp: @type ref, string name: string ref); + +key_type(unique int map: @maptype ref, int tp: @type ref); + +element_type(unique int container: @containertype ref, int tp: @type ref); + +base_type(unique int ptr: @pointertype ref, int tp: @type ref); + +underlying_type(unique int defined: @definedtype ref, int tp: @type ref); + +#keyset[parent, index] +component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); + +#keyset[parent, index] +struct_tags(int parent: @structtype ref, int index: int ref, string tag: string ref); + +#keyset[interface, index] +interface_private_method_ids(int interface: @interfacetype ref, int index: int ref, string id: string ref); + +array_length(unique int tp: @arraytype ref, string len: string ref); + +type_objects(unique int tp: @type ref, int object: @object ref); + +packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); + +#keyset[parent, idx] +modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); + +#keyset[parent, idx] +modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); + +#keyset[package, idx] +errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, + string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); + +has_ellipsis(int id: @callorconversionexpr ref); + +variadic(int id: @signaturetype ref); + +#keyset[parent, idx] +typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @compositetype ref, + int parent: @typeparamparentobject ref, int idx: int ref); + +@container = @file | @folder; + +@locatable = @xmllocatable | @node | @localscope; + +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @typeparamdeclparent + | @scopenode | @comment_group | @comment; + +@documentable = @file | @field | @typeparamdecl | @spec | @gendecl | @funcdecl | @modexpr; + +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @typeparamdecl | @spec; + +@modexprparent = @file | @modexpr; + +@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; + +@stmtparent = @funcdef | @stmt | @decl; + +@declparent = @file | @declstmt; + +@typeparamdeclparent = @funcdecl | @typespec; + +@funcdef = @funclit | @funcdecl; + +@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; + +@location = @location_default; + +@sourceline = @locatable; + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment; + +case @expr.kind of + 0 = @badexpr +| 1 = @ident +| 2 = @ellipsis +| 3 = @intlit +| 4 = @floatlit +| 5 = @imaglit +| 6 = @charlit +| 7 = @stringlit +| 8 = @funclit +| 9 = @compositelit +| 10 = @parenexpr +| 11 = @selectorexpr +| 12 = @indexexpr +| 13 = @genericfunctioninstantiationexpr +| 14 = @generictypeinstantiationexpr +| 15 = @sliceexpr +| 16 = @typeassertexpr +| 17 = @callorconversionexpr +| 18 = @starexpr +| 19 = @keyvalueexpr +| 20 = @arraytypeexpr +| 21 = @structtypeexpr +| 22 = @functypeexpr +| 23 = @interfacetypeexpr +| 24 = @maptypeexpr +| 25 = @typesetliteralexpr +| 26 = @plusexpr +| 27 = @minusexpr +| 28 = @notexpr +| 29 = @complementexpr +| 30 = @derefexpr +| 31 = @addressexpr +| 32 = @arrowexpr +| 33 = @lorexpr +| 34 = @landexpr +| 35 = @eqlexpr +| 36 = @neqexpr +| 37 = @lssexpr +| 38 = @leqexpr +| 39 = @gtrexpr +| 40 = @geqexpr +| 41 = @addexpr +| 42 = @subexpr +| 43 = @orexpr +| 44 = @xorexpr +| 45 = @mulexpr +| 46 = @quoexpr +| 47 = @remexpr +| 48 = @shlexpr +| 49 = @shrexpr +| 50 = @andexpr +| 51 = @andnotexpr +| 52 = @sendchantypeexpr +| 53 = @recvchantypeexpr +| 54 = @sendrcvchantypeexpr; + +@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; + +@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; + +@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; + +@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; + +@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; + +@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; + +@logicalunaryexpr = @notexpr; + +@bitwiseunaryexpr = @complementexpr; + +@arithmeticunaryexpr = @plusexpr | @minusexpr; + +@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; + +@logicalbinaryexpr = @lorexpr | @landexpr; + +@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; + +@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; + +@shiftexpr = @shlexpr | @shrexpr; + +@comparison = @equalitytest | @relationalcomparison; + +@equalitytest = @eqlexpr | @neqexpr; + +@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; + +@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; + +case @stmt.kind of + 0 = @badstmt +| 1 = @declstmt +| 2 = @emptystmt +| 3 = @labeledstmt +| 4 = @exprstmt +| 5 = @sendstmt +| 6 = @incstmt +| 7 = @decstmt +| 8 = @gostmt +| 9 = @deferstmt +| 10 = @returnstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @gotostmt +| 14 = @fallthroughstmt +| 15 = @blockstmt +| 16 = @ifstmt +| 17 = @caseclause +| 18 = @exprswitchstmt +| 19 = @typeswitchstmt +| 20 = @commclause +| 21 = @selectstmt +| 22 = @forstmt +| 23 = @rangestmt +| 24 = @assignstmt +| 25 = @definestmt +| 26 = @addassignstmt +| 27 = @subassignstmt +| 28 = @mulassignstmt +| 29 = @quoassignstmt +| 30 = @remassignstmt +| 31 = @andassignstmt +| 32 = @orassignstmt +| 33 = @xorassignstmt +| 34 = @shlassignstmt +| 35 = @shrassignstmt +| 36 = @andnotassignstmt; + +@incdecstmt = @incstmt | @decstmt; + +@assignment = @simpleassignstmt | @compoundassignstmt; + +@simpleassignstmt = @assignstmt | @definestmt; + +@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt + | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; + +@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; + +@switchstmt = @exprswitchstmt | @typeswitchstmt; + +@loopstmt = @forstmt | @rangestmt; + +case @decl.kind of + 0 = @baddecl +| 1 = @importdecl +| 2 = @constdecl +| 3 = @typedecl +| 4 = @vardecl +| 5 = @funcdecl; + +@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; + +case @spec.kind of + 0 = @importspec +| 1 = @valuespec +| 2 = @typedefspec +| 3 = @aliasspec; + +@typespec = @typedefspec | @aliasspec; + +case @object.kind of + 0 = @pkgobject +| 1 = @decltypeobject +| 2 = @builtintypeobject +| 3 = @declconstobject +| 4 = @builtinconstobject +| 5 = @declvarobject +| 6 = @declfunctionobject +| 7 = @builtinfunctionobject +| 8 = @labelobject; + +@typeparamparentobject = @decltypeobject | @declfunctionobject; + +@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; + +@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; + +@typeobject = @decltypeobject | @builtintypeobject; + +@valueobject = @constobject | @varobject | @functionobject; + +@constobject = @declconstobject | @builtinconstobject; + +@varobject = @declvarobject; + +@functionobject = @declfunctionobject | @builtinfunctionobject; + +case @scope.kind of + 0 = @universescope +| 1 = @packagescope +| 2 = @localscope; + +case @type.kind of + 0 = @invalidtype +| 1 = @boolexprtype +| 2 = @inttype +| 3 = @int8type +| 4 = @int16type +| 5 = @int32type +| 6 = @int64type +| 7 = @uinttype +| 8 = @uint8type +| 9 = @uint16type +| 10 = @uint32type +| 11 = @uint64type +| 12 = @uintptrtype +| 13 = @float32type +| 14 = @float64type +| 15 = @complex64type +| 16 = @complex128type +| 17 = @stringexprtype +| 18 = @unsafepointertype +| 19 = @boolliteraltype +| 20 = @intliteraltype +| 21 = @runeliteraltype +| 22 = @floatliteraltype +| 23 = @complexliteraltype +| 24 = @stringliteraltype +| 25 = @nilliteraltype +| 26 = @typeparamtype +| 27 = @arraytype +| 28 = @slicetype +| 29 = @structtype +| 30 = @pointertype +| 31 = @interfacetype +| 32 = @tupletype +| 33 = @signaturetype +| 34 = @maptype +| 35 = @sendchantype +| 36 = @recvchantype +| 37 = @sendrcvchantype +| 38 = @definedtype +| 39 = @typesetliteraltype; + +@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; + +@booltype = @boolexprtype | @boolliteraltype; + +@numerictype = @integertype | @floattype | @complextype; + +@integertype = @signedintegertype | @unsignedintegertype; + +@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; + +@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; + +@floattype = @float32type | @float64type | @floatliteraltype; + +@complextype = @complex64type | @complex128type | @complexliteraltype; + +@stringtype = @stringexprtype | @stringliteraltype; + +@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype + | @stringliteraltype | @nilliteraltype; + +@compositetype = @typeparamtype | @containertype | @structtype | @pointertype | @interfacetype | @tupletype + | @signaturetype | @definedtype | @typesetliteraltype; + +@containertype = @arraytype | @slicetype | @maptype | @chantype; + +@chantype = @sendchantype | @recvchantype | @sendrcvchantype; + +case @modexpr.kind of + 0 = @modcommentblock +| 1 = @modline +| 2 = @modlineblock +| 3 = @modlparen +| 4 = @modrparen; + +case @error.kind of + 0 = @unknownerror +| 1 = @listerror +| 2 = @parseerror +| 3 = @typeerror; + diff --git a/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/typeparam.ql b/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/typeparam.ql new file mode 100644 index 000000000000..041c33577f75 --- /dev/null +++ b/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/typeparam.ql @@ -0,0 +1,15 @@ +class TypeParamType extends @typeparamtype { + string toString() { none() } +} + +class CompositeType extends @compositetype { + string toString() { none() } +} + +class TypeParamParentObject extends @typeparamparentobject { + string toString() { none() } +} + +from TypeParamType tp, string name, CompositeType bound, TypeParamParentObject parent, int idx +where typeparam(tp, name, bound, parent, idx) +select tp, name, bound, parent, idx, false diff --git a/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/upgrade.properties b/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/upgrade.properties new file mode 100644 index 000000000000..d383bdf4ad30 --- /dev/null +++ b/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/upgrade.properties @@ -0,0 +1,3 @@ +description: Track whether a type parameter is from a receiver +compatibility: partial +typeparam.rel: run typeparam.qlo From e17508ca5118b2298785fc41d4c7bcc9f91ba21d Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 13 Jul 2026 12:34:16 +0200 Subject: [PATCH 24/39] Go: Expose new TypeParamType column and use in test --- go/ql/lib/semmle/go/Types.qll | 3 + .../semmle/go/Function/TypeParamType.expected | 66 +++++++++---------- .../semmle/go/Function/TypeParamType.ql | 3 +- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/go/ql/lib/semmle/go/Types.qll b/go/ql/lib/semmle/go/Types.qll index 217ac5d211c8..e1b5c70f0959 100644 --- a/go/ql/lib/semmle/go/Types.qll +++ b/go/ql/lib/semmle/go/Types.qll @@ -395,6 +395,9 @@ class TypeParamType extends @typeparamtype, CompositeType { /** Gets the index of this type parameter type. */ int getIndex() { typeparam(this, _, _, _, result, _) } + /** Holds if this type parameter type is declared as part of a receiver */ + boolean isFromReceiver() { typeparam(this, _, _, _, _, result) } + override string pp() { result = this.getParamName() } /** diff --git a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected index bda7c1517975..42be6e28ab3b 100644 --- a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected +++ b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected @@ -20,36 +20,36 @@ numberOfTypeParameters | genericFunctions.go:152:6:152:36 | multipleAnonymousTypeParamsType | 3 | | genericFunctions.go:154:51:154:51 | f | 3 | #select -| codeql-go-tests/function.EdgeConstraint | 0 | Node | interface { } | -| codeql-go-tests/function.Element | 0 | S | interface { } | -| codeql-go-tests/function.GenericFunctionInAnotherFile | 0 | T | interface { } | -| codeql-go-tests/function.GenericFunctionOneTypeParam | 0 | T | interface { } | -| codeql-go-tests/function.GenericFunctionTwoTypeParams | 0 | K | comparable | -| codeql-go-tests/function.GenericFunctionTwoTypeParams | 1 | V | interface { int64 \| float64 } | -| codeql-go-tests/function.GenericStruct1 | 0 | T | interface { } | -| codeql-go-tests/function.GenericStruct1.f1 | 0 | TF1 | interface { } | -| codeql-go-tests/function.GenericStruct1.g1 | 0 | TG1 | interface { } | -| codeql-go-tests/function.GenericStruct2 | 0 | S | interface { } | -| codeql-go-tests/function.GenericStruct2 | 1 | T | interface { } | -| codeql-go-tests/function.GenericStruct2.f2 | 0 | SF2 | interface { } | -| codeql-go-tests/function.GenericStruct2.f2 | 1 | TF2 | interface { } | -| codeql-go-tests/function.GenericStruct2.g2 | 0 | SG2 | interface { } | -| codeql-go-tests/function.GenericStruct2.g2 | 1 | TG2 | interface { } | -| codeql-go-tests/function.Graph | 0 | Node | NodeConstraint | -| codeql-go-tests/function.Graph | 1 | Edge | EdgeConstraint | -| codeql-go-tests/function.Graph.ShortestPath | 0 | Node | NodeConstraint | -| codeql-go-tests/function.Graph.ShortestPath | 1 | Edge | EdgeConstraint | -| codeql-go-tests/function.List | 0 | T | interface { } | -| codeql-go-tests/function.List.MyLen | 0 | U | interface { } | -| codeql-go-tests/function.New | 0 | Node | NodeConstraint | -| codeql-go-tests/function.New | 1 | Edge | EdgeConstraint | -| codeql-go-tests/function.NodeConstraint | 0 | Edge | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 0 | _ | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 1 | _ | interface { string } | -| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 2 | _ | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType | 0 | _ | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType | 1 | _ | interface { string } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType | 2 | _ | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 0 | _ | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 1 | _ | interface { string } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 2 | _ | interface { } | +| codeql-go-tests/function.EdgeConstraint | 0 | false | Node | interface { } | +| codeql-go-tests/function.Element | 0 | false | S | interface { } | +| codeql-go-tests/function.GenericFunctionInAnotherFile | 0 | false | T | interface { } | +| codeql-go-tests/function.GenericFunctionOneTypeParam | 0 | false | T | interface { } | +| codeql-go-tests/function.GenericFunctionTwoTypeParams | 0 | false | K | comparable | +| codeql-go-tests/function.GenericFunctionTwoTypeParams | 1 | false | V | interface { int64 \| float64 } | +| codeql-go-tests/function.GenericStruct1 | 0 | false | T | interface { } | +| codeql-go-tests/function.GenericStruct1.f1 | 0 | true | TF1 | interface { } | +| codeql-go-tests/function.GenericStruct1.g1 | 0 | true | TG1 | interface { } | +| codeql-go-tests/function.GenericStruct2 | 0 | false | S | interface { } | +| codeql-go-tests/function.GenericStruct2 | 1 | false | T | interface { } | +| codeql-go-tests/function.GenericStruct2.f2 | 0 | true | SF2 | interface { } | +| codeql-go-tests/function.GenericStruct2.f2 | 1 | true | TF2 | interface { } | +| codeql-go-tests/function.GenericStruct2.g2 | 0 | true | SG2 | interface { } | +| codeql-go-tests/function.GenericStruct2.g2 | 1 | true | TG2 | interface { } | +| codeql-go-tests/function.Graph | 0 | false | Node | NodeConstraint | +| codeql-go-tests/function.Graph | 1 | false | Edge | EdgeConstraint | +| codeql-go-tests/function.Graph.ShortestPath | 0 | true | Node | NodeConstraint | +| codeql-go-tests/function.Graph.ShortestPath | 1 | true | Edge | EdgeConstraint | +| codeql-go-tests/function.List | 0 | false | T | interface { } | +| codeql-go-tests/function.List.MyLen | 0 | true | U | interface { } | +| codeql-go-tests/function.New | 0 | false | Node | NodeConstraint | +| codeql-go-tests/function.New | 1 | false | Edge | EdgeConstraint | +| codeql-go-tests/function.NodeConstraint | 0 | false | Edge | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 0 | false | _ | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 1 | false | _ | interface { string } | +| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 2 | false | _ | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType | 0 | false | _ | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType | 1 | false | _ | interface { string } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType | 2 | false | _ | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 0 | true | _ | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 1 | true | _ | interface { string } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 2 | true | _ | interface { } | diff --git a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql index 02dec23d2a86..365f58493f1b 100644 --- a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql +++ b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql @@ -11,4 +11,5 @@ where // Note that we cannot use the location of `tpt` itself as we currently fail // to extract an object for type parameters for methods on generic structs. exists(ty.getLocation()) -select ty.getQualifiedName(), tpt.getIndex(), tpt.getParamName(), tpt.getConstraint().pp() +select ty.getQualifiedName(), tpt.getIndex(), tpt.isFromReceiver(), tpt.getParamName(), + tpt.getConstraint().pp() From 9c1f5c9be2fb6e9561d014d715e21a263e03084c Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 13 Jul 2026 12:47:17 +0200 Subject: [PATCH 25/39] Go: Fix dowgrade script formatting --- .../5ff5325d274ae4f86defa195577bc7c1370b72fa/typeparam.ql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/typeparam.ql b/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/typeparam.ql index 00c17208e6ea..ad7685ac16a2 100644 --- a/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/typeparam.ql +++ b/go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/typeparam.ql @@ -10,6 +10,8 @@ class TypeParamParentObject extends @typeparamparentobject { string toString() { none() } } -from TypeParamType tp, string name, CompositeType bound, TypeParamParentObject parent, int idx, boolean is_from_recv +from + TypeParamType tp, string name, CompositeType bound, TypeParamParentObject parent, int idx, + boolean is_from_recv where typeparam(tp, name, bound, parent, idx, is_from_recv) select tp, name, bound, parent, idx From 9efcc49af706de6e342e577b1d8c932d7ca89c42 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 13 Jul 2026 13:06:15 +0200 Subject: [PATCH 26/39] C++: Update the CWE tag of `cpp/new-free-mismatch` --- cpp/ql/src/Critical/NewFreeMismatch.ql | 2 +- cpp/ql/src/change-notes/2026-07-13-new-free.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 cpp/ql/src/change-notes/2026-07-13-new-free.md diff --git a/cpp/ql/src/Critical/NewFreeMismatch.ql b/cpp/ql/src/Critical/NewFreeMismatch.ql index 19b9b197214a..7443ad97731e 100644 --- a/cpp/ql/src/Critical/NewFreeMismatch.ql +++ b/cpp/ql/src/Critical/NewFreeMismatch.ql @@ -8,7 +8,7 @@ * @id cpp/new-free-mismatch * @tags reliability * security - * external/cwe/cwe-401 + * external/cwe/cwe-762 */ import NewDelete diff --git a/cpp/ql/src/change-notes/2026-07-13-new-free.md b/cpp/ql/src/change-notes/2026-07-13-new-free.md new file mode 100644 index 000000000000..aef5a58aa480 --- /dev/null +++ b/cpp/ql/src/change-notes/2026-07-13-new-free.md @@ -0,0 +1,4 @@ +--- +category: queryMetadata +--- +* Added the tag `external/cwe/cwe-762` to `cpp/new-free-mismatch`, and removed the tag `external/cwe/cwe-401`. This better matches the behavior of the query. From e0f0987b81a126d21a91451e2d7a0f4c774ed237 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 13 Jul 2026 13:24:23 +0200 Subject: [PATCH 27/39] Go: Make `isFromReceiver` and actual `predicate` --- go/ql/lib/semmle/go/Types.qll | 2 +- .../semmle/go/Function/TypeParamType.expected | 66 +++++++++---------- .../semmle/go/Function/TypeParamType.ql | 8 +-- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/go/ql/lib/semmle/go/Types.qll b/go/ql/lib/semmle/go/Types.qll index e1b5c70f0959..1ea5b5c65c1d 100644 --- a/go/ql/lib/semmle/go/Types.qll +++ b/go/ql/lib/semmle/go/Types.qll @@ -396,7 +396,7 @@ class TypeParamType extends @typeparamtype, CompositeType { int getIndex() { typeparam(this, _, _, _, result, _) } /** Holds if this type parameter type is declared as part of a receiver */ - boolean isFromReceiver() { typeparam(this, _, _, _, _, result) } + predicate isFromReceiver() { typeparam(this, _, _, _, _, true) } override string pp() { result = this.getParamName() } diff --git a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected index 42be6e28ab3b..4addfa50f349 100644 --- a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected +++ b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected @@ -20,36 +20,36 @@ numberOfTypeParameters | genericFunctions.go:152:6:152:36 | multipleAnonymousTypeParamsType | 3 | | genericFunctions.go:154:51:154:51 | f | 3 | #select -| codeql-go-tests/function.EdgeConstraint | 0 | false | Node | interface { } | -| codeql-go-tests/function.Element | 0 | false | S | interface { } | -| codeql-go-tests/function.GenericFunctionInAnotherFile | 0 | false | T | interface { } | -| codeql-go-tests/function.GenericFunctionOneTypeParam | 0 | false | T | interface { } | -| codeql-go-tests/function.GenericFunctionTwoTypeParams | 0 | false | K | comparable | -| codeql-go-tests/function.GenericFunctionTwoTypeParams | 1 | false | V | interface { int64 \| float64 } | -| codeql-go-tests/function.GenericStruct1 | 0 | false | T | interface { } | -| codeql-go-tests/function.GenericStruct1.f1 | 0 | true | TF1 | interface { } | -| codeql-go-tests/function.GenericStruct1.g1 | 0 | true | TG1 | interface { } | -| codeql-go-tests/function.GenericStruct2 | 0 | false | S | interface { } | -| codeql-go-tests/function.GenericStruct2 | 1 | false | T | interface { } | -| codeql-go-tests/function.GenericStruct2.f2 | 0 | true | SF2 | interface { } | -| codeql-go-tests/function.GenericStruct2.f2 | 1 | true | TF2 | interface { } | -| codeql-go-tests/function.GenericStruct2.g2 | 0 | true | SG2 | interface { } | -| codeql-go-tests/function.GenericStruct2.g2 | 1 | true | TG2 | interface { } | -| codeql-go-tests/function.Graph | 0 | false | Node | NodeConstraint | -| codeql-go-tests/function.Graph | 1 | false | Edge | EdgeConstraint | -| codeql-go-tests/function.Graph.ShortestPath | 0 | true | Node | NodeConstraint | -| codeql-go-tests/function.Graph.ShortestPath | 1 | true | Edge | EdgeConstraint | -| codeql-go-tests/function.List | 0 | false | T | interface { } | -| codeql-go-tests/function.List.MyLen | 0 | true | U | interface { } | -| codeql-go-tests/function.New | 0 | false | Node | NodeConstraint | -| codeql-go-tests/function.New | 1 | false | Edge | EdgeConstraint | -| codeql-go-tests/function.NodeConstraint | 0 | false | Edge | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 0 | false | _ | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 1 | false | _ | interface { string } | -| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 2 | false | _ | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType | 0 | false | _ | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType | 1 | false | _ | interface { string } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType | 2 | false | _ | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 0 | true | _ | interface { } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 1 | true | _ | interface { string } | -| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 2 | true | _ | interface { } | +| codeql-go-tests/function.EdgeConstraint | 0 | | Node | interface { } | +| codeql-go-tests/function.Element | 0 | | S | interface { } | +| codeql-go-tests/function.GenericFunctionInAnotherFile | 0 | | T | interface { } | +| codeql-go-tests/function.GenericFunctionOneTypeParam | 0 | | T | interface { } | +| codeql-go-tests/function.GenericFunctionTwoTypeParams | 0 | | K | comparable | +| codeql-go-tests/function.GenericFunctionTwoTypeParams | 1 | | V | interface { int64 \| float64 } | +| codeql-go-tests/function.GenericStruct1 | 0 | | T | interface { } | +| codeql-go-tests/function.GenericStruct1.f1 | 0 | from receiver | TF1 | interface { } | +| codeql-go-tests/function.GenericStruct1.g1 | 0 | from receiver | TG1 | interface { } | +| codeql-go-tests/function.GenericStruct2 | 0 | | S | interface { } | +| codeql-go-tests/function.GenericStruct2 | 1 | | T | interface { } | +| codeql-go-tests/function.GenericStruct2.f2 | 0 | from receiver | SF2 | interface { } | +| codeql-go-tests/function.GenericStruct2.f2 | 1 | from receiver | TF2 | interface { } | +| codeql-go-tests/function.GenericStruct2.g2 | 0 | from receiver | SG2 | interface { } | +| codeql-go-tests/function.GenericStruct2.g2 | 1 | from receiver | TG2 | interface { } | +| codeql-go-tests/function.Graph | 0 | | Node | NodeConstraint | +| codeql-go-tests/function.Graph | 1 | | Edge | EdgeConstraint | +| codeql-go-tests/function.Graph.ShortestPath | 0 | from receiver | Node | NodeConstraint | +| codeql-go-tests/function.Graph.ShortestPath | 1 | from receiver | Edge | EdgeConstraint | +| codeql-go-tests/function.List | 0 | | T | interface { } | +| codeql-go-tests/function.List.MyLen | 0 | from receiver | U | interface { } | +| codeql-go-tests/function.New | 0 | | Node | NodeConstraint | +| codeql-go-tests/function.New | 1 | | Edge | EdgeConstraint | +| codeql-go-tests/function.NodeConstraint | 0 | | Edge | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 0 | | _ | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 1 | | _ | interface { string } | +| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 2 | | _ | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType | 0 | | _ | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType | 1 | | _ | interface { string } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType | 2 | | _ | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 0 | from receiver | _ | interface { } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 1 | from receiver | _ | interface { string } | +| codeql-go-tests/function.multipleAnonymousTypeParamsType.f | 2 | from receiver | _ | interface { } | diff --git a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql index 365f58493f1b..323defa04934 100644 --- a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql +++ b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql @@ -5,11 +5,11 @@ query predicate numberOfTypeParameters(TypeParamParentEntity parent, int n) { n = strictcount(TypeParamType tpt | tpt.getParent() = parent) } -from TypeParamType tpt, TypeParamParentEntity ty +from TypeParamType tpt, TypeParamParentEntity ty, string fr where ty = tpt.getParent() and // Note that we cannot use the location of `tpt` itself as we currently fail // to extract an object for type parameters for methods on generic structs. - exists(ty.getLocation()) -select ty.getQualifiedName(), tpt.getIndex(), tpt.isFromReceiver(), tpt.getParamName(), - tpt.getConstraint().pp() + exists(ty.getLocation()) and + if tpt.isFromReceiver() then fr = "from receiver" else fr = "" +select ty.getQualifiedName(), tpt.getIndex(), fr, tpt.getParamName(), tpt.getConstraint().pp() From a8885aeebd465ec65db7913b452d44a8179f9d00 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 13 Jul 2026 15:18:38 +0200 Subject: [PATCH 28/39] C#: Remove the HttpRequest.RawUrl barrier model. --- csharp/ql/lib/ext/System.Web.model.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/csharp/ql/lib/ext/System.Web.model.yml b/csharp/ql/lib/ext/System.Web.model.yml index 63c539fbe5ec..9e24158bc092 100644 --- a/csharp/ql/lib/ext/System.Web.model.yml +++ b/csharp/ql/lib/ext/System.Web.model.yml @@ -1,10 +1,4 @@ extensions: - - addsTo: - pack: codeql/csharp-all - extensible: barrierModel - data: - # The RawUrl property is considered to be safe for URL redirects - - ["System.Web", "HttpRequest", False, "get_RawUrl", "()", "", "ReturnValue", "url-redirection", "manual"] - addsTo: pack: codeql/csharp-all extensible: sinkModel From 03e44f548fcc56c8ccf07cf4b489e8fb63f10c57 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 13 Jul 2026 15:19:04 +0200 Subject: [PATCH 29/39] C#: Update test and expected output. --- .../Security Features/CWE-601/UrlRedirect/UrlRedirect.cs | 4 ++-- .../CWE-601/UrlRedirect/UrlRedirect.expected | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.cs b/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.cs index 498b004505e3..efb190365337 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.cs +++ b/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.cs @@ -38,8 +38,8 @@ public void ProcessRequest(HttpContext ctx) ctx.Response.AddHeader("Location", ctx.Request.QueryString["page"]); // $ Alert=r3 Alert=r3 ctx.Response.AppendHeader("Location", ctx.Request.QueryString["page"]); // $ Alert=r4 Alert=r4 - // GOOD: Redirecting to the RawUrl only reloads the current Url - ctx.Response.Redirect(ctx.Request.RawUrl); + // BAD: The RawUrl contains the un-normalized request line. + ctx.Response.Redirect(ctx.Request.RawUrl); // $ Alert // GOOD: The attacker can only control the parameters, not the location ctx.Response.Redirect("foo.asp?param=" + url); diff --git a/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected b/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected index e7fced7fde3f..b5ff8e88234a 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-601/UrlRedirect/UrlRedirect.expected @@ -3,6 +3,7 @@ | UrlRedirect.cs:13:31:13:61 | access to indexer | UrlRedirect.cs:13:31:13:53 | access to property QueryString : NameValueCollection | UrlRedirect.cs:13:31:13:61 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:13:31:13:53 | access to property QueryString | user-provided value | | UrlRedirect.cs:38:44:38:74 | access to indexer | UrlRedirect.cs:38:44:38:66 | access to property QueryString : NameValueCollection | UrlRedirect.cs:38:44:38:74 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:38:44:38:66 | access to property QueryString | user-provided value | | UrlRedirect.cs:39:47:39:77 | access to indexer | UrlRedirect.cs:39:47:39:69 | access to property QueryString : NameValueCollection | UrlRedirect.cs:39:47:39:77 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:39:47:39:69 | access to property QueryString | user-provided value | +| UrlRedirect.cs:42:31:42:48 | access to property RawUrl | UrlRedirect.cs:42:31:42:48 | access to property RawUrl | UrlRedirect.cs:42:31:42:48 | access to property RawUrl | Untrusted URL redirection due to $@. | UrlRedirect.cs:42:31:42:48 | access to property RawUrl | user-provided value | | UrlRedirect.cs:48:29:48:31 | access to local variable url | UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:48:29:48:31 | access to local variable url | Untrusted URL redirection due to $@. | UrlRedirect.cs:23:22:23:44 | access to property QueryString | user-provided value | | UrlRedirect.cs:64:31:64:52 | $"..." | UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:64:31:64:52 | $"..." | Untrusted URL redirection due to $@. | UrlRedirect.cs:23:22:23:44 | access to property QueryString | user-provided value | | UrlRedirect.cs:70:31:70:69 | call to method Format | UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:70:31:70:69 | call to method Format | Untrusted URL redirection due to $@. | UrlRedirect.cs:23:22:23:44 | access to property QueryString | user-provided value | @@ -66,6 +67,7 @@ nodes | UrlRedirect.cs:38:44:38:74 | access to indexer | semmle.label | access to indexer | | UrlRedirect.cs:39:47:39:69 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | | UrlRedirect.cs:39:47:39:77 | access to indexer | semmle.label | access to indexer | +| UrlRedirect.cs:42:31:42:48 | access to property RawUrl | semmle.label | access to property RawUrl | | UrlRedirect.cs:48:29:48:31 | access to local variable url | semmle.label | access to local variable url | | UrlRedirect.cs:64:31:64:52 | $"..." | semmle.label | $"..." | | UrlRedirect.cs:70:31:70:69 | call to method Format | semmle.label | call to method Format | From ae06b778e5203ee3773b987c34bfa151bce6727a Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 13 Jul 2026 15:29:18 +0200 Subject: [PATCH 30/39] C#: Add change-note. --- csharp/ql/src/change-notes/2026-07-13-CWE-601.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 csharp/ql/src/change-notes/2026-07-13-CWE-601.md diff --git a/csharp/ql/src/change-notes/2026-07-13-CWE-601.md b/csharp/ql/src/change-notes/2026-07-13-CWE-601.md new file mode 100644 index 000000000000..237ee562a529 --- /dev/null +++ b/csharp/ql/src/change-notes/2026-07-13-CWE-601.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* `System.Web.HttpRequest.RawUrl` is no longer treated as a sanitizer for `cs/web/unvalidated-url-redirection`, since it contains the un-normalized request line. This may lead to more results. From 2d0095826b8082e43b613c08779119dffe18b460 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Mon, 13 Jul 2026 19:40:31 +0100 Subject: [PATCH 31/39] Address review comment --- .../code/java/security/PathSanitizer.qll | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/PathSanitizer.qll b/java/ql/lib/semmle/code/java/security/PathSanitizer.qll index 7c428a6691cd..b4344c50cea3 100644 --- a/java/ql/lib/semmle/code/java/security/PathSanitizer.qll +++ b/java/ql/lib/semmle/code/java/security/PathSanitizer.qll @@ -108,17 +108,19 @@ private class FileGetNameCall extends MethodCall { */ private predicate dotDotCheckGuard(Guard g, Expr e, boolean branch) { pathTraversalGuard(g, e, branch) and - ( - exists(Guard previousGuard | - previousGuard.(AllowedPrefixGuard).controls(g.getBasicBlock(), true) - or - previousGuard.(BlockListGuard).controls(g.getBasicBlock(), false) - ) + exists(Guard previousGuard | + previousGuard.(AllowedPrefixGuard).controls(g.getBasicBlock(), true) or - // `File.getName` strips any directory prefix, returning only the final path - // component. The only remaining path traversal risk is when that component is - // itself `..`, so a check for `..` components completes the sanitization. - exists(FileGetNameCall getName | localTaintFlowToPathGuard(getName, g)) + previousGuard.(BlockListGuard).controls(g.getBasicBlock(), false) + ) + or + // `File.getName` strips any directory prefix, returning only the final path + // component. The only remaining path traversal risk is when that component is + // itself `..`, so a check for `..` components on the result of `getName` + // completes the sanitization. + exists(FileGetNameCall getName | + pathTraversalGuard(g, getName, branch) and + TaintTracking::localExprTaint(getName, e) ) } From 09da46d8bd583d289ee9559deafbfbc339810b19 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 14 Jul 2026 10:23:43 +0200 Subject: [PATCH 32/39] Go: Address review comments --- go/extractor/extractor.go | 18 +++++++++--------- .../typeparam.ql | 8 +++++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/go/extractor/extractor.go b/go/extractor/extractor.go index f421688e52ad..8c2178376141 100644 --- a/go/extractor/extractor.go +++ b/go/extractor/extractor.go @@ -2010,11 +2010,9 @@ func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.L } func populateTypeParamParentsFromFunction(funcObj *types.Func) { - recvTypeParams := funcObj.Type().(*types.Signature).RecvTypeParams() - populateTypeParamParents(recvTypeParams, funcObj, true) - typeParams := funcObj.Type().(*types.Signature).TypeParams() - populateTypeParamParents(typeParams, funcObj, false) - + signature := funcObj.Type().(*types.Signature) + populateTypeParamParents(signature.RecvTypeParams(), funcObj, true) + populateTypeParamParents(signature.TypeParams(), funcObj, false) } // populateTypeParamParents sets `parent` as the parent of the elements of `typeparams` @@ -2053,12 +2051,14 @@ func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) (trap.Label, return parentlbl, entry.isFromReceiver } -func setTypeParamParent(tp *types.TypeParam, newobj types.Object, isFromReceiver bool) { +func setTypeParamParent(tp *types.TypeParam, parent types.Object, isFromReceiver bool) { entry, exists := typeParamParent[tp] + newEntry := typeParamParentEntry{parent, isFromReceiver} if !exists { - typeParamParent[tp] = typeParamParentEntry{newobj, isFromReceiver} - } else if entry.parent != newobj { - log.Fatalf("Parent of type parameter '%s %s' being set to a different value: '%s' vs '%s'", tp.String(), tp.Constraint().String(), entry.parent, newobj) + typeParamParent[tp] = newEntry + } else if entry != newEntry { + log.Fatalf("Parent of type parameter '%s %s' being set to a different value: {'%s', %t}' vs {'%s', %t}", + tp.String(), tp.Constraint().String(), entry.parent, entry.isFromReceiver, parent, isFromReceiver) } } diff --git a/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/typeparam.ql b/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/typeparam.ql index 041c33577f75..05ceab703446 100644 --- a/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/typeparam.ql +++ b/go/ql/lib/upgrades/b1341734d6870b105e5c9d168ce7dec25d7f72d0/typeparam.ql @@ -10,6 +10,12 @@ class TypeParamParentObject extends @typeparamparentobject { string toString() { none() } } +// In Go 1.26 and below, a type parameter is from a receiver exactly when its +// parent is a method. +boolean isFromReceiver(TypeParamParentObject parent) { + if methodreceivers(parent, _) then result = true else result = false +} + from TypeParamType tp, string name, CompositeType bound, TypeParamParentObject parent, int idx where typeparam(tp, name, bound, parent, idx) -select tp, name, bound, parent, idx, false +select tp, name, bound, parent, idx, isFromReceiver(parent) From b15c243ecad2acab4cb1ee1e2c33e4a7c2f15fa2 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 15 Jul 2026 08:36:49 +0200 Subject: [PATCH 33/39] C#: Exclude cs/useless-assignment-to-local from the code-quality suite. --- csharp/ql/src/codeql-suites/csharp-code-quality.qls | 3 +++ 1 file changed, 3 insertions(+) diff --git a/csharp/ql/src/codeql-suites/csharp-code-quality.qls b/csharp/ql/src/codeql-suites/csharp-code-quality.qls index 2074f9378cfd..26c7e7437ee5 100644 --- a/csharp/ql/src/codeql-suites/csharp-code-quality.qls +++ b/csharp/ql/src/codeql-suites/csharp-code-quality.qls @@ -1,3 +1,6 @@ - queries: . - apply: code-quality-selectors.yml from: codeql/suite-helpers +- exclude: + id: + - cs/useless-assignment-to-local From 2395053ed6e9c2a14b06986276e53f8aedca9238 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 15 Jul 2026 08:54:56 +0200 Subject: [PATCH 34/39] C#: Update integration test expected output. --- .../posix/query-suite/csharp-code-quality.qls.expected | 1 - 1 file changed, 1 deletion(-) diff --git a/csharp/ql/integration-tests/posix/query-suite/csharp-code-quality.qls.expected b/csharp/ql/integration-tests/posix/query-suite/csharp-code-quality.qls.expected index 893eaeb75607..b944b848df8e 100644 --- a/csharp/ql/integration-tests/posix/query-suite/csharp-code-quality.qls.expected +++ b/csharp/ql/integration-tests/posix/query-suite/csharp-code-quality.qls.expected @@ -22,7 +22,6 @@ ql/csharp/ql/src/Concurrency/FutileSyncOnField.ql ql/csharp/ql/src/Concurrency/LockOrder.ql ql/csharp/ql/src/Concurrency/LockThis.ql ql/csharp/ql/src/Concurrency/LockedWait.ql -ql/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql ql/csharp/ql/src/Language Abuse/CastThisToTypeParameter.ql ql/csharp/ql/src/Language Abuse/CatchOfGenericException.ql ql/csharp/ql/src/Language Abuse/DubiousDowncastOfThis.ql From cb3f8a8394825fb42b6e6b7f5e98033e4d6b04b9 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 15 Jul 2026 09:03:34 +0200 Subject: [PATCH 35/39] C#: Add change-note. --- .../2026-07-15-code-quality-useless-assignment.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 csharp/ql/src/change-notes/2026-07-15-code-quality-useless-assignment.md diff --git a/csharp/ql/src/change-notes/2026-07-15-code-quality-useless-assignment.md b/csharp/ql/src/change-notes/2026-07-15-code-quality-useless-assignment.md new file mode 100644 index 000000000000..d1fa51ea3bfd --- /dev/null +++ b/csharp/ql/src/change-notes/2026-07-15-code-quality-useless-assignment.md @@ -0,0 +1,4 @@ +--- +category: queryMetadata +--- +* The query `cs/useless-assignment-to-local` has been removed from the `code-quality` suite, but it remains in the `code-quality-extended` suite. From 574ef4d1aca5ddce03cb4167a8e524b74077f357 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Wed, 15 Jul 2026 10:17:58 +0200 Subject: [PATCH 36/39] Kotlin: Support Kotlin 2.4.10 --- docs/codeql/reusables/supported-versions-compilers.rst | 2 +- java/kotlin-extractor/dev/wrapper.py | 2 +- .../diagnostics/kotlin-version-too-new/diagnostics.expected | 2 +- java/ql/lib/change-notes/2026-07-15-kotlin-2.4.10.md | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 java/ql/lib/change-notes/2026-07-15-kotlin-2.4.10.md diff --git a/docs/codeql/reusables/supported-versions-compilers.rst b/docs/codeql/reusables/supported-versions-compilers.rst index 7a88dd035c2a..69def9ffeb19 100644 --- a/docs/codeql/reusables/supported-versions-compilers.rst +++ b/docs/codeql/reusables/supported-versions-compilers.rst @@ -21,7 +21,7 @@ Java,"Java 7 to 26 [6]_","javac (OpenJDK and Oracle JDK), Eclipse compiler for Java (ECJ) [7]_",``.java`` - Kotlin,"Kotlin 1.8.0 to 2.4.0\ *x*","kotlinc",``.kt`` + Kotlin,"Kotlin 1.8.0 to 2.4.1\ *x*","kotlinc",``.kt`` JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [8]_" Python [9]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13",Not applicable,``.py`` Ruby [10]_,"up to 3.3",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" diff --git a/java/kotlin-extractor/dev/wrapper.py b/java/kotlin-extractor/dev/wrapper.py index 34b9d6b9425e..1b29de23f766 100755 --- a/java/kotlin-extractor/dev/wrapper.py +++ b/java/kotlin-extractor/dev/wrapper.py @@ -27,7 +27,7 @@ import io import os -DEFAULT_VERSION = "2.4.0" +DEFAULT_VERSION = "2.4.10" def options(): diff --git a/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected b/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected index 33ef093cb9a2..09429027c5d6 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected +++ b/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected @@ -1,5 +1,5 @@ { - "markdownMessage": "The Kotlin version installed (`999.999.999`) is too recent for this version of CodeQL. Install a version lower than 2.4.10.", + "markdownMessage": "The Kotlin version installed (`999.999.999`) is too recent for this version of CodeQL. Install a version lower than 2.4.20.", "severity": "error", "source": { "extractorName": "java", diff --git a/java/ql/lib/change-notes/2026-07-15-kotlin-2.4.10.md b/java/ql/lib/change-notes/2026-07-15-kotlin-2.4.10.md new file mode 100644 index 000000000000..43aa417d9593 --- /dev/null +++ b/java/ql/lib/change-notes/2026-07-15-kotlin-2.4.10.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Kotlin versions up to 2.4.10 are now supported. From a701922dbd0678aba6cc091b543873c5beaef448 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 14 Jul 2026 21:58:36 +0200 Subject: [PATCH 37/39] Swift: Update to Swift 6.3.3 --- swift/ql/lib/change-notes/2026-07-15-swift-6.3.3.md | 4 ++++ swift/third_party/resources/resource-dir-linux.zip | 4 ++-- swift/third_party/resources/resource-dir-macos.zip | 4 ++-- swift/third_party/resources/swift-prebuilt-linux.tar.zst | 4 ++-- swift/third_party/resources/swift-prebuilt-macos.tar.zst | 4 ++-- 5 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 swift/ql/lib/change-notes/2026-07-15-swift-6.3.3.md diff --git a/swift/ql/lib/change-notes/2026-07-15-swift-6.3.3.md b/swift/ql/lib/change-notes/2026-07-15-swift-6.3.3.md new file mode 100644 index 000000000000..1542f2f68dc5 --- /dev/null +++ b/swift/ql/lib/change-notes/2026-07-15-swift-6.3.3.md @@ -0,0 +1,4 @@ +--- +category: majorAnalysis +--- +* Upgraded to allow analysis of Swift 6.3.3. diff --git a/swift/third_party/resources/resource-dir-linux.zip b/swift/third_party/resources/resource-dir-linux.zip index da93aefcc706..1fbff22aa74c 100644 --- a/swift/third_party/resources/resource-dir-linux.zip +++ b/swift/third_party/resources/resource-dir-linux.zip @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd132a4fb44688913eff72f94110e2745048ceda3354ba199d8338750881e0e5 -size 408312701 +oid sha256:68da3c1158d6682f09644e4557b7bdbe74e3e7110d1a64cb61ec26b3a91d0b0e +size 408306334 diff --git a/swift/third_party/resources/resource-dir-macos.zip b/swift/third_party/resources/resource-dir-macos.zip index 11ac3ddf0d7d..2a606b4771fe 100644 --- a/swift/third_party/resources/resource-dir-macos.zip +++ b/swift/third_party/resources/resource-dir-macos.zip @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd4eaa3a688849279e990da69768da796f6a130ca6e01572d022142ff09e4868 -size 548155566 +oid sha256:da5cbdd44ee4728606f2dc0dd0718e3c7c8748ae1ec8d6261365282d54ba8db9 +size 548170081 diff --git a/swift/third_party/resources/swift-prebuilt-linux.tar.zst b/swift/third_party/resources/swift-prebuilt-linux.tar.zst index 2adf23cda687..95eabd7a2a9d 100644 --- a/swift/third_party/resources/swift-prebuilt-linux.tar.zst +++ b/swift/third_party/resources/swift-prebuilt-linux.tar.zst @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e00f464ad0b793c8e14df844aecbeec5cdd587e8f34a85915593a470dba1beda -size 143508114 +oid sha256:e79d3f333cbbbeb39c226f5ea605f6a88f4f32f83e38b8c0a082d18d9caf095d +size 143468582 diff --git a/swift/third_party/resources/swift-prebuilt-macos.tar.zst b/swift/third_party/resources/swift-prebuilt-macos.tar.zst index f63c93f1caa9..1914d1048a02 100644 --- a/swift/third_party/resources/swift-prebuilt-macos.tar.zst +++ b/swift/third_party/resources/swift-prebuilt-macos.tar.zst @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:212229f7f0545aab03e3034f55d13a665bc568633a5defc72a662c83a06e90cc -size 125133134 +oid sha256:f5fddabc442a5c7664be4f022f09889177f420477060c1199eb714394987ad93 +size 125119625 From e9e360ae445c4a7cbaf00d006db7867fef2128bc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 16 Jul 2026 11:18:13 +0000 Subject: [PATCH 38/39] update codeql documentation --- .../codeql-changelog/codeql-cli-2.26.1.rst | 98 +++++++++++++++++++ .../codeql-changelog/index.rst | 1 + 2 files changed, 99 insertions(+) create mode 100644 docs/codeql/codeql-overview/codeql-changelog/codeql-cli-2.26.1.rst diff --git a/docs/codeql/codeql-overview/codeql-changelog/codeql-cli-2.26.1.rst b/docs/codeql/codeql-overview/codeql-changelog/codeql-cli-2.26.1.rst new file mode 100644 index 000000000000..47d9bf781482 --- /dev/null +++ b/docs/codeql/codeql-overview/codeql-changelog/codeql-cli-2.26.1.rst @@ -0,0 +1,98 @@ +.. _codeql-cli-2.26.1: + +========================== +CodeQL 2.26.1 (2026-07-15) +========================== + +.. contents:: Contents + :depth: 2 + :local: + :backlinks: none + +This is an overview of changes in the CodeQL CLI and relevant CodeQL query and library packs. For additional updates on changes to the CodeQL code scanning experience, check out the `code scanning section on the GitHub blog `__, `relevant GitHub Changelog updates `__, `changes in the CodeQL extension for Visual Studio Code `__, and the `CodeQL Action changelog `__. + +Security Coverage +----------------- + +CodeQL 2.26.1 runs a total of 497 security queries when configured with the Default suite (covering 170 CWE). The Extended suite enables an additional 131 queries (covering 32 more CWE). + +CodeQL CLI +---------- + +There are no user-facing CLI changes in this release. + +Query Packs +----------- + +Minor Analysis Improvements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Rust +"""" + +* The :code:`rust/hard-coded-cryptographic-value` query now treats arithmetic and bitwise operations, including string append operations, as barriers. This addresses false positive results where hard-coded constants are combined with non-constant data, such as incrementing a nonce or appending variable data to a constant prefix. + +Query Metadata Changes +~~~~~~~~~~~~~~~~~~~~~~ + +C/C++ +""""" + +* Added the tags :code:`external/cwe/cwe-073` and :code:`external/cwe/cwe-078` to :code:`cpp/uncontrolled-process-operation`. + +C# +"" + +* Added the tag :code:`external/cwe/cwe-073` to :code:`cs/assembly-path-injection`. + +Language Libraries +------------------ + +Breaking Changes +~~~~~~~~~~~~~~~~ + +C/C++ +""""" + +* Removed support for using variables as sources and sinks in models-as-data. Users of this feature should convert such sources and sinks to models defined using the QL language. + +Major Analysis Improvements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +C# +"" + +* Simplified and streamlined the use of NuGet sources when downloading dependencies via :code:`[mono] nuget.exe` in :code:`build-mode: none`\ : NuGet sources are now supplied via the :code:`-Source` flag instead of moving or creating :code:`nuget.config` files in the checked-out repository, private registries are used if configured, and only reachable feeds are used when NuGet feed checking is enabled (the default). + +Minor Analysis Improvements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Golang +"""""" + +* Improved models for the :code:`log/slog` package (Go 1.21+), including :code:`*slog.Logger` methods, :code:`With`\ /\ :code:`WithGroup`, and :code:`Attr`\ /\ :code:`Value` helpers, improving coverage for the :code:`go/log-injection` and :code:`go/clear-text-logging` queries. + +Java/Kotlin +""""""""""" + +* Regular expression checks via annotation with :code:`@javax.validation.constraints.Pattern` are now recognized as sanitizers for :code:`java/path-injection`. +* Added summary and LLM-generated source and sink models for :code:`org.apache.poi`. +* The first argument of the :code:`uri` method of :code:`WebClient$UriSpec` in :code:`org.springframework.web.reactive.function.client` is now considered a request forgery sink. Previously only the first arguments of the :code:`WebClient.create` and :code:`WebClient$Builder.baseUrl` methods were considered. This may lead to more alerts for the query :code:`java/ssrf` (Server-side request forgery). + +JavaScript/TypeScript +""""""""""""""""""""" + +* Added support for Angular's :code:`@HostListener('window:message', ...)` and :code:`@HostListener('document:message', ...)` decorators as :code:`postMessage` event handlers. The decorated method's event parameter is now recognized as a client-side remote flow source, and is considered by the :code:`js/missing-origin-check` query. + +Python +"""""" + +* :code:`Flask::FlaskApp::instance()` will now also return instances of subclasses defined in the source tree. Previously, these were filtered out. :code:`Flask::FlaskApp::classRef()` has been deprecated in favor of :code:`Flask::FlaskApp::subclassRef()` since it already returned some subclasses. + +Deprecated APIs +~~~~~~~~~~~~~~~ + +C/C++ +""""" + +* Models-as-data flow summaries now use fully qualified field names (for example, :code:`MyNamespace::MyStruct::myField`) instead of unqualified field names such as :code:`myField`. We recommend updating existing flow summaries to use fully qualified field names. Unqualified field names are still supported, but that support will be removed in a future release. diff --git a/docs/codeql/codeql-overview/codeql-changelog/index.rst b/docs/codeql/codeql-overview/codeql-changelog/index.rst index 301adddeedbc..837fa757368d 100644 --- a/docs/codeql/codeql-overview/codeql-changelog/index.rst +++ b/docs/codeql/codeql-overview/codeql-changelog/index.rst @@ -11,6 +11,7 @@ A list of queries for each suite and language `is available here Date: Fri, 17 Jul 2026 17:22:11 +0200 Subject: [PATCH 39/39] Shared: Add missing QLdoc --- shared/util/codeql/util/Strings.qll | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/util/codeql/util/Strings.qll b/shared/util/codeql/util/Strings.qll index c82c23a9988b..b032bb73672b 100644 --- a/shared/util/codeql/util/Strings.qll +++ b/shared/util/codeql/util/Strings.qll @@ -1,3 +1,4 @@ +/** Provides predicates for working with strings. */ overlay[local?] module;