Skip to content

feat(doc): add v2 XML content guards for bare ampersands and deprecated tags - #822

Closed
herbertliu wants to merge 5 commits into
mainfrom
feat/docs-v2-xml-content-guard
Closed

feat(doc): add v2 XML content guards for bare ampersands and deprecated tags#822
herbertliu wants to merge 5 commits into
mainfrom
feat/docs-v2-xml-content-guard

Conversation

@herbertliu

@herbertliu herbertliu commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add pre-flight static checks for the v2 XML document path to catch two categories of silent failures before the API call is made.

Changes

  • shortcuts/doc/docs_update_check.go: Add CheckV2XMLBareAmpersand (hard error) and CheckV2XMLWarnings (non-fatal warnings) with table-driven tests
  • shortcuts/doc/docs_create_v2.go: Integrate bare-ampersand check in validateCreateV2 and XML warnings in executeCreateV2
  • shortcuts/doc/docs_update_v2.go: Same integration in validateUpdateV2 / executeUpdateV2
  • shortcuts/doc/docs_update_check_test.go: Add TestCheckV2XMLBareAmpersand and TestCheckV2XMLWarnings

Checks

CheckV2XMLBareAmpersand — hard error (returned from Validate):

  • Fires when content contains a & that is not a recognised XML entity (&, <, >, ', ", &#N;, &#xH;).
  • The v2 XML parser rejects such requests outright; catching this early gives a clear error instead of an opaque API failure.

CheckV2XMLWarnings — non-fatal warnings (printed to stderr before the API call):

  1. <quote-container> — v2 silently drops the block; the correct tag is <blockquote>.
  2. <column width="N"> with an integer value — has no effect in v2; the correct attribute is width-ratio="0.5" (float 0–1).

Both checks only fire when --doc-format is xml (the default).

Test Plan

  • go test ./shortcuts/doc/... — all pass, 66.4% coverage
  • go vet ./... — clean
  • gofmt -l . — clean

Summary by CodeRabbit

  • New Features

    • Enhanced v2 document validation: submissions (when not using markdown) are blocked for malformed bare ampersands.
    • Emits user-facing warnings to stderr for unsupported XML constructs (e.g., quote-container) and integer column-width attributes before processing.
  • Tests

    • Added unit tests covering bare-ampersand detection and XML warning conditions.
    • Added CLI dry-run end-to-end tests verifying create/update guard and warning behavior.

Review Change Stack

…ed tags

Add pre-flight checks for the v2 XML document path:

- CheckV2XMLBareAmpersand: returns a hard error when content contains a
  bare & that is not a valid XML entity reference (&amp;, &lt;, &gt;,
  &apos;, &quot;, &#N;, &#xH;). Such bare ampersands cause the v2 XML
  parser to reject the request.

- CheckV2XMLWarnings: returns non-fatal warnings for two silently-wrong
  constructs — <quote-container> (v2 drops it; use <blockquote>) and
  <column width="N"> with an integer value (has no effect; use
  width-ratio="0.N").

Both checks are integrated into validateCreateV2/validateUpdateV2 (hard
error) and executeCreateV2/executeUpdateV2 (warnings to stderr). Only
fires when --doc-format is xml (the default).
@github-actions github-actions Bot added domain/ccm PR touches the ccm domain size/M Single-domain feat or fix with limited business impact labels May 11, 2026
@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds v2 XML validators: a fatal bare-ampersand check and non-fatal warnings for unsupported constructs; wires validators into create/update v2 CLI validate/execute paths; adds unit tests and E2E dry-run tests covering failure and warning behaviors.

Changes

XML V2 Validation and Warning System

Layer / File(s) Summary
Core XML Validators
shortcuts/doc/docs_update_check.go
CheckV2XMLBareAmpersand detects unescaped & using entity-aware regex replacement; CheckV2XMLWarnings returns warnings for <quote-container> usage and integer width="N" on <column> elements.
Validator unit tests
shortcuts/doc/docs_update_check_test.go
Table-driven tests for isIntWidth, CheckV2XMLBareAmpersand, and CheckV2XMLWarnings covering valid entities, bare ampersands, quote-container and column width edge cases, and combined-warning scenarios.
Create V2 Integration
shortcuts/doc/docs_create_v2.go
Adds fmt import; validateCreateV2 fails on bare ampersands when --doc-format != markdown; executeCreateV2 prints warning: ... lines from CheckV2XMLWarnings to stderr before calling the create API.
Update V2 Integration
shortcuts/doc/docs_update_v2.go
validateUpdateV2 fails on bare ampersands when doc-format != markdown; executeUpdateV2 prints warning: ... lines from CheckV2XMLWarnings to stderr before calling the update API.
E2E dry-run tests
tests/cli_e2e/docs/docs_update_dryrun_test.go
Adds dry-run tests for docs +create and docs +update verifying bare-ampersand rejection, expected API call in create dry-run, and that execute-path warnings don't appear in dry-run output for update.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • larksuite/cli#638: Prior addition of the v2 docs API flow that this PR extends with XML validation/warnings.

Suggested labels

size/L

Suggested reviewers

  • fangshuyu-768
  • liangshuo-1

Poem

🐰 I nibble through tags with gentle care,
Bare ampersands no longer sniff the air,
Warnings whisper of width and quote,
I tidy XML so docs safely float,

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically summarizes the primary change: adding v2 XML content guards for bare ampersands and deprecated tags, which matches the core feature introduced in this PR.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering summary, detailed changes, check explanations, and test plan with all required template sections addressed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/docs-v2-xml-content-guard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shortcuts/doc/docs_update_check.go`:
- Around line 313-317: The current regex columnIntWidthRe is too permissive and
misses valid forms; change it to require a preceding whitespace before the
attribute, allow optional spaces around '=', accept single or double quotes, and
capture the integer value (e.g. `<column\b[^>]*\swidth\s*=\s*(['"])(\d+)\1`) so
it won't match attributes like data-width and will match forms like width='50'
or width = "50". Apply the same tightening to the other related regex defined
around lines 335-341 so both matchers use `\swidth\s*=\s*(['"])(\d+)\1` (or the
float equivalent) and keep the surrounding `<column\b[^>]*` context to limit
matches to column elements.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bc2981b2-3f9c-4a87-909d-8b36a95bd73c

📥 Commits

Reviewing files that changed from the base of the PR and between 25c72ce and a29a8bf.

📒 Files selected for processing (4)
  • shortcuts/doc/docs_create_v2.go
  • shortcuts/doc/docs_update_check.go
  • shortcuts/doc/docs_update_check_test.go
  • shortcuts/doc/docs_update_v2.go

Comment thread shortcuts/doc/docs_update_check.go Outdated
@codecov

codecov Bot commented May 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.74419% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.93%. Comparing base (0ed63b0) to head (f501752).
⚠️ Report is 41 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/doc/docs_update_v2.go 0.00% 6 Missing ⚠️
shortcuts/doc/docs_create_v2.go 33.33% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #822      +/-   ##
==========================================
+ Coverage   65.67%   65.93%   +0.25%     
==========================================
  Files         513      523      +10     
  Lines       47655    49737    +2082     
==========================================
+ Hits        31297    32793    +1496     
- Misses      13652    14142     +490     
- Partials     2706     2802      +96     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented May 11, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@f50175243be99ce47169f42837a81af6919ed9b2

🧩 Skill update

npx skills add larksuite/cli#feat/docs-v2-xml-content-guard -y -g

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
shortcuts/doc/docs_update_check_test.go (1)

474-497: ⚡ Quick win

Use strings.Contains instead of custom substring scanner.

The containsStr function at line 487 is locally scoped to this test file, used only once at line 479, and the strings package is already imported. Replace it with strings.Contains to eliminate the unnecessary custom implementation.

Proposed simplification
@@
 			for _, sub := range tt.wantContains {
-				if !containsStr(combined, sub) {
+				if !strings.Contains(combined, sub) {
 					t.Errorf("expected warning to contain %q, got: %s", sub, combined)
 				}
 			}
 		})
 	}
 }
-
-func containsStr(s, sub string) bool {
-	return len(s) >= len(sub) && (s == sub || len(sub) == 0 ||
-		func() bool {
-			for i := 0; i+len(sub) <= len(s); i++ {
-				if s[i:i+len(sub)] == sub {
-					return true
-				}
-			}
-			return false
-		}())
-}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/doc/docs_update_check_test.go` around lines 474 - 497, The test
defines a local containsStr function and calls it to check substrings; replace
that call with the standard strings.Contains and remove the custom containsStr
function to simplify the test (ensure the strings package is imported and used
in the assertion where containsStr was invoked, and delete the containsStr
function declaration to avoid redundancy).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@shortcuts/doc/docs_update_check_test.go`:
- Around line 474-497: The test defines a local containsStr function and calls
it to check substrings; replace that call with the standard strings.Contains and
remove the custom containsStr function to simplify the test (ensure the strings
package is imported and used in the assertion where containsStr was invoked, and
delete the containsStr function declaration to avoid redundancy).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ae956902-4be3-4375-a43b-58e4b2cd6efa

📥 Commits

Reviewing files that changed from the base of the PR and between a29a8bf and 9fa94ab.

📒 Files selected for processing (2)
  • shortcuts/doc/docs_update_check.go
  • shortcuts/doc/docs_update_check_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • shortcuts/doc/docs_update_check.go

@fangshuyu-768 fangshuyu-768 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for adding these pre-flight checks — the overall approach of catching silent failures early is solid. I have a few concerns below, ordered by impact.

Comment thread shortcuts/doc/docs_update_check.go
Comment thread shortcuts/doc/docs_update_check.go Outdated
Comment thread shortcuts/doc/docs_update_check_test.go Outdated
Comment thread shortcuts/doc/docs_create_v2.go
Comment thread shortcuts/doc/docs_update_check_test.go
Comment thread shortcuts/doc/docs_update_check_test.go
herbertliu and others added 2 commits May 14, 2026 10:14
… containsStr helper, align empty-content guard
…E tests

- Replace columnIntWidthRe with columnWidthAttrRe + isIntWidth to reject mixed-quote matches since Go RE2 lacks backreferences; capture opening/closing quotes and verify they match
- Remove redundant content != empty guards in validate paths since CheckV2XMLBareAmpersand returns empty safely
- Add TestIsIntWidth unit test and mixed-quote test case
- Add dry-run E2E tests for v2 create/update XML guard paths

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/cli_e2e/docs/docs_update_dryrun_test.go`:
- Around line 77-83: Add isolated config directories for the new E2E tests by
setting LARKSUITE_CLI_CONFIG_DIR to t.TempDir() where the other environment vars
are set (next to t.Setenv("LARKSUITE_CLI_APP_ID", ...),
t.Setenv("LARKSUITE_CLI_APP_SECRET", ...) and t.Setenv("LARKSUITE_CLI_BRAND",
...)) in both new tests (the block shown and the second block around the 125-130
area) so each test uses its own config directory.
- Around line 152-170: In the subtest "warning-only content passes dry-run" add
assertions that the dry-run request envelope in the CLI output matches the
expected update shape (action/update, doc "doxcnDryRunE2E", command "overwrite",
content includes the quote-container HTML, and dryRun true) similar to the
create-test pattern; parse the CLI output (from result.Stdout or combined
output) to extract the request JSON and assert those fields on the request
object returned by clie2e.RunCmd without invoking any real APIs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aed3c6b6-2baf-49bc-8260-e7679bdfd9fb

📥 Commits

Reviewing files that changed from the base of the PR and between 259bc0e and b8b5f3c.

📒 Files selected for processing (5)
  • shortcuts/doc/docs_create_v2.go
  • shortcuts/doc/docs_update_check.go
  • shortcuts/doc/docs_update_check_test.go
  • shortcuts/doc/docs_update_v2.go
  • tests/cli_e2e/docs/docs_update_dryrun_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • shortcuts/doc/docs_create_v2.go
  • shortcuts/doc/docs_update_v2.go

Comment thread tests/cli_e2e/docs/docs_update_dryrun_test.go
Comment thread tests/cli_e2e/docs/docs_update_dryrun_test.go
…tion and envelope assertions in E2E tests

- isIntWidth now returns false for empty string instead of true
- Add LARKSUITE_CLI_CONFIG_DIR isolation in v2 create/update E2E tests
- Add gjson assertions for API method/URL in update dry-run success path

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
shortcuts/doc/docs_update_check.go (1)

326-336: ⚡ Quick win

Consider using strconv.Atoi for clearer integer validation.

The current implementation only checks for absence of a decimal point, relying on the regex pre-filtering the input to [0-9.]+. While this works correctly in context, using strconv.Atoi would be more idiomatic Go, eliminate the hidden coupling to the regex, and make the function reusable.

♻️ More idiomatic implementation
 func isIntWidth(s string) bool {
-	if s == "" {
-		return false
-	}
-	for _, c := range s {
-		if c == '.' {
-			return false
-		}
-	}
-	return true
+	_, err := strconv.Atoi(s)
+	return err == nil
 }

Don't forget to add "strconv" to the imports at the top of the file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/doc/docs_update_check.go` around lines 326 - 336, Replace the
manual dot-check in isIntWidth with an attempt to parse the string as an integer
using strconv.Atoi: in isIntWidth(s string) call strconv.Atoi(s) and return true
only when it succeeds (err == nil); also import "strconv" at the top of the file
so the function no longer relies on external regex assumptions and becomes a
proper integer validator.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@shortcuts/doc/docs_update_check.go`:
- Around line 326-336: Replace the manual dot-check in isIntWidth with an
attempt to parse the string as an integer using strconv.Atoi: in isIntWidth(s
string) call strconv.Atoi(s) and return true only when it succeeds (err == nil);
also import "strconv" at the top of the file so the function no longer relies on
external regex assumptions and becomes a proper integer validator.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0795fb4f-44ff-4e6a-b61c-ac08ab42608d

📥 Commits

Reviewing files that changed from the base of the PR and between b8b5f3c and f501752.

📒 Files selected for processing (3)
  • shortcuts/doc/docs_update_check.go
  • shortcuts/doc/docs_update_check_test.go
  • tests/cli_e2e/docs/docs_update_dryrun_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • shortcuts/doc/docs_update_check_test.go
  • tests/cli_e2e/docs/docs_update_dryrun_test.go

@SunPeiYang996

Copy link
Copy Markdown
Collaborator

Thanks for looking into this and for the thoughtful guardrails here.

For the unescaped ampersand case, for example <p>A & B</p>, the server currently handles it in a compatible way and normalizes it to <p>A &amp; B</p> automatically. If the CLI blocks this before sending the request, it would reduce the overall success rate for model-generated content.

For quote-container and column width="50", these are Doc v1 usages and should not appear in the v2 XML path. In essence, this is a mix of v1 and v2 conventions. If we want to add guidance for this, we would prefer to do it on the server side so it can be maintained consistently over time. The CLI is a client here and should mainly be responsible for routing.

I am going to close this PR for now. If you have a better idea or want to discuss another approach, I would be happy to continue the conversation.

@liangshuo-1
liangshuo-1 deleted the feat/docs-v2-xml-content-guard branch June 3, 2026 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/ccm PR touches the ccm domain size/M Single-domain feat or fix with limited business impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants