dhscanner-kbgen 2.1.1 → 2.1.2
raw patch · 2 files changed
+1613/−1612 lines, 2 filesdep ~dhscanner-astdep ~dhscanner-bitcodePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: dhscanner-ast, dhscanner-bitcode
API changes (from Hackage documentation)
+ Kbgen: Annotation :: Location -> Annotation
+ Kbgen: CallableAnnotation :: Callable -> Annotation -> CallableAnnotation
+ Kbgen: ClassAnnotation :: Class -> Annotation -> ClassAnnotation
+ Kbgen: data Annotation
Files
- dhscanner-kbgen.cabal +99/−99
- src/Kbgen.hs +1514/−1513
dhscanner-kbgen.cabal view
@@ -1,99 +1,99 @@-cabal-version: 3.12 -name: dhscanner-kbgen -category: parsing -synopsis: knowledge base predicates for static code analysis -description: - - * The [knowledge base](https://en.wikipedia.org/wiki/Knowledge_representation_and_reasoning) ( kb ) - aims to be a data structure able to: - - * represent /multiple/ facts about the source code repo - * from /various/ programming languages - * each fact can be translated to a [Prolog](https://en.wikipedia.org/wiki/Prolog) fact - * facts can be combined to create [predicates](https://en.wikipedia.org/wiki/Predicate_%28logic%29) - * predictaes can be combined to formulate /security queries/ - - * [Prolog](https://en.wikipedia.org/wiki/Prolog) facts describe relations between: - - * code locations - * const strings - * const integers - - * [Prolog](https://en.wikipedia.org/wiki/Prolog) facts can be /combined/: - - * create /expressive/ queries from /simple/ facts - * [conjunction](https://en.wikipedia.org/wiki/Logical_conjunction) - * [disjunction](https://en.wikipedia.org/wiki/Logical_disjunction) - * [negation](https://en.wikipedia.org/wiki/Negation) - - * [Prolog](https://en.wikipedia.org/wiki/Prolog) queries are /easy/ to write: - - * you /don't/ have to be a Prolog expert - * copy-paste the basic facts to /any/ [LLM](https://en.wikipedia.org/wiki/Large_language_model) - * explain in plain English your query's purpose - * et voilà ! - -version: 2.1.1 -license: GPL-3.0-only -license-file: LICENSE -author: OrenGitHub -maintainer: Oren Ish Shalom -copyright: (c) 2025 Oren Ish Shalom -homepage: https://github.com/OrenGitHub/dhscanner -stability: experimental -build-type: Simple - -common ghc_options - - ghc-options: -Wall -Werror=incomplete-patterns -O2 - -library - - import: - ghc_options - - exposed-modules: - Kbgen - - build-depends: - aeson >= 2.2 && < 2.4, - attoparsec >= 0.14 && < 0.15, - filepath >= 1.4 && < 1.6, - base >= 4.22 && < 4.23, - containers >= 0.8 && < 0.9, - text >= 2.0 && < 2.2, - dhscanner-ast >= 1.3.1 && < 1.4, - dhscanner-bitcode >= 2.1.1 && < 2.2 - - hs-source-dirs: - src - - default-language: - GHC2024 - -test-suite restoreloc - - import: - ghc_options - - type: - exitcode-stdio-1.0 - - hs-source-dirs: - test - - main-is: - Test.hs - - other-modules: - SmokeTests - - build-depends: - base, - dhscanner-kbgen, - dhscanner-ast, - hspec, - QuickCheck - - default-language: - GHC2024 +cabal-version: 3.12+name: dhscanner-kbgen+category: parsing+synopsis: knowledge base predicates for static code analysis+description:++ * The [knowledge base](https://en.wikipedia.org/wiki/Knowledge_representation_and_reasoning) ( kb )+ aims to be a data structure able to:++ * represent /multiple/ facts about the source code repo+ * from /various/ programming languages+ * each fact can be translated to a [Prolog](https://en.wikipedia.org/wiki/Prolog) fact+ * facts can be combined to create [predicates](https://en.wikipedia.org/wiki/Predicate_%28logic%29)+ * predictaes can be combined to formulate /security queries/++ * [Prolog](https://en.wikipedia.org/wiki/Prolog) facts describe relations between:++ * code locations+ * const strings+ * const integers++ * [Prolog](https://en.wikipedia.org/wiki/Prolog) facts can be /combined/:++ * create /expressive/ queries from /simple/ facts+ * [conjunction](https://en.wikipedia.org/wiki/Logical_conjunction)+ * [disjunction](https://en.wikipedia.org/wiki/Logical_disjunction)+ * [negation](https://en.wikipedia.org/wiki/Negation)++ * [Prolog](https://en.wikipedia.org/wiki/Prolog) queries are /easy/ to write:++ * you /don't/ have to be a Prolog expert+ * copy-paste the basic facts to /any/ [LLM](https://en.wikipedia.org/wiki/Large_language_model)+ * explain in plain English your query's purpose+ * et voilà !++version: 2.1.2+license: GPL-3.0-only+license-file: LICENSE+author: OrenGitHub+maintainer: Oren Ish Shalom+copyright: (c) 2025 Oren Ish Shalom+homepage: https://github.com/OrenGitHub/dhscanner+stability: experimental+build-type: Simple++common ghc_options++ ghc-options: -Wall -Werror=incomplete-patterns -O2++library++ import:+ ghc_options++ exposed-modules:+ Kbgen++ build-depends:+ aeson >= 2.2 && < 2.4,+ attoparsec >= 0.14 && < 0.15,+ filepath >= 1.4 && < 1.6,+ base >= 4.22 && < 4.23,+ containers >= 0.8 && < 0.9,+ text >= 2.0 && < 2.2,+ dhscanner-ast >= 1.3.1 && < 1.5,+ dhscanner-bitcode >= 2.1.1 && < 2.3++ hs-source-dirs:+ src++ default-language:+ GHC2024++test-suite restoreloc++ import:+ ghc_options++ type:+ exitcode-stdio-1.0++ hs-source-dirs:+ test++ main-is:+ Test.hs++ other-modules:+ SmokeTests++ build-depends:+ base,+ dhscanner-kbgen,+ dhscanner-ast,+ hspec,+ QuickCheck++ default-language:+ GHC2024
src/Kbgen.hs view
@@ -1,1514 +1,1515 @@--- | --- --- * The [knowledge base](https://en.wikipedia.org/wiki/Knowledge_representation_and_reasoning) ( kb ) --- aims to be a data structure able to: --- --- * represent /multiple/ facts about the source code repo --- * from /various/ programming languages --- * each fact can be translated to a [Prolog](https://en.wikipedia.org/wiki/Prolog) fact --- * facts can be combined to create [predicates](https://en.wikipedia.org/wiki/Predicate_%28logic%29) --- * [predicates](https://en.wikipedia.org/wiki/Predicate_%28logic%29) can be combined to formulate /security queries/ --- --- * Facts describe relations between: --- --- * code locations --- * const strings --- * const integers --- --- * Code locations are capable of representing /all/ program entities --- --- * classes --- * methods --- * lambdas --- * annotations --- * arguments --- * parameters --- * etc. --- --- * Facts can be /combined/: --- --- * [conjunction](https://en.wikipedia.org/wiki/Logical_conjunction) --- * [disjunction](https://en.wikipedia.org/wiki/Logical_disjunction) --- * [negation](https://en.wikipedia.org/wiki/Negation) --- --- * [Prolog](https://en.wikipedia.org/wiki/Prolog) queries are /easy/ to write: --- --- * you /don't/ have to be a Prolog expert --- * copy-paste the basic facts to /any/ [LLM](https://en.wikipedia.org/wiki/Large_language_model) --- * explain in plain English your query's purpose --- * et voilà ! --- --- * Its main purpose is to serve as the: --- --- * penultimate step for /static code analysis/ --- * part of the [dhscanner](https://github.com/OrenGitHub/dhscanner.vps) framework --- --- * [SAST](https://en.wikipedia.org/wiki/Static_application_security_testing) for security scans 🔒 --- * [PII](https://en.wikipedia.org/wiki/Personal_data) leaks detection 🪪 --- - -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE OverloadedStrings #-} - -module Kbgen ( - KeywordArgForCall(..), - ParamResolvedType(..), - ClassDef(..), - FuncDef(..), - Call1stPartyFuncDefinedInDir(..), - AssignValueToToplevelVarName(..), - Call1stPartyFuncDefinedInFile(..), - ParamName(..), - CallMethodOfClass(..), - ConstString(..), - ConstInteger(..), - MethodOfClass(..), - ClassHas1stPartySuper(..), - ClassHas3rdPartySuper(..), - CallMethodOfUntypedNamedParam(..), - ArgiForCall(..), - DataflowEdge(..), - ConstBoolTrue(..), - ConstNull(..), - GatedReturn(..), - Comparison(..), - CallableReturnsValue(..), - CallableReturnsWithoutValue(..), - Cond(..), - Lhs(..), - Rhs(..), - ComparisonOp(..), - ReturnedValue(..), - ClassAnnotation, - CallableAnnotation, - CallableSourceBodyLength(..), - ParamiOfCallable(..), - prologify, - To(..), - Arg(..), - From(..), - Func(..), - Call(..), - Class(..), - Param(..), - Method(..), - Callable(..), - Keyword(..), - Resolved(..), - ArgIndex(..), - ParamIndex(..), - FuncName(..), - MethodName(..), - ResolvedType(..), - CallResolved(..), - CalledFrom(..), - AssignedValue(..), - ResolvedSuper(..), - FuncDefinedInDir(..), - FuncDefinedInFile(..), - ClassDefinedInFile(..), - SuperDefinedInFile(..), - SuperQualifiedName(..), - ConstStr(..), - ConstInt(..), - Fact(..), - locationify, - restoreloc -) - -where - --- general imports -import Data.Aeson -import Text.Printf -import GHC.Generics (Generic) -import System.FilePath (takeDirectory) -import Data.Attoparsec.Text (Parser, char, decimal, parseOnly, string, takeText) -import Data.Text (Text) -import qualified Data.Text as T - --- general qualified imports -import qualified Data.List as List - --- project imports -import Location -import qualified Token -import qualified Fqn - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_keyword_arg_for_call( Keyword, Arg, Call ). --- @ --- --- __When should I use this fact__ ( motivation: [CVE-2024-52803](https://nvd.nist.gov/vuln/detail/CVE-2024-52803) ) --- --- Code snippet ( Python ): --- --- @ --- Popen(f'llamafactory-cli train {save_cmd(args)}', env=env, shell=True) --- @ --- --- See complete source example: [here](https://github.com/hiyouga/LLaMA-Factory/commit/b3aa80d54a67da45e9e237e349486fb9c162b2ac) --- --- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-52803](https://nvd.nist.gov/vuln/detail/CVE-2024-52803) ) --- --- @ --- subprocess_Popen_called_with_shell_eq_True( Call ) :- --- kb_keyword_arg_for_call( \'shell\', TrueValue, Call ), --- kb_call_resolved( Call, \'subprocess.Popen\' ), --- kb_const_bool_true( TrueValue ). --- @ --- --- Other facts combined in this example predicate: --- --- * 'CallResolved' --- * 'ConstBoolTrue' --- -data KeywordArgForCall = KeywordArgForCall - Keyword -- ^ - Arg -- ^ - Call -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_param_has_resolved_type( Param, ResolvedType ). --- @ --- --- __When should I use this fact__ ( motivation: [CVE-2024-49380](https://nvd.nist.gov/vuln/detail/CVE-2024-49380) ) --- --- Code snippet ( Golang ): --- --- @ --- func postLocal(w http.ResponseWriter, r *http.Request) { ... } --- @ --- --- See complete source example: [here](https://github.com/plentico/plenti/blob/01825e0dcd3505fac57adc2edf29f772d585c008/cmd/serve.go#L205) --- --- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-49380](https://nvd.nist.gov/vuln/detail/CVE-2024-49380) ) --- --- @ --- looks_like_user_input( RequestParam ) :- --- kb_param_has_resolved_type( RequestParam, \'net/http.Request\' ), --- kb_param_has_resolved_type( ResponseParam, \'net/http.ResponseWriter\' ), --- kb_param_i_of_callable( ResponseParam, 0, Handler ), --- kb_param_i_of_callable( RequestParam, 1, Handler ). --- @ --- --- Other facts combined in this predicate: --- --- * 'ParamiOfCallable' --- -data ParamResolvedType = ParamResolvedType - Param -- ^ - ResolvedType -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_class_def( Class, Name, DefinedInFile ). --- @ --- --- __When should I use this fact__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) ) --- --- @ --- # authentication.py --- class LoginHandler(BaseHandler): ... --- --- # index.py --- from tornado.web import RequestHandler --- class BaseHandler(RequestHandler): ... --- @ --- --- See complete source example [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/authentication.py#L10), --- [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L35) --- and [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L15) --- --- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) ) --- --- @ --- skip_level_subclass_of( Subclass, \'tornado.web.RequestHandler\' ) :- --- kb_class_def( Subclass, _, _ ), --- kb_class_has_1st_party_super( Subclass, ClassName, ClassDefinedInFile ), --- kb_class_def( Class, ClassName, ClassDefinedInFile ) --- kb_class_has_3rd_party_super( Class, _, \'tornado.web.RequestHandler\' ). --- @ --- --- Other facts combined in this predicate: --- --- * 'ClassHas1stPartySuper' --- * 'ClassHas3rdPartySuper' --- -data ClassDef = ClassDef - Class -- ^ - Token.ClassName -- ^ - ClassDefinedInFile -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_func_def( Func, Name, DefinedInFile, DefinedInDir ). --- @ --- --- __When should I use this fact__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) ) --- --- @ --- # http_getter.go --- g.GET("/get/httpmeta", func(c echo.Context) error { --- urlStr := c.QueryParam("url") --- ... --- getter.GetHTMLMeta(urlStr) --- --- # html_meta.go --- func GetHTMLMeta(urlStr string) (*HTMLMeta, error) { --- ... --- http.Get(urlStr) --- @ --- --- See complete source example [here](https://github.com/usememos/memos/blob/v0.14.0/api/v1/http_getter.go#L23), --- and [here](https://github.com/usememos/memos/blob/v0.14.0/plugin/http-getter/html_meta.go#L24) --- --- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) ) --- --- @ --- suspected_ssrf_sink( Func ) :- --- kb_resolved_call( Call, \'net/http.Get\' ), --- kb_arg_i_for_call( Arg, 0, Call ), --- utils_dataflow_path( Param, Arg ), --- kb_param_has_resolved_type( Param, \'string\' ), --- kb_param_i_of_callable( Param, _, Func ), --- kb_func_def( Func, Name, _, FuncDefinedInDir ), --- kb_call_1st_party_func_defined_in_dir( _, Name, FuncDefinedInDir ). --- @ --- --- Other facts combined in this predicate: --- --- * 'CallResolved' --- * 'ArgiForCall' --- * 'ParamResolvedType' --- * 'ParamiOfCallable' --- * 'Call1stPartyFuncDefinedInDir' --- -data FuncDef = FuncDef - Func -- ^ - Token.FuncName -- ^ - FuncDefinedInFile -- ^ - FuncDefinedInDir -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_call_1st_party_func_defined_in_dir( Call, Name, DefinedInDir ). --- @ --- --- __When should I use this fact__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) ) --- --- @ --- # http_getter.go --- g.GET("/get/httpmeta", func(c echo.Context) error { --- urlStr := c.QueryParam("url") --- ... --- getter.GetHTMLMeta(urlStr) --- --- # html_meta.go --- func GetHTMLMeta(urlStr string) (*HTMLMeta, error) { --- ... --- http.Get(urlStr) --- @ --- --- See complete source example [here](https://github.com/usememos/memos/blob/v0.14.0/api/v1/http_getter.go#L23), --- and [here](https://github.com/usememos/memos/blob/v0.14.0/plugin/http-getter/html_meta.go#L24) --- --- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) ) --- --- @ --- suspected_ssrf_sink( Func ) :- --- kb_resolved_call( Call, \'net/http.Get\' ), --- kb_arg_i_for_call( Arg, 0, Call ), --- utils_dataflow_path( Param, Arg ), --- kb_param_has_resolved_type( Param, \'string\' ), --- kb_param_i_of_callable( Param, _, Func ), --- kb_func_def( Func, Name, _, FuncDefinedInDir ), --- kb_call_1st_party_func_from_dir( _, Name, FuncDefinedInDir ). --- @ --- --- Other facts combined in this predicate: --- --- * 'CallResolved' --- * 'ArgiForCall' --- * 'ParamResolvedType' --- * 'ParamiOfCallable' --- -data Call1stPartyFuncDefinedInDir = Call1stPartyFuncDefinedInDir - Call -- ^ - FuncName -- ^ - FuncDefinedInDir -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - -data Call1stPartyFuncDefinedInFile = Call1stPartyFuncDefinedInFile - Call -- ^ - FuncName -- ^ - FuncDefinedInFile -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - -data AssignValueToToplevelVarName = AssignValueToToplevelVarName - AssignedValue -- ^ - Token.VarName -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Description:__ --- --- Input parameter of some callable ( method, function, lambda ) has the specified name --- --- __Prolog signature:__ --- --- @ --- kb_param_has_name( Param, Name ). --- @ --- --- __Code Example ( Javascript ):__ --- --- Motivated by [GHSL-2023-203](https://github.com/advplyr/audiobookshelf/security/advisories/GHSA-mgj7-rfx8-vhpr#event-116560) --- ( [source code](https://github.com/advplyr/audiobookshelf/blob/d7b2476473ef1934eedec41425837cddf2d4b13e/server/controllers/AuthorController.js#L66) --- and see also [here](https://github.com/advplyr/audiobookshelf/blob/d7b2476473ef1934eedec41425837cddf2d4b13e/server/controllers/AuthorController.js#L74) --- and [here](https://github.com/advplyr/audiobookshelf/blob/d7b2476473ef1934eedec41425837cddf2d4b13e/server/routers/ApiRouter.js#L201) ) --- --- @ --- // ApiRouter.js --- const express = require(\'express\') --- --- class ApiRouter { --- constructor(Server) { this.router = express() } --- init() { this.router.patch(\'\/authors\/:id', ... , AuthorController.update.bind(this)) } --- --- // AuthorController.js --- class AuthorController { async update(req, res) { ... } } --- @ --- --- __Writing new predicates:__ --- --- Facts combined in this predicate: --- --- * 'ParamName' --- * 'ParamiOfCallable' --- --- @ --- http_request_param_of_method( Param, Method ) :- --- kb_param_name( Param, \'req\' ), --- kb_param_name( SecondParam, \'res\' ), --- kb_param_i_of_callable( Param, 0, Method ), --- kb_param_i_of_callable( SecondParam, 1, Method ). --- @ --- --- Facts combined in this predicate: --- --- * 'ArgiForCall' --- * 'ClassName' --- * 'MethodName' --- --- @ --- request_handler( Method ) :- --- kb_resolved_call( Call, \'express.patch\' ), --- kb_arg_i_for_call( RequestHandler, 2, Call ), --- kb_resolved_part_i_call( RequestHandler, 0, ClassName ), --- kb_resolved_part_i_call( RequestHandler, 1, MethodName ), --- kb_method_of_class( Method, Class ), --- kb_method_name( Method, MethodName ), --- kb_class_name( Class, ClassName ). --- @ --- --- -data ParamName = ParamName - Param -- ^ - Token.ParamName -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_class_has_1st_party_super( Class, SuperName, SuperDefinedInFile ). --- @ --- --- __When should I use this fact__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) ) --- --- @ --- # authentication.py --- class LoginHandler(BaseHandler): ... --- --- # index.py --- from tornado.web import RequestHandler --- class BaseHandler(RequestHandler): ... --- @ --- --- See complete source example [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/authentication.py#L10), --- [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L35) --- and [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L15) --- --- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) ) --- --- @ --- skip_level_subclass_of( Subclass, \'tornado.web.RequestHandler\' ) :- --- kb_class_def( Subclass, _, _ ), --- kb_class_has_1st_party_super( Subclass, ClassName, ClassDefinedInFile ), --- kb_class_def( Class, ClassName, ClassDefinedInFile ) --- kb_class_has_3rd_party_super( Class, _, \'tornado.web.RequestHandler\' ). --- @ --- --- Other facts combined in this predicate: --- --- * 'ClassDef' --- * 'ClassHas3rdPartySuper' --- -data ClassHas1stPartySuper = ClassHas1stPartySuper - Class -- ^ - Token.SuperName -- ^ - SuperDefinedInFile -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_class_has_3rd_party_super( Class, SuperName, SuperQualifiedName ). --- @ --- --- __When should I use this fact__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) ) --- --- @ --- # authentication.py --- class LoginHandler(BaseHandler): ... --- --- # index.py --- from tornado.web import RequestHandler --- class BaseHandler(RequestHandler): ... --- @ --- --- See complete source example [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/authentication.py#L10), --- [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L35) --- and [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L15) --- --- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) ) --- --- @ --- skip_level_subclass_of( Subclass, \'tornado.web.RequestHandler\' ) :- --- kb_class_def( Subclass, _, _ ), --- kb_class_has_1st_party_super( Subclass, ClassName, ClassDefinedInFile ), --- kb_class_def( Class, ClassName, ClassDefinedInFile ) --- kb_class_has_3rd_party_super( Class, _, \'tornado.web.RequestHandler\' ). --- @ --- --- Other facts combined in this predicate: --- --- * 'ClassDef' --- * 'ClassHas1stPartySuper' --- -data ClassHas3rdPartySuper = ClassHas3rdPartySuper - Class -- ^ - Token.SuperName -- ^ - SuperQualifiedName -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_call_method_of_class( Call, Method, Class ). --- @ --- --- __When should I use this fact__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) ) --- --- @ --- # index.py --- from tornado.web import RequestHandler --- class BaseHandler(RequestHandler): ... --- --- # authentication.py --- class LoginHandler(BaseHandler): --- def post(self, ...): --- n = self.get_query_argument("next", ...) --- self.redirect(n or ...) --- @ --- --- See complete source example [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/authentication.py#L10), --- [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L35) --- and [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L15) --- --- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) ) --- --- @ --- user_controlled_query_argument( Call ) :- --- kb_call_method_of_class( Call, \'get_query_argument\', Class ), --- kb_class_has_3rd_party_super( Class, \'tornado.web.RequestHandler\'), --- kb_arg_i_for_call( QueryParamName, 0, Call), --- kb_const_string( QueryParamName, _ ). --- @ --- --- Other facts combined in this predicate: --- --- * 'ClassHas3rdPartySuper' --- * 'ArgiForCall' --- * 'ConstString' --- -data CallMethodOfClass = CallMethodOfClass - Call -- ^ - MethodName -- ^ - Class -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_call_method_of_untyped_named_param( Param, ParamName, MethodName ). --- @ --- --- __When should I use this fact__ ( motivation: [CVE-2024-47769](https://nvd.nist.gov/vuln/detail/CVE-2024-47769) ) --- --- @ --- // corePublicRouter.js --- const express = require('express'); --- const router = express.Router(); --- --- router.route('...').get(function (req, res) { --- const { ..., file } = req.params; --- const fileName = file; --- return res.sendFile(fileName, ...); --- } --- @ --- --- See complete source example [here](https://github.com/idurar/idurar-erp-crm/blob/d7b2215a17bb2b52acfdab8f1646685d13df9a00/backend/src/routes/coreRoutes/corePublicRouter.js#L8-L24), --- --- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-47769](https://nvd.nist.gov/vuln/detail/CVE-2024-47769) ) --- --- @ --- arbitrary_file_read( Call ) :- --- kb_call_resolved( Call, \'express.Router.route.get\'), --- kb_call_method_of_untyped_named_param( Param, \'res\', \'sendFile\' ), --- kb_arg_i_for_call( Lambda, 0, Call ), --- kb_param_i_of_callable( Param, 1, Lambda ). --- @ --- --- Other facts combined in this predicate: --- --- * 'CallResolved' --- * 'ArgiForCall' --- * 'ParamiOfCallable' --- -data CallMethodOfUntypedNamedParam = CallMethodOfUntypedNamedParam - Call -- ^ - MethodName -- ^ - Token.ParamName -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- capture inheritance from third party classes -data ClassAnnotation = ClassAnnotation - Class -- ^ - Annotation -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - -data CallableAnnotation = CallableAnnotation - Callable -- ^ - Annotation -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_callable_source_body_length( Callable, N ). --- @ --- --- Number of source-level @[ Ast.Stmt ]@ statements in the callable's body, --- as observed at codegen time (before any lowering to bitcode). Independent --- of how many bitcode instructions the body lowered to. --- --- Intended for query predicates that need a source-fidelity signal ( e.g. --- \"this wrapper's source body is a single statement\" for HOC-unwrap ), --- rather than trying to reconstruct that shape from flat SSA bitcode after --- the fact. --- --- Emitted for 'Method', 'Lambda' and 'Function' callables. Not emitted for --- 'Script' \-- see 'Callable.numOriginalSourceInstructions'. -data CallableSourceBodyLength = CallableSourceBodyLength - Callable -- ^ - Word -- ^ number of source-level statements in the body - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- capture usage of inherited third party methods -data MethodOfClass = MethodOfClass - Method -- ^ - Class -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_arg_i_for_call( Arg, Index, Call ). --- @ --- --- __When should I use this fact__ ( motivation: [CVE-2024-47769](https://nvd.nist.gov/vuln/detail/CVE-2024-47769) ) --- --- @ --- // corePublicRouter.js --- const express = require('express'); --- const router = express.Router(); --- --- router.route('...').get(function (req, res) { --- const { ..., file } = req.params; --- const fileName = file; --- return res.sendFile(fileName, ...); --- } --- @ --- --- See complete source example [here](https://github.com/idurar/idurar-erp-crm/blob/d7b2215a17bb2b52acfdab8f1646685d13df9a00/backend/src/routes/coreRoutes/corePublicRouter.js#L8-L24), --- --- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-47769](https://nvd.nist.gov/vuln/detail/CVE-2024-47769) ) --- --- @ --- arbitrary_file_read( Call ) :- --- kb_call_resolved( Call, \'express.Router.route.get\'), --- kb_call_method_of_untyped_named_param( Param, \'res\', \'sendFile\' ), --- kb_arg_i_for_call( Lambda, 0, Call ), --- kb_param_i_of_callable( Param, 1, Lambda ). --- @ --- --- Other facts combined in this predicate: --- --- * 'CallResolved' --- * 'CallMethodOfUntypedNamedParam' --- * 'ParamiOfCallable' --- --- ==== __Tip 💡__ --- --- When the exact index is irrelevant, use: --- --- @ --- kb_arg_i_for_call( Arg, _, Call ). --- @ --- -data ArgiForCall = ArgiForCall - Arg -- ^ - ArgIndex -- ^ - Call -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_param_i_of_callable( Param, Index, Callable ). --- @ --- --- __When should I use this fact__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) ) --- --- @ --- # http_getter.go --- g.GET("/get/httpmeta", func(c echo.Context) error { --- urlStr := c.QueryParam("url") --- ... --- getter.GetHTMLMeta(urlStr) --- --- # html_meta.go --- func GetHTMLMeta(urlStr string) (*HTMLMeta, error) { --- ... --- http.Get(urlStr) --- @ --- --- See complete source example [here](https://github.com/usememos/memos/blob/v0.14.0/api/v1/http_getter.go#L23), --- and [here](https://github.com/usememos/memos/blob/v0.14.0/plugin/http-getter/html_meta.go#L24) --- --- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) ) --- --- @ --- suspected_ssrf_sink( Func ) :- --- kb_resolved_call( Call, \'net/http.Get\' ), --- kb_arg_i_for_call( Arg, 0, Call ), --- utils_dataflow_path( Param, Arg ), --- kb_param_has_resolved_type( Param, \'string\' ), --- kb_param_i_of_callable( Param, _, Func ), --- kb_func_def( Func, Name, _, FuncDefinedInDir ), --- kb_call_1st_party_func_defined_in_dir( _, Name, FuncDefinedInDir ). --- @ --- --- Other facts combined in this predicate: --- --- * 'CallResolved' --- * 'ArgiForCall' --- * 'ParamResolvedType' --- * 'FuncDef' --- * 'Call1stPartyFuncDefinedInDir' --- --- ==== __Tip 💡__ --- --- When the _exact index_ is _irrelevant_, use: --- --- @ --- kb_param_i_of_callable( Param, _, Callable ). --- @ --- -data ParamiOfCallable = ParamiOfCallable - Param -- ^ - ParamIndex -- ^ 0-based - Callable -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - -data CallResolved = CallResolved - Call -- ^ - Resolved -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_called_from( Call, Callable ). --- @ --- --- __When should I use this fact__ --- --- Web-app request handlers routinely /extract fields/ from the incoming --- request \- headers, cookies, query parameters, body \- and the /name/ --- of the field is often the security-relevant piece ( e.g. the constant --- string \'x-api-key\' says "this callable does API-key authentication" ). --- To reason about /which/ callable extracts /which/ field, you need to --- know that a particular call site lives inside the body of a particular --- callable. --- --- No pre-existing fact tied a 'Call' back to its enclosing 'Callable' \- --- 'ParamiOfCallable' does the analogous thing for /parameters/ only. --- Because 'CalledFrom' exposes the 'Call' /location/ ( not just its FQN ), --- it composes with 'ArgiForCall' and 'ConstString' to also surface the --- /arguments/ passed at that specific call site. --- --- Code snippet ( Typescript, Next.js \- formbricks at v3.16.0 ): --- --- @ --- export const authenticateRequest = async (request: Request) => { --- const apiKey = request.headers.get(\"x-api-key\"); --- if (!apiKey) return null; --- const apiKeyData = await getApiKeyWithPermissions(apiKey); --- ... --- }; --- @ --- --- See complete source example [here](https://github.com/formbricks/formbricks/blob/v3.16.0/apps/web/app/api/v1/auth.ts#L7-L36) --- --- __Writing a predicate with this fact and others__ --- --- Compose 'CalledFrom' with 'ArgiForCall' and 'ConstString' to enumerate --- the constant-string header names each callable extracts from the --- incoming request: --- --- @ --- extracts_request_header_named( Callable, HeaderName ) :- --- kb_called_from( Call, Callable ), --- kb_call_resolved( Call, \'Request.headers.get\' ), --- kb_arg_i_for_call( HeaderConst, 0, Call ), --- kb_const_string( HeaderConst, HeaderName ). --- @ --- --- Applied to @authenticateRequest@ above, this yields --- @extracts_request_header_named( authenticateRequest, \'x-api-key\' )@ \- --- one of the six structural indicators for recognizing Shape-A --- ( identity-carrier ) request authenticators. --- --- Other facts combined in this predicate: --- --- * 'CallResolved' --- * 'ArgiForCall' --- * 'ConstString' --- -data CalledFrom = CalledFrom - Call -- ^ - Callable -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - - --- | --- --- Usage: --- --- @ --- kb_const_str( Param, Index, Callable ). --- @ --- --- ==== __Tip 💡__ --- --- sometimes the exact index is irrelevant, see 'Arg_for_Call' --- -data ConstString = ConstString - ConstStr -- ^ - Token.ConstStr -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_const_int( Loc, Value ). --- @ --- --- __When should I use this fact__ --- --- Any time a query needs to discriminate on the /value/ of a constant --- integer literal appearing in source code, typically as an argument --- to a function that carries semantic information in that integer. --- The canonical example is the HTTP status code passed to a response --- constructor: --- --- @ --- return Response.json({ error: \"unauthorized\" }, { status: 401 }); --- @ --- --- Recognizing this shape ( a bad status code returned early from a --- callable ) is used to identify authenticating functions. --- --- __Writing a predicate with this fact and others__ --- --- @ --- utils_ts_response_json_with_status( Function, Code ) :- --- kb_call_resolved( OuterCall, \'nodejs.Response.json\' ), --- kb_arg_i_for_call( DictifyCall, 1, OuterCall ), --- kb_arg_i_for_call( KvCall, _, DictifyCall ), --- kb_arg_i_for_call( KeyLoc, 0, KvCall ), --- kb_const_string( KeyLoc, \'status\' ), --- kb_arg_i_for_call( ValueLoc, 1, KvCall ), --- kb_const_int( ValueLoc, Code ), --- kb_called_from( OuterCall, Function ). --- @ --- --- Other facts combined in this example predicate: --- --- * 'CallResolved' --- * 'ArgiForCall' --- * 'ConstString' --- * 'CalledFrom' --- -data ConstInteger = ConstInteger - ConstInt -- ^ - Token.ConstInt -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_const_null( Loc ). --- @ --- --- __When should I use this fact__ --- --- Returning early from a function with a null value has many uses cases related to security. --- ( Python @None@ and Ruby @nil@ use this predicate as well ) --- --- __Writing a predicate with this fact and others__ --- --- @ --- kb_gated_return_null( Cond ) :- --- kb_gated_return( Cond, ReturnedValue ), --- kb_const_null( ReturnedValue ). --- @ --- --- Other facts combined in this example predicate: --- --- * 'GatedReturn' --- -data ConstNull = ConstNull Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_gated_return( Cond, ReturnedValue ). --- @ --- --- __When should I use this fact__ --- --- Authenticating functions reject invalid credentials with an early return --- from a guarded @if@. This fact pinpoints that rejection gate. --- --- __Writing a predicate with this fact and others__ --- --- @ --- utils_early_return_null_on_missing_request_header_value( Callable, KeyName ) :- --- kb_called_from( Call, Callable ), --- kb_call_resolved( Call, \'nodejs.Request.headers.get\' ), --- kb_arg_i_for_call( KeyArg, 0, Call ), --- kb_const_string( KeyArg, KeyName ), --- utils_intra_dataflow_path( Call, Var, _ ), --- kb_gated_return_null( Cond ), --- utils_intra_dataflow_path( Var, Cond, _ ). --- --- kb_gated_return_null( Cond ) :- kb_gated_return( Cond, RV ), kb_const_null( RV ). --- @ --- --- Other facts combined in this example predicate: --- --- * 'ConstNull' --- * 'CallResolved' --- * 'ArgiForCall' --- * 'ConstString' --- * 'CalledFrom' --- -data GatedReturn = GatedReturn - Cond -- ^ - ReturnedValue -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_comparison( Cond, Lhs, Rhs, Op ). --- @ --- --- __When should I use this fact__ --- --- Binary comparison instructions ( currently @==@ \/ @===@ mapped to --- the Prolog atom @eq@, and @!=@ \/ @!==@ mapped to @neq@ ) fire this --- fact. The @Cond@ slot is the location of the comparison\'s output --- variable, which makes composing with 'GatedReturn' straightforward: --- when the boolean condition of an @if@ that early-returns is itself a --- comparison, both facts share the same location in the @Cond@ slot. --- --- __Writing a predicate with this fact and others__ --- --- @ --- gated_return_on_comparison( Lhs, Rhs, Op, ReturnedValue ) :- --- kb_gated_return( Cond, ReturnedValue ), --- kb_comparison( Cond, Lhs, Rhs, Op ). --- @ --- --- Other facts combined in this example predicate: --- --- * 'GatedReturn' --- -data Comparison = Comparison - Cond -- ^ - Lhs -- ^ - Rhs -- ^ - ComparisonOp -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_callable_returns_value( Callable, ReturnedValue ). --- @ --- --- __When should I use this fact__ --- --- Every explicit `return exp;` inside a callable's body fires this fact --- once, keyed by the enclosing callable and the returned expression's --- location. Downstream predicates like `kb_const_null` or --- `kb_call_resolved` add semantic meaning to the value slot. --- -data CallableReturnsValue = CallableReturnsValue - Callable -- ^ - ReturnedValue -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_callable_returns_without_value( Callable, ReturnStmtLocation ). --- @ --- --- __When should I use this fact__ --- --- User-written bare `return;` (no value expression) fires this fact once, --- keyed by the enclosing callable and the location of the return --- statement itself. Distinct fact from 'CallableReturnsValue' because --- there is no value-expression location to anchor on; the return --- statement location is used instead. --- -data CallableReturnsWithoutValue = CallableReturnsWithoutValue - Callable -- ^ - Location -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- --- __Name__ --- --- This is how the fact will look inside the Prolog file --- --- @ --- kb_dataflow_edge( From, To ). --- @ --- -data DataflowEdge = DataflowEdge - From -- ^ - To -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - -data Arg = Arg Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data Call = Call Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data Param = Param Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data Class = Class Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - -data To = To Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data From = From Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data Func = Func Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data Method = Method Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data Callable = Callable Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data ConstStr = ConstStr Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data ConstInt = ConstInt Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data Annotation = Annotation Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data AssignedValue = AssignedValue Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data ConstBoolTrue = ConstBoolTrue Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data Cond = Cond Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data Lhs = Lhs Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data Rhs = Rhs Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data ComparisonOp = ComparisonOp String deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data ReturnedValue = ReturnedValue Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - -data Keyword = Keyword String deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data Resolved = Resolved Fqn.Fqn deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data ArgIndex = ArgIndex Word deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data ParamIndex = ParamIndex Word deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data FuncName = FuncName String deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data MethodName = MethodName String deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data ConstStrValue = ConstStrValue String deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data SuperQualifiedName = SuperQualifiedName Fqn.Fqn deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - -data FuncDefinedInDir = FuncDefinedInDir FilePath deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data FuncDefinedInFile = FuncDefinedInFile FilePath deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data ClassDefinedInFile = ClassDefinedInFile FilePath deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) -data SuperDefinedInFile = SuperDefinedInFile FilePath deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | -data ResolvedType = ResolvedType - Fqn.Fqn -- ^ - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - -data ResolvedSuper = ResolvedSuper Fqn.Fqn deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- * each fact is derived from a /single/ bitcode 'Bitcode.Instruction' --- --- * often, a single bitcode instruction will yield /more/ than one fact --- -data Fact - = FuncDefCtor FuncDef - | ClassDefCtor ClassDef - | ParamNameCtor ParamName - | ArgiForCallCtor ArgiForCall - | ConstStringCtor ConstString - | ConstIntegerCtor ConstInteger - | DataflowEdgeCtor DataflowEdge - | CallResolvedCtor CallResolved - | CalledFromCtor CalledFrom - | ConstBoolTrueCtor ConstBoolTrue - | ConstNullCtor ConstNull - | GatedReturnCtor GatedReturn - | ComparisonCtor Comparison - | CallableReturnsValueCtor CallableReturnsValue - | CallableReturnsWithoutValueCtor CallableReturnsWithoutValue - | MethodOfClassCtor MethodOfClass - | ClassAnnotationCtor ClassAnnotation - | ParamiOfCallableCtor ParamiOfCallable - | KeywordArgForCallCtor KeywordArgForCall - | ParamResolvedTypeCtor ParamResolvedType - | CallMethodOfClassCtor CallMethodOfClass - | CallableAnnotationCtor CallableAnnotation - | ClassHas1stPartySuperCtor ClassHas1stPartySuper - | ClassHas3rdPartySuperCtor ClassHas3rdPartySuper - | CallableSourceBodyLengthCtor CallableSourceBodyLength - | AssignValueToToplevelVarNameCtor AssignValueToToplevelVarName - | Call1stPartyFuncDefinedInDirCtor Call1stPartyFuncDefinedInDir - | Call1stPartyFuncDefinedInFileCtor Call1stPartyFuncDefinedInFile - | CallMethodOfUntypedNamedParamCtor CallMethodOfUntypedNamedParam - deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON ) - --- | --- Translate a code fact into a prolog fact --- --- 'ParamName' --- --- @ --- kb_param_has_name( Param, Name ). --- @ --- -prologify :: Fact -> String -prologify (FuncDefCtor content) = prologify_FuncDef content -prologify (ClassDefCtor content) = prologify_ClassDef content -prologify (ParamNameCtor content) = prologify_ParamName content -prologify (ArgiForCallCtor content) = prologifyArgiForCall content -prologify (ConstStringCtor content) = prologify_ConstString content -prologify (ConstIntegerCtor content) = prologify_ConstInteger content -prologify (DataflowEdgeCtor content) = prologify_DataflowEdge content -prologify (CallResolvedCtor content) = prologify_CallResolved content -prologify (CalledFromCtor content) = prologify_CalledFrom content -prologify (ConstBoolTrueCtor content) = prologify_ConstBoolTrue content -prologify (ConstNullCtor content) = prologify_ConstNull content -prologify (GatedReturnCtor content) = prologify_GatedReturn content -prologify (ComparisonCtor content) = prologify_Comparison content -prologify (CallableReturnsValueCtor content) = prologify_CallableReturnsValue content -prologify (CallableReturnsWithoutValueCtor content) = prologify_CallableReturnsWithoutValue content -prologify (MethodOfClassCtor content) = prologify_MethodOfClass content -prologify (ClassAnnotationCtor content) = prologify_ClassAnnotation content -prologify (ParamiOfCallableCtor content) = prologify_ParamiOfCallable content -prologify (ParamResolvedTypeCtor content) = prologify_ParamResolvedType content -prologify (CallMethodOfClassCtor content) = prologify_CallMethodOfClass content -prologify (KeywordArgForCallCtor content) = prologify_KeywordArgForCall content -prologify (CallableAnnotationCtor content) = prologify_CallableAnnotation content -prologify (CallableSourceBodyLengthCtor content) = prologify_CallableSourceBodyLength content -prologify (ClassHas1stPartySuperCtor content) = prologify_ClassHas1stPartySuper content -prologify (ClassHas3rdPartySuperCtor content) = prologify_ClassHas3rdPartySuper content -prologify (AssignValueToToplevelVarNameCtor content) = prologify_AssignValueToToplevelVarName content -prologify (Call1stPartyFuncDefinedInDirCtor content) = prologify_Call1stPartyFuncDefinedInDir content -prologify (Call1stPartyFuncDefinedInFileCtor content) = prologify_Call1stPartyFuncDefinedInFile content -prologify (CallMethodOfUntypedNamedParamCtor content) = prologify_CallMethodOfUntypedNamedParam content - -prologify_ParamResolvedType' :: Location -> String -> String -prologify_ParamResolvedType' l fqn = printf "kb_param_has_resolved_type( %s, \'%s\' )." (locationify l) fqn - -prologify_ParamResolvedType :: ParamResolvedType -> String -prologify_ParamResolvedType (ParamResolvedType (Param loc) (ResolvedType fqn)) = prologify_ParamResolvedType' loc (prologifyFqn fqn) - -prologify_CallMethodOfClass' :: Location -> String -> Location -> String -prologify_CallMethodOfClass' call method klass = printf "kb_call_method_of_class( %s, \'%s\', %s )." (locationify call) method (locationify klass) - -prologify_CallMethodOfClass :: CallMethodOfClass -> String -prologify_CallMethodOfClass (CallMethodOfClass (Call loc) (MethodName m) (Class c)) = prologify_CallMethodOfClass' loc m c - -prologify_ParamName' :: Location -> String -> String -prologify_ParamName' l name = printf "kb_param_has_name( %s, \'%s\' )." (locationify l) name - -prologify_ParamName :: ParamName -> String -prologify_ParamName (ParamName (Param loc) (Token.ParamName (Token.Named name _))) = prologify_ParamName' loc name - -prologify_ClassDef' :: Location -> String -> FilePath -> String -prologify_ClassDef' l name f = printf "kb_class_def( %s, \'%s\', \'%s\' )." (locationify l) name f - -prologify_ClassDef :: ClassDef -> String -prologify_ClassDef (ClassDef (Class loc) (Token.ClassName (Token.Named name _)) (ClassDefinedInFile f)) = prologify_ClassDef' loc name f - -prologify_FuncDef' :: Location -> String -> FilePath -> FilePath -> String -prologify_FuncDef' l name f d = printf "kb_func_def( %s, \'%s\', \'%s\', \'%s\' )." (locationify l) name f d - -prologify_FuncDef :: FuncDef -> String -prologify_FuncDef (FuncDef (Func loc) (Token.FuncName (Token.Named name _)) (FuncDefinedInFile f) (FuncDefinedInDir d)) = prologify_FuncDef' loc name f d - -unquote :: String -> String -unquote = filter (/= '\'') - -prologify_ConstString' :: Location -> String -> String -prologify_ConstString' l value = printf "kb_const_string( %s, \'%s\' )." (locationify l) (unquote value) - -prologify_ConstString :: ConstString -> String -prologify_ConstString (ConstString (ConstStr loc) (Token.ConstStr value _)) = prologify_ConstString' loc value - -prologify_ConstInteger' :: Location -> Int -> String -prologify_ConstInteger' l value = printf "kb_const_int( %s, %d )." (locationify l) value - -prologify_ConstInteger :: ConstInteger -> String -prologify_ConstInteger (ConstInteger (ConstInt loc) (Token.ConstInt value _)) = prologify_ConstInteger' loc value - -prologify_CallResolved'' :: Location -> String -> Token.ClassName -> String -prologify_CallResolved'' call m c = printf "kb_call_method_of_class( %s, %s, %s )." (locationify call) m (locationify (Token.getClassNameLocation c)) - -prologify_CallResolved' :: Location -> Fqn.Fqn -> String -prologify_CallResolved' call (Fqn.CallMethodOfClass _ m c) = prologify_CallResolved'' call m c -prologify_CallResolved' call fqn = printf "kb_call_resolved( %s, \'%s\' )." (locationify call) (prologifyFqn fqn) - -prologify_DataflowEdge :: DataflowEdge -> String -prologify_DataflowEdge (DataflowEdge (From u) (To v)) = prologify_DataflowEdge' u v - -prologify_DataflowEdge' :: Location -> Location -> String -prologify_DataflowEdge' u v = printf "kb_dataflow_edge( %s, %s )." (locationify u) (locationify v) - -prologify_CallResolved :: CallResolved -> String -prologify_CallResolved (CallResolved (Call call) (Resolved fqn)) = prologify_CallResolved' call fqn - -prologify_ConstBoolTrue' :: Location -> String -prologify_ConstBoolTrue' trueValue = printf "kb_class_name( %s, \'%s\' )." (locationify trueValue) - -prologify_ConstBoolTrue :: ConstBoolTrue -> String -prologify_ConstBoolTrue (ConstBoolTrue trueValue) = prologify_ConstBoolTrue' trueValue - -prologify_ConstNull' :: Location -> String -prologify_ConstNull' l = printf "kb_const_null( %s )." (locationify l) - -prologify_ConstNull :: ConstNull -> String -prologify_ConstNull (ConstNull loc) = prologify_ConstNull' loc - -prologify_GatedReturn' :: Location -> Location -> String -prologify_GatedReturn' c v = printf "kb_gated_return( %s, %s )." (locationify c) (locationify v) - -prologify_GatedReturn :: GatedReturn -> String -prologify_GatedReturn (GatedReturn (Cond c) (ReturnedValue v)) = prologify_GatedReturn' c v - -prologify_Comparison' :: Location -> Location -> Location -> String -> String -prologify_Comparison' c l r op = printf "kb_comparison( %s, %s, %s, %s )." (locationify c) (locationify l) (locationify r) op - -prologify_Comparison :: Comparison -> String -prologify_Comparison (Comparison (Cond c) (Lhs l) (Rhs r) (ComparisonOp op)) = prologify_Comparison' c l r op - -prologify_CallableReturnsValue' :: Location -> Location -> String -prologify_CallableReturnsValue' c v = printf "kb_callable_returns_value( %s, %s )." (locationify c) (locationify v) - -prologify_CallableReturnsValue :: CallableReturnsValue -> String -prologify_CallableReturnsValue (CallableReturnsValue (Callable c) (ReturnedValue v)) = prologify_CallableReturnsValue' c v - -prologify_CallableReturnsWithoutValue' :: Location -> Location -> String -prologify_CallableReturnsWithoutValue' c s = printf "kb_callable_returns_without_value( %s, %s )." (locationify c) (locationify s) - -prologify_CallableReturnsWithoutValue :: CallableReturnsWithoutValue -> String -prologify_CallableReturnsWithoutValue (CallableReturnsWithoutValue (Callable c) s) = prologify_CallableReturnsWithoutValue' c s - -prologify_MethodOfClass' :: Location -> Location -> String -prologify_MethodOfClass' m c = printf "kb_method_of_class( %s, %s )." (locationify m) (locationify c) - -prologify_MethodOfClass :: MethodOfClass -> String -prologify_MethodOfClass (MethodOfClass (Method m) (Class c)) = prologify_MethodOfClass' m c - -prologify_ClassHas1stPartySuper' :: Location -> String -> FilePath -> String -prologify_ClassHas1stPartySuper' c s f = printf "kb_class_has_1st_party_super( %s, \'%s\', \'%s\' )." (locationify c) s f - -prologify_ClassHas1stPartySuper :: ClassHas1stPartySuper -> String -prologify_ClassHas1stPartySuper (ClassHas1stPartySuper (Class c) (Token.SuperName (Token.Named s _)) (SuperDefinedInFile f)) = prologify_ClassHas1stPartySuper' c s f - -prologify_ClassHas3rdPartySuper' :: Location -> String -> Fqn.Fqn -> String -prologify_ClassHas3rdPartySuper' c s f = printf "kb_class_has_3rd_party_super( %s, \'%s\', \'%s\' )." (locationify c) s (prologifyFqn f) - -prologify_ClassHas3rdPartySuper :: ClassHas3rdPartySuper -> String -prologify_ClassHas3rdPartySuper (ClassHas3rdPartySuper (Class c) (Token.SuperName (Token.Named s _)) (SuperQualifiedName f)) = prologify_ClassHas3rdPartySuper' c s f - -prologify_AssignValueToToplevelVarName :: AssignValueToToplevelVarName -> String -prologify_AssignValueToToplevelVarName (AssignValueToToplevelVarName value varname) = prologify_AssignValueToToplevelVarName' value varname - -prologify_AssignValueToToplevelVarName' :: AssignedValue -> Token.VarName -> String -prologify_AssignValueToToplevelVarName' (AssignedValue value) (Token.VarName (Token.Named varname l)) = prologify_AssignValueToToplevelVarName'' value varname (Location.filename l) - -prologify_AssignValueToToplevelVarName'' :: Location -> String -> FilePath -> String -prologify_AssignValueToToplevelVarName'' value varname f = printf "kb_assign_value_to_toplevel_varname( %s, \'%s\', \'%s\', \'%s\')." (locationify value) varname f (takeDirectory f) - -prologify_Call1stPartyFuncDefinedInFile :: Call1stPartyFuncDefinedInFile -> String -prologify_Call1stPartyFuncDefinedInFile (Call1stPartyFuncDefinedInFile call func f) = prologify_Call1stPartyFuncDefinedInFile' call func f - -prologify_Call1stPartyFuncDefinedInFile' :: Call -> FuncName -> FuncDefinedInFile -> String -prologify_Call1stPartyFuncDefinedInFile' (Call call) (FuncName func) (FuncDefinedInFile f) = prologify_Call1stPartyFuncDefinedInFile'' call func f - -prologify_Call1stPartyFuncDefinedInFile'' :: Location -> String -> FilePath -> String -prologify_Call1stPartyFuncDefinedInFile'' call func f = printf "kb_call_1st_party_func_defined_in_file( %s, \'%s\', \'%s\' )." (locationify call) func f - -prologify_Call1stPartyFuncDefinedInDir :: Call1stPartyFuncDefinedInDir -> String -prologify_Call1stPartyFuncDefinedInDir (Call1stPartyFuncDefinedInDir call func d) = prologify_Call1stPartyFuncDefinedInDir' call func d - -prologify_Call1stPartyFuncDefinedInDir' :: Call -> FuncName -> FuncDefinedInDir -> String -prologify_Call1stPartyFuncDefinedInDir' (Call call) (FuncName f) (FuncDefinedInDir d) = prologify_Call1stPartyFuncDefinedInDir'' call f d - -prologify_Call1stPartyFuncDefinedInDir'' :: Location -> String -> FilePath -> String -prologify_Call1stPartyFuncDefinedInDir'' call f d = printf "kb_call_1st_party_func_defined_in_dir( %s, \'%s\', \'%s\' )." (locationify call) f d - -prologify_CallMethodOfUntypedNamedParam :: CallMethodOfUntypedNamedParam -> String -prologify_CallMethodOfUntypedNamedParam (CallMethodOfUntypedNamedParam call method p) = prologify_CallMethodOfUntypedNamedParam' call method p - -prologify_CallMethodOfUntypedNamedParam' :: Call -> MethodName -> Token.ParamName -> String -prologify_CallMethodOfUntypedNamedParam' (Call call) (MethodName m) (Token.ParamName (Token.Named _ p)) = prologify_CallMethodOfUntypedNamedParam'' call m p - -prologify_CallMethodOfUntypedNamedParam'' :: Location -> String -> Location -> String -prologify_CallMethodOfUntypedNamedParam'' call m p = printf "kb_call_method_of_untyped_named_param( %s, \'%s\', %s )." (locationify call) m (locationify p) - -prologifyArgiForCall' :: Location -> Word -> Location -> String -prologifyArgiForCall' a i c = printf "kb_arg_i_for_call( %s, %u, %s )." (locationify a) i (locationify c) - -prologifyArgiForCall :: ArgiForCall -> String -prologifyArgiForCall (ArgiForCall (Arg a) (ArgIndex i) (Call c)) = prologifyArgiForCall' a i c - -prologify_ClassAnnotation' :: Location -> Location -> String -prologify_ClassAnnotation' c a = printf "kb_class_has_annotation( %s, %s )." (locationify c) (locationify a) - -prologify_ClassAnnotation :: ClassAnnotation -> String -prologify_ClassAnnotation (ClassAnnotation (Class c) (Annotation a)) = prologify_ClassAnnotation' c a - -prologify_KeywordArgForCall' :: String -> Location -> Location -> String -prologify_KeywordArgForCall' kw a c = printf "kb_keyword_arg_for_call( %s, %s, %s )." (locationify a) kw (locationify c) - -prologify_KeywordArgForCall :: KeywordArgForCall -> String -prologify_KeywordArgForCall (KeywordArgForCall (Keyword kw) (Arg a) (Call c)) = prologify_KeywordArgForCall' kw a c - -prologify_CallableAnnotation' :: Location -> Location -> String -prologify_CallableAnnotation' c a = printf "kb_callable_has_annotation( %s, %s )." (locationify c) (locationify a) - -prologify_CallableAnnotation :: CallableAnnotation -> String -prologify_CallableAnnotation (CallableAnnotation (Callable c) (Annotation a)) = prologify_CallableAnnotation' c a - -prologify_CallableSourceBodyLength' :: Location -> Word -> String -prologify_CallableSourceBodyLength' c n = printf "kb_callable_source_body_length( %s, %u )." (locationify c) n - -prologify_CallableSourceBodyLength :: CallableSourceBodyLength -> String -prologify_CallableSourceBodyLength (CallableSourceBodyLength (Callable c) n) = prologify_CallableSourceBodyLength' c n - -prologify_ParamiOfCallable' :: Location -> Word -> Location -> String -prologify_ParamiOfCallable' p i c = printf "kb_param_i_of_callable( %s, %u, %s )." (locationify p) i (locationify c) - -prologify_ParamiOfCallable :: ParamiOfCallable -> String -prologify_ParamiOfCallable (ParamiOfCallable (Param p) (ParamIndex i) (Callable c)) = prologify_ParamiOfCallable' p i c - -prologify_CalledFrom' :: Location -> Location -> String -prologify_CalledFrom' call callable = printf "kb_called_from( %s, %s )." (locationify call) (locationify callable) - -prologify_CalledFrom :: CalledFrom -> String -prologify_CalledFrom (CalledFrom (Call call) (Callable callable)) = prologify_CalledFrom' call callable - -normalizeChar :: Char -> String -normalizeChar '/' = "_slash_" -normalizeChar '.' = "_dot_" -normalizeChar '-' = "_dash_" -normalizeChar '[' = "_lbrack_" -normalizeChar ']' = "_rbrack_" -normalizeChar '(' = "_lparen_" -normalizeChar ')' = "_rparen_" -normalizeChar c = [c] - -normalize :: FilePath -> FilePath -normalize path = concatMap normalizeChar path - -prologifyFqn :: Fqn.Fqn -> String -prologifyFqn (Fqn.ThirdPartyImport fqn) = prologifyThirdPartyImport fqn -prologifyFqn (Fqn.FieldedAccess fqn fieldName) = prologifyFieldedAccess fqn fieldName -prologifyFqn fqn = show fqn - -prologifyThirdPartyImport :: Fqn.ThirdPartyImportContent -> String -prologifyThirdPartyImport (Fqn.ThirdPartyImportContent p [] Nothing _) = p -prologifyThirdPartyImport (Fqn.ThirdPartyImportContent p [] (Just name) _) = p ++ "." ++ name -prologifyThirdPartyImport (Fqn.ThirdPartyImportContent p rest Nothing _) = p ++ "." ++ List.intercalate "." rest -prologifyThirdPartyImport (Fqn.ThirdPartyImportContent p rest (Just name) _) = p ++ "." ++ List.intercalate "." rest ++ "." ++ name - -prologifyFieldedAccess :: Fqn.Fqn -> Token.FieldName -> String -prologifyFieldedAccess fqn (Token.FieldName (Token.Named fieldName _)) = (prologifyFqn fqn) ++ "." ++ fieldName - -locationify :: Location -> String -locationify l = let - x = Location.lineStart l - y = Location.colStart l - z = Location.lineEnd l - w = Location.colEnd l - f = normalize (Location.filename l) - in printf "startloc_%u_%u_endloc_%u_%u_%s" x y z w f - -restoreloc :: String -> Maybe Location -restoreloc s = case parseOnly locationParser (T.pack s) of { Right l -> Just l; _ -> Nothing; } - -locationParser :: Parser Location -locationParser = do { - - _ <- string "startloc_"; x <- decimal; _ <- char '_'; y <- decimal; - _ <- string "_endloc_" ; z <- decimal; _ <- char '_'; w <- decimal; - _ <- char '_'; fname <- takeText; - - pure Location { - filename = restoreFilename (T.unpack fname), - lineStart = x, - colStart = y, - lineEnd = z, - colEnd = w - } -} - -data Rule = Rule { from :: Text, to :: Text } - -rules :: [ Rule ] -rules = - [ Rule { from = "_slash_", to = "/" } - , Rule { from = "_dot_", to = "." } - , Rule { from = "_dash_", to = "-" } - , Rule { from = "_lbrack_", to = "[" } - , Rule { from = "_rbrack_", to = "]" } - , Rule { from = "_lparen_", to = "(" } - , Rule { from = "_rparen_", to = ")" } - ] - -applyRule :: Rule -> Text -> Text -applyRule Rule { from = f, to = t } = T.replace f t - -applyRuleStep :: Text -> Rule -> Text -applyRuleStep acc r = applyRule r acc - -restore :: Text -> Text -restore txt = List.foldl' applyRuleStep txt rules - -textToStringAdapter :: (Text -> Text) -> String -> String -textToStringAdapter f = T.unpack . f . T.pack - -restoreFilename :: String -> String +-- |+--+-- * The [knowledge base](https://en.wikipedia.org/wiki/Knowledge_representation_and_reasoning) ( kb )+-- aims to be a data structure able to:+--+-- * represent /multiple/ facts about the source code repo+-- * from /various/ programming languages+-- * each fact can be translated to a [Prolog](https://en.wikipedia.org/wiki/Prolog) fact+-- * facts can be combined to create [predicates](https://en.wikipedia.org/wiki/Predicate_%28logic%29)+-- * [predicates](https://en.wikipedia.org/wiki/Predicate_%28logic%29) can be combined to formulate /security queries/+--+-- * Facts describe relations between:+--+-- * code locations+-- * const strings+-- * const integers+--+-- * Code locations are capable of representing /all/ program entities+--+-- * classes+-- * methods+-- * lambdas+-- * annotations+-- * arguments+-- * parameters+-- * etc.+--+-- * Facts can be /combined/:+--+-- * [conjunction](https://en.wikipedia.org/wiki/Logical_conjunction)+-- * [disjunction](https://en.wikipedia.org/wiki/Logical_disjunction)+-- * [negation](https://en.wikipedia.org/wiki/Negation)+--+-- * [Prolog](https://en.wikipedia.org/wiki/Prolog) queries are /easy/ to write:+--+-- * you /don't/ have to be a Prolog expert+-- * copy-paste the basic facts to /any/ [LLM](https://en.wikipedia.org/wiki/Large_language_model)+-- * explain in plain English your query's purpose+-- * et voilà !+--+-- * Its main purpose is to serve as the:+--+-- * penultimate step for /static code analysis/ +-- * part of the [dhscanner](https://github.com/OrenGitHub/dhscanner.vps) framework+--+-- * [SAST](https://en.wikipedia.org/wiki/Static_application_security_testing) for security scans 🔒+-- * [PII](https://en.wikipedia.org/wiki/Personal_data) leaks detection 🪪+--++{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE OverloadedStrings #-}++module Kbgen (+ KeywordArgForCall(..),+ ParamResolvedType(..),+ ClassDef(..),+ FuncDef(..),+ Call1stPartyFuncDefinedInDir(..),+ AssignValueToToplevelVarName(..),+ Call1stPartyFuncDefinedInFile(..),+ ParamName(..),+ CallMethodOfClass(..),+ ConstString(..),+ ConstInteger(..),+ MethodOfClass(..),+ ClassHas1stPartySuper(..),+ ClassHas3rdPartySuper(..),+ CallMethodOfUntypedNamedParam(..),+ ArgiForCall(..),+ DataflowEdge(..),+ ConstBoolTrue(..),+ ConstNull(..),+ GatedReturn(..),+ Comparison(..),+ CallableReturnsValue(..),+ CallableReturnsWithoutValue(..),+ Cond(..),+ Lhs(..),+ Rhs(..),+ ComparisonOp(..),+ ReturnedValue(..),+ ClassAnnotation(..),+ CallableAnnotation(..),+ Annotation(..),+ CallableSourceBodyLength(..),+ ParamiOfCallable(..),+ prologify,+ To(..),+ Arg(..),+ From(..),+ Func(..),+ Call(..),+ Class(..),+ Param(..),+ Method(..),+ Callable(..),+ Keyword(..),+ Resolved(..),+ ArgIndex(..),+ ParamIndex(..),+ FuncName(..),+ MethodName(..),+ ResolvedType(..),+ CallResolved(..),+ CalledFrom(..),+ AssignedValue(..),+ ResolvedSuper(..),+ FuncDefinedInDir(..),+ FuncDefinedInFile(..),+ ClassDefinedInFile(..),+ SuperDefinedInFile(..),+ SuperQualifiedName(..),+ ConstStr(..),+ ConstInt(..),+ Fact(..),+ locationify,+ restoreloc+)++where++-- general imports+import Data.Aeson+import Text.Printf+import GHC.Generics (Generic)+import System.FilePath (takeDirectory)+import Data.Attoparsec.Text (Parser, char, decimal, parseOnly, string, takeText)+import Data.Text (Text)+import qualified Data.Text as T++-- general qualified imports+import qualified Data.List as List++-- project imports+import Location+import qualified Token+import qualified Fqn++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_keyword_arg_for_call( Keyword, Arg, Call ).+-- @+--+-- __When should I use this fact__ ( motivation: [CVE-2024-52803](https://nvd.nist.gov/vuln/detail/CVE-2024-52803) )+--+-- Code snippet ( Python ):+--+-- @+-- Popen(f'llamafactory-cli train {save_cmd(args)}', env=env, shell=True)+-- @+--+-- See complete source example: [here](https://github.com/hiyouga/LLaMA-Factory/commit/b3aa80d54a67da45e9e237e349486fb9c162b2ac)+--+-- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-52803](https://nvd.nist.gov/vuln/detail/CVE-2024-52803) )+--+-- @+-- subprocess_Popen_called_with_shell_eq_True( Call ) :-+-- kb_keyword_arg_for_call( \'shell\', TrueValue, Call ),+-- kb_call_resolved( Call, \'subprocess.Popen\' ),+-- kb_const_bool_true( TrueValue ).+-- @+--+-- Other facts combined in this example predicate:+--+-- * 'CallResolved'+-- * 'ConstBoolTrue'+--+data KeywordArgForCall = KeywordArgForCall+ Keyword -- ^+ Arg -- ^+ Call -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_param_has_resolved_type( Param, ResolvedType ).+-- @+--+-- __When should I use this fact__ ( motivation: [CVE-2024-49380](https://nvd.nist.gov/vuln/detail/CVE-2024-49380) )+--+-- Code snippet ( Golang ):+--+-- @+-- func postLocal(w http.ResponseWriter, r *http.Request) { ... }+-- @+--+-- See complete source example: [here](https://github.com/plentico/plenti/blob/01825e0dcd3505fac57adc2edf29f772d585c008/cmd/serve.go#L205)+--+-- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-49380](https://nvd.nist.gov/vuln/detail/CVE-2024-49380) )+--+-- @+-- looks_like_user_input( RequestParam ) :-+-- kb_param_has_resolved_type( RequestParam, \'net/http.Request\' ),+-- kb_param_has_resolved_type( ResponseParam, \'net/http.ResponseWriter\' ),+-- kb_param_i_of_callable( ResponseParam, 0, Handler ),+-- kb_param_i_of_callable( RequestParam, 1, Handler ).+-- @+--+-- Other facts combined in this predicate:+--+-- * 'ParamiOfCallable'+--+data ParamResolvedType = ParamResolvedType+ Param -- ^+ ResolvedType -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_class_def( Class, Name, DefinedInFile ).+-- @+--+-- __When should I use this fact__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) )+--+-- @+-- # authentication.py+-- class LoginHandler(BaseHandler): ...+--+-- # index.py+-- from tornado.web import RequestHandler+-- class BaseHandler(RequestHandler): ...+-- @+--+-- See complete source example [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/authentication.py#L10),+-- [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L35)+-- and [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L15)+--+-- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) )+--+-- @+-- skip_level_subclass_of( Subclass, \'tornado.web.RequestHandler\' ) :-+-- kb_class_def( Subclass, _, _ ),+-- kb_class_has_1st_party_super( Subclass, ClassName, ClassDefinedInFile ),+-- kb_class_def( Class, ClassName, ClassDefinedInFile )+-- kb_class_has_3rd_party_super( Class, _, \'tornado.web.RequestHandler\' ).+-- @+--+-- Other facts combined in this predicate:+--+-- * 'ClassHas1stPartySuper'+-- * 'ClassHas3rdPartySuper'+--+data ClassDef = ClassDef+ Class -- ^+ Token.ClassName -- ^+ ClassDefinedInFile -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_func_def( Func, Name, DefinedInFile, DefinedInDir ).+-- @+--+-- __When should I use this fact__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) )+--+-- @+-- # http_getter.go+-- g.GET("/get/httpmeta", func(c echo.Context) error {+-- urlStr := c.QueryParam("url")+-- ...+-- getter.GetHTMLMeta(urlStr)+--+-- # html_meta.go+-- func GetHTMLMeta(urlStr string) (*HTMLMeta, error) {+-- ...+-- http.Get(urlStr)+-- @+--+-- See complete source example [here](https://github.com/usememos/memos/blob/v0.14.0/api/v1/http_getter.go#L23),+-- and [here](https://github.com/usememos/memos/blob/v0.14.0/plugin/http-getter/html_meta.go#L24)+--+-- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) )+--+-- @+-- suspected_ssrf_sink( Func ) :-+-- kb_resolved_call( Call, \'net/http.Get\' ),+-- kb_arg_i_for_call( Arg, 0, Call ),+-- utils_dataflow_path( Param, Arg ),+-- kb_param_has_resolved_type( Param, \'string\' ),+-- kb_param_i_of_callable( Param, _, Func ),+-- kb_func_def( Func, Name, _, FuncDefinedInDir ),+-- kb_call_1st_party_func_defined_in_dir( _, Name, FuncDefinedInDir ).+-- @+--+-- Other facts combined in this predicate:+--+-- * 'CallResolved'+-- * 'ArgiForCall'+-- * 'ParamResolvedType'+-- * 'ParamiOfCallable'+-- * 'Call1stPartyFuncDefinedInDir'+--+data FuncDef = FuncDef+ Func -- ^+ Token.FuncName -- ^+ FuncDefinedInFile -- ^+ FuncDefinedInDir -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_call_1st_party_func_defined_in_dir( Call, Name, DefinedInDir ).+-- @+--+-- __When should I use this fact__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) )+--+-- @+-- # http_getter.go+-- g.GET("/get/httpmeta", func(c echo.Context) error {+-- urlStr := c.QueryParam("url")+-- ...+-- getter.GetHTMLMeta(urlStr)+--+-- # html_meta.go+-- func GetHTMLMeta(urlStr string) (*HTMLMeta, error) {+-- ...+-- http.Get(urlStr)+-- @+--+-- See complete source example [here](https://github.com/usememos/memos/blob/v0.14.0/api/v1/http_getter.go#L23),+-- and [here](https://github.com/usememos/memos/blob/v0.14.0/plugin/http-getter/html_meta.go#L24)+--+-- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) )+--+-- @+-- suspected_ssrf_sink( Func ) :-+-- kb_resolved_call( Call, \'net/http.Get\' ),+-- kb_arg_i_for_call( Arg, 0, Call ),+-- utils_dataflow_path( Param, Arg ),+-- kb_param_has_resolved_type( Param, \'string\' ),+-- kb_param_i_of_callable( Param, _, Func ),+-- kb_func_def( Func, Name, _, FuncDefinedInDir ),+-- kb_call_1st_party_func_from_dir( _, Name, FuncDefinedInDir ).+-- @+--+-- Other facts combined in this predicate:+--+-- * 'CallResolved'+-- * 'ArgiForCall'+-- * 'ParamResolvedType'+-- * 'ParamiOfCallable'+--+data Call1stPartyFuncDefinedInDir = Call1stPartyFuncDefinedInDir+ Call -- ^+ FuncName -- ^+ FuncDefinedInDir -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++data Call1stPartyFuncDefinedInFile = Call1stPartyFuncDefinedInFile+ Call -- ^+ FuncName -- ^+ FuncDefinedInFile -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++data AssignValueToToplevelVarName = AssignValueToToplevelVarName+ AssignedValue -- ^+ Token.VarName -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Description:__+--+-- Input parameter of some callable ( method, function, lambda ) has the specified name+--+-- __Prolog signature:__+--+-- @+-- kb_param_has_name( Param, Name ).+-- @+--+-- __Code Example ( Javascript ):__+--+-- Motivated by [GHSL-2023-203](https://github.com/advplyr/audiobookshelf/security/advisories/GHSA-mgj7-rfx8-vhpr#event-116560)+-- ( [source code](https://github.com/advplyr/audiobookshelf/blob/d7b2476473ef1934eedec41425837cddf2d4b13e/server/controllers/AuthorController.js#L66)+-- and see also [here](https://github.com/advplyr/audiobookshelf/blob/d7b2476473ef1934eedec41425837cddf2d4b13e/server/controllers/AuthorController.js#L74)+-- and [here](https://github.com/advplyr/audiobookshelf/blob/d7b2476473ef1934eedec41425837cddf2d4b13e/server/routers/ApiRouter.js#L201) )+--+-- @+-- // ApiRouter.js+-- const express = require(\'express\')+--+-- class ApiRouter {+-- constructor(Server) { this.router = express() }+-- init() { this.router.patch(\'\/authors\/:id', ... , AuthorController.update.bind(this)) }+--+-- // AuthorController.js+-- class AuthorController { async update(req, res) { ... } }+-- @+--+-- __Writing new predicates:__+--+-- Facts combined in this predicate:+--+-- * 'ParamName'+-- * 'ParamiOfCallable'+--+-- @+-- http_request_param_of_method( Param, Method ) :-+-- kb_param_name( Param, \'req\' ),+-- kb_param_name( SecondParam, \'res\' ),+-- kb_param_i_of_callable( Param, 0, Method ),+-- kb_param_i_of_callable( SecondParam, 1, Method ).+-- @+--+-- Facts combined in this predicate:+--+-- * 'ArgiForCall'+-- * 'ClassName'+-- * 'MethodName'+--+-- @+-- request_handler( Method ) :-+-- kb_resolved_call( Call, \'express.patch\' ),+-- kb_arg_i_for_call( RequestHandler, 2, Call ),+-- kb_resolved_part_i_call( RequestHandler, 0, ClassName ),+-- kb_resolved_part_i_call( RequestHandler, 1, MethodName ),+-- kb_method_of_class( Method, Class ),+-- kb_method_name( Method, MethodName ),+-- kb_class_name( Class, ClassName ).+-- @+--+--+data ParamName = ParamName+ Param -- ^+ Token.ParamName -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_class_has_1st_party_super( Class, SuperName, SuperDefinedInFile ).+-- @+--+-- __When should I use this fact__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) )+--+-- @+-- # authentication.py+-- class LoginHandler(BaseHandler): ...+--+-- # index.py+-- from tornado.web import RequestHandler+-- class BaseHandler(RequestHandler): ...+-- @+--+-- See complete source example [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/authentication.py#L10),+-- [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L35)+-- and [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L15)+--+-- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) )+--+-- @+-- skip_level_subclass_of( Subclass, \'tornado.web.RequestHandler\' ) :-+-- kb_class_def( Subclass, _, _ ),+-- kb_class_has_1st_party_super( Subclass, ClassName, ClassDefinedInFile ),+-- kb_class_def( Class, ClassName, ClassDefinedInFile )+-- kb_class_has_3rd_party_super( Class, _, \'tornado.web.RequestHandler\' ).+-- @+--+-- Other facts combined in this predicate:+--+-- * 'ClassDef'+-- * 'ClassHas3rdPartySuper'+--+data ClassHas1stPartySuper = ClassHas1stPartySuper+ Class -- ^+ Token.SuperName -- ^+ SuperDefinedInFile -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_class_has_3rd_party_super( Class, SuperName, SuperQualifiedName ).+-- @+--+-- __When should I use this fact__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) )+--+-- @+-- # authentication.py+-- class LoginHandler(BaseHandler): ...+--+-- # index.py+-- from tornado.web import RequestHandler+-- class BaseHandler(RequestHandler): ...+-- @+--+-- See complete source example [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/authentication.py#L10),+-- [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L35)+-- and [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L15)+--+-- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) )+--+-- @+-- skip_level_subclass_of( Subclass, \'tornado.web.RequestHandler\' ) :-+-- kb_class_def( Subclass, _, _ ),+-- kb_class_has_1st_party_super( Subclass, ClassName, ClassDefinedInFile ),+-- kb_class_def( Class, ClassName, ClassDefinedInFile )+-- kb_class_has_3rd_party_super( Class, _, \'tornado.web.RequestHandler\' ).+-- @+--+-- Other facts combined in this predicate:+--+-- * 'ClassDef'+-- * 'ClassHas1stPartySuper'+--+data ClassHas3rdPartySuper = ClassHas3rdPartySuper+ Class -- ^+ Token.SuperName -- ^+ SuperQualifiedName -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_call_method_of_class( Call, Method, Class ).+-- @+--+-- __When should I use this fact__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) )+--+-- @+-- # index.py+-- from tornado.web import RequestHandler+-- class BaseHandler(RequestHandler): ...+--+-- # authentication.py+-- class LoginHandler(BaseHandler):+-- def post(self, ...):+-- n = self.get_query_argument("next", ...)+-- self.redirect(n or ...)+-- @+--+-- See complete source example [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/authentication.py#L10),+-- [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L35)+-- and [here](https://github.com/SickChill/sickchill/blob/846adafdfab579281353ea08a27bbb813f9a9872/sickchill/views/index.py#L15)+--+-- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-53995](https://nvd.nist.gov/vuln/detail/CVE-2024-53995) )+--+-- @+-- user_controlled_query_argument( Call ) :-+-- kb_call_method_of_class( Call, \'get_query_argument\', Class ),+-- kb_class_has_3rd_party_super( Class, \'tornado.web.RequestHandler\'),+-- kb_arg_i_for_call( QueryParamName, 0, Call),+-- kb_const_string( QueryParamName, _ ).+-- @+--+-- Other facts combined in this predicate:+--+-- * 'ClassHas3rdPartySuper'+-- * 'ArgiForCall'+-- * 'ConstString'+--+data CallMethodOfClass = CallMethodOfClass+ Call -- ^+ MethodName -- ^+ Class -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_call_method_of_untyped_named_param( Param, ParamName, MethodName ).+-- @+--+-- __When should I use this fact__ ( motivation: [CVE-2024-47769](https://nvd.nist.gov/vuln/detail/CVE-2024-47769) )+--+-- @+-- // corePublicRouter.js+-- const express = require('express');+-- const router = express.Router();+--+-- router.route('...').get(function (req, res) {+-- const { ..., file } = req.params;+-- const fileName = file;+-- return res.sendFile(fileName, ...);+-- }+-- @+--+-- See complete source example [here](https://github.com/idurar/idurar-erp-crm/blob/d7b2215a17bb2b52acfdab8f1646685d13df9a00/backend/src/routes/coreRoutes/corePublicRouter.js#L8-L24),+--+-- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-47769](https://nvd.nist.gov/vuln/detail/CVE-2024-47769) )+--+-- @+-- arbitrary_file_read( Call ) :-+-- kb_call_resolved( Call, \'express.Router.route.get\'),+-- kb_call_method_of_untyped_named_param( Param, \'res\', \'sendFile\' ),+-- kb_arg_i_for_call( Lambda, 0, Call ),+-- kb_param_i_of_callable( Param, 1, Lambda ).+-- @+--+-- Other facts combined in this predicate:+--+-- * 'CallResolved'+-- * 'ArgiForCall'+-- * 'ParamiOfCallable'+--+data CallMethodOfUntypedNamedParam = CallMethodOfUntypedNamedParam+ Call -- ^+ MethodName -- ^+ Token.ParamName -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+-- capture inheritance from third party classes+data ClassAnnotation = ClassAnnotation+ Class -- ^+ Annotation -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++data CallableAnnotation = CallableAnnotation+ Callable -- ^+ Annotation -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_callable_source_body_length( Callable, N ).+-- @+--+-- Number of source-level @[ Ast.Stmt ]@ statements in the callable's body,+-- as observed at codegen time (before any lowering to bitcode). Independent+-- of how many bitcode instructions the body lowered to.+--+-- Intended for query predicates that need a source-fidelity signal ( e.g.+-- \"this wrapper's source body is a single statement\" for HOC-unwrap ),+-- rather than trying to reconstruct that shape from flat SSA bitcode after+-- the fact.+--+-- Emitted for 'Method', 'Lambda' and 'Function' callables. Not emitted for+-- 'Script' \-- see 'Callable.numOriginalSourceInstructions'.+data CallableSourceBodyLength = CallableSourceBodyLength+ Callable -- ^+ Word -- ^ number of source-level statements in the body+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+-- capture usage of inherited third party methods+data MethodOfClass = MethodOfClass+ Method -- ^+ Class -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_arg_i_for_call( Arg, Index, Call ).+-- @+--+-- __When should I use this fact__ ( motivation: [CVE-2024-47769](https://nvd.nist.gov/vuln/detail/CVE-2024-47769) )+--+-- @+-- // corePublicRouter.js+-- const express = require('express');+-- const router = express.Router();+--+-- router.route('...').get(function (req, res) {+-- const { ..., file } = req.params;+-- const fileName = file;+-- return res.sendFile(fileName, ...);+-- }+-- @+--+-- See complete source example [here](https://github.com/idurar/idurar-erp-crm/blob/d7b2215a17bb2b52acfdab8f1646685d13df9a00/backend/src/routes/coreRoutes/corePublicRouter.js#L8-L24),+--+-- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-47769](https://nvd.nist.gov/vuln/detail/CVE-2024-47769) )+--+-- @+-- arbitrary_file_read( Call ) :-+-- kb_call_resolved( Call, \'express.Router.route.get\'),+-- kb_call_method_of_untyped_named_param( Param, \'res\', \'sendFile\' ),+-- kb_arg_i_for_call( Lambda, 0, Call ),+-- kb_param_i_of_callable( Param, 1, Lambda ).+-- @+--+-- Other facts combined in this predicate:+--+-- * 'CallResolved'+-- * 'CallMethodOfUntypedNamedParam'+-- * 'ParamiOfCallable'+--+-- ==== __Tip 💡__+--+-- When the exact index is irrelevant, use:+--+-- @+-- kb_arg_i_for_call( Arg, _, Call ).+-- @+--+data ArgiForCall = ArgiForCall+ Arg -- ^+ ArgIndex -- ^+ Call -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_param_i_of_callable( Param, Index, Callable ).+-- @+--+-- __When should I use this fact__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) )+--+-- @+-- # http_getter.go+-- g.GET("/get/httpmeta", func(c echo.Context) error {+-- urlStr := c.QueryParam("url")+-- ...+-- getter.GetHTMLMeta(urlStr)+--+-- # html_meta.go+-- func GetHTMLMeta(urlStr string) (*HTMLMeta, error) {+-- ...+-- http.Get(urlStr)+-- @+--+-- See complete source example [here](https://github.com/usememos/memos/blob/v0.14.0/api/v1/http_getter.go#L23),+-- and [here](https://github.com/usememos/memos/blob/v0.14.0/plugin/http-getter/html_meta.go#L24)+--+-- __Writing a predicate with this fact and others__ ( motivation: [CVE-2024-29028](https://nvd.nist.gov/vuln/detail/CVE-2024-29028) )+--+-- @+-- suspected_ssrf_sink( Func ) :-+-- kb_resolved_call( Call, \'net/http.Get\' ),+-- kb_arg_i_for_call( Arg, 0, Call ),+-- utils_dataflow_path( Param, Arg ),+-- kb_param_has_resolved_type( Param, \'string\' ),+-- kb_param_i_of_callable( Param, _, Func ),+-- kb_func_def( Func, Name, _, FuncDefinedInDir ),+-- kb_call_1st_party_func_defined_in_dir( _, Name, FuncDefinedInDir ).+-- @+--+-- Other facts combined in this predicate:+--+-- * 'CallResolved'+-- * 'ArgiForCall'+-- * 'ParamResolvedType'+-- * 'FuncDef'+-- * 'Call1stPartyFuncDefinedInDir'+--+-- ==== __Tip 💡__+--+-- When the _exact index_ is _irrelevant_, use:+--+-- @+-- kb_param_i_of_callable( Param, _, Callable ).+-- @+--+data ParamiOfCallable = ParamiOfCallable+ Param -- ^+ ParamIndex -- ^ 0-based+ Callable -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++data CallResolved = CallResolved+ Call -- ^+ Resolved -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_called_from( Call, Callable ).+-- @+--+-- __When should I use this fact__+--+-- Web-app request handlers routinely /extract fields/ from the incoming+-- request \- headers, cookies, query parameters, body \- and the /name/+-- of the field is often the security-relevant piece ( e.g. the constant+-- string \'x-api-key\' says "this callable does API-key authentication" ).+-- To reason about /which/ callable extracts /which/ field, you need to+-- know that a particular call site lives inside the body of a particular+-- callable.+--+-- No pre-existing fact tied a 'Call' back to its enclosing 'Callable' \-+-- 'ParamiOfCallable' does the analogous thing for /parameters/ only.+-- Because 'CalledFrom' exposes the 'Call' /location/ ( not just its FQN ),+-- it composes with 'ArgiForCall' and 'ConstString' to also surface the+-- /arguments/ passed at that specific call site.+--+-- Code snippet ( Typescript, Next.js \- formbricks at v3.16.0 ):+--+-- @+-- export const authenticateRequest = async (request: Request) => {+-- const apiKey = request.headers.get(\"x-api-key\");+-- if (!apiKey) return null;+-- const apiKeyData = await getApiKeyWithPermissions(apiKey);+-- ...+-- };+-- @+--+-- See complete source example [here](https://github.com/formbricks/formbricks/blob/v3.16.0/apps/web/app/api/v1/auth.ts#L7-L36)+--+-- __Writing a predicate with this fact and others__+--+-- Compose 'CalledFrom' with 'ArgiForCall' and 'ConstString' to enumerate+-- the constant-string header names each callable extracts from the+-- incoming request:+--+-- @+-- extracts_request_header_named( Callable, HeaderName ) :-+-- kb_called_from( Call, Callable ),+-- kb_call_resolved( Call, \'Request.headers.get\' ),+-- kb_arg_i_for_call( HeaderConst, 0, Call ),+-- kb_const_string( HeaderConst, HeaderName ).+-- @+--+-- Applied to @authenticateRequest@ above, this yields+-- @extracts_request_header_named( authenticateRequest, \'x-api-key\' )@ \-+-- one of the six structural indicators for recognizing Shape-A+-- ( identity-carrier ) request authenticators.+--+-- Other facts combined in this predicate:+--+-- * 'CallResolved'+-- * 'ArgiForCall'+-- * 'ConstString'+--+data CalledFrom = CalledFrom+ Call -- ^+ Callable -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+++-- |+--+-- Usage:+--+-- @+-- kb_const_str( Param, Index, Callable ).+-- @+--+-- ==== __Tip 💡__+--+-- sometimes the exact index is irrelevant, see 'Arg_for_Call'+--+data ConstString = ConstString+ ConstStr -- ^+ Token.ConstStr -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_const_int( Loc, Value ).+-- @+--+-- __When should I use this fact__+--+-- Any time a query needs to discriminate on the /value/ of a constant+-- integer literal appearing in source code, typically as an argument+-- to a function that carries semantic information in that integer.+-- The canonical example is the HTTP status code passed to a response+-- constructor:+--+-- @+-- return Response.json({ error: \"unauthorized\" }, { status: 401 });+-- @+--+-- Recognizing this shape ( a bad status code returned early from a+-- callable ) is used to identify authenticating functions.+--+-- __Writing a predicate with this fact and others__+--+-- @+-- utils_ts_response_json_with_status( Function, Code ) :-+-- kb_call_resolved( OuterCall, \'nodejs.Response.json\' ),+-- kb_arg_i_for_call( DictifyCall, 1, OuterCall ),+-- kb_arg_i_for_call( KvCall, _, DictifyCall ),+-- kb_arg_i_for_call( KeyLoc, 0, KvCall ),+-- kb_const_string( KeyLoc, \'status\' ),+-- kb_arg_i_for_call( ValueLoc, 1, KvCall ),+-- kb_const_int( ValueLoc, Code ),+-- kb_called_from( OuterCall, Function ).+-- @+--+-- Other facts combined in this example predicate:+--+-- * 'CallResolved'+-- * 'ArgiForCall'+-- * 'ConstString'+-- * 'CalledFrom'+--+data ConstInteger = ConstInteger+ ConstInt -- ^+ Token.ConstInt -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_const_null( Loc ).+-- @+--+-- __When should I use this fact__+--+-- Returning early from a function with a null value has many uses cases related to security.+-- ( Python @None@ and Ruby @nil@ use this predicate as well )+--+-- __Writing a predicate with this fact and others__+--+-- @+-- kb_gated_return_null( Cond ) :-+-- kb_gated_return( Cond, ReturnedValue ),+-- kb_const_null( ReturnedValue ).+-- @+--+-- Other facts combined in this example predicate:+--+-- * 'GatedReturn'+--+data ConstNull = ConstNull Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_gated_return( Cond, ReturnedValue ).+-- @+--+-- __When should I use this fact__+--+-- Authenticating functions reject invalid credentials with an early return+-- from a guarded @if@. This fact pinpoints that rejection gate.+--+-- __Writing a predicate with this fact and others__+--+-- @+-- utils_early_return_null_on_missing_request_header_value( Callable, KeyName ) :-+-- kb_called_from( Call, Callable ),+-- kb_call_resolved( Call, \'nodejs.Request.headers.get\' ),+-- kb_arg_i_for_call( KeyArg, 0, Call ),+-- kb_const_string( KeyArg, KeyName ),+-- utils_intra_dataflow_path( Call, Var, _ ),+-- kb_gated_return_null( Cond ),+-- utils_intra_dataflow_path( Var, Cond, _ ).+--+-- kb_gated_return_null( Cond ) :- kb_gated_return( Cond, RV ), kb_const_null( RV ).+-- @+--+-- Other facts combined in this example predicate:+--+-- * 'ConstNull'+-- * 'CallResolved'+-- * 'ArgiForCall'+-- * 'ConstString'+-- * 'CalledFrom'+--+data GatedReturn = GatedReturn+ Cond -- ^+ ReturnedValue -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_comparison( Cond, Lhs, Rhs, Op ).+-- @+--+-- __When should I use this fact__+--+-- Binary comparison instructions ( currently @==@ \/ @===@ mapped to+-- the Prolog atom @eq@, and @!=@ \/ @!==@ mapped to @neq@ ) fire this+-- fact. The @Cond@ slot is the location of the comparison\'s output+-- variable, which makes composing with 'GatedReturn' straightforward:+-- when the boolean condition of an @if@ that early-returns is itself a+-- comparison, both facts share the same location in the @Cond@ slot.+--+-- __Writing a predicate with this fact and others__+--+-- @+-- gated_return_on_comparison( Lhs, Rhs, Op, ReturnedValue ) :-+-- kb_gated_return( Cond, ReturnedValue ),+-- kb_comparison( Cond, Lhs, Rhs, Op ).+-- @+--+-- Other facts combined in this example predicate:+--+-- * 'GatedReturn'+--+data Comparison = Comparison+ Cond -- ^+ Lhs -- ^+ Rhs -- ^+ ComparisonOp -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_callable_returns_value( Callable, ReturnedValue ).+-- @+--+-- __When should I use this fact__+--+-- Every explicit `return exp;` inside a callable's body fires this fact+-- once, keyed by the enclosing callable and the returned expression's+-- location. Downstream predicates like `kb_const_null` or+-- `kb_call_resolved` add semantic meaning to the value slot.+--+data CallableReturnsValue = CallableReturnsValue+ Callable -- ^+ ReturnedValue -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_callable_returns_without_value( Callable, ReturnStmtLocation ).+-- @+--+-- __When should I use this fact__+--+-- User-written bare `return;` (no value expression) fires this fact once,+-- keyed by the enclosing callable and the location of the return+-- statement itself. Distinct fact from 'CallableReturnsValue' because+-- there is no value-expression location to anchor on; the return+-- statement location is used instead.+--+data CallableReturnsWithoutValue = CallableReturnsWithoutValue+ Callable -- ^+ Location -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+--+-- __Name__+--+-- This is how the fact will look inside the Prolog file+--+-- @+-- kb_dataflow_edge( From, To ).+-- @+--+data DataflowEdge = DataflowEdge+ From -- ^+ To -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++data Arg = Arg Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data Call = Call Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data Param = Param Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data Class = Class Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++data To = To Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data From = From Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data Func = Func Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data Method = Method Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data Callable = Callable Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data ConstStr = ConstStr Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data ConstInt = ConstInt Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data Annotation = Annotation Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data AssignedValue = AssignedValue Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data ConstBoolTrue = ConstBoolTrue Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data Cond = Cond Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data Lhs = Lhs Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data Rhs = Rhs Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data ComparisonOp = ComparisonOp String deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data ReturnedValue = ReturnedValue Location deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++data Keyword = Keyword String deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data Resolved = Resolved Fqn.Fqn deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data ArgIndex = ArgIndex Word deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data ParamIndex = ParamIndex Word deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data FuncName = FuncName String deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data MethodName = MethodName String deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data ConstStrValue = ConstStrValue String deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data SuperQualifiedName = SuperQualifiedName Fqn.Fqn deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++data FuncDefinedInDir = FuncDefinedInDir FilePath deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data FuncDefinedInFile = FuncDefinedInFile FilePath deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data ClassDefinedInFile = ClassDefinedInFile FilePath deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )+data SuperDefinedInFile = SuperDefinedInFile FilePath deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+data ResolvedType = ResolvedType+ Fqn.Fqn -- ^+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++data ResolvedSuper = ResolvedSuper Fqn.Fqn deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+-- * each fact is derived from a /single/ bitcode 'Bitcode.Instruction'+--+-- * often, a single bitcode instruction will yield /more/ than one fact+--+data Fact+ = FuncDefCtor FuncDef + | ClassDefCtor ClassDef+ | ParamNameCtor ParamName+ | ArgiForCallCtor ArgiForCall+ | ConstStringCtor ConstString+ | ConstIntegerCtor ConstInteger+ | DataflowEdgeCtor DataflowEdge+ | CallResolvedCtor CallResolved+ | CalledFromCtor CalledFrom+ | ConstBoolTrueCtor ConstBoolTrue+ | ConstNullCtor ConstNull+ | GatedReturnCtor GatedReturn+ | ComparisonCtor Comparison+ | CallableReturnsValueCtor CallableReturnsValue+ | CallableReturnsWithoutValueCtor CallableReturnsWithoutValue+ | MethodOfClassCtor MethodOfClass+ | ClassAnnotationCtor ClassAnnotation+ | ParamiOfCallableCtor ParamiOfCallable+ | KeywordArgForCallCtor KeywordArgForCall+ | ParamResolvedTypeCtor ParamResolvedType+ | CallMethodOfClassCtor CallMethodOfClass+ | CallableAnnotationCtor CallableAnnotation+ | ClassHas1stPartySuperCtor ClassHas1stPartySuper+ | ClassHas3rdPartySuperCtor ClassHas3rdPartySuper+ | CallableSourceBodyLengthCtor CallableSourceBodyLength+ | AssignValueToToplevelVarNameCtor AssignValueToToplevelVarName+ | Call1stPartyFuncDefinedInDirCtor Call1stPartyFuncDefinedInDir+ | Call1stPartyFuncDefinedInFileCtor Call1stPartyFuncDefinedInFile+ | CallMethodOfUntypedNamedParamCtor CallMethodOfUntypedNamedParam+ deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )++-- |+-- Translate a code fact into a prolog fact+--+-- 'ParamName'+--+-- @+-- kb_param_has_name( Param, Name ).+-- @+--+prologify :: Fact -> String+prologify (FuncDefCtor content) = prologify_FuncDef content+prologify (ClassDefCtor content) = prologify_ClassDef content+prologify (ParamNameCtor content) = prologify_ParamName content+prologify (ArgiForCallCtor content) = prologifyArgiForCall content+prologify (ConstStringCtor content) = prologify_ConstString content+prologify (ConstIntegerCtor content) = prologify_ConstInteger content+prologify (DataflowEdgeCtor content) = prologify_DataflowEdge content+prologify (CallResolvedCtor content) = prologify_CallResolved content+prologify (CalledFromCtor content) = prologify_CalledFrom content+prologify (ConstBoolTrueCtor content) = prologify_ConstBoolTrue content+prologify (ConstNullCtor content) = prologify_ConstNull content+prologify (GatedReturnCtor content) = prologify_GatedReturn content+prologify (ComparisonCtor content) = prologify_Comparison content+prologify (CallableReturnsValueCtor content) = prologify_CallableReturnsValue content+prologify (CallableReturnsWithoutValueCtor content) = prologify_CallableReturnsWithoutValue content+prologify (MethodOfClassCtor content) = prologify_MethodOfClass content+prologify (ClassAnnotationCtor content) = prologify_ClassAnnotation content+prologify (ParamiOfCallableCtor content) = prologify_ParamiOfCallable content+prologify (ParamResolvedTypeCtor content) = prologify_ParamResolvedType content+prologify (CallMethodOfClassCtor content) = prologify_CallMethodOfClass content+prologify (KeywordArgForCallCtor content) = prologify_KeywordArgForCall content+prologify (CallableAnnotationCtor content) = prologify_CallableAnnotation content+prologify (CallableSourceBodyLengthCtor content) = prologify_CallableSourceBodyLength content+prologify (ClassHas1stPartySuperCtor content) = prologify_ClassHas1stPartySuper content+prologify (ClassHas3rdPartySuperCtor content) = prologify_ClassHas3rdPartySuper content+prologify (AssignValueToToplevelVarNameCtor content) = prologify_AssignValueToToplevelVarName content+prologify (Call1stPartyFuncDefinedInDirCtor content) = prologify_Call1stPartyFuncDefinedInDir content+prologify (Call1stPartyFuncDefinedInFileCtor content) = prologify_Call1stPartyFuncDefinedInFile content+prologify (CallMethodOfUntypedNamedParamCtor content) = prologify_CallMethodOfUntypedNamedParam content++prologify_ParamResolvedType' :: Location -> String -> String+prologify_ParamResolvedType' l fqn = printf "kb_param_has_resolved_type( %s, \'%s\' )." (locationify l) fqn++prologify_ParamResolvedType :: ParamResolvedType -> String+prologify_ParamResolvedType (ParamResolvedType (Param loc) (ResolvedType fqn)) = prologify_ParamResolvedType' loc (prologifyFqn fqn)++prologify_CallMethodOfClass' :: Location -> String -> Location -> String+prologify_CallMethodOfClass' call method klass = printf "kb_call_method_of_class( %s, \'%s\', %s )." (locationify call) method (locationify klass)++prologify_CallMethodOfClass :: CallMethodOfClass -> String+prologify_CallMethodOfClass (CallMethodOfClass (Call loc) (MethodName m) (Class c)) = prologify_CallMethodOfClass' loc m c++prologify_ParamName' :: Location -> String -> String+prologify_ParamName' l name = printf "kb_param_has_name( %s, \'%s\' )." (locationify l) name++prologify_ParamName :: ParamName -> String+prologify_ParamName (ParamName (Param loc) (Token.ParamName (Token.Named name _))) = prologify_ParamName' loc name++prologify_ClassDef' :: Location -> String -> FilePath -> String+prologify_ClassDef' l name f = printf "kb_class_def( %s, \'%s\', \'%s\' )." (locationify l) name f++prologify_ClassDef :: ClassDef -> String+prologify_ClassDef (ClassDef (Class loc) (Token.ClassName (Token.Named name _)) (ClassDefinedInFile f)) = prologify_ClassDef' loc name f++prologify_FuncDef' :: Location -> String -> FilePath -> FilePath -> String+prologify_FuncDef' l name f d = printf "kb_func_def( %s, \'%s\', \'%s\', \'%s\' )." (locationify l) name f d++prologify_FuncDef :: FuncDef -> String+prologify_FuncDef (FuncDef (Func loc) (Token.FuncName (Token.Named name _)) (FuncDefinedInFile f) (FuncDefinedInDir d)) = prologify_FuncDef' loc name f d++unquote :: String -> String+unquote = filter (/= '\'')++prologify_ConstString' :: Location -> String -> String+prologify_ConstString' l value = printf "kb_const_string( %s, \'%s\' )." (locationify l) (unquote value)++prologify_ConstString :: ConstString -> String+prologify_ConstString (ConstString (ConstStr loc) (Token.ConstStr value _)) = prologify_ConstString' loc value++prologify_ConstInteger' :: Location -> Int -> String+prologify_ConstInteger' l value = printf "kb_const_int( %s, %d )." (locationify l) value++prologify_ConstInteger :: ConstInteger -> String+prologify_ConstInteger (ConstInteger (ConstInt loc) (Token.ConstInt value _)) = prologify_ConstInteger' loc value++prologify_CallResolved'' :: Location -> String -> Token.ClassName -> String+prologify_CallResolved'' call m c = printf "kb_call_method_of_class( %s, %s, %s )." (locationify call) m (locationify (Token.getClassNameLocation c))++prologify_CallResolved' :: Location -> Fqn.Fqn -> String+prologify_CallResolved' call (Fqn.CallMethodOfClass _ m c) = prologify_CallResolved'' call m c +prologify_CallResolved' call fqn = printf "kb_call_resolved( %s, \'%s\' )." (locationify call) (prologifyFqn fqn)++prologify_DataflowEdge :: DataflowEdge -> String+prologify_DataflowEdge (DataflowEdge (From u) (To v)) = prologify_DataflowEdge' u v++prologify_DataflowEdge' :: Location -> Location -> String+prologify_DataflowEdge' u v = printf "kb_dataflow_edge( %s, %s )." (locationify u) (locationify v)++prologify_CallResolved :: CallResolved -> String+prologify_CallResolved (CallResolved (Call call) (Resolved fqn)) = prologify_CallResolved' call fqn++prologify_ConstBoolTrue' :: Location -> String+prologify_ConstBoolTrue' trueValue = printf "kb_class_name( %s, \'%s\' )." (locationify trueValue)++prologify_ConstBoolTrue :: ConstBoolTrue -> String+prologify_ConstBoolTrue (ConstBoolTrue trueValue) = prologify_ConstBoolTrue' trueValue++prologify_ConstNull' :: Location -> String+prologify_ConstNull' l = printf "kb_const_null( %s )." (locationify l)++prologify_ConstNull :: ConstNull -> String+prologify_ConstNull (ConstNull loc) = prologify_ConstNull' loc++prologify_GatedReturn' :: Location -> Location -> String+prologify_GatedReturn' c v = printf "kb_gated_return( %s, %s )." (locationify c) (locationify v)++prologify_GatedReturn :: GatedReturn -> String+prologify_GatedReturn (GatedReturn (Cond c) (ReturnedValue v)) = prologify_GatedReturn' c v++prologify_Comparison' :: Location -> Location -> Location -> String -> String+prologify_Comparison' c l r op = printf "kb_comparison( %s, %s, %s, %s )." (locationify c) (locationify l) (locationify r) op++prologify_Comparison :: Comparison -> String+prologify_Comparison (Comparison (Cond c) (Lhs l) (Rhs r) (ComparisonOp op)) = prologify_Comparison' c l r op++prologify_CallableReturnsValue' :: Location -> Location -> String+prologify_CallableReturnsValue' c v = printf "kb_callable_returns_value( %s, %s )." (locationify c) (locationify v)++prologify_CallableReturnsValue :: CallableReturnsValue -> String+prologify_CallableReturnsValue (CallableReturnsValue (Callable c) (ReturnedValue v)) = prologify_CallableReturnsValue' c v++prologify_CallableReturnsWithoutValue' :: Location -> Location -> String+prologify_CallableReturnsWithoutValue' c s = printf "kb_callable_returns_without_value( %s, %s )." (locationify c) (locationify s)++prologify_CallableReturnsWithoutValue :: CallableReturnsWithoutValue -> String+prologify_CallableReturnsWithoutValue (CallableReturnsWithoutValue (Callable c) s) = prologify_CallableReturnsWithoutValue' c s++prologify_MethodOfClass' :: Location -> Location -> String+prologify_MethodOfClass' m c = printf "kb_method_of_class( %s, %s )." (locationify m) (locationify c)++prologify_MethodOfClass :: MethodOfClass -> String+prologify_MethodOfClass (MethodOfClass (Method m) (Class c)) = prologify_MethodOfClass' m c++prologify_ClassHas1stPartySuper' :: Location -> String -> FilePath -> String+prologify_ClassHas1stPartySuper' c s f = printf "kb_class_has_1st_party_super( %s, \'%s\', \'%s\' )." (locationify c) s f+ +prologify_ClassHas1stPartySuper :: ClassHas1stPartySuper -> String+prologify_ClassHas1stPartySuper (ClassHas1stPartySuper (Class c) (Token.SuperName (Token.Named s _)) (SuperDefinedInFile f)) = prologify_ClassHas1stPartySuper' c s f++prologify_ClassHas3rdPartySuper' :: Location -> String -> Fqn.Fqn -> String+prologify_ClassHas3rdPartySuper' c s f = printf "kb_class_has_3rd_party_super( %s, \'%s\', \'%s\' )." (locationify c) s (prologifyFqn f)+ +prologify_ClassHas3rdPartySuper :: ClassHas3rdPartySuper -> String+prologify_ClassHas3rdPartySuper (ClassHas3rdPartySuper (Class c) (Token.SuperName (Token.Named s _)) (SuperQualifiedName f)) = prologify_ClassHas3rdPartySuper' c s f++prologify_AssignValueToToplevelVarName :: AssignValueToToplevelVarName -> String+prologify_AssignValueToToplevelVarName (AssignValueToToplevelVarName value varname) = prologify_AssignValueToToplevelVarName' value varname++prologify_AssignValueToToplevelVarName' :: AssignedValue -> Token.VarName -> String+prologify_AssignValueToToplevelVarName' (AssignedValue value) (Token.VarName (Token.Named varname l)) = prologify_AssignValueToToplevelVarName'' value varname (Location.filename l)++prologify_AssignValueToToplevelVarName'' :: Location -> String -> FilePath -> String+prologify_AssignValueToToplevelVarName'' value varname f = printf "kb_assign_value_to_toplevel_varname( %s, \'%s\', \'%s\', \'%s\')." (locationify value) varname f (takeDirectory f)++prologify_Call1stPartyFuncDefinedInFile :: Call1stPartyFuncDefinedInFile -> String+prologify_Call1stPartyFuncDefinedInFile (Call1stPartyFuncDefinedInFile call func f) = prologify_Call1stPartyFuncDefinedInFile' call func f++prologify_Call1stPartyFuncDefinedInFile' :: Call -> FuncName -> FuncDefinedInFile -> String+prologify_Call1stPartyFuncDefinedInFile' (Call call) (FuncName func) (FuncDefinedInFile f) = prologify_Call1stPartyFuncDefinedInFile'' call func f++prologify_Call1stPartyFuncDefinedInFile'' :: Location -> String -> FilePath -> String+prologify_Call1stPartyFuncDefinedInFile'' call func f = printf "kb_call_1st_party_func_defined_in_file( %s, \'%s\', \'%s\' )." (locationify call) func f++prologify_Call1stPartyFuncDefinedInDir :: Call1stPartyFuncDefinedInDir -> String+prologify_Call1stPartyFuncDefinedInDir (Call1stPartyFuncDefinedInDir call func d) = prologify_Call1stPartyFuncDefinedInDir' call func d++prologify_Call1stPartyFuncDefinedInDir' :: Call -> FuncName -> FuncDefinedInDir -> String+prologify_Call1stPartyFuncDefinedInDir' (Call call) (FuncName f) (FuncDefinedInDir d) = prologify_Call1stPartyFuncDefinedInDir'' call f d++prologify_Call1stPartyFuncDefinedInDir'' :: Location -> String -> FilePath -> String+prologify_Call1stPartyFuncDefinedInDir'' call f d = printf "kb_call_1st_party_func_defined_in_dir( %s, \'%s\', \'%s\' )." (locationify call) f d++prologify_CallMethodOfUntypedNamedParam :: CallMethodOfUntypedNamedParam -> String+prologify_CallMethodOfUntypedNamedParam (CallMethodOfUntypedNamedParam call method p) = prologify_CallMethodOfUntypedNamedParam' call method p++prologify_CallMethodOfUntypedNamedParam' :: Call -> MethodName -> Token.ParamName -> String+prologify_CallMethodOfUntypedNamedParam' (Call call) (MethodName m) (Token.ParamName (Token.Named _ p)) = prologify_CallMethodOfUntypedNamedParam'' call m p++prologify_CallMethodOfUntypedNamedParam'' :: Location -> String -> Location -> String+prologify_CallMethodOfUntypedNamedParam'' call m p = printf "kb_call_method_of_untyped_named_param( %s, \'%s\', %s )." (locationify call) m (locationify p)++prologifyArgiForCall' :: Location -> Word -> Location -> String+prologifyArgiForCall' a i c = printf "kb_arg_i_for_call( %s, %u, %s )." (locationify a) i (locationify c)++prologifyArgiForCall :: ArgiForCall -> String+prologifyArgiForCall (ArgiForCall (Arg a) (ArgIndex i) (Call c)) = prologifyArgiForCall' a i c++prologify_ClassAnnotation' :: Location -> Location -> String+prologify_ClassAnnotation' c a = printf "kb_class_has_annotation( %s, %s )." (locationify c) (locationify a)++prologify_ClassAnnotation :: ClassAnnotation -> String+prologify_ClassAnnotation (ClassAnnotation (Class c) (Annotation a)) = prologify_ClassAnnotation' c a++prologify_KeywordArgForCall' :: String -> Location -> Location -> String+prologify_KeywordArgForCall' kw a c = printf "kb_keyword_arg_for_call( %s, %s, %s )." (locationify a) kw (locationify c)++prologify_KeywordArgForCall :: KeywordArgForCall -> String+prologify_KeywordArgForCall (KeywordArgForCall (Keyword kw) (Arg a) (Call c)) = prologify_KeywordArgForCall' kw a c++prologify_CallableAnnotation' :: Location -> Location -> String+prologify_CallableAnnotation' c a = printf "kb_callable_has_annotation( %s, %s )." (locationify c) (locationify a)++prologify_CallableAnnotation :: CallableAnnotation -> String+prologify_CallableAnnotation (CallableAnnotation (Callable c) (Annotation a)) = prologify_CallableAnnotation' c a++prologify_CallableSourceBodyLength' :: Location -> Word -> String+prologify_CallableSourceBodyLength' c n = printf "kb_callable_source_body_length( %s, %u )." (locationify c) n++prologify_CallableSourceBodyLength :: CallableSourceBodyLength -> String+prologify_CallableSourceBodyLength (CallableSourceBodyLength (Callable c) n) = prologify_CallableSourceBodyLength' c n++prologify_ParamiOfCallable' :: Location -> Word -> Location -> String+prologify_ParamiOfCallable' p i c = printf "kb_param_i_of_callable( %s, %u, %s )." (locationify p) i (locationify c)++prologify_ParamiOfCallable :: ParamiOfCallable -> String+prologify_ParamiOfCallable (ParamiOfCallable (Param p) (ParamIndex i) (Callable c)) = prologify_ParamiOfCallable' p i c++prologify_CalledFrom' :: Location -> Location -> String+prologify_CalledFrom' call callable = printf "kb_called_from( %s, %s )." (locationify call) (locationify callable)++prologify_CalledFrom :: CalledFrom -> String+prologify_CalledFrom (CalledFrom (Call call) (Callable callable)) = prologify_CalledFrom' call callable++normalizeChar :: Char -> String+normalizeChar '/' = "_slash_"+normalizeChar '.' = "_dot_"+normalizeChar '-' = "_dash_"+normalizeChar '[' = "_lbrack_"+normalizeChar ']' = "_rbrack_"+normalizeChar '(' = "_lparen_"+normalizeChar ')' = "_rparen_"+normalizeChar c = [c]++normalize :: FilePath -> FilePath+normalize path = concatMap normalizeChar path++prologifyFqn :: Fqn.Fqn -> String+prologifyFqn (Fqn.ThirdPartyImport fqn) = prologifyThirdPartyImport fqn+prologifyFqn (Fqn.FieldedAccess fqn fieldName) = prologifyFieldedAccess fqn fieldName+prologifyFqn fqn = show fqn++prologifyThirdPartyImport :: Fqn.ThirdPartyImportContent -> String+prologifyThirdPartyImport (Fqn.ThirdPartyImportContent p [] Nothing _) = p+prologifyThirdPartyImport (Fqn.ThirdPartyImportContent p [] (Just name) _) = p ++ "." ++ name+prologifyThirdPartyImport (Fqn.ThirdPartyImportContent p rest Nothing _) = p ++ "." ++ List.intercalate "." rest+prologifyThirdPartyImport (Fqn.ThirdPartyImportContent p rest (Just name) _) = p ++ "." ++ List.intercalate "." rest ++ "." ++ name++prologifyFieldedAccess :: Fqn.Fqn -> Token.FieldName -> String+prologifyFieldedAccess fqn (Token.FieldName (Token.Named fieldName _)) = (prologifyFqn fqn) ++ "." ++ fieldName++locationify :: Location -> String+locationify l = let+ x = Location.lineStart l+ y = Location.colStart l+ z = Location.lineEnd l+ w = Location.colEnd l+ f = normalize (Location.filename l)+ in printf "startloc_%u_%u_endloc_%u_%u_%s" x y z w f++restoreloc :: String -> Maybe Location+restoreloc s = case parseOnly locationParser (T.pack s) of { Right l -> Just l; _ -> Nothing; }++locationParser :: Parser Location+locationParser = do {++ _ <- string "startloc_"; x <- decimal; _ <- char '_'; y <- decimal;+ _ <- string "_endloc_" ; z <- decimal; _ <- char '_'; w <- decimal;+ _ <- char '_'; fname <- takeText;++ pure Location {+ filename = restoreFilename (T.unpack fname),+ lineStart = x,+ colStart = y,+ lineEnd = z,+ colEnd = w+ }+}++data Rule = Rule { from :: Text, to :: Text }++rules :: [ Rule ]+rules =+ [ Rule { from = "_slash_", to = "/" }+ , Rule { from = "_dot_", to = "." }+ , Rule { from = "_dash_", to = "-" }+ , Rule { from = "_lbrack_", to = "[" }+ , Rule { from = "_rbrack_", to = "]" }+ , Rule { from = "_lparen_", to = "(" }+ , Rule { from = "_rparen_", to = ")" }+ ]++applyRule :: Rule -> Text -> Text+applyRule Rule { from = f, to = t } = T.replace f t++applyRuleStep :: Text -> Rule -> Text+applyRuleStep acc r = applyRule r acc++restore :: Text -> Text+restore txt = List.foldl' applyRuleStep txt rules++textToStringAdapter :: (Text -> Text) -> String -> String+textToStringAdapter f = T.unpack . f . T.pack++restoreFilename :: String -> String restoreFilename = textToStringAdapter restore