Skip to content

feat: add create-wiki-page safe output#61

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/create-wiki-page-safe-output
Draft

feat: add create-wiki-page safe output#61
Copilot wants to merge 2 commits intomainfrom
copilot/create-wiki-page-safe-output

Conversation

Copy link
Contributor

Copilot AI commented Mar 19, 2026

Adds create-wiki-page as a new safe output tool, complementing edit-wiki-page. Creation fails if the page already exists (directing the agent to use edit-wiki-page instead), while edit-wiki-page fails if the page doesn't exist.

Changes

  • src/tools/create_wiki_page.rs — New tool implementation mirroring edit-wiki-page:

    • Same config shape: wiki-name, wiki-project, path-prefix, title-prefix, comment
    • GET-then-guard pattern: rejects if page already exists, then PUTs without If-Match (no ETag needed for new pages)
    • Default commit comment: "Created by agent"
    • Full sanitization, path traversal protection, and path-prefix enforcement
  • src/compile/common.rs — Added create-wiki-page to WRITE_REQUIRING_SAFE_OUTPUTS; compilation fails without permissions.write

  • src/execute.rs — Dispatch case for create-wiki-page

  • src/mcp.rs — Exposes create-wiki-page as an MCP tool with identical sanitization pipeline to edit-wiki-page

Example front matter

permissions:
  write: my-write-sc
