Fixes dejima-guard: heartbeat_url contains a character that is not allowed here, which appears once the SSH side is working.
The check refused the URL this project recommends
The permitted character list did not include &, so any URL with two query parameters was rejected. That includes the push URL Uptime Kuma hands out, which is the one value heartbeat_url is documented to hold.
Refusing a legitimate URL is not the safe direction here. It costs the heartbeat, and the heartbeat is the one signal a compromised devbox cannot suppress — so an over-strict rule quietly removes the control that does not depend on the watched machine cooperating. It removes it quietly because a guard that never pings looks exactly like one nobody configured.
The set is RFC 3986's now: unreserved characters, gen-delims, sub-delims and %. What stays refused is what cannot appear in a URL anyway — whitespace, control characters, quotes, backslash, and < > ^ \ { | }. The original reason for the rule is unaffected: a value must still start with http://orhttps://, so curl cannot read a leading -K` as an option.
Two things found while fixing it
An embedded newline passed. Deleting the permitted characters and testing what remains cannot see a trailing one, because command substitution strips it — so the remainder is empty and the value reads as clean. Whitespace and control characters are checked separately now, ahead of that test.
The shipped config.example carried a path with no query string, so the example passed a check that would have refused the real thing. It is the full URL now, which also makes the test that runs the example through the check worth having.
The message
It names the characters it found. contains a character that is not allowed here was the entire report, on a value long enough that locating the character meant guessing.
Tested: 64 passing. The new tests were run against the old check and five of the six fail on it.
**Summary generated by LLM agent**
Fixes `dejima-guard: heartbeat_url contains a character that is not allowed here`, which appears once the SSH side is working.
## The check refused the URL this project recommends
The permitted character list did not include `&`, so any URL with two query parameters was rejected. That includes the push URL Uptime Kuma hands out, which is the one value `heartbeat_url` is documented to hold.
Refusing a legitimate URL is not the safe direction here. It costs the heartbeat, and the heartbeat is the one signal a compromised devbox cannot suppress — so an over-strict rule quietly removes the control that does not depend on the watched machine cooperating. It removes it quietly because a guard that never pings looks exactly like one nobody configured.
The set is RFC 3986's now: unreserved characters, gen-delims, sub-delims and `%`. What stays refused is what cannot appear in a URL anyway — whitespace, control characters, quotes, backslash, and `< > ^ \` { | }`. The original reason for the rule is unaffected: a value must still start with `http://` or `https://`, so curl cannot read a leading `-K` as an option.
## Two things found while fixing it
An embedded newline passed. Deleting the permitted characters and testing what remains cannot see a trailing one, because command substitution strips it — so the remainder is empty and the value reads as clean. Whitespace and control characters are checked separately now, ahead of that test.
The shipped `config.example` carried a path with no query string, so the example passed a check that would have refused the real thing. It is the full URL now, which also makes the test that runs the example through the check worth having.
## The message
It names the characters it found. `contains a character that is not allowed here` was the entire report, on a value long enough that locating the character meant guessing.
Tested: 64 passing. The new tests were run against the old check and five of the six fail on it.
branderson
approved these changes 2026-08-28 20:55:15 -07:00
The URL check permitted a short list of characters that did not include "&", so
it refused any URL with two query parameters - including the push URL Uptime
Kuma hands out, which is the one value heartbeat_url is documented to hold. The
project rejected the setting the project recommends.
Refusing a legitimate URL is not the safe direction. It costs the heartbeat,
and the heartbeat is the one signal a compromised devbox cannot suppress, so an
over-strict rule removes the control that does not depend on the watched
machine cooperating - and removes it quietly, since a guard that never pings is
indistinguishable from one that was never configured to.
The set is RFC 3986's now: unreserved, gen-delims, sub-delims and %. What stays
refused is what cannot appear in a URL anyway - whitespace, control characters,
quotes, backslash and < > ^ ` { | }.
Whitespace and control characters are checked separately, because the test that
deletes permitted characters cannot see a trailing newline: command
substitution strips it, so the remainder reads as empty and the value passes.
The message names the characters it found. "contains a character that is not
allowed here" was the whole report, on values long enough that finding it meant
guessing.
The shipped example carried a path with no query, so it passed a check that
would have refused the real thing. It is the whole URL now.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary generated by LLM agent
Fixes
dejima-guard: heartbeat_url contains a character that is not allowed here, which appears once the SSH side is working.The check refused the URL this project recommends
The permitted character list did not include
&, so any URL with two query parameters was rejected. That includes the push URL Uptime Kuma hands out, which is the one valueheartbeat_urlis documented to hold.Refusing a legitimate URL is not the safe direction here. It costs the heartbeat, and the heartbeat is the one signal a compromised devbox cannot suppress — so an over-strict rule quietly removes the control that does not depend on the watched machine cooperating. It removes it quietly because a guard that never pings looks exactly like one nobody configured.
The set is RFC 3986's now: unreserved characters, gen-delims, sub-delims and
%. What stays refused is what cannot appear in a URL anyway — whitespace, control characters, quotes, backslash, and< > ^ \{ | }. The original reason for the rule is unaffected: a value must still start withhttp://orhttps://, so curl cannot read a leading-K` as an option.Two things found while fixing it
An embedded newline passed. Deleting the permitted characters and testing what remains cannot see a trailing one, because command substitution strips it — so the remainder is empty and the value reads as clean. Whitespace and control characters are checked separately now, ahead of that test.
The shipped
config.examplecarried a path with no query string, so the example passed a check that would have refused the real thing. It is the full URL now, which also makes the test that runs the example through the check worth having.The message
It names the characters it found.
contains a character that is not allowed herewas the entire report, on a value long enough that locating the character meant guessing.Tested: 64 passing. The new tests were run against the old check and five of the six fail on it.
The URL check permitted a short list of characters that did not include "&", so it refused any URL with two query parameters - including the push URL Uptime Kuma hands out, which is the one value heartbeat_url is documented to hold. The project rejected the setting the project recommends. Refusing a legitimate URL is not the safe direction. It costs the heartbeat, and the heartbeat is the one signal a compromised devbox cannot suppress, so an over-strict rule removes the control that does not depend on the watched machine cooperating - and removes it quietly, since a guard that never pings is indistinguishable from one that was never configured to. The set is RFC 3986's now: unreserved, gen-delims, sub-delims and %. What stays refused is what cannot appear in a URL anyway - whitespace, control characters, quotes, backslash and < > ^ ` { | }. Whitespace and control characters are checked separately, because the test that deletes permitted characters cannot see a trailing newline: command substitution strips it, so the remainder reads as empty and the value passes. The message names the characters it found. "contains a character that is not allowed here" was the whole report, on values long enough that finding it meant guessing. The shipped example carried a path with no query, so it passed a check that would have refused the real thing. It is the whole URL now.988f14490ato179498ff0e