Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
IT Security NachrichtenKI-Agenten hebeln klassisches IT-Asset-Management aus(24.09.2026 um 07:14 Uhr)
IT Security NachrichtenMicrosoft erneuert Surface Pro und Laptop mit Snapdragon X2 Plus(24.09.2026 um 07:42 Uhr)
IT Security DownloadsGitHub Release: cline/cline vsdk/shared/v0.0.86 (24.09.2026)(24.09.2026 um 07:43 Uhr)
IT Security DownloadsGitHub Release: cline/cline vsdk/llms/v0.0.86 (24.09.2026)(24.09.2026 um 07:43 Uhr)
IT Security DownloadsGitHub Release: cline/cline vsdk/agents/v0.0.86 (24.09.2026)(24.09.2026 um 07:43 Uhr)
IT Security DownloadsGitHub Release: cline/cline vsdk/core/v0.0.86 (24.09.2026)(24.09.2026 um 07:43 Uhr)
IT Security DownloadsGitHub Release: cline/cline vcli-v3.0.65 (24.09.2026)(24.09.2026 um 07:54 Uhr)
IT Security NachrichtenKI-Agenten hebeln klassisches IT-Asset-Management aus(24.09.2026 um 07:14 Uhr)
IT Security NachrichtenMicrosoft erneuert Surface Pro und Laptop mit Snapdragon X2 Plus(24.09.2026 um 07:42 Uhr)
IT Security DownloadsGitHub Release: cline/cline vsdk/shared/v0.0.86 (24.09.2026)(24.09.2026 um 07:43 Uhr)
IT Security DownloadsGitHub Release: cline/cline vsdk/llms/v0.0.86 (24.09.2026)(24.09.2026 um 07:43 Uhr)
IT Security DownloadsGitHub Release: cline/cline vsdk/agents/v0.0.86 (24.09.2026)(24.09.2026 um 07:43 Uhr)
IT Security DownloadsGitHub Release: cline/cline vsdk/core/v0.0.86 (24.09.2026)(24.09.2026 um 07:43 Uhr)
IT Security DownloadsGitHub Release: cline/cline vcli-v3.0.65 (24.09.2026)(24.09.2026 um 07:54 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Tsonnet #16 - Late binding and Jsonnet inconsistency

Welcome to the Tsonnet series! If you're just joining, you can check out how it all started in the first post of the series. In the previous post, we added support for local variables: Tsonnet #15…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Welcome to the Tsonnet series!



If you're just joining, you can check out how it all started in the first post of the series.



In the previous post, we added support for local variables:







There's one aspect that I intentionally left out of scope that I want to talk about now: late binding.



What is late binding? Paraphrasing Wikipedia:




Late binding is a computer programming mechanism in which the method being called upon an object, or the function being called with arguments, is looked up by name at runtime.




In Tsonnet, even though we lack objects and functions yet, we aim to make the whole language lazily evaluated -- a requirement to be compatible with Jsonnet.






The Rationale for Lazy Semantics



In the page Rationale for Lazy Semantics, the design rationale of Jsonnet explains why it adopts the lazy evaluation semantics.



It says:




Therefore, for consistency, the whole language is lazy.




However, Jsonnet is rather inconsistent with late binding.



Let me show you.






Late binding examples



Here's a late binding assignment in Jsonnet:




// samples/variables/late_binding_array.jsonnet
local x = ["apple", y[1]],
y = [x[0], "banana"];
x






This works according to the design rationale:




$ jsonnet samples/variables/late_binding_array.jsonnet
[
"apple",
"banana"
]






Now, let's go one step back and try a simpler scenario:




// samples/variables/late_binding_simple.jsonnet
local a = 1;
local c = a + b;
local b = 2;
c






It does not work:




$ jsonnet samples/variables/late_binding_simple.jsonnet
samples/variables/late_binding_simple.jsonnet:2:15-16 Unknown variable: b

local c = a + b;






not funny



Our minds are conditioned to think in a linear fashion, but given lazy semantics, the local c binding should be left unevaluated, not causing any error. When c is eventually required, b, which was not in scope earlier, now is, making c valid.



I know it seems confusing if you're coming from a language where the compiler eagerly evaluates definitions from top to bottom. How are we supposed to use something not in scope yet, right!? But in lazy semantics, the compiler defers the evaluation, keeping track of the symbols that refer to the blocks of code (still unevaluated).



Turns out, Tsonnet supports late binding out of the box, given the implementation covered in the previous post:




$ dune exec -- tsonnet samples/variables/late_binding_simple.jsonnet
3






Array access, not yet:




$ dune exec -- tsonnet samples/variables/late_binding_array.jsonnet
samples/variables/late_binding_array.jsonnet:1:22 Invalid syntax

1: local x = ["apple", y[1]],
^^^^^^^^^^^^^^^^^^^^^^^






But only because we haven't implemented index-based access for arrays.



Let's solve this now.






Index-based access for arrays



We need a new expr variant type to cover index-based access:




diff --git a/lib/ast.ml b/lib/ast.ml
index cf2bf81..d712ba5 100644
--- a/lib/ast.ml
+++ b/lib/ast.ml
@@ -32,6 +32,7 @@ type expr =
| Local of position * (string * expr) list
| Unit
| Seq of expr list
+ | IndexedExpr of position * string * expr

let dummy_pos = {
startpos = Lexing.dummy_pos;
@@ -43,4 +44,36 @@ let dummy_expr = Unit
let pos_from_lexbuf (lexbuf : Lexing.lexbuf) : position =
{ startpos = lexbuf.lex_curr_p;
endpos = lexbuf.lex_curr_p;
- };
+ }
+
+let string_of_type = function
+ | Null _ -> "Null"
+ | Number (_, number) ->
+ (match number with
+ | Int _ -> "Int"
+ | Float _ -> "Float")
+ | Bool _ -> "Bool"
+ | String _ -> "String"
+ | Ident _ -> "Identity"
+ | Array _ -> "Array"
+ | Object _ -> "Object"
+ | BinOp (_, bin_op, _, _) ->
+ let prefix = "Binary Operation" in
+ let bin_op = match bin_op with
+ | Add -> "+"
+ | Subtract -> "-"
+ | Multiply -> "*"
+ | Divide -> "/"
+ in prefix ^ " " ^ bin_op
+ | UnaryOp (_, unary_op, _) ->
+ let prefix = "Unary Operation" in
+ let unary_op = match unary_op with
+ | Plus -> "+"
+ | Minus -> "-"
+ | Not -> "!"
+ | BitwiseNot -> "~"
+ in prefix ^ " " ^ unary_op
+ | Local _ -> "Local"
+ | Unit -> "()"
+ | Seq _ -> "Sequence"
+ | IndexedExpr _ -> "Indexed Expression"






The string_of_type will come in handy to output friendlier error messages.



The parser can then make use of IndexedExpr:




diff --git a/lib/parser.mly b/lib/parser.mly
index c4fc9b4..bf88591 100644
--- a/lib/parser.mly
+++ b/lib/parser.mly
@@ -50,7 +50,8 @@ assignable_expr:
| e = scoped_expr { e }
| e = literal { e }
| e1 = assignable_expr; op = bin_op; e2 = assignable_expr { BinOp (with_pos $startpos $endpos, op, e1, e2) }
| op = unary_op; e = assignable_expr; { UnaryOp (with_pos $startpos $endpos, op, e) }
+ | varname = ID; LEFT_SQR_BRACKET; e = assignable_expr; RIGHT_SQR_BRACKET { IndexedExpr (with_pos $startpos $endpos, varname, e) }
;

scoped_expr:






Let's add a new function to the Env module to facilitate the variable lookups:




let find_var varname env ~succ ~err =
match Map.find_opt varname env with
| Some expr ->
let* (env', evaluated_expr) = succ env expr in
(* Since `succ` has evaluated expr, we can now memoize it
and subsequent look ups operating in this new environment
will already have it evaluated *)

let updated_env = Map.add varname evaluated_expr env' in
Result.ok (updated_env, evaluated_expr)
| None -> err ("Undefined variable: " ^ varname)






The memoizing is a bonus to make our implementation faster in future lookups. It's nice when we can get a performance boost almost for free.



Now comes the interpreter.



We were returning arrays as-is before, but to make indexed access work properly, we need to evaluate each entry before returning it:




diff --git a/lib/tsonnet.ml b/lib/tsonnet.ml
index f136fd6..574d2af 100644
@@ -1,8 +1,6 @@
open Ast
open Result
-
-let (let*) = Result.bind
-let (>>=) = Result.bind
+open Syntax_sugar

(** [parse s] parses [s] into an AST. *)
let parse (filename: string) =
@@ -60,11 +58,21 @@ let interpret_unary_op (op: unary_op) (evaluated_expr: expr) =
(** [interpret expr] interprets and reduce the intermediate AST [expr] into a result AST. *)
let rec interpret env expr =
match expr with
- | Null _ | Bool _ | String _ | Number _ | Array _ | Object _ -> ok (env, expr)
+ | Null _ | Bool _ | String _ | Number _ | Object _ -> ok (env, expr)
+ | Array (pos, exprs) ->
+ (let rec eval' env' exprs' =
+ match exprs' with
+ | [] -> ok (env', [])
+ | e :: exprs ->
+ (let* (env1, expr') = interpret env' e in
+ let* (env2, rest) = eval' env1 exprs in
+ ok (env2, expr' :: rest))
+ in eval' env exprs >>= fun (env3, exprs') -> ok (env3, Array (pos, exprs'))
+ )






The identity match can now use Env.find_var:




   | Ident (pos, varname) ->
- (match Env.Map.find_opt varname env with
- | Some expr -> interpret env expr
- | None -> Error.trace ("Undefined variable: " ^ varname) pos >>= error)
+ Env.find_var varname env
+ ~succ:(fun env' expr -> interpret env' expr)
+ ~err:(fun err_msg -> Error.trace err_msg pos >>= error)
| BinOp (pos, op, e1, e2) ->
(let* (env1, e1') = interpret env e1 in
let* (env2, e2') = interpret env1 e2 in






Didn't change much here, but we now have memorized lookups.



And, the IndexedExpr to wrap it up:




@@ -93,6 +101,23 @@ let rec interpret env expr =
| [] -> ok (env, Unit)
| [expr] -> interpret env expr
| (expr :: exprs) -> interpret env expr >>= fun (env', _) -> interpret env' (Seq exprs))
+ | IndexedExpr (pos, varname, index_expr) ->
+ Env.find_var varname env
+ ~succ:(fun env' expr ->
+ match expr with
+ | Array (_, exprs) ->
+ let* (env', idx_expr') = interpret env' index_expr in
+ (match idx_expr' with
+ | Number (_, Int i)->
+ (let len = List.length exprs in
+ if i >= 0 && i < len
+ then ok (env', List.nth exprs i)
+ else Error.trace ("Index out of bounds. Trying to access index " ^ string_of_int i ^ " but \"" ^ varname ^ "\" length is " ^ string_of_int len) pos >>= error)
+ | expr' -> Error.trace ("Expected Integer index, got " ^ Ast.string_of_type expr') pos >>= error
+ )
+ | _ -> Error.trace ("Expected array, found: " ^ varname) pos >>= error
+ )
+ ~err:(fun err_msg -> Error.trace err_msg pos >>= error)

let run (filename: string) : (string, string) result =
let env = Env.Map.empty in






Since we need to look up the array index value here, Env.find_var will look it up, and then we can do all sorts of checks, like index out of bounds, invalid index type, etc.



It looks ugly, I know, sorry! Refactoring will follow, but this should be sufficient to get done with our important feature.



Attentive readers might have noticed the open Syntax_sugar. This is a new module to combine function aliases reused across the project:




let (let*) = Result.bind
let (>>=) = Result.bind






Let's add a few new sample files to cover some use cases -- I'm ignoring the others already presented earlier.



This is to cover arrays' index out of bounds:




// samples/errors/array_index_out_of_bounds.jsonnet
local list = [1,2,3];
local index = 4;
list[index]






This is to cover arrays' invalid index based access:




// samples/errors/array_index_not_int.jsonnet
local list = [1,2,3];
local index = "O";
list[index]






Cram tests for the happy paths:




diff --git a/test/cram/variables.t b/test/cram/variables.t
index b9f54f1..2889c7f 100644
--- a/test/cram/variables.t
+++ b/test/cram/variables.t
@@ -9,3 +9,10 @@

$ tsonnet ../../samples/variables/scoped.jsonnet
42
+
+ $ tsonnet ../../samples/variables/late_binding_simple.jsonnet
+ 3
+
+ $ tsonnet ../../samples/variables/late_binding_array.jsonnet
+ [ "apple", "banana" ]
+






And here are the cram tests for the unhappy paths:




diff --git a/test/cram/errors.t b/test/cram/errors.t
index 7b2e2ab..6a85e3b 100644
--- a/test/cram/errors.t
+++ b/test/cram/errors.t
@@ -37,3 +37,24 @@
^^^^^^^^^^^^^^^^
[1]

+ $ tsonnet ../../samples/errors/undefined_local.jsonnet
+ ../../samples/errors/undefined_local.jsonnet:3:0 Undefined variable: c
+
+ 3: c
+ ^
+ [1]
+
+ $ tsonnet ../../samples/errors/array_index_out_of_bounds.jsonnet
+ ../../samples/errors/array_index_out_of_bounds.jsonnet:3:0 Index out of bounds. Trying to access index 4 but "list" length is 3
+
+ 3: list[index]
+ ^^^^^^^^^^^
+ [1]
+
+ $ tsonnet ../../samples/errors/array_index_not_int.jsonnet
+ ../../samples/errors/array_index_not_int.jsonnet:3:0 Expected Integer index, got String
+
+ 3: list[index]
+ ^^^^^^^^^^^
+ [1]
+






And we are done:




$ dune exec -- tsonnet samples/variables/late_binding_simple.jsonnet
3

$ dune exec -- tsonnet samples/variables/late_binding_array.jsonnet
[ "apple", "banana" ]

$ dune exec -- tsonnet samples/errors/array_index_not_int.jsonnet
samples/errors/array_index_not_int.jsonnet:3:0 Expected Integer index, got String

3: list[index]
^^^^^^^^^^^
$ dune exec -- tsonnet samples/errors/array_index_out_of_bounds.jsonnet
samples/errors/array_index_out_of_bounds.jsonnet:3:0 Index out of bounds. Trying to access index 4 but "list" length is 3

3: list[index]
^^^^^^^^^^^









Conclusion



We can write code declaratively in Tsonnet, independent of declaration order. It should keep working consistently, and tests will guarantee that this property still holds in the future.



Eventually, when Tsonnet is ready for practical use, editor tooling such as jump to definition will help with the non-linear declarations. Tools surrounding the language are as important as the language itself.



As mentioned, refactoring will follow to address the ugly IndexedExpr. The interpret function is becoming bloated the more we introduce new code, making it difficult to read. I will explore some improvements soon.



The diff with all the changes can be seen here.






Don't bind yourself late to Tsonnet updates! Subscribe to Bit Maybe Wise now, but feel free to read the posts in any order you want.



Photo by Erik Mclean on Unsplash

CTI Threat Relationship Graph2 Knoten / 1 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
SOC Incident Playbook: Vulnerability Remediation & Verification
title: Detect Exploitation - Tsonnet #16 - Late binding and Jsonnet inconsistency
id: 4f0d2ffb-534a-4e1b-8a34-5bb686a7d0c0
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-24
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-24"
        description = "YARA Signature for "
    strings:
        $str = "Tsonnet #16 - Late binding and" ascii wide
    condition:
        any of them
}
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich Tsonnet #16 - Late binding and Jsonnet i.... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Tsonnet #16 - Late binding and Jsonnet inconsistency

Thematisch verwandte Begriffe: Tsonnet, Late, binding, Jsonnet · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-96676 | A vulnerability was identified in Fast FAC1900R 20190827_2.0.2. The impa…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel TTP ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick