Skip to content

Log a workspace-containment refusal as a refusal, not an internal error - #24

Merged
n1ckyb merged 1 commit into
release/v0.0.2-rcfrom
fix/lsp-containment-refusal
Aug 9, 2026
Merged

Log a workspace-containment refusal as a refusal, not an internal error#24
n1ckyb merged 1 commit into
release/v0.0.2-rcfrom
fix/lsp-containment-refusal

Conversation

@n1ckyb

@n1ckyb n1ckyb commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Closes #23.

A URI resolving outside the workspace root raised a bare ValueError, caught by the blanket except Exception in server.py and logged with the same prefix, at the same level, as an internal fault:

WARNING  intentumdiff.lsp_server.server: intentumdiff/semanticDiff error: URI '.../evil/old.py'
resolves to '.../evil/old.py' which is outside the workspace root '.../project'.
PASSED

An operator could not tell "someone asked for a path outside the workspace and we blocked it" from "the diff engine threw". The refusal is a security control, so its audit trail is part of the control.

The fix

WorkspaceContainmentError, subclassing ValueError so every existing caller and test that catches ValueError keeps working. Raised for both the explicit-traversal and containment cases, handled separately from unexpected faults:

WARNING  intentumdiff/semanticDiff refused (workspace containment): URI '...' resolves to ...

The response now carries code: "workspace_containment", so a client can act on a refusal without string-matching a message.

The test now asserts the log line

Previously nothing checked it — the warning could have been deleted entirely and every test would still have passed. It now asserts both that the refusal is logged and that the internal-error prefix is absent, so the two cannot silently converge again.

Capturing it with caplog also keeps the suite output clean: this warning used to print in full beside a PASSED line, which reads like something went wrong and was ignored. Log noise is how genuinely important warnings get skimmed past — the habit that let 0.0.1 ship printing 69 errors per run while exiting 0.

Verification

pytest tests/unit/test_lsp_server.py24 passed.

🤖 Generated with Claude Code

Closes #23.

A URI resolving outside the workspace root raised a bare ValueError, caught by
the blanket `except Exception` in server.py and logged with the same prefix at
the same level as an internal fault:

    WARNING intentumdiff.lsp_server.server: intentumdiff/semanticDiff error:
    URI '...' resolves to '...' which is outside the workspace root '...'
    PASSED

An operator could not tell "someone asked for a path outside the workspace and
we blocked it" from "the diff engine threw". The refusal is a security control,
so its audit trail is part of the control.

Adds WorkspaceContainmentError, subclassing ValueError so every existing caller
and test that catches ValueError keeps working. Raised for both the explicit
traversal case and the containment case, handled separately, and the response
carries code="workspace_containment" so a client can act on it without
string-matching a message.

The test now ASSERTS the log line, and asserts the internal-error prefix is
absent. Previously nothing checked it: the warning could have been deleted and
every test would still have passed. Capturing it also stops the warning printing
in full beside a PASSED line, which reads like something went wrong and was
ignored.

pytest tests/unit/test_lsp_server.py: 24 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e4998e5066

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


return json.loads(diff.model_dump_json())

except WorkspaceContainmentError as exc:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Classify same-URI containment failures

When oldUri == newUri and the live document URI is outside the workspace (or contains explicit traversal), _do_diff catches the ValueError/WorkspaceContainmentError internally and returns None. Consequently, this new handler never runs for that documented request path: the client receives the generic Diff computation failed response without code: "workspace_containment", and the audit log still says intentumdiff: diff failed rather than recording a refusal. The containment exception must be preserved or classified before _do_diff swallows it.

Useful? React with 👍 / 👎.

@n1ckyb
n1ckyb merged commit 1de20a1 into release/v0.0.2-rc Aug 9, 2026
1 check passed
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.

1 participant