Skip to content

feat(sheets): guard +csv-put --csv against a path passed without @#1337

Merged
xiongyuanwen-byted merged 1 commit into
mainfrom
feat/input-filename-guard
Jun 9, 2026
Merged

feat(sheets): guard +csv-put --csv against a path passed without @#1337
xiongyuanwen-byted merged 1 commit into
mainfrom
feat/input-filename-guard

Conversation

@xiongyuanwen-byted

@xiongyuanwen-byted xiongyuanwen-byted commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

What

+csv-put --csv data.csv (a forgotten @ prefix) was silently written as one-cell content — any string is valid CSV, so unlike malformed JSON it never surfaced as an error, and the filename landed in the sheet instead of the file's contents.

How

+csv-put's Validate now rejects a --csv value when it names a real file in the cwd subtree — the strong signal for a forgotten @. It fails fast with a hint to use --csv @file, or to pipe the literal text via stdin (--csv -).

  • Scoped to --csv only (guardCSVValueIsNotFilePath, in +csv-put's Validate). No framework change, no other flag/command affected.
  • Runs after input resolution, so @file / stdin values are already their contents (a real CSV blob, never a path) and never trip it — only a bare value is checked.
  • Checks real existence via fileIO.Stat, fail-open: any Stat error or a directory leaves the value untouched.

Why existence, not a name-shape check. Prose that merely ends in or mentions a filename — e.g. --csv "改完记得更新config.json" — is not a real file and passes through. A "looks like a path" heuristic would wrongly reject such content, and its comma guard never fired for CJK text (full-width commas aren't ASCII ,). Checking actual existence sidesteps both.

Test

  • TestGuardCSVValueIsNotFilePath: a bare value naming an existing file is rejected with a fix-it hint; prose, multi-cell CSV, plain text, and path-shaped-but-nonexistent values all pass through.
  • go test ./shortcuts/sheets/ green; gofmt clean; go build ./... OK; golangci-lint 0 issues on the package.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • --csv input is now validated: if it references an existing file without the required prefix, the command fails with guidance to use the file-prefix form or provide CSV via stdin; directory paths are also handled.
  • Tests

    • Added tests covering file-path rejection and acceptance of various inline CSV inputs (plain text, prose, path-shaped strings that don't exist, and empty).

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 70d2446a-0c00-452a-a409-06146a1af6a4

📥 Commits

Reviewing files that changed from the base of the PR and between 6b45e99 and ab71e20.

📒 Files selected for processing (2)
  • shortcuts/sheets/csv_put_guard_test.go
  • shortcuts/sheets/lark_sheet_write_cells.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • shortcuts/sheets/csv_put_guard_test.go
  • shortcuts/sheets/lark_sheet_write_cells.go

📝 Walkthrough

Walkthrough

This PR adds a safety guard to the +csv-put command that rejects bare filename values for the --csv flag, requiring users to explicitly prefix file paths with @ instead. The implementation includes the guard function, its integration into the validation flow, and comprehensive test coverage.

Changes

CSV file-path guard for +csv-put

Layer / File(s) Summary
File-path guard implementation
shortcuts/sheets/lark_sheet_write_cells.go
guardCSVValueIsNotFilePath checks if --csv values point to existing regular files; if so, it raises FlagErrorf with an @file fix-it hint; otherwise succeeds (failing open on stat errors).
Validation integration
shortcuts/sheets/lark_sheet_write_cells.go
+csv-put's Validate handler is updated to call the guard before the existing schema validation, ensuring file-path checks run early.
Guard test cases
shortcuts/sheets/csv_put_guard_test.go
Test helper newCSVGuardRuntime and TestGuardCSVValueIsNotFilePath verify rejection of existing filenames and pass-through of inline/non-filename CSV values.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

size/M, domain/ccm

Suggested reviewers

  • wittam-01

Poem

🐰 A bunny checks your flags with care,
If CSV files are sitting there,
It says "Use @file, don't play games,"
To save you from confusing names! 📋✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.71% 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 clearly and concisely summarizes the main change: adding a guard to prevent forgotten '@' prefixes in the --csv flag for +csv-put.
Description check ✅ Passed The description covers all required sections: a clear 'What' explaining the problem, detailed 'How' describing the implementation, and 'Test' documenting verification steps.
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/input-filename-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.

@github-actions github-actions Bot added the size/M Single-domain feat or fix with limited business impact label Jun 9, 2026

@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 `@shortcuts/common/runner_input_test.go`:
- Around line 275-388: Tests call newTestRuntimeWithStdin directly and don't
isolate config/env; replace those usages with cmdutil.TestFactory(t, config) and
set t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) at the start of each test,
then use the factory's runtime (and set factory's stdin payload where tests used
the second arg of newTestRuntimeWithStdin) before calling resolveInputFlags;
update TestResolveInputFlags_FilenameMistakenForContent,
TestResolveInputFlags_ContentNotMistakenForPath,
TestResolveInputFlags_PathLikeContentViaStdin, and
TestResolveInputFlags_PathCheckSkippedWithoutFileInput to use the factory
pattern and provide stdin via the factory instead of newTestRuntimeWithStdin so
tests respect the repo test-factory isolation rules.

In `@shortcuts/common/runner.go`:
- Around line 1229-1230: The comment in shortcuts/common/runner.go incorrectly
states that the escape-hatch "@@" preserves a bare value; update the comment to
reflect actual behavior: "@@" prefixes the payload with a single '@' rather than
preserving the original bare value, and the true bypass is using stdin (the
"--flag -" convention) as implemented earlier. Locate the comment around the
argument parsing/trigger description (near the logic that handles "@@" and the
stdin "-" case) and rewrite it to mention that "@@" yields a leading "@" in the
payload and that "--flag -" is the correct way to force-passthrough via stdin.
🪄 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: b158748c-2eae-465b-9245-7a18e9e7e0e0

📥 Commits

Reviewing files that changed from the base of the PR and between ed3fe93 and dfc82ee.

📒 Files selected for processing (2)
  • shortcuts/common/runner.go
  • shortcuts/common/runner_input_test.go

Comment thread shortcuts/common/runner_input_test.go Outdated
Comment thread shortcuts/common/runner.go Outdated
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#feat/input-filename-guard -y -g

@github-actions github-actions Bot added domain/mail PR touches the mail domain size/L Large or sensitive change across domains or core paths and removed size/M Single-domain feat or fix with limited business impact labels Jun 9, 2026
@xiongyuanwen-byted xiongyuanwen-byted changed the title feat(shortcuts): guard file-input flags against path-like literals feat(shortcuts): guard file-input flags against a forgotten @ Jun 9, 2026
@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.77778% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.79%. Comparing base (8f5504c) to head (ab71e20).
⚠️ Report is 10 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/sheets/lark_sheet_write_cells.go 77.77% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1337      +/-   ##
==========================================
+ Coverage   71.47%   71.79%   +0.31%     
==========================================
  Files         688      690       +2     
  Lines       65482    65596     +114     
==========================================
+ Hits        46806    47094     +288     
+ Misses      15031    14845     -186     
- Partials     3645     3657      +12     

☔ View full report in Codecov by Harness.
📢 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 added domain/ccm PR touches the ccm domain size/M Single-domain feat or fix with limited business impact and removed domain/mail PR touches the mail domain size/L Large or sensitive change across domains or core paths labels Jun 9, 2026
@xiongyuanwen-byted xiongyuanwen-byted changed the title feat(shortcuts): guard file-input flags against a forgotten @ feat(sheets): guard +csv-put --csv against a path passed without @ Jun 9, 2026

@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 (2)
shortcuts/sheets/csv_put_guard_test.go (2)

45-57: ⚡ Quick win

Add test case for directory to exercise IsDir() guard branch.

The guard at line 324 has an explicit info.IsDir() check, but the test doesn't verify that a directory value passes through without error. Consider adding a directory to the passing test cases.

🧪 Add directory test coverage
 	dir := t.TempDir()
 	cmdutil.TestChdir(t, dir)
+	if err := os.Mkdir("testdir", 0755); err != nil {
+		t.Fatal(err)
+	}
 	if err := os.WriteFile("data.csv", []byte("a,b\n1,2\n"), 0644); err != nil {
 		t.Fatal(err)
 	}
 
 	// ... existing test ...
 
 	// Content that is not a real file must pass through unchanged.
 	for _, v := range []string{
 		"改完记得更新config.json",           // prose ending in a filename — not a real file
 		"remember to update data.csv", // mentions the real file but isn't its name
 		"a,b\n1,2",                    // multi-cell CSV
 		"hello world",
 		"nope.csv", // path-shaped but no such file
+		"testdir",  // directory — not a regular 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/sheets/csv_put_guard_test.go` around lines 45 - 57, Add a test case
that ensures a directory value passes through the guard that checks
info.IsDir(): in the existing test loop that calls
guardCSVValueIsNotFilePath(newCSVGuardRuntime(v)), include a value representing
a directory (e.g., a temp dir path created with os.MkdirTemp or t.TempDir()) so
the IsDir() branch is exercised and the call returns no error; locate the test
using the function name guardCSVValueIsNotFilePath and helper newCSVGuardRuntime
to add this passing-case directory value.

17-23: ⚡ Quick win

Prefer common.TestNewRuntimeContext for proper test setup.

Per learnings, validate/dry-run unit tests should use common.TestNewRuntimeContext(t, config) instead of manually constructing a RuntimeContext. This ensures proper initialization of all fields including FileIO.

♻️ Refactor the helper to use the recommended pattern
-func newCSVGuardRuntime(csvVal string) *common.RuntimeContext {
-	cmd := &cobra.Command{Use: "test"}
-	cmd.Flags().String("csv", "", "")
-	cmd.ParseFlags(nil)
-	cmd.Flags().Set("csv", csvVal)
-	return &common.RuntimeContext{Cmd: cmd}
+func newCSVGuardRuntime(t *testing.T, csvVal string) *common.RuntimeContext {
+	rt := common.TestNewRuntimeContext(t, nil)
+	rt.Cmd.Flags().String("csv", "", "")
+	rt.Cmd.ParseFlags(nil)
+	rt.Cmd.Flags().Set("csv", csvVal)
+	return rt
 }

Then update the test calls to pass t:

-	err := guardCSVValueIsNotFilePath(newCSVGuardRuntime("data.csv"))
+	err := guardCSVValueIsNotFilePath(newCSVGuardRuntime(t, "data.csv"))

Based on learnings, validate/dry-run unit tests should use common.TestNewRuntimeContext for consistent test setup patterns.

🤖 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/sheets/csv_put_guard_test.go` around lines 17 - 23, The helper
newCSVGuardRuntime currently constructs a RuntimeContext manually and misses
proper initialization (e.g., FileIO); replace its implementation to call
common.TestNewRuntimeContext(t, map[string]string{"csv": csvVal}) so the
returned *common.RuntimeContext is fully initialized; rename or change the
helper signature to accept testing.T (t) and update all test call sites to pass
t when invoking newCSVGuardRuntime (or inline the common.TestNewRuntimeContext
call in tests) and reference the csv flag via the config map instead of manually
setting Cmd flags.

Source: Learnings

🤖 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/sheets/csv_put_guard_test.go`:
- Around line 45-57: Add a test case that ensures a directory value passes
through the guard that checks info.IsDir(): in the existing test loop that calls
guardCSVValueIsNotFilePath(newCSVGuardRuntime(v)), include a value representing
a directory (e.g., a temp dir path created with os.MkdirTemp or t.TempDir()) so
the IsDir() branch is exercised and the call returns no error; locate the test
using the function name guardCSVValueIsNotFilePath and helper newCSVGuardRuntime
to add this passing-case directory value.
- Around line 17-23: The helper newCSVGuardRuntime currently constructs a
RuntimeContext manually and misses proper initialization (e.g., FileIO); replace
its implementation to call common.TestNewRuntimeContext(t,
map[string]string{"csv": csvVal}) so the returned *common.RuntimeContext is
fully initialized; rename or change the helper signature to accept testing.T (t)
and update all test call sites to pass t when invoking newCSVGuardRuntime (or
inline the common.TestNewRuntimeContext call in tests) and reference the csv
flag via the config map instead of manually setting Cmd flags.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 53605484-c876-431f-a562-cebdede7e404

📥 Commits

Reviewing files that changed from the base of the PR and between b5c23ce and 6b45e99.

📒 Files selected for processing (2)
  • shortcuts/sheets/csv_put_guard_test.go
  • shortcuts/sheets/lark_sheet_write_cells.go

+csv-put --csv data.csv (a forgotten @) was silently written as one-cell content, because any string parses as valid CSV — unlike malformed JSON it never errored, so the filename landed in the sheet instead of the file's contents.

+csv-put's Validate now rejects a --csv value when it names a real file in the cwd subtree (guardCSVValueIsNotFilePath; fileIO.Stat, fail-open), hinting to use --csv @file or stdin (--csv -). Scoped to --csv only — no framework or other-flag change. Checking real existence (not name shape) lets inline content that merely ends in a filename pass through. Adds TestGuardCSVValueIsNotFilePath.
@xiongyuanwen-byted
xiongyuanwen-byted force-pushed the feat/input-filename-guard branch from 6b45e99 to ab71e20 Compare June 9, 2026 11:12
@xiongyuanwen-byted
xiongyuanwen-byted merged commit eed711b into main Jun 9, 2026
21 checks passed
@xiongyuanwen-byted
xiongyuanwen-byted deleted the feat/input-filename-guard branch June 9, 2026 11:48
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