Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ All notable changes to ShellClaw are documented here. Format follows [Keep a Cha
### Security
- Camera auto-output keeps the exclusive `mkstemp` inode (no unlink + `${tmpl}.jpg` sibling).
- Reject I2C `bus` outside 0–255 at the tool JSON boundary.
- Inbound ASAP response builder no longer double-frees the payload cJSON when a required envelope field cannot be allocated (unauthenticated `POST /asap` `state.query` / `task.cancel`).

---

Expand Down
3 changes: 2 additions & 1 deletion src/asap/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,14 @@ static int fill_response_envelope(asap_envelope_t *out, const asap_envelope_t *i
out->sender = in->recipient ? strdup(in->recipient) : NULL;
out->recipient = in->sender ? strdup(in->sender) : NULL;
out->payload_type = strdup(payload_type);
/* Ownership of payload moves to out; asap_envelope_clear frees it once. */
out->payload = payload;
payload = NULL;
if (in->correlation_id)
out->correlation_id = strdup(in->correlation_id);
if (in->trace_id)
out->trace_id = strdup(in->trace_id);
if (!out->id || !out->asap_version || !out->sender || !out->recipient || !out->payload_type) {
cJSON_Delete(payload);
asap_envelope_clear(out);
asap_envelope_init(out);
return -32603;
Expand Down
33 changes: 33 additions & 0 deletions tests/test_asap_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,38 @@ static int test_tool_execute_nonzero_reports_error(void)
return 0;
}

/*
* fill_response_envelope used to cJSON_Delete(payload) after assigning
* out->payload, then asap_envelope_clear(out) deleted the same object.
* HTTP parse always supplies sender/recipient, so production hits this on
* post-attach strdup OOM; dropping sender here is the same cleanup path.
*/
static int test_response_builder_missing_sender_does_not_double_free(void)
{
asap_envelope_t in;
asap_envelope_t out;
asap_server_ctx_t ctx;
char err[128];
cJSON *pl;
int rc;

pl = cJSON_CreateObject();
ASSERT(pl != NULL);
ASSERT(cJSON_AddNullToObject(pl, "task_id") != NULL);
ASSERT(wrap_build(&in, "task.cancel", pl) == 0);
free(in.sender);
in.sender = NULL;
memset(&ctx, 0, sizeof ctx);
asap_envelope_init(&out);
rc = asap_server_handle(&in, &out, &ctx, err, sizeof err);
ASSERT(rc == -32603);
ASSERT(strstr(err, "envelope") != NULL);
ASSERT(out.payload == NULL);
teardown_env(&in);
teardown_env(&out);
return 0;
}

static int test_trust_sender_rejects_blank_sender_when_list_nonempty(void)
{
const char *path = "/tmp/shellclaw_test_asap_trust_blank.toml";
Expand Down Expand Up @@ -747,5 +779,6 @@ int main(void)
r |= test_tool_call_hook_overrides_builtin_dispatch();
r |= test_tool_execute_nonzero_reports_error();
r |= test_trust_sender_rejects_blank_sender_when_list_nonempty();
r |= test_response_builder_missing_sender_does_not_double_free();
return r;
}
Loading