new file mode 100644
@@ -0,0 +1,206 @@
+From 5192a59c2c6ca6ab0667cbee608db29c0710f346 Mon Sep 17 00:00:00 2001
+From: itchyny <itchyny@cybozu.co.jp>
+Date: Sat, 9 May 2026 17:08:43 +0900
+Subject: [PATCH 1/2] Reject embedded NUL bytes in module import paths
+
+jq accepts embedded NUL bytes at the language level but resolves
+module import paths through NUL-terminated C strings, so the path
+validated by policy or audit code could differ from the on-disk
+path jq actually opens. Pass jv through gen_import so the AST
+preserves the original bytes, and reject embedded NULs in
+validate_relpath.
+
+Fixes CVE-2026-43895.
+
+CVE: CVE-2026-43895
+Upstream-Status: Backport [https://github.com/jqlang/jq/commit/9d223f153c3632a207fa071caaa6292da33ae361]
+
+Backport Changes:
+- Kept jq 1.7.1 HOME lookup in linker.c.
+ The newer get_home() flow is absent from the target.
+ Only the jv-based gen_import() call was required.
+- Limited parser.c to three import/include action changes.
+ The newer grammar caused unrelated generated-parser churn.
+ Keeping target numbering avoids unrelated generated changes.
+
+(cherry picked from commit 9d223f153c3632a207fa071caaa6292da33ae361)
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ src/compile.c | 12 ++++++++----
+ src/compile.h | 2 +-
+ src/linker.c | 6 +++++-
+ src/parser.c | 16 +++-------------
+ src/parser.y | 16 +++-------------
+ tests/shtest | 17 +++++++++++++++++
+ 6 files changed, 37 insertions(+), 32 deletions(-)
+
+diff --git a/src/compile.c b/src/compile.c
+index e5e65f2..27b2cfd 100644
+--- a/src/compile.c
++++ b/src/compile.c
+@@ -526,13 +526,17 @@ jv block_module_meta(block b) {
+ return jv_null();
+ }
+
+-block gen_import(const char* name, const char* as, int is_data) {
++block gen_import(jv name, jv as, int is_data) {
++ assert(jv_get_kind(name) == JV_KIND_STRING);
++ assert(!jv_is_valid(as) || jv_get_kind(as) == JV_KIND_STRING);
+ inst* i = inst_new(DEPS);
+ jv meta = jv_object();
+- if (as != NULL)
+- meta = jv_object_set(meta, jv_string("as"), jv_string(as));
++ if (jv_is_valid(as))
++ meta = jv_object_set(meta, jv_string("as"), as);
++ else
++ jv_free(as);
+ meta = jv_object_set(meta, jv_string("is_data"), is_data ? jv_true() : jv_false());
+- meta = jv_object_set(meta, jv_string("relpath"), jv_string(name));
++ meta = jv_object_set(meta, jv_string("relpath"), name);
+ i->imm.constant = meta;
+ return inst_block(i);
+ }
+diff --git a/src/compile.h b/src/compile.h
+index c1512e6..bac65ef 100644
+--- a/src/compile.h
++++ b/src/compile.h
+@@ -33,7 +33,7 @@ block gen_op_pushk_under(jv constant);
+
+ block gen_module(block metadata);
+ jv block_module_meta(block b);
+-block gen_import(const char* name, const char *as, int is_data);
++block gen_import(jv name, jv as, int is_data);
+ block gen_import_meta(block import, block metadata);
+ block gen_function(const char* name, block formals, block body);
+ block gen_param_regular(const char* name);
+diff --git a/src/linker.c b/src/linker.c
+index e7d1024..4b15008 100644
+--- a/src/linker.c
++++ b/src/linker.c
+@@ -93,6 +93,10 @@ static jv build_lib_search_chain(jq_state *jq, jv search_path, jv jq_origin, jv
+ // in between).
+ static jv validate_relpath(jv name) {
+ const char *s = jv_string_value(name);
++ if (strlen(s) != (size_t)jv_string_length_bytes(jv_copy(name))) {
++ jv_free(name);
++ return jv_invalid_with_msg(jv_string("Module path contains a NUL byte"));
++ }
+ if (strchr(s, '\\')) {
+ jv res = jv_invalid_with_msg(jv_string_fmt("Modules must be named by relative paths using '/', not '\\' (%s)", s));
+ jv_free(name);
+@@ -423,7 +427,7 @@ int load_program(jq_state *jq, struct locfile* src, block *out_block) {
+ char* home = getenv("HOME");
+ if (home) { // silently ignore no $HOME
+ /* Import ~/.jq as a library named "" found in $HOME */
+- block import = gen_import_meta(gen_import("", NULL, 0),
++ block import = gen_import_meta(gen_import(jv_string(""), jv_invalid(), 0),
+ gen_const(JV_OBJECT(
+ jv_string("optional"), jv_true(),
+ jv_string("search"), jv_string(home))));
+diff --git a/src/parser.c b/src/parser.c
+index 0599db7..c50f2fb 100644
+--- a/src/parser.c
++++ b/src/parser.c
+@@ -3081,13 +3081,8 @@ yyreduce:
+ case 50: /* ImportWhat: "import" ImportFrom "as" BINDING */
+ #line 506 "src/parser.y"
+ {
+- jv v = block_const((yyvsp[-2].blk));
+- // XXX Make gen_import take only blocks and the int is_data so we
+- // don't have to free so much stuff here
+- (yyval.blk) = gen_import(jv_string_value(v), jv_string_value((yyvsp[0].literal)), 1);
++ (yyval.blk) = gen_import(block_const((yyvsp[-2].blk)), (yyvsp[0].literal), 1);
+ block_free((yyvsp[-2].blk));
+- jv_free((yyvsp[0].literal));
+- jv_free(v);
+ }
+ #line 3093 "src/parser.c"
+ break;
+@@ -3095,11 +3090,8 @@ yyreduce:
+ case 51: /* ImportWhat: "import" ImportFrom "as" IDENT */
+ #line 515 "src/parser.y"
+ {
+- jv v = block_const((yyvsp[-2].blk));
+- (yyval.blk) = gen_import(jv_string_value(v), jv_string_value((yyvsp[0].literal)), 0);
++ (yyval.blk) = gen_import(block_const((yyvsp[-2].blk)), (yyvsp[0].literal), 0);
+ block_free((yyvsp[-2].blk));
+- jv_free((yyvsp[0].literal));
+- jv_free(v);
+ }
+ #line 3105 "src/parser.c"
+ break;
+@@ -3107,10 +3099,8 @@ yyreduce:
+ case 52: /* ImportWhat: "include" ImportFrom */
+ #line 522 "src/parser.y"
+ {
+- jv v = block_const((yyvsp[0].blk));
+- (yyval.blk) = gen_import(jv_string_value(v), NULL, 0);
++ (yyval.blk) = gen_import(block_const((yyvsp[0].blk)), jv_invalid(), 0);
+ block_free((yyvsp[0].blk));
+- jv_free(v);
+ }
+ #line 3116 "src/parser.c"
+ break;
+diff --git a/src/parser.y b/src/parser.y
+index 3d24689..2901cab 100644
+--- a/src/parser.y
++++ b/src/parser.y
+@@ -504,26 +504,16 @@ ImportWhat Exp ';' {
+
+ ImportWhat:
+ "import" ImportFrom "as" BINDING {
+- jv v = block_const($2);
+- // XXX Make gen_import take only blocks and the int is_data so we
+- // don't have to free so much stuff here
+- $$ = gen_import(jv_string_value(v), jv_string_value($4), 1);
++ $$ = gen_import(block_const($2), $4, 1);
+ block_free($2);
+- jv_free($4);
+- jv_free(v);
+ } |
+ "import" ImportFrom "as" IDENT {
+- jv v = block_const($2);
+- $$ = gen_import(jv_string_value(v), jv_string_value($4), 0);
++ $$ = gen_import(block_const($2), $4, 0);
+ block_free($2);
+- jv_free($4);
+- jv_free(v);
+ } |
+ "include" ImportFrom {
+- jv v = block_const($2);
+- $$ = gen_import(jv_string_value(v), NULL, 0);
++ $$ = gen_import(block_const($2), jv_invalid(), 0);
+ block_free($2);
+- jv_free(v);
+ }
+
+ ImportFrom:
+diff --git a/tests/shtest b/tests/shtest
+index 505d45d..4a5978c 100755
+--- a/tests/shtest
++++ b/tests/shtest
+@@ -622,4 +622,21 @@ if echo '42' | $JQ -f "$d/nul_prog.jq" >/dev/null 2>/dev/null; then
+ exit 1
+ fi
+
++# CVE-2026-43895: No NUL bytes in module/data import paths
++printf 'import "a\\u0000b" as $x; .' > "$d/nul_import.jq"
++if $JQ -nf "$d/nul_import.jq" >/dev/null 2>/dev/null; then
++ printf 'Error expected for import path with NUL bytes\n' 1>&2
++ exit 1
++fi
++printf 'include "a\\u0000b"; .' > "$d/nul_include.jq"
++if $JQ -nf "$d/nul_include.jq" >/dev/null 2>/dev/null; then
++ printf 'Error expected for include path with NUL bytes\n' 1>&2
++ exit 1
++fi
++printf '"a\\u0000b" | modulemeta' > "$d/nul_modulemeta.jq"
++if $JQ -nf "$d/nul_modulemeta.jq" >/dev/null 2>/dev/null; then
++ printf 'Error expected for modulemeta with NUL bytes\n' 1>&2
++ exit 1
++fi
++
+ exit 0
+--
+2.44.4
+
@@ -25,6 +25,7 @@ SRC_URI = "${GITHUB_BASE_URI}/download/${BPN}-${PV}/${BPN}-${PV}.tar.gz \
file://CVE-2026-41257.patch \
file://CVE-2026-43894.patch \
file://CVE-2026-43896.patch \
+ file://CVE-2026-43895.patch \
"
SRC_URI[sha256sum] = "478c9ca129fd2e3443fe27314b455e211e0d8c60bc8ff7df703873deeee580c2"