module

Noir::TreeSitterKotlinParameterExtractor

Tree-sitter-backed parameter extractor for Kotlin Spring.

Mirrors TreeSitterJavaParameterExtractor for the Kotlin AST (parameter modifiers come before the parameter, primary constructor properties carry DTO fields, annotation arguments use value_arguments instead of annotation_argument_list).

Covered:

  • @PathVariable (skipped — URL carries it)
  • @RequestBody / messaging @Payload — defaults to "json" (or "form" via consumes=)
  • @RequestParam(value/name = "x", defaultValue = "y") — query
  • @RequestHeader(value/name = "x") / messaging @Header — header, including HttpHeaders.X_FOO constant normalisation
  • @CookieValue(name = "lorem", defaultValue = "ipsum") — cookie
  • Primitive types (Long/Int/String/Boolean/MultipartFile) emit directly with the declared parameter name
  • User-defined classes — DTO field expansion via the caller-supplied class_fields index. Kotlin DTOs are usually data class Foo(val a, var b) — properties live on the primary constructor and are all exposed as if they were public fields with synthesised setters.
  • params = ["x=v"] and headers = ["X-H=v"] on the method's mapping annotation synthesise extra Params. params= for GET/HEAD/OPTIONS is "query"; otherwise "form" (matches Spring's dispatch convention).

Not covered yet (see #1298):

  • HttpServletRequest body scan — Kotlin idiom rarely uses it.
  • Meta-annotations (custom annotations composing @RequestMapping).

Constants

HTTP_HEADER_SPECIAL_CASES = {"Etag" => "ETag", "Te" => "TE", "Www-Authenticate" => "WWW-Authenticate", "X-Frame-Options" => "X-Frame-Options"}
INJECTED_PARAM_ANNOTATIONS = Set {"AuthenticationPrincipal", "CurrentUser", "CurrentSecurityContext", "SessionAttribute", "RequestAttribute"}

Parameter annotations whose value is supplied by a Spring argument resolver, not bound from the HTTP request the client controls. A parameter carrying one of these is NOT an attack-surface input even when its type is a bindable DTO (the classic @AuthenticationPrincipal user: User, or a custom meta-annotation like @CurrentUser), so it must be excluded from the un-annotated implicit-binding path that would otherwise expand its DTO fields into phantom params. Validation annotations (@Valid, …) are intentionally absent — they leave binding semantics untouched.

PRIMITIVE_TYPES = Set {"long", "int", "integer", "short", "byte", "char", "character", "boolean", "string", "float", "double", "number", "bigdecimal", "biginteger", "uuid", "date", "localdate", "localdatetime", "localtime", "instant", "multipartfile"}

Scalar types Spring binds from a single request value. Type names are first resolved to their leaf identifier by leaf_type_name (so List<Long> reduces to List); annotated request params emit by name regardless of type. Mirrors the Java extractor's SIMPLE_PARAM_TYPES so @RequestParam ids: List<Long> surfaces instead of being silently dropped.

QUERY_VERB_PARAMS = Set {"GET", "HEAD", "OPTIONS"}

Verbs whose params = [...] constraint emits as query parameters. Anything else uses form data.

Instance methods

extract_class_fields(source : String) : Hash(String, Array(FieldInfo))

Extract {class_name => [FieldInfo]} from source. Kotlin DTO fields can come from:

  1. Primary constructor parameters declared with val / var (the common data class Foo(val a: Int, var b: String) idiom).
  2. property_declaration nodes inside the class body (var/ val properties on regular classes).

Properties are treated as setter-accessible by default — Kotlin synthesises setters for var, and val properties usually serialise just fine for our endpoint-fan-out purposes.

Source
extract_class_fields_from(root : LibTreeSitter::TSNode, source : String) : Hash(String, Array(FieldInfo))

_from(root, source, ...) variants accept a pre-parsed root so the Kotlin Spring analyzer can amortise the tree-sitter parse across multiple extractions on the same file. Tree lifetime is the caller's responsibility.

Source
extract_class_supertypes(source : String) : Hash(String, String)

Map each class to the simple name of its superCLASS (the supertype invoked with (), e.g. class Owner : Person() → {"Owner" => "Person"}). Interface supertypes (: Foo without parens) carry no bindable fields and are skipped. Drives the DTO index's cross-file inheritance merge so a command object that extends a base class inherits its bindable fields.

Source
extract_class_supertypes_from(root : LibTreeSitter::TSNode, source : String) : Hash(String, String)
Source
extract_consumes(source : String, class_name : String, method_name : String) : String | Nil

Read consumes = ["..."] / consumes = arrayOf("...") off the method's mapping annotation. Returns "form" / "json" / nil.

Source
extract_consumes_from(root : LibTreeSitter::TSNode, source : String, class_name : String, method_name : String) : String | Nil
Source
extract_consumes_from_method(method : LibTreeSitter::TSNode, source : String) : String | Nil
Source
extract_imports(source : String) : Array(ImportDecl)
Source
extract_imports_from(root : LibTreeSitter::TSNode, source : String) : Array(ImportDecl)
Source
extract_method_parameters(source : String, class_name : String, method_name : String, verb : String, parameter_format : String | Nil, class_fields : Hash(String, Array(FieldInfo)), string_constants = Hash(String, String).new, local_string_constants = Hash(String, String).new) : Array(Param)

Walk method formal parameters + synthesised params=/headers= constraints on the method's mapping annotation. Returns the combined parameter list in the order the legacy analyzer emitted them: formal-parameter sweep first, constraint sweep appended.

Source
extract_method_parameters_from(root : LibTreeSitter::TSNode, source : String, class_name : String, method_name : String, verb : String, parameter_format : String | Nil, class_fields : Hash(String, Array(FieldInfo)), string_constants = Hash(String, String).new, local_string_constants = Hash(String, String).new) : Array(Param)
Source
extract_method_parameters_from_method(method : LibTreeSitter::TSNode, source : String, verb : String, parameter_format : String | Nil, class_fields : Hash(String, Array(FieldInfo)), string_constants = Hash(String, String).new, local_string_constants = Hash(String, String).new) : Array(Param)
Source
extract_package_name(source : String) : String
Source
extract_package_name_from(root : LibTreeSitter::TSNode, source : String) : String
Source
extract_server_request_parameters_from_method(method : LibTreeSitter::TSNode, source : String, verb : String, parameter_format : String | Nil, class_fields : Hash(String, Array(FieldInfo))) : Array(Param)
Source
index_functions_from(root : LibTreeSitter::TSNode, source : String) : Hash(String, LibTreeSitter::TSNode)
Source

Nested types