safe-outputs:
  create-wiki-page:
    wiki-name: "MyProject.wiki"
    path-prefix: "/agent-output"
    title-prefix: "[Agent] "
    comment: "Created by agent"

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.github.com (HTTP Only)
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw proxy --allow api.github.com 18gjmaxdo7vm2jyfidd.1e4llqk.rcgu.o n09t5bwvfg5z17tzt47.1e4llqk.rcgu.o b.05y3skou2n8gywnq1xg30s6nw.0azpbko.rcgu.o b.0a9mzns1w8h8hjt5t2ctpscqh.0azpbko.rcgu.o b.0dvg5p338ze4yo8sclenf881a.0azpbko.rcgu.o b.0dx997ws7aolj1y340bx0d6e5.0azpbko.rcgu.o b.0q4dyj2s5hiuc6j4v5i9q6jg7.0azpbko.rcgu.o b.0r3pe41pju7n15rf01z828jy9.0azpbko.rcgu.o b.0st8ftpil8vvr8aptfgk5l9d4.0azpbko.rcgu.o b.115rxut0y51w7elubrycm7rr6.0azpbko.rcgu.o b.18jyy94t4oev2upsdapziepnb.0azpbko.rcgu.o b.1a3v69ywrr870cv3s0my1xrqv.0azpbko.rcgu.o b.1n70h8mnc4lv6myvt7xv1774j.0azpbko.rcgu.o b.1rzfl8n5jj6pb28b4t6wqgpti.0azpbko.rcgu.o b.220a3tz1x7lsbsl14itqakolp.0azpbko.rcgu.o b.2b3v53f7x475pleph3cuavv68.0azpbko.rcgu.o (packet block)
  • dev.azure.com
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-65dae42eaa050fb2 /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-65dae42eaa050fb2 create_wiki --emit=dep-info,metadata,link -C embed-bitcode=no -C debuginfo=2 --check-cfg cfg(docsrs,test) --check-cfg cfg(feature, values()) -C 7.th�� 7.thiserror_impl.971f6a7430b99441-cgu.13.rcgu.o 7.thiserror_impl.971f6a7430b99441-cgu.14.rcgu.o f/memchr-2.7.6/s/home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/schemars_derive-486665cf5f9386--noprofile lib/rustlib/x86_/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/bin/rustc 0b7c67-cgu.0.rcg--crate-name u.o lib/rustlib/x86_--edition=2015 (dns block)
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-65dae42eaa050fb2 /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-65dae42eaa050fb2 --cfg feature="default" --cfg 7.th�� 7.thiserror_impl.971f6a7430b99441-cgu.13.rcgu.o 0e408.00mdgrb20p5x77eazk2p9gjyy.1uk7wf0.rcgu.o 0e408.01teq1ycpdi29xtq38m58avzw.1uk7wf0.rcgu.o 0e408.04wdksz2udhbsmhbmz31ifrn2.1uk7wf0.rcgu.o 0e408.0roi7kz8fyd01vr8wopkslw4t.1uk7wf0.rcgu.o 0e408.0w0s0sshyajplf2l699oxuwfk.1uk7wf0.rcgu.o 0e408.0zz0925kng4sqhjswpq47h7d1.1uk7wf0.rcgu.o 0e408.1h8m7eedzoqh87j6ntnde3cky.1uk7wf0.rcgu.o 0e408.21isguhevtn32dqcdl1j05fjf.1uk7wf0.rcgu.o 0e408.27hw7b2y70hemqls64pwyeeau.1uk7wf0.rcgu.o 0e408.2ap69xa5jhcu8bzvi8nf665ru.1uk7wf0.rcgu.o 0e408.2l7sln5w36yyxwfvup96uk3iy.1uk7wf0.rcgu.o 0e408.2z3o4ny3w29nexl27pg3lovk0.1uk7wf0.rcgu.o 0e408.31ku1szu3n6wt3joczv7xzi8i.1uk7wf0.rcgu.o 0e408.3q1z69vza816v6i52bhfjdqwn.1uk7wf0.rcgu.o (dns block)
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw proxy --allow api.github.com --allow dev.azure.com --allow *.visualstudio.com b.0dvg5p338ze4yo8sclenf881a.0azpbko.rcgu.o b.0dx997ws7aolj1y340bx0d6e5.0azpbko.rcgu.o b.0q4dyj2s5hiuc6j4v5i9q6jg7.0azpbko.rcgu.o b.0r3pe41pju7n15rf01z828jy9.0azpbko.rcgu.o b.0st8ftpil8vvr8aptfgk5l9d4.0azpbko.rcgu.o b.115rxut0y51w7elubrycm7rr6.0azpbko.rcgu.o b.18jyy94t4oev2upsdapziepnb.0azpbko.rcgu.o b.1a3v69ywrr870cv3s0my1xrqv.0azpbko.rcgu.o b.1n70h8mnc4lv6myvt7xv1774j.0azpbko.rcgu.o b.1rzfl8n5jj6pb28b4t6wqgpti.0azpbko.rcgu.o b.220a3tz1x7lsbsl14itqakolp.0azpbko.rcgu.o b.2b3v53f7x475pleph3cuavv68.0azpbko.rcgu.o (dns block)
  • msazuresphere.visualstudio.com
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw proxy --allow api.github.com --allow dev.azure.com --allow *.visualstudio.com b.0dvg5p338ze4yo8sclenf881a.0azpbko.rcgu.o b.0dx997ws7aolj1y340bx0d6e5.0azpbko.rcgu.o b.0q4dyj2s5hiuc6j4v5i9q6jg7.0azpbko.rcgu.o b.0r3pe41pju7n15rf01z828jy9.0azpbko.rcgu.o b.0st8ftpil8vvr8aptfgk5l9d4.0azpbko.rcgu.o b.115rxut0y51w7elubrycm7rr6.0azpbko.rcgu.o b.18jyy94t4oev2upsdapziepnb.0azpbko.rcgu.o b.1a3v69ywrr870cv3s0my1xrqv.0azpbko.rcgu.o b.1n70h8mnc4lv6myvt7xv1774j.0azpbko.rcgu.o b.1rzfl8n5jj6pb28b4t6wqgpti.0azpbko.rcgu.o b.220a3tz1x7lsbsl14itqakolp.0azpbko.rcgu.o b.2b3v53f7x475pleph3cuavv68.0azpbko.rcgu.o (dns block)
  • raw.github.com
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw /home/REDACTED/work/ado-aw/ado-aw/target/debug/ado-aw proxy --allow *.github.com 18gjmaxdo7vm2jyfidd.1e4llqk.rcgu.o n09t5bwvfg5z17tzt47.1e4llqk.rcgu.o b.05y3skou2n8gywnq1xg30s6nw.0azpbko.rcgu.o b.0a9mzns1w8h8hjt5t2ctpscqh.0azpbko.rcgu.o b.0dvg5p338ze4yo8sclenf881a.0azpbko.rcgu.o b.0dx997ws7aolj1y340bx0d6e5.0azpbko.rcgu.o b.0q4dyj2s5hiuc6j4v5i9q6jg7.0azpbko.rcgu.o b.0r3pe41pju7n15rf01z828jy9.0azpbko.rcgu.o b.0st8ftpil8vvr8aptfgk5l9d4.0azpbko.rcgu.o b.115rxut0y51w7elubrycm7rr6.0azpbko.rcgu.o b.18jyy94t4oev2upsdapziepnb.0azpbko.rcgu.o b.1a3v69ywrr870cv3s0my1xrqv.0azpbko.rcgu.o b.1n70h8mnc4lv6myvt7xv1774j.0azpbko.rcgu.o b.1rzfl8n5jj6pb28b4t6wqgpti.0azpbko.rcgu.o b.220a3tz1x7lsbsl14itqakolp.0azpbko.rcgu.o b.2b3v53f7x475pleph3cuavv68.0azpbko.rcgu.o (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


⌨️ Start Copilot coding agent tasks without leaving your editor — available in VS Code, Visual Studio, JetBrains IDEs and Eclipse.

Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Copilot AI changed the title [WIP] Add create wiki page safe output feat: add create-wiki-page safe output Mar 19, 2026
Copilot AI requested a review from jamesadevine March 19, 2026 22:53
@jamesadevine
Copy link
Collaborator

/rust-review

@github-actions
Copy link
Contributor

github-actions bot commented Mar 19, 2026

Rust PR Reviewer completed successfully!

@github-actions
Copy link
Contributor

🔍 Rust PR Review

Summary: Looks good overall — solid security posture with a couple of correctness/testing concerns worth addressing.

Findings

🐛 Bugs / Logic Issues

  • src/tools/create_wiki_page.rs:280-293 — PUT without If-Match is an upsert, not a create-only
    The ADO Wiki Pages API (PUT pages) [creates or edits]((learn.microsoft.com/redacted) when no If-Match header is provided. The GET-then-guard pattern correctly blocks the common case, but a TOCTOU race between the GET (404) and the PUT allows a concurrent request to create the page first — then the second PUT will silently update the existing page, violating the stated "create-only" contract. To make the guarantee atomic, consider using If-Match: "" (an empty ETag string), which some ADO APIs interpret as "create only — fail with 412 if the resource already exists." If the API supports it, a 412 response should then be surfaced as a clean failure message rather than an opaque HTTP error.

⚠️ Suggestions

  • src/tools/create_wiki_page.rs:742-773 — Guard tests don't cover execute_impl
    test_page_already_exists_guard_returns_failure and test_new_page_passes_guard duplicate the guard logic inline and test a local if page_exists { … } conditional, not the actual code path inside execute_impl. If the guard were accidentally removed from execute_impl, both tests would still pass. Consider replacing them with a direct call to result.execute_impl(&ctx).await using a mock HTTP layer, or at minimum leave a comment noting these test the logic prototype rather than the live code.

  • src/tools/create_wiki_page.rs:29-50path = "/" passes validation
    A bare / passes all three validate() checks (non-empty, no null bytes, no ..). After apply_title_prefix("/", "[Agent] "), this yields "/[Agent] " — a page with a trailing-space title. If this matters to you, add ensure!(self.path.trim_matches('/') != "", "path must contain at least one non-slash segment"). The path-prefix config mitigates this in practice, but an unconfigured deployment is exposed.

✅ What Looks Good

  • Defense-in-depth sanitization: path control-char stripping happens at MCP layer (Stage 1) and again via Executor::execute_sanitized (Stage 2) — the layering is correct and matches the existing edit_wiki_page pattern.
  • Belt-and-suspenders path traversal check: .. is rejected at validate() time and re-checked after normalize_wiki_path in execute_impl before any network call. Good.
  • Path-prefix enforcement: The prefix check correctly uses starts_with("{prefix}/") (not bare starts_with(prefix)), so /agent-output-extra/foo is not incorrectly allowed under /agent-output. Well done.
  • URL encoding: wiki_name and project are utf8_percent_encode-d into base_url; path and comment are passed via reqwest's .query() which URL-encodes them automatically. No injection risk.
  • Test coverage: Comprehensive unit tests for validation edge cases, sanitization, path helpers, config deserialization, and executor error paths. The integration tests for the write-SC requirement gate compile correctly too.

Generated by Rust PR Reviewer for issue #61 ·

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