Skip to content

fix: address Copilot review on #73 (kick auth bug + path traversal) + README tagline#75

Merged
joelteply merged 2 commits intocanaryfrom
fix/copilot-review-pr73
Apr 25, 2026
Merged

fix: address Copilot review on #73 (kick auth bug + path traversal) + README tagline#75
joelteply merged 2 commits intocanaryfrom
fix/copilot-review-pr73

Conversation

@joelteply
Copy link
Copy Markdown
Contributor

Summary

Addresses Copilot's review on PR #73 and adds a README tagline per Joel.

Copilot fixes

# severity what fix
1 HIGH cmd_kick was reading <peer>.pub (signing key) from authorized_keys, not the SSH auth key — kicked peers retained SSH access Host handshake now stores ssh_pub in peer JSON; cmd_kick reads from there
2 HIGH (security) cmd_whois / cmd_kick interpolated peer name into local + remote paths without sanitization (path traversal, shell injection vector) New _validate_peer_name helper, [a-z0-9-]+ only
3 MED scenario_kick never asserted authorized_keys actually changed Now greps before/after for the joiner's SSH pubkey
4 LOW airc identity link usage said handle was required, but empty handle unlinks Usage + help updated to reflect optional
5 LOW Regression test for path-traversal rejection in whois + kick New assertions in scenario_kick

One Copilot comment was a false positive: it read shift 2>/dev/null as shift 2 (count 2), but 2>/dev/null is stderr redirection — the shift count is 1, which is correct. No change needed.

README tagline

One italic line right under the title:

Collaborative agentic systems are the unlock — proven in continuum. airc is the chat substrate that came out of that work, distilled into the IRC primitives every model already knows.

Anvil DM'd separately for input — happy to revise to whatever Anvil's framing if she has a tighter line.

Test plan

  • 130/130 integration tests pass (4 new in scenario_kick)
  • kick now actually removes SSH key from authorized_keys (verified by new assertion)
  • airc whois ../config and airc kick ../config both rejected with "invalid peer name"
  • airc identity link continuum Earl then airc identity link continuum (no handle) properly unlinks

joelteply and others added 2 commits April 25, 2026 07:51
Five concrete fixes from Copilot's review of #73 (one false positive
about a `shift 2>/dev/null` mis-read as `shift 2` left as-is):

1. **kick was removing the wrong pubkey** — `<peer>.pub` holds the
   signing key (sign_pub), not the SSH auth key. cmd_kick was reading
   that file and trying to grep it out of authorized_keys, which never
   matched. Result: kicked peers kept SSH access. Now host handshake
   stores `ssh_pub` in the peer JSON record; cmd_kick reads from
   there. Falls back to a clear "no SSH key recorded" warning for
   pre-#34 peers (whose handshake didn't carry ssh_pub).

2. **path traversal in cmd_whois / cmd_kick** — peer name was
   interpolated into local file paths AND remote SSH cat commands
   without sanitization. `airc whois ../config` would have been
   happily resolved against the joiner's host. New _validate_peer_name
   helper enforces `[a-z0-9-]+` (same charset cmd_rename already
   uses), no leading dash. Wired into both commands.

3. **kick test never asserted authorized_keys removal** — scenario
   covered the peer file removal but not the SSH key revocation,
   which is the actual security boundary. Now greps authorized_keys
   for the joiner's pubkey before/after kick. Would have caught (1)
   immediately.

4. **identity link usage string lied** — claimed `<handle>` was
   required, but the code actually unlinks when handle is omitted.
   Updated usage + help to reflect optional/blank handle = unlink.

5. **path-traversal regression test** — scenario_kick now also
   asserts that `airc whois ../config` and `airc kick ../config`
   both fail with "invalid peer name". Defense in depth.

130/130 integration tests pass (4 new).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per Joel: airc is the chat substrate that came out of continuum
work, and the broader claim is that collaborative agentic systems are
the unlock. One italic line right under the title to set context for
fresh-clone readers before they hit the install instructions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 25, 2026 12:51
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses security/correctness issues in peer management (kick/whois) and tightens integration coverage around SSH access revocation, plus a small README tagline update.

Changes:

  • Persist joiner ssh_pub in peer JSON records and use it in airc kick to actually revoke SSH access via authorized_keys removal.
  • Add _validate_peer_name and apply it to whois/kick to prevent path traversal / injection via peer names.
  • Strengthen scenario_kick to assert authorized_keys removal and reject traversal-style peer names; update identity link help text; add README tagline.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
airc Stores ssh_pub in peer JSON, validates peer names for whois/kick, updates kick logic and identity-link usage text.
test/integration.sh Adds pre/post assertions that kick removes the joiner’s SSH key from authorized_keys and rejects traversal peer names.
README.md Adds a short italicized tagline under the title.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread airc
Comment on lines +180 to +191
# Validate a peer name matches the allowed nick charset BEFORE using it in
# filesystem paths or remote SSH commands. Same charset cmd_rename uses
# (a-z 0-9 -) — anything else is unreachable as a real airc identity and
# almost certainly a path-traversal / shell-injection attempt
# (`../config`, `; rm -rf /`, etc.). Rejects empty as well.
_validate_peer_name() {
local name="${1:-}"
case "$name" in
"") die "peer name required" ;;
*[!a-z0-9-]*) die "invalid peer name '$name' — must match [a-z0-9-]+ (use airc peers to list valid names)" ;;
-*) die "invalid peer name '$name' — must not start with '-'" ;;
esac
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_validate_peer_name rejects names starting with '-' but cmd_rename can still produce a leading '-' after sanitization (e.g., renaming to '.foo' becomes '-foo'). That would make such a peer name impossible to whois/kick and contradicts the "reject leading dash" intent. Consider trimming leading '-' after cmd_rename sanitization and re-validating, or otherwise aligning name-generation rules with this validator.

Copilot uses AI. Check for mistakes.
Comment thread test/integration.sh
Comment on lines +1345 to +1352
# ── Capture joiner's SSH pubkey BEFORE kick so we can assert removal ──
local kj_ssh_pub
kj_ssh_pub=$(cat /tmp/airc-it-k-j/state/identity/ssh_key.pub 2>/dev/null | tr -d '\n' || true)
if [ -n "$kj_ssh_pub" ] && [ -f "$HOME/.ssh/authorized_keys" ]; then
grep -qF "$kj_ssh_pub" "$HOME/.ssh/authorized_keys" \
&& pass "joiner's SSH key present in authorized_keys before kick" \
|| fail "joiner's SSH key missing from authorized_keys before kick (handshake regression?)"
fi
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new authorized_keys assertions are skipped entirely if kj_ssh_pub is empty or authorized_keys is missing. In this scenario, both should exist (init_identity generates ssh_key.pub and creates/updates authorized_keys), so skipping can let a regression slip through silently. Consider failing the test when the joiner pubkey can't be read or authorized_keys isn't present, so the kick-revocation check is always exercised.

Copilot uses AI. Check for mistakes.
@joelteply joelteply merged commit 4ea2bb5 into canary Apr 25, 2026
5 checks passed
@joelteply joelteply deleted the fix/copilot-review-pr73 branch April 25, 2026 12:55
joelteply added a commit that referenced this pull request Apr 25, 2026
fix: PR #75 Copilot follow-up + 'pure shell' title hyphen
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants