[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-2903: Null pointer dereference in re2c 4.4: check_and_merge_special_rules() in src/parse/ast.cc does not verify whether a grammar block has any rules before proceeding with determinization, allowing crafted input that specifies entry, pre-rule, or post-rule hooks without any rules to reach the determinization phase with a null root TNFA node and crash the re2c process.

# Vulnerable source (src/parse/ast.cc)
```cpp
Ret check_and_merge_special_rules(AstGrams& grams, const opt_t* opts, Msg& msg, Ast& ast) {
    if (grams.empty()) return Ret::OK;

    for (const AstGram& g : grams) {
        // Check for redundant special rules and actions.
#define CHECK_SPECIAL(what, str) \
        if (g.what.size() > 1) { \
            RET_FAIL(msg.error(g.what[1]->loc, "%s %sis already defined at line %u", \
                str, incond(g.name).c_str(), g.what[0]->loc.line)); \
        }
        CHECK_SPECIAL(defs, "default rule");
        CHECK_SPECIAL(entry, "entry action");
        CHECK_SPECIAL(pre_rule, "pre-rule action");
        CHECK_SPECIAL(post_rule, "post-rule action");
#undef CHECK_SPECIAL
    }

    if (!opts->start_conditions) {
        // normal mode: there must be no named grams corresponding to conditions
        for (const AstGram& g : grams) {
            if (!g.name.empty()) { // found named gram
                RET_FAIL(msg.error(find_rule_location(g),
                    "conditions are only allowed with '-c', '--conditions' option"));
            }
        }
    } else if (grams.size() == 1 && grams[0].name.empty()) {
        // condition mode, a single unnamed gram => ok, normal blocks are allowed
    } else {
        // condition mode, at least one named gram => this is a block with conditions
        for (const AstGram& g : grams) {
            if (g.name.empty()) { // found unnamed gram
                RET_FAIL(msg.error(find_rule_location(g),
                    "cannot mix conditions with normal rules"));
            }
        }

        // Check for redundant actions in `<*>` condiiton or nonexistent conditions.
#define CHECK_ACTION(action, str) do { \
        bool all_conds_have_it = true; \
        const SemAct* star_action = nullptr; \
        for (const AstGram& g : grams) { \
            if (g.action.empty()) { \
                all_conds_have_it = false; \
            } else if (g.name == STAR_COND) { \
                star_action = g.action[0]; \
            } else if (g.rules.empty()) { \
                RET_FAIL(msg.error(g.action[0]->loc, \
                    "%s action for non-existing condition `%s` found", \
                    str, g.name.c_str())); \
            } \
```

[Assistant]
--- a/src/parse/ast.cc
+++ b/src/parse/ast.cc
@@ -332,10 +332,6 @@ Ret check_and_merge_special_rules(AstGrams& grams, const opt_t* opts, Msg& msg,
             all_conds_have_it = false; \
         } else if (g.name == STAR_COND) { \
             star_action = g.action[0]; \
-            } else if (g.rules.empty()) { \
-                RET_FAIL(msg.error(g.action[0]->loc, \
-                    "%s action for non-existing condition `%s` found", \
-                    str, g.name.c_str())); \
         } \
         if (star_action && all_conds_have_it) { \
@@ -422,6 +418,21 @@ Ret check_and_merge_special_rules(AstGrams& grams, const opt_t* opts, Msg& msg,
         }
     }

+    for (const AstGram& g : grams) {
+        if (g.rules.empty()) {
+#define CHECK_ACTION(action, str) do { \
+            if (!g.action.empty()) { \
+                RET_FAIL(msg.error(g.action[0]->loc, \
+                    "%s action %sbut no rules found", str, incond(g.name).c_str())); \
+            } \
+} while(0)
+            CHECK_ACTION(entry, "entry");
+            CHECK_ACTION(pre_rule, "pre-rule");
+            CHECK_ACTION(post_rule, "post-rule");
+#undef CHECK_ACTION
+        }
+    }
+


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-31842: HTTP request handling defect in tinyproxy 1.11.3: the transfer-encoding detection in src/reqs.c uses case-sensitive string comparison when checking for chunked encoding, causing tinyproxy to mishandle HTTP requests where the Transfer-Encoding header value uses non-lowercase casing.

# Vulnerable source (src/reqs.c)
```c
 * -1.
 */
static long get_content_length (pseudomap *hashofheaders)
{
        char *data;
        long content_length = -1;

        data = pseudomap_find (hashofheaders, "content-length");

        if (data)
                content_length = atol (data);

        return content_length;
}

static int is_chunked_transfer (pseudomap *hashofheaders)
{
        char *data;
        data = pseudomap_find (hashofheaders, "transfer-encoding");
        return data ? !strcmp (data, "chunked") : 0;
}

/*
 * Search for Via header in a hash of headers and either write a new Via
 * header, or append our information to the end of an existing Via header.
 *
 * FIXME: Need to add code to "hide" our internal information for security
 * purposes.
 */
static int
write_via_header (int fd, pseudomap *hashofheaders,
                  unsigned int major, unsigned int minor)
{
        char hostname[512];
        char *data;
```

[Assistant]
--- a/src/reqs.c
+++ b/src/reqs.c
@@ -847,7 +847,7 @@ static int is_chunked_transfer (pseudomap *hashofheaders)
 {
         char *data;
         data = pseudomap_find (hashofheaders, "transfer-encoding");
-        return data ? !strcmp (data, "chunked") : 0;
+        return data ? !strcasecmp (data, "chunked") : 0;
 }


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-32316: jq performs buffer-size arithmetic in 32-bit unsigned integers before allocating heap memory, allowing an attacker to provide an extremely long JSON value that wraps the allocation size calculation and causes a subsequent write to extend far beyond the allocated region.

# Vulnerable source (src/jv.c)
```c
    }
    if (idx > -1)
      res = jv_array_append(res, jv_number(idx));
    idx = -1;
  }
  jv_free(a);
  jv_free(b);
  return res;
}

/*
 * Strings (internal helpers)
 */

#define JVP_FLAGS_STRING  JVP_MAKE_FLAGS(JV_KIND_STRING, JVP_PAYLOAD_ALLOCATED)

typedef struct {
  jv_refcnt refcnt;
  uint32_t hash;
  // high 31 bits are length, low bit is a flag
  // indicating whether hash has been computed.
  uint32_t length_hashed;
  uint32_t alloc_length;
  char data[];
} jvp_string;

static jvp_string* jvp_string_ptr(jv a) {
  assert(JVP_HAS_KIND(a, JV_KIND_STRING));
  return (jvp_string*)a.u.ptr;
}

static jvp_string* jvp_string_alloc(uint32_t size) {
  jvp_string* s = jv_mem_alloc(sizeof(jvp_string) + size + 1);
  s->refcnt.count = 1;
  s->alloc_length = size;
  return s;
}

/* Copy a UTF8 string, replacing all badly encoded points with U+FFFD */
static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) {
  const char* end = data + length;
  const char* i = data;

  uint32_t maxlength = length * 3 + 1; // worst case: all bad bytes, each becomes a 3-byte U+FFFD
  jvp_string* s = jvp_string_alloc(maxlength);
  char* out = s->data;
  int c = 0;

  while ((i = jvp_utf8_next(i, end, &c))) {
    if (c == -1) {
      c = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER
    }
    out += jvp_utf8_encode(c, out);
    assert(out < s->data + maxlength);
  }
  length = out - s->data;
  s->data[length] = 0;
  s->length_hashed = length << 1;
  jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}};
  return r;
}

/* Assumes valid UTF8 */
static jv jvp_string_new(const char* data, uint32_t length) {
  jvp_string* s = jvp_string_alloc(length);
  s->length_hashed = length << 1;
  if (data != NULL)
    memcpy(s->data, data, length);
  s->data[length] = 0;
  jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}};
  return r;
}

static jv jvp_string_empty_new(uint32_t length) {
  jvp_string* s = jvp_string_alloc(length);
  s->length_hashed = 0;
  memset(s->data, 0, length);
  s->data[length] = 0;
  jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}};
  return r;
}

static void jvp_string_free(jv js) {
  jvp_string* s = jvp_string_ptr(js);
  if (jvp_refcnt_dec(&s->refcnt)) {
    jv_mem_free(s);
  }
}

static uint32_t jvp_string_length(jvp_string* s) {
  return s->length_hashed >> 1;
}

static uint32_t jvp_string_remaining_space(jvp_string* s) {
  assert(s->alloc_length >= jvp_string_length(s));
  uint32_t r = s->alloc_length - jvp_string_length(s);
  return r;
}

static jv jvp_string_append(jv string, const char* data, uint32_t len) {
  jvp_string* s = jvp_string_ptr(string);
  uint32_t currlen = jvp_string_length(s);

  if (jvp_refcnt_unshared(string.u.ptr) &&
      jvp_string_remaining_space(s) >= len) {
    // the next string fits at the end of a
    memcpy(s->data + currlen, data, len);
    s->data[currlen + len] = 0;
    s->length_hashed = (currlen + len) << 1;
    return string;
  } else {
    // allocate a bigger buffer and copy
    uint32_t allocsz = (currlen + len) * 2;
    if (allocsz < 32) allocsz = 32;
    jvp_string* news = jvp_string_alloc(allocsz);
    news->length_hashed = (currlen + len) << 1;
    memcpy(news->data, s->data, currlen);
    memcpy(news->data + currlen, data, len);
    news->data[currlen + len] = 0;
    jvp_string_free(string);
    jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&news->refcnt}};
    return r;
  }
}

static const uint32_t HASH_SEED = 0x432A9843;

static uint32_t rotl32 (uint32_t x, int8_t r){
  return (x << r) | (x >> (32 - r));
}

static uint32_t jvp_string_hash(jv jstr) {
  jvp_string* str = jvp_string_ptr(jstr);
  if (str->length_hashed & 1)
    return str->hash;

  /* The following is based on MurmurHash3.
     MurmurHash3 was written by Austin Appleby, and is placed
     in the public domain. */

  const uint8_t* data = (const uint8_t*)str->data;
  int len = (int)jvp_string_length(str);
  const int nblocks = len / 4;

  uint32_t h1 = HASH_SEED;
```

[Assistant]
diff --git a/src/jv.c b/src/jv.c
index 722a539391..2a62b48419 100644
--- a/src/jv.c
+++ b/src/jv.c
@@ -1114,7 +1114,12 @@ static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) {
   const char* end = data + length;
   const char* i = data;

-  uint32_t maxlength = length * 3 + 1; // worst case: all bad bytes, each becomes a 3-byte U+FFFD
+  // worst case: all bad bytes, each becomes a 3-byte U+FFFD
+  uint64_t maxlength = (uint64_t)length * 3 + 1;
+  if (maxlength >= INT_MAX) {
+    return jv_invalid_with_msg(jv_string("String too long"));
+  }
+
   jvp_string* s = jvp_string_alloc(maxlength);
   char* out = s->data;
   int c = 0;
@@ -1174,6 +1179,10 @@ static uint32_t jvp_string_remaining_space(jvp_string* s) {
 static jv jvp_string_append(jv string, const char* data, uint32_t len) {
   jvp_string* s = jvp_string_ptr(string);
   uint32_t currlen = jvp_string_length(s);
+  if ((uint64_t)currlen + len >= INT_MAX) {
+    jv_free(string);
+    return jv_invalid_with_msg(jv_string("String too long"));
+  }

   if (jvp_refcnt_unshared(string.u.ptr) &&
       jvp_string_remaining_space(s) >= len) {


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-32316: jq 1.8.1 jv.c has two heap buffer overflows in text handling functions: in the bad-byte replacement function, the buffer size calculation could overflow a 32-bit integer when processing a sufficiently large input, resulting in a tiny buffer with a large write beyond it; in the append function, the same wrap-around applies when appending data that pushes the combined total beyond the 32-bit maximum, causing an out-of-bounds write.

# Vulnerable source (src/jv.c)
```c
    }
    if (idx > -1)
      res = jv_array_append(res, jv_number(idx));
    idx = -1;
  }
  jv_free(a);
  jv_free(b);
  return res;
}

/*
 * Strings (internal helpers)
 */

#define JVP_FLAGS_STRING  JVP_MAKE_FLAGS(JV_KIND_STRING, JVP_PAYLOAD_ALLOCATED)

typedef struct {
  jv_refcnt refcnt;
  uint32_t hash;
  // high 31 bits are length, low bit is a flag
  // indicating whether hash has been computed.
  uint32_t length_hashed;
  uint32_t alloc_length;
  char data[];
} jvp_string;

static jvp_string* jvp_string_ptr(jv a) {
  assert(JVP_HAS_KIND(a, JV_KIND_STRING));
  return (jvp_string*)a.u.ptr;
}

static jvp_string* jvp_string_alloc(uint32_t size) {
  jvp_string* s = jv_mem_alloc(sizeof(jvp_string) + size + 1);
  s->refcnt.count = 1;
  s->alloc_length = size;
  return s;
}

/* Copy a UTF8 string, replacing all badly encoded points with U+FFFD */
static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) {
  const char* end = data + length;
  const char* i = data;

  uint32_t maxlength = length * 3 + 1; // worst case: all bad bytes, each becomes a 3-byte U+FFFD
  jvp_string* s = jvp_string_alloc(maxlength);
  char* out = s->data;
  int c = 0;

  while ((i = jvp_utf8_next(i, end, &c))) {
    if (c == -1) {
      c = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER
    }
    out += jvp_utf8_encode(c, out);
    assert(out < s->data + maxlength);
  }
  length = out - s->data;
  s->data[length] = 0;
  s->length_hashed = length << 1;
  jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}};
  return r;
}

/* Assumes valid UTF8 */
static jv jvp_string_new(const char* data, uint32_t length) {
  jvp_string* s = jvp_string_alloc(length);
  s->length_hashed = length << 1;
  if (data != NULL)
    memcpy(s->data, data, length);
  s->data[length] = 0;
  jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}};
  return r;
}

static jv jvp_string_empty_new(uint32_t length) {
  jvp_string* s = jvp_string_alloc(length);
  s->length_hashed = 0;
  memset(s->data, 0, length);
  s->data[length] = 0;
  jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}};
  return r;
}

static void jvp_string_free(jv js) {
  jvp_string* s = jvp_string_ptr(js);
  if (jvp_refcnt_dec(&s->refcnt)) {
    jv_mem_free(s);
  }
}

static uint32_t jvp_string_length(jvp_string* s) {
  return s->length_hashed >> 1;
}

static uint32_t jvp_string_remaining_space(jvp_string* s) {
  assert(s->alloc_length >= jvp_string_length(s));
  uint32_t r = s->alloc_length - jvp_string_length(s);
  return r;
}

static jv jvp_string_append(jv string, const char* data, uint32_t len) {
  jvp_string* s = jvp_string_ptr(string);
  uint32_t currlen = jvp_string_length(s);

  if (jvp_refcnt_unshared(string.u.ptr) &&
      jvp_string_remaining_space(s) >= len) {
    // the next string fits at the end of a
    memcpy(s->data + currlen, data, len);
    s->data[currlen + len] = 0;
    s->length_hashed = (currlen + len) << 1;
    return string;
  } else {
    // allocate a bigger buffer and copy
    uint32_t allocsz = (currlen + len) * 2;
    if (allocsz < 32) allocsz = 32;
    jvp_string* news = jvp_string_alloc(allocsz);
    news->length_hashed = (currlen + len) << 1;
    memcpy(news->data, s->data, currlen);
    memcpy(news->data + currlen, data, len);
    news->data[currlen + len] = 0;
    jvp_string_free(string);
    jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&news->refcnt}};
    return r;
  }
}

static const uint32_t HASH_SEED = 0x432A9843;

static uint32_t rotl32 (uint32_t x, int8_t r){
  return (x << r) | (x >> (32 - r));
}

static uint32_t jvp_string_hash(jv jstr) {
  jvp_string* str = jvp_string_ptr(jstr);
  if (str->length_hashed & 1)
    return str->hash;

  /* The following is based on MurmurHash3.
     MurmurHash3 was written by Austin Appleby, and is placed
     in the public domain. */

  const uint8_t* data = (const uint8_t*)str->data;
  int len = (int)jvp_string_length(str);
  const int nblocks = len / 4;

  uint32_t h1 = HASH_SEED;
```

[Assistant]
diff --git a/src/jv.c b/src/jv.c
index 722a539391..2a62b48419 100644
--- a/src/jv.c
+++ b/src/jv.c
@@ -1114,7 +1114,12 @@ static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) {
   const char* end = data + length;
   const char* i = data;

-  uint32_t maxlength = length * 3 + 1; // worst case: all bad bytes, each becomes a 3-byte U+FFFD
+  // worst case: all bad bytes, each becomes a 3-byte U+FFFD
+  uint64_t maxlength = (uint64_t)length * 3 + 1;
+  if (maxlength >= INT_MAX) {
+    return jv_invalid_with_msg(jv_string("String too long"));
+  }
+
   jvp_string* s = jvp_string_alloc(maxlength);
   char* out = s->data;
   int c = 0;
@@ -1174,6 +1179,10 @@ static uint32_t jvp_string_remaining_space(jvp_string* s) {
 static jv jvp_string_append(jv string, const char* data, uint32_t len) {
   jvp_string* s = jvp_string_ptr(string);
   uint32_t currlen = jvp_string_length(s);
+  if ((uint64_t)currlen + len >= INT_MAX) {
+    jv_free(string);
+    return jv_invalid_with_msg(jv_string("String too long"));
+  }

   if (jvp_refcnt_unshared(string.u.ptr) &&
       jvp_string_remaining_space(s) >= len) {


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-32746: GNU inetutils includes a telnetd server that passes certain environment variables from clients to the spawned login process. The util-linux login utility, since version 2.40, reads the CREDENTIALS_DIRECTORY environment variable: if a directory at that path contains a 'login.noauth' file set to 'yes', the utility bypasses password authentication. Because telnetd relays client-supplied environment variables directly to login, a remote attacker can supply a path they control as CREDENTIALS_DIRECTORY and place the 'login.noauth' bypass file there, gaining unauthenticated access as any user including root.

# Vulnerable source (telnetd/pty.c)
```c
 * Remove a few things from the environment that
 * don't need to be there.
 *
 * Security fix included in telnet-95.10.23.NE of David Borman <deb@cray.com>.
 */
static void
scrub_env (void)
{
  char **cpp, **cpp2;

  for (cpp2 = cpp = environ; *cpp; cpp++)
    {
      if (strncmp (*cpp, "LD_", 3)
	  && strncmp (*cpp, "_RLD_", 5)
	  && strncmp (*cpp, "LIBPATH=", 8) && strncmp (*cpp, "IFS=", 4))
	*cpp2++ = *cpp;
    }
  *cpp2 = 0;
}

void
start_login (char *host, int autologin, char *name)
{
  char *cmd;
  int argc;
  char **argv;

  (void) host;			/* Silence warnings.  Diagnostic use?  */
  (void) autologin;
  (void) name;

  scrub_env ();

  /* Set the environment variable "LINEMODE" to indicate our linemode */
  if (lmodetype == REAL_LINEMODE)
    setenv ("LINEMODE", "real", 1);
  else if (lmodetype == KLUDGE_LINEMODE || lmodetype == KLUDGE_OK)
    setenv ("LINEMODE", "kludge", 1);

  cmd = expand_line (login_invocation);
  if (!cmd)
    fatal (net, "can't expand login command line");
  argcv_get (cmd, "", &argc, &argv);
  execv (argv[0], argv);
  syslog (LOG_ERR, "%s: %m\n", cmd);
  fatalperror (net, cmd);
}

/* SIG is generally naught every time the server itself
 * decides to close the connection out of an error condition.
 * In response to TELOPT_LOGOUT from the client, SIG is set
 * to SIGHUP, so we consider the exit as a success.  In other
 * cases, when the forked client process is caught exiting,
 * then SIG will be SIGCHLD.  Then we deliver the clients's
 * reported exit code.
 */
void
cleanup (int sig)
{
  int status = EXIT_FAILURE;
  char *p;

  if (sig == SIGCHLD)
    {
      pid_t pid = waitpid ((pid_t) - 1, &status, WNOHANG);
      syslog (LOG_INFO, "child process %ld exited: %d",
	      (long) pid, WEXITSTATUS (status));

      status = WEXITSTATUS (status);
    }
  else if (sig == SIGHUP)
    status = EXIT_SUCCESS;	/* Response to TELOPT_LOGOUT.  */

  p = line + sizeof (PATH_TTY_PFX) - 1;
  utmp_logout (p);
  chmod (line, 0644);
  chown (line, 0, 0);
  shutdown (net, 2);
  exit (status);
}
```

[Assistant]
diff --git a/telnetd/pty.c b/telnetd/pty.c
index c727e7be..f3518049 100644
--- a/telnetd/pty.c
+++ b/telnetd/pty.c
@@ -129,6 +129,14 @@ start_login (char *host, int autologin, char *name)
   if (!cmd)
     fatal (net, "can't expand login command line");
   argcv_get (cmd, "", &argc, &argv);
+
+  /* util-linux's "login" introduced an authentication bypass method
+   * via environment variable "CREDENTIALS_DIRECTORY" in version 2.40.
+   * Clear it from the environment before executing "login" to prevent
+   * abuse via Telnet.
+   */
+  unsetenv ("CREDENTIALS_DIRECTORY");
+
   execv (argv[0], argv);
   syslog (LOG_ERR, "%s: %m\n", cmd);
   fatalperror (net, cmd);


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-33236: CWE-22 in python3Packages.nltk 3.9.2. The NLTK corpus fetch engine reads package catalog data from a CDN endpoint (https://raw.githubusercontent.com/nltk/nltk_data/gh-pages/packages.xml). Each catalog entry specifies a data-category value (e.g. corpora) and a resource identifier (e.g. punkt). Both strings pass directly into os.path.join() when constructing the target disk path, with no sanitization for path-escape sequences. An attacker able to serve a hostile catalog via MITM, CDN compromise, or rogue mirror can embed sequences of two-dot path elements in either field, directing corpus file writes to arbitrary OS paths beyond the NLTK data root.

# Vulnerable source (nltk/downloader.py)
```python
        author="Unknown",
        unzip=True,
        **kw,
    ):
        self.id = id
        """A unique identifier for this package."""

        self.name = name or id
        """A string name for this package."""

        self.subdir = subdir
        """The subdirectory where this package should be installed.
           E.g., ``'corpora'`` or ``'taggers'``."""

        self.url = url
        """A URL that can be used to download this package's file."""

        self.size = int(size)
        """The filesize (in bytes) of the package file."""

        self.unzipped_size = int(unzipped_size)
        """The total filesize of the files contained in the package's
           zipfile."""

        self.checksum = checksum
        """The MD-5 checksum of the package file."""

        self.svn_revision = svn_revision
        """A subversion revision number for this package."""

/* ... */

        # Check for (and remove) any old/stale version.
        filepath = os.path.join(download_dir, info.filename)
        if os.path.exists(filepath):
            if status == self.STALE:
                yield StaleMessage(info)
            os.remove(filepath)

        # Ensure the download_dir exists
        os.makedirs(download_dir, exist_ok=True)
        os.makedirs(os.path.join(download_dir, info.subdir), exist_ok=True)

        # Download the file.  This will raise an IOError if the url
        # is not found.

```

[Assistant]
diff --git a/nltk/downloader.py b/nltk/downloader.py
index a8010f5cd2..c181b94361 100644
--- a/nltk/downloader.py
+++ b/nltk/downloader.py
@@ -216,10 +216,22 @@ def __init__(
         self.name = name or id
         """A string name for this package."""

+        # Validate subdir to prevent path traversal from malicious XML index
+        if os.path.isabs(subdir) or ".." in subdir.replace("\\", "/").split("/"):
+            raise ValueError(
+                f"Invalid package subdir {subdir!r}: must be a relative path "
+                f"without parent directory references"
+            )
         self.subdir = subdir
         """The subdirectory where this package should be installed.
            E.g., ``'corpora'`` or ``'taggers'``."""

+        # Validate id to prevent path traversal
+        if os.sep in id or "/" in id or "\\" in id or ".." in id:
+            raise ValueError(
+                f"Invalid package id {id!r}: must not contain path separators"
+            )
+
         self.url = url
         """A URL that can be used to download this package's file."""

@@ -677,6 +689,18 @@ def _download_package(self, info, download_dir, force):

         # Check for (and remove) any old/stale version.
         filepath = os.path.join(download_dir, info.filename)
+
+        # Defense-in-depth: verify filepath stays within download_dir
+        real_download = os.path.realpath(os.path.abspath(download_dir))
+        real_filepath = os.path.realpath(os.path.abspath(filepath))
+        if not real_filepath.startswith(real_download + os.sep):
+            yield ErrorMessage(
+                info,
+                f"Path traversal blocked: package '{info.id}' attempted to "
+                f"write outside download directory (subdir='{info.subdir}')",
+            )
+            return
+
         if os.path.exists(filepath):
             if status == self.STALE:
                 yield StaleMessage(info)


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-33947: jq 1.8.1 has a stack overflow vulnerability in its navigation functions that modify, read, or remove JSON members using index arrays: none of these functions imposed a constraint on the number of nesting levels, so an attacker who constructs a query with thousands of nested indices can drive the interpreter into unbounded recursion until the stack is exhausted.

# Vulnerable source (src/jv_aux.c)
```c
        }
        if (i < delidx) {
          break;
        }
        neg_idx++;
      }
      while (nonneg_idx < jv_array_length(jv_copy(nonneg_keys))) {
        int delidx = (int)jv_number_get_value_and_consume(jv_array_get(jv_copy(nonneg_keys), nonneg_idx));
        if (i == delidx) {
          del = 1;
        }
        if (i < delidx) {
          break;
        }
        nonneg_idx++;
      }
      for (int sidx=0; !del && sidx<jv_array_length(jv_copy(starts)); sidx++) {
        if ((int)jv_number_get_value_and_consume(jv_array_get(jv_copy(starts), sidx)) <= i &&
            i < (int)jv_number_get_value_and_consume(jv_array_get(jv_copy(ends), sidx))) {
          del = 1;
        }
      }
      if (!del)
        new_array = jv_array_append(new_array, jv_array_get(jv_copy(t), i));
    }
  arr_out:
    jv_free(neg_keys);
    jv_free(nonneg_keys);
    jv_free(starts);
    jv_free(ends);
    jv_free(t);
    t = new_array;
  } else if (jv_get_kind(t) == JV_KIND_OBJECT) {
    jv_array_foreach(keys, i, k) {
      if (jv_get_kind(k) != JV_KIND_STRING) {
        jv_free(t);
        t = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s field of object",
                                              jv_kind_name(jv_get_kind(k))));
        jv_free(k);
        break;
      }
      t = jv_object_delete(t, k);
    }
  } else {
    jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete fields from %s",
                                               jv_kind_name(jv_get_kind(t))));
    jv_free(t);
    t = err;
  }
  jv_free(keys);
  return t;
}

jv jv_setpath(jv root, jv path, jv value) {
  if (jv_get_kind(path) != JV_KIND_ARRAY) {
    jv_free(value);
    jv_free(root);
    jv_free(path);
    return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
  }
  if (!jv_is_valid(root)){
    jv_free(value);
    jv_free(path);
    return root;
  }
  if (jv_array_length(jv_copy(path)) == 0) {
    jv_free(path);
    jv_free(root);
    return value;
  }
  jv pathcurr = jv_array_get(jv_copy(path), 0);
  jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));

  /*
   * We need to be careful not to make extra copies since that leads to
   * quadratic behavior (e.g., when growing large data structures in a
   * reduction with `setpath/2`, i.e., with `|=`.
   */
  if (jv_get_kind(pathcurr) == JV_KIND_OBJECT) {
    // Assignment to slice -- dunno yet how to avoid the extra copy
    return jv_set(root, pathcurr,
                  jv_setpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest, value));
  }

  jv subroot = jv_get(jv_copy(root), jv_copy(pathcurr));
  if (!jv_is_valid(subroot)) {
    jv_free(root);
    jv_free(pathcurr);
    jv_free(pathrest);
    jv_free(value);
    return subroot;
  }

  // To avoid the extra copy we drop the reference from `root` by setting that
  // to null first.
  root = jv_set(root, jv_copy(pathcurr), jv_null());
  if (!jv_is_valid(root)) {
    jv_free(subroot);
    jv_free(pathcurr);
    jv_free(pathrest);
    jv_free(value);
    return root;
  }
  return jv_set(root, pathcurr, jv_setpath(subroot, pathrest, value));
}

jv jv_getpath(jv root, jv path) {
  if (jv_get_kind(path) != JV_KIND_ARRAY) {
    jv_free(root);
    jv_free(path);
    return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
  }
  if (!jv_is_valid(root)) {
    jv_free(path);
    return root;
  }
  if (jv_array_length(jv_copy(path)) == 0) {
    jv_free(path);
    return root;
  }
  jv pathcurr = jv_array_get(jv_copy(path), 0);
  jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));
  return jv_getpath(jv_get(root, pathcurr), pathrest);
}

// assumes paths is a sorted array of arrays
static jv delpaths_sorted(jv object, jv paths, int start) {
  jv delkeys = jv_array();
  for (int i=0; i<jv_array_length(jv_copy(paths));) {
    int j = i;
    assert(jv_array_length(jv_array_get(jv_copy(paths), i)) > start);
    int delkey = jv_array_length(jv_array_get(jv_copy(paths), i)) == start + 1;
    jv key = jv_array_get(jv_array_get(jv_copy(paths), i), start);
    while (j < jv_array_length(jv_copy(paths)) &&
           jv_equal(jv_copy(key), jv_array_get(jv_array_get(jv_copy(paths), j), start)))
      j++;
    // if i <= entry < j, then entry starts with key
    if (delkey) {
      // deleting this entire key, we don't care about any more specific deletions
      delkeys = jv_array_append(delkeys, key);
    } else {
      // deleting certain sub-parts of this key
      jv subobject = jv_get(jv_copy(object), jv_copy(key));
      if (!jv_is_valid(subobject)) {
        jv_free(key);
        jv_free(object);
        object = subobject;
        break;
      } else if (jv_get_kind(subobject) == JV_KIND_NULL) {
        jv_free(key);
        jv_free(subobject);
      } else {
        jv newsubobject = delpaths_sorted(subobject, jv_array_slice(jv_copy(paths), i, j), start+1);
        if (!jv_is_valid(newsubobject)) {
          jv_free(key);
          jv_free(object);
          object = newsubobject;
          break;
        }
        object = jv_set(object, key, newsubobject);
      }
      if (!jv_is_valid(object)) break;
    }
    i = j;
  }
  jv_free(paths);
  if (jv_is_valid(object))
    object = jv_dels(object, delkeys);
  else
    jv_free(delkeys);
  return object;
}

jv jv_delpaths(jv object, jv paths) {
  if (jv_get_kind(paths) != JV_KIND_ARRAY) {
    jv_free(object);
    jv_free(paths);
    return jv_invalid_with_msg(jv_string("Paths must be specified as an array"));
  }
  paths = jv_sort(paths, jv_copy(paths));
  jv_array_foreach(paths, i, elem) {
    if (jv_get_kind(elem) != JV_KIND_ARRAY) {
      jv_free(object);
      jv_free(paths);
      jv err = jv_invalid_with_msg(jv_string_fmt("Path must be specified as array, not %s",
                                                 jv_kind_name(jv_get_kind(elem))));
      jv_free(elem);
      return err;
    }
    jv_free(elem);
  }
  if (jv_array_length(jv_copy(paths)) == 0) {
    // nothing is being deleted
    jv_free(paths);
    return object;
  }
  if (jv_array_length(jv_array_get(jv_copy(paths), 0)) == 0) {
    // everything is being deleted
    jv_free(paths);
    jv_free(object);
    return jv_null();
  }
  return delpaths_sorted(object, paths, 0);
}

static int string_cmp(const void* pa, const void* pb){
  const jv* a = pa;
  const jv* b = pb;
  int lena = jv_string_length_bytes(jv_copy(*a));
  int lenb = jv_string_length_bytes(jv_copy(*b));
  int minlen = lena < lenb ? lena : lenb;
  int r = memcmp(jv_string_value(*a), jv_string_value(*b), minlen);
  if (r == 0) r = lena - lenb;
  return r;
}

jv jv_keys_unsorted(jv x) {
  if (jv_get_kind(x) != JV_KIND_OBJECT)
    return jv_keys(x);
  jv answer = jv_array_sized(jv_object_length(jv_copy(x)));
  jv_object_foreach(x, key, value) {
    answer = jv_array_append(answer, key);
```

[Assistant]
diff --git a/src/jv_aux.c b/src/jv_aux.c
index 018f380b10..fd5ff96684 100644
--- a/src/jv_aux.c
+++ b/src/jv_aux.c
@@ -365,6 +365,10 @@ static jv jv_dels(jv t, jv keys) {
   return t;
 }

+#ifndef MAX_PATH_DEPTH
+#define MAX_PATH_DEPTH (10000)
+#endif
+
 jv jv_setpath(jv root, jv path, jv value) {
   if (jv_get_kind(path) != JV_KIND_ARRAY) {
     jv_free(value);
@@ -372,6 +376,12 @@ jv jv_setpath(jv root, jv path, jv value) {
     jv_free(path);
     return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
   }
+  if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) {
+    jv_free(value);
+    jv_free(root);
+    jv_free(path);
+    return jv_invalid_with_msg(jv_string("Path too deep"));
+  }
   if (!jv_is_valid(root)){
     jv_free(value);
     jv_free(path);
@@ -424,6 +434,11 @@ jv jv_getpath(jv root, jv path) {
     jv_free(path);
     return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
   }
+  if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) {
+    jv_free(root);
+    jv_free(path);
+    return jv_invalid_with_msg(jv_string("Path too deep"));
+  }
   if (!jv_is_valid(root)) {
     jv_free(path);
     return root;
@@ -502,6 +517,12 @@ jv jv_delpaths(jv object, jv paths) {
       jv_free(elem);
       return err;
     }
+    if (jv_array_length(jv_copy(elem)) > MAX_PATH_DEPTH) {
+      jv_free(object);
+      jv_free(paths);
+      jv_free(elem);
+      return jv_invalid_with_msg(jv_string("Path too deep"));
+    }
     jv_free(elem);
   }
   if (jv_array_length(jv_copy(paths)) == 0) {


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-33947: A crafted JSON input with deeply nested structures can cause jq to recurse without bound while following path expressions, exhausting process stack or memory and crashing the interpreter.

# Vulnerable source (src/jv_aux.c)
```c
        }
        if (i < delidx) {
          break;
        }
        neg_idx++;
      }
      while (nonneg_idx < jv_array_length(jv_copy(nonneg_keys))) {
        int delidx = (int)jv_number_get_value_and_consume(jv_array_get(jv_copy(nonneg_keys), nonneg_idx));
        if (i == delidx) {
          del = 1;
        }
        if (i < delidx) {
          break;
        }
        nonneg_idx++;
      }
      for (int sidx=0; !del && sidx<jv_array_length(jv_copy(starts)); sidx++) {
        if ((int)jv_number_get_value_and_consume(jv_array_get(jv_copy(starts), sidx)) <= i &&
            i < (int)jv_number_get_value_and_consume(jv_array_get(jv_copy(ends), sidx))) {
          del = 1;
        }
      }
      if (!del)
        new_array = jv_array_append(new_array, jv_array_get(jv_copy(t), i));
    }
  arr_out:
    jv_free(neg_keys);
    jv_free(nonneg_keys);
    jv_free(starts);
    jv_free(ends);
    jv_free(t);
    t = new_array;
  } else if (jv_get_kind(t) == JV_KIND_OBJECT) {
    jv_array_foreach(keys, i, k) {
      if (jv_get_kind(k) != JV_KIND_STRING) {
        jv_free(t);
        t = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s field of object",
                                              jv_kind_name(jv_get_kind(k))));
        jv_free(k);
        break;
      }
      t = jv_object_delete(t, k);
    }
  } else {
    jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete fields from %s",
                                               jv_kind_name(jv_get_kind(t))));
    jv_free(t);
    t = err;
  }
  jv_free(keys);
  return t;
}

jv jv_setpath(jv root, jv path, jv value) {
  if (jv_get_kind(path) != JV_KIND_ARRAY) {
    jv_free(value);
    jv_free(root);
    jv_free(path);
    return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
  }
  if (!jv_is_valid(root)){
    jv_free(value);
    jv_free(path);
    return root;
  }
  if (jv_array_length(jv_copy(path)) == 0) {
    jv_free(path);
    jv_free(root);
    return value;
  }
  jv pathcurr = jv_array_get(jv_copy(path), 0);
  jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));

  /*
   * We need to be careful not to make extra copies since that leads to
   * quadratic behavior (e.g., when growing large data structures in a
   * reduction with `setpath/2`, i.e., with `|=`.
   */
  if (jv_get_kind(pathcurr) == JV_KIND_OBJECT) {
    // Assignment to slice -- dunno yet how to avoid the extra copy
    return jv_set(root, pathcurr,
                  jv_setpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest, value));
  }

  jv subroot = jv_get(jv_copy(root), jv_copy(pathcurr));
  if (!jv_is_valid(subroot)) {
    jv_free(root);
    jv_free(pathcurr);
    jv_free(pathrest);
    jv_free(value);
    return subroot;
  }

  // To avoid the extra copy we drop the reference from `root` by setting that
  // to null first.
  root = jv_set(root, jv_copy(pathcurr), jv_null());
  if (!jv_is_valid(root)) {
    jv_free(subroot);
    jv_free(pathcurr);
    jv_free(pathrest);
    jv_free(value);
    return root;
  }
  return jv_set(root, pathcurr, jv_setpath(subroot, pathrest, value));
}

jv jv_getpath(jv root, jv path) {
  if (jv_get_kind(path) != JV_KIND_ARRAY) {
    jv_free(root);
    jv_free(path);
    return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
  }
  if (!jv_is_valid(root)) {
    jv_free(path);
    return root;
  }
  if (jv_array_length(jv_copy(path)) == 0) {
    jv_free(path);
    return root;
  }
  jv pathcurr = jv_array_get(jv_copy(path), 0);
  jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));
  return jv_getpath(jv_get(root, pathcurr), pathrest);
}

// assumes paths is a sorted array of arrays
static jv delpaths_sorted(jv object, jv paths, int start) {
  jv delkeys = jv_array();
  for (int i=0; i<jv_array_length(jv_copy(paths));) {
    int j = i;
    assert(jv_array_length(jv_array_get(jv_copy(paths), i)) > start);
    int delkey = jv_array_length(jv_array_get(jv_copy(paths), i)) == start + 1;
    jv key = jv_array_get(jv_array_get(jv_copy(paths), i), start);
    while (j < jv_array_length(jv_copy(paths)) &&
           jv_equal(jv_copy(key), jv_array_get(jv_array_get(jv_copy(paths), j), start)))
      j++;
    // if i <= entry < j, then entry starts with key
    if (delkey) {
      // deleting this entire key, we don't care about any more specific deletions
      delkeys = jv_array_append(delkeys, key);
    } else {
      // deleting certain sub-parts of this key
      jv subobject = jv_get(jv_copy(object), jv_copy(key));
      if (!jv_is_valid(subobject)) {
        jv_free(key);
        jv_free(object);
        object = subobject;
        break;
      } else if (jv_get_kind(subobject) == JV_KIND_NULL) {
        jv_free(key);
        jv_free(subobject);
      } else {
        jv newsubobject = delpaths_sorted(subobject, jv_array_slice(jv_copy(paths), i, j), start+1);
        if (!jv_is_valid(newsubobject)) {
          jv_free(key);
          jv_free(object);
          object = newsubobject;
          break;
        }
        object = jv_set(object, key, newsubobject);
      }
      if (!jv_is_valid(object)) break;
    }
    i = j;
  }
  jv_free(paths);
  if (jv_is_valid(object))
    object = jv_dels(object, delkeys);
  else
    jv_free(delkeys);
  return object;
}

jv jv_delpaths(jv object, jv paths) {
  if (jv_get_kind(paths) != JV_KIND_ARRAY) {
    jv_free(object);
    jv_free(paths);
    return jv_invalid_with_msg(jv_string("Paths must be specified as an array"));
  }
  paths = jv_sort(paths, jv_copy(paths));
  jv_array_foreach(paths, i, elem) {
    if (jv_get_kind(elem) != JV_KIND_ARRAY) {
      jv_free(object);
      jv_free(paths);
      jv err = jv_invalid_with_msg(jv_string_fmt("Path must be specified as array, not %s",
                                                 jv_kind_name(jv_get_kind(elem))));
      jv_free(elem);
      return err;
    }
    jv_free(elem);
  }
  if (jv_array_length(jv_copy(paths)) == 0) {
    // nothing is being deleted
    jv_free(paths);
    return object;
  }
  if (jv_array_length(jv_array_get(jv_copy(paths), 0)) == 0) {
    // everything is being deleted
    jv_free(paths);
    jv_free(object);
    return jv_null();
  }
  return delpaths_sorted(object, paths, 0);
}

static int string_cmp(const void* pa, const void* pb){
  const jv* a = pa;
  const jv* b = pb;
  int lena = jv_string_length_bytes(jv_copy(*a));
  int lenb = jv_string_length_bytes(jv_copy(*b));
  int minlen = lena < lenb ? lena : lenb;
  int r = memcmp(jv_string_value(*a), jv_string_value(*b), minlen);
  if (r == 0) r = lena - lenb;
  return r;
}

jv jv_keys_unsorted(jv x) {
  if (jv_get_kind(x) != JV_KIND_OBJECT)
    return jv_keys(x);
  jv answer = jv_array_sized(jv_object_length(jv_copy(x)));
  jv_object_foreach(x, key, value) {
    answer = jv_array_append(answer, key);
```

[Assistant]
diff --git a/src/jv_aux.c b/src/jv_aux.c
index 018f380b10..fd5ff96684 100644
--- a/src/jv_aux.c
+++ b/src/jv_aux.c
@@ -365,6 +365,10 @@ static jv jv_dels(jv t, jv keys) {
   return t;
 }

+#ifndef MAX_PATH_DEPTH
+#define MAX_PATH_DEPTH (10000)
+#endif
+
 jv jv_setpath(jv root, jv path, jv value) {
   if (jv_get_kind(path) != JV_KIND_ARRAY) {
     jv_free(value);
@@ -372,6 +376,12 @@ jv jv_setpath(jv root, jv path, jv value) {
     jv_free(path);
     return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
   }
+  if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) {
+    jv_free(value);
+    jv_free(root);
+    jv_free(path);
+    return jv_invalid_with_msg(jv_string("Path too deep"));
+  }
   if (!jv_is_valid(root)){
     jv_free(value);
     jv_free(path);
@@ -424,6 +434,11 @@ jv jv_getpath(jv root, jv path) {
     jv_free(path);
     return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
   }
+  if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) {
+    jv_free(root);
+    jv_free(path);
+    return jv_invalid_with_msg(jv_string("Path too deep"));
+  }
   if (!jv_is_valid(root)) {
     jv_free(path);
     return root;
@@ -502,6 +517,12 @@ jv jv_delpaths(jv object, jv paths) {
       jv_free(elem);
       return err;
     }
+    if (jv_array_length(jv_copy(elem)) > MAX_PATH_DEPTH) {
+      jv_free(object);
+      jv_free(paths);
+      jv_free(elem);
+      return jv_invalid_with_msg(jv_string("Path too deep"));
+    }
     jv_free(elem);
   }
   if (jv_array_length(jv_copy(paths)) == 0) {


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-33947: jq through 1.8.1 allows deeply nested path arrays to cause unbounded recursion in jv_setpath, jv_getpath, and jv_delpaths, leading to stack overflow. A crafted input document with paths exceeding ten thousand levels of nesting triggers the vulnerability when processed with path manipulation filters.

# Vulnerable source (src/jv_aux.c)
```c
        }
        if (i < delidx) {
          break;
        }
        neg_idx++;
      }
      while (nonneg_idx < jv_array_length(jv_copy(nonneg_keys))) {
        int delidx = (int)jv_number_get_value_and_consume(jv_array_get(jv_copy(nonneg_keys), nonneg_idx));
        if (i == delidx) {
          del = 1;
        }
        if (i < delidx) {
          break;
        }
        nonneg_idx++;
      }
      for (int sidx=0; !del && sidx<jv_array_length(jv_copy(starts)); sidx++) {
        if ((int)jv_number_get_value_and_consume(jv_array_get(jv_copy(starts), sidx)) <= i &&
            i < (int)jv_number_get_value_and_consume(jv_array_get(jv_copy(ends), sidx))) {
          del = 1;
        }
      }
      if (!del)
        new_array = jv_array_append(new_array, jv_array_get(jv_copy(t), i));
    }
  arr_out:
    jv_free(neg_keys);
    jv_free(nonneg_keys);
    jv_free(starts);
    jv_free(ends);
    jv_free(t);
    t = new_array;
  } else if (jv_get_kind(t) == JV_KIND_OBJECT) {
    jv_array_foreach(keys, i, k) {
      if (jv_get_kind(k) != JV_KIND_STRING) {
        jv_free(t);
        t = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s field of object",
                                              jv_kind_name(jv_get_kind(k))));
        jv_free(k);
        break;
      }
      t = jv_object_delete(t, k);
    }
  } else {
    jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete fields from %s",
                                               jv_kind_name(jv_get_kind(t))));
    jv_free(t);
    t = err;
  }
  jv_free(keys);
  return t;
}

jv jv_setpath(jv root, jv path, jv value) {
  if (jv_get_kind(path) != JV_KIND_ARRAY) {
    jv_free(value);
    jv_free(root);
    jv_free(path);
    return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
  }
  if (!jv_is_valid(root)){
    jv_free(value);
    jv_free(path);
    return root;
  }
  if (jv_array_length(jv_copy(path)) == 0) {
    jv_free(path);
    jv_free(root);
    return value;
  }
  jv pathcurr = jv_array_get(jv_copy(path), 0);
  jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));

  /*
   * We need to be careful not to make extra copies since that leads to
   * quadratic behavior (e.g., when growing large data structures in a
   * reduction with `setpath/2`, i.e., with `|=`.
   */
  if (jv_get_kind(pathcurr) == JV_KIND_OBJECT) {
    // Assignment to slice -- dunno yet how to avoid the extra copy
    return jv_set(root, pathcurr,
                  jv_setpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest, value));
  }

  jv subroot = jv_get(jv_copy(root), jv_copy(pathcurr));
  if (!jv_is_valid(subroot)) {
    jv_free(root);
    jv_free(pathcurr);
    jv_free(pathrest);
    jv_free(value);
    return subroot;
  }

  // To avoid the extra copy we drop the reference from `root` by setting that
  // to null first.
  root = jv_set(root, jv_copy(pathcurr), jv_null());
  if (!jv_is_valid(root)) {
    jv_free(subroot);
    jv_free(pathcurr);
    jv_free(pathrest);
    jv_free(value);
    return root;
  }
  return jv_set(root, pathcurr, jv_setpath(subroot, pathrest, value));
}

jv jv_getpath(jv root, jv path) {
  if (jv_get_kind(path) != JV_KIND_ARRAY) {
    jv_free(root);
    jv_free(path);
    return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
  }
  if (!jv_is_valid(root)) {
    jv_free(path);
    return root;
  }
  if (jv_array_length(jv_copy(path)) == 0) {
    jv_free(path);
    return root;
  }
  jv pathcurr = jv_array_get(jv_copy(path), 0);
  jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));
  return jv_getpath(jv_get(root, pathcurr), pathrest);
}

// assumes paths is a sorted array of arrays
static jv delpaths_sorted(jv object, jv paths, int start) {
  jv delkeys = jv_array();
  for (int i=0; i<jv_array_length(jv_copy(paths));) {
    int j = i;
    assert(jv_array_length(jv_array_get(jv_copy(paths), i)) > start);
    int delkey = jv_array_length(jv_array_get(jv_copy(paths), i)) == start + 1;
    jv key = jv_array_get(jv_array_get(jv_copy(paths), i), start);
    while (j < jv_array_length(jv_copy(paths)) &&
           jv_equal(jv_copy(key), jv_array_get(jv_array_get(jv_copy(paths), j), start)))
      j++;
    // if i <= entry < j, then entry starts with key
    if (delkey) {
      // deleting this entire key, we don't care about any more specific deletions
      delkeys = jv_array_append(delkeys, key);
    } else {
      // deleting certain sub-parts of this key
      jv subobject = jv_get(jv_copy(object), jv_copy(key));
      if (!jv_is_valid(subobject)) {
        jv_free(key);
        jv_free(object);
        object = subobject;
        break;
      } else if (jv_get_kind(subobject) == JV_KIND_NULL) {
        jv_free(key);
        jv_free(subobject);
      } else {
        jv newsubobject = delpaths_sorted(subobject, jv_array_slice(jv_copy(paths), i, j), start+1);
        if (!jv_is_valid(newsubobject)) {
          jv_free(key);
          jv_free(object);
          object = newsubobject;
          break;
        }
        object = jv_set(object, key, newsubobject);
      }
      if (!jv_is_valid(object)) break;
    }
    i = j;
  }
  jv_free(paths);
  if (jv_is_valid(object))
    object = jv_dels(object, delkeys);
  else
    jv_free(delkeys);
  return object;
}

jv jv_delpaths(jv object, jv paths) {
  if (jv_get_kind(paths) != JV_KIND_ARRAY) {
    jv_free(object);
    jv_free(paths);
    return jv_invalid_with_msg(jv_string("Paths must be specified as an array"));
  }
  paths = jv_sort(paths, jv_copy(paths));
  jv_array_foreach(paths, i, elem) {
    if (jv_get_kind(elem) != JV_KIND_ARRAY) {
      jv_free(object);
      jv_free(paths);
      jv err = jv_invalid_with_msg(jv_string_fmt("Path must be specified as array, not %s",
                                                 jv_kind_name(jv_get_kind(elem))));
      jv_free(elem);
      return err;
    }
    jv_free(elem);
  }
  if (jv_array_length(jv_copy(paths)) == 0) {
    // nothing is being deleted
    jv_free(paths);
    return object;
  }
  if (jv_array_length(jv_array_get(jv_copy(paths), 0)) == 0) {
    // everything is being deleted
    jv_free(paths);
    jv_free(object);
    return jv_null();
  }
  return delpaths_sorted(object, paths, 0);
}

static int string_cmp(const void* pa, const void* pb){
  const jv* a = pa;
  const jv* b = pb;
  int lena = jv_string_length_bytes(jv_copy(*a));
  int lenb = jv_string_length_bytes(jv_copy(*b));
  int minlen = lena < lenb ? lena : lenb;
  int r = memcmp(jv_string_value(*a), jv_string_value(*b), minlen);
  if (r == 0) r = lena - lenb;
  return r;
}

jv jv_keys_unsorted(jv x) {
  if (jv_get_kind(x) != JV_KIND_OBJECT)
    return jv_keys(x);
  jv answer = jv_array_sized(jv_object_length(jv_copy(x)));
  jv_object_foreach(x, key, value) {
    answer = jv_array_append(answer, key);
```

[Assistant]
diff --git a/src/jv_aux.c b/src/jv_aux.c
index 018f380b10..fd5ff96684 100644
--- a/src/jv_aux.c
+++ b/src/jv_aux.c
@@ -365,6 +365,10 @@ static jv jv_dels(jv t, jv keys) {
   return t;
 }

+#ifndef MAX_PATH_DEPTH
+#define MAX_PATH_DEPTH (10000)
+#endif
+
 jv jv_setpath(jv root, jv path, jv value) {
   if (jv_get_kind(path) != JV_KIND_ARRAY) {
     jv_free(value);
@@ -372,6 +376,12 @@ jv jv_setpath(jv root, jv path, jv value) {
     jv_free(path);
     return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
   }
+  if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) {
+    jv_free(value);
+    jv_free(root);
+    jv_free(path);
+    return jv_invalid_with_msg(jv_string("Path too deep"));
+  }
   if (!jv_is_valid(root)){
     jv_free(value);
     jv_free(path);
@@ -424,6 +434,11 @@ jv jv_getpath(jv root, jv path) {
     jv_free(path);
     return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
   }
+  if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) {
+    jv_free(root);
+    jv_free(path);
+    return jv_invalid_with_msg(jv_string("Path too deep"));
+  }
   if (!jv_is_valid(root)) {
     jv_free(path);
     return root;
@@ -502,6 +517,12 @@ jv jv_delpaths(jv object, jv paths) {
       jv_free(elem);
       return err;
     }
+    if (jv_array_length(jv_copy(elem)) > MAX_PATH_DEPTH) {
+      jv_free(object);
+      jv_free(paths);
+      jv_free(elem);
+      return jv_invalid_with_msg(jv_string("Path too deep"));
+    }
     jv_free(elem);
   }
   if (jv_array_length(jv_copy(paths)) == 0) {


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-33948: Input like '{}\x00{}' was thus parsed as only '{}' and jq exited 0, dropping the trailing bytes after the NUL — so jq and another NUL-aware consumer could disagree on the document. Affects jq 1.8.x.

# Vulnerable source (src/util.c)
```c

  state->buf[0] = 0;
  state->buf_valid_len = 0;
  if (state->current_input) {
    char *res;
    memset(state->buf, 0xff, sizeof(state->buf));

    const int max_utf8_len = 4;
    const int max_gets_len = sizeof(state->buf) - max_utf8_len;
    while (!(res = fgets(state->buf, max_gets_len, state->current_input)) &&
           ferror(state->current_input) && errno == EINTR)
      clearerr(state->current_input);
    if (res == NULL) {
      state->buf[0] = 0;
      if (ferror(state->current_input))
        state->failures++;
    } else {
      const char *p = memchr(state->buf, '\n', max_gets_len);

      if (p != NULL)
        state->current_line++;

      if (p == NULL && state->parser != NULL) {
        /*
         * There should be no NULs in JSON texts (but JSON text
         * sequences are another story).
         */
        state->buf_valid_len = strlen(state->buf);
      } else if (p == NULL && feof(state->current_input)) {
        size_t i;

        /*
         * XXX We don't know how many bytes we've read!
         *
         * We can't use getline() because there need not be any newlines
         * in the input.  The only entirely correct choices are: use
         * fgetc() or fread().  Using fread() will complicate buffer
         * management here.
         *
         * For now we check how much fgets() read by scanning backwards for the
```

[Assistant]
diff --git a/src/util.c b/src/util.c
--- a/src/util.c
+++ b/src/util.c
@@ -312,13 +312,7 @@ static int jq_util_input_read_more(jq_util_input_state *state) {
       if (p != NULL)
         state->current_line++;

-      if (p == NULL && state->parser != NULL) {
-        /*
-         * There should be no NULs in JSON texts (but JSON text
-         * sequences are another story).
-         */
-        state->buf_valid_len = strlen(state->buf);
-      } else if (p == NULL && feof(state->current_input)) {
+      if (p == NULL && feof(state->current_input)) {
         size_t i;

         /*


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-39979: jq 1.8.1 jv_parse_sized_custom_flags function has an out-of-bounds read when a parse error is reported: the error message formatter used the input pointer with a plain format specifier that reads until NUL rather than stopping at the declared input boundary, allowing a crafted JSON input without a terminating NUL to trigger reads past the allocated buffer.

# Vulnerable source (src/jv_parse.c)
```c
      value = p->next;
    }
    p->next = jv_invalid();
    if ((p->flags & JV_PARSE_SEQ) && !p->last_ch_was_ws && jv_get_kind(value) == JV_KIND_NUMBER) {
      jv_free(value);
      return make_error(p, "Potentially truncated top-level numeric value at EOF at line %d, column %d", p->line, p->column);
    }
    return value;
  }
}

jv jv_parse_sized_custom_flags(const char* string, int length, int flags) {
  struct jv_parser parser;
  parser_init(&parser, flags);
  jv_parser_set_buf(&parser, string, length, 0);
  jv value = jv_parser_next(&parser);
  if (jv_is_valid(value)) {
    jv next = jv_parser_next(&parser);
    if (jv_is_valid(next)) {
      // multiple JSON values, we only wanted one
      jv_free(value);
      jv_free(next);
      value = jv_invalid_with_msg(jv_string("Unexpected extra JSON values"));
    } else if (jv_invalid_has_msg(jv_copy(next))) {
      // parser error after the first JSON value
      jv_free(value);
      value = next;
    } else {
      // a single valid JSON value
      jv_free(next);
    }
  } else if (jv_invalid_has_msg(jv_copy(value))) {
    // parse error, we'll return it
  } else {
    // no value at all
    jv_free(value);
    value = jv_invalid_with_msg(jv_string("Expected JSON value"));
  }
  parser_free(&parser);

  if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) {
    jv msg = jv_invalid_get_msg(value);
    value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')",
                                              jv_string_value(msg),
                                              string));
    jv_free(msg);
  }
  return value;
}

jv jv_parse_sized(const char* string, int length) {
  return jv_parse_sized_custom_flags(string, length, 0);
}

jv jv_parse(const char* string) {
  return jv_parse_sized(string, strlen(string));
}

jv jv_parse_custom_flags(const char* string, int flags) {
  return jv_parse_sized_custom_flags(string, strlen(string), flags);
}
```

[Assistant]
diff --git a/src/jv_parse.c b/src/jv_parse.c
index aa2054cc09..56847b5eaa 100644
--- a/src/jv_parse.c
+++ b/src/jv_parse.c
@@ -893,8 +893,9 @@ jv jv_parse_sized_custom_flags(const char* string, int length, int flags) {

   if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) {
     jv msg = jv_invalid_get_msg(value);
-    value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')",
+    value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%.*s')",
                                               jv_string_value(msg),
+                                              length,
                                               string));
     jv_free(msg);
   }


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-39979: Out-of-bounds read in jq's jv_parse_sized_custom_flags() (src/jv_parse.c). Reached through the public jv_parse_sized() API with a non-NUL-terminated invalid-JSON buffer. Affects jq 1.8.x.

# Vulnerable source (src/jv_parse.c)
```c
      jv_free(value);
      jv_free(next);
      value = jv_invalid_with_msg(jv_string("Unexpected extra JSON values"));
    } else if (jv_invalid_has_msg(jv_copy(next))) {
      // parser error after the first JSON value
      jv_free(value);
      value = next;
    } else {
      // a single valid JSON value
      jv_free(next);
    }
  } else if (jv_invalid_has_msg(jv_copy(value))) {
    // parse error, we'll return it
  } else {
    // no value at all
    jv_free(value);
    value = jv_invalid_with_msg(jv_string("Expected JSON value"));
  }
  parser_free(&parser);

  if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) {
    jv msg = jv_invalid_get_msg(value);
    value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')",
                                              jv_string_value(msg),
                                              string));
    jv_free(msg);
  }
  return value;
}

jv jv_parse_sized(const char* string, int length) {
  return jv_parse_sized_custom_flags(string, length, 0);
}

jv jv_parse(const char* string) {
  return jv_parse_sized(string, strlen(string));
}

jv jv_parse_custom_flags(const char* string, int flags) {
  return jv_parse_sized_custom_flags(string, strlen(string), flags);
```

[Assistant]
diff --git a/src/jv_parse.c b/src/jv_parse.c
--- a/src/jv_parse.c
+++ b/src/jv_parse.c
@@ -893,8 +893,9 @@ jv jv_parse_sized_custom_flags(const char* string, int length, int flags) {

   if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) {
     jv msg = jv_invalid_get_msg(value);
-    value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')",
+    value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%.*s')",
                                               jv_string_value(msg),
+                                              length,
                                               string));
     jv_free(msg);
   }