feat: support reading external files as Code Review rules via use_file_path#87
feat: support reading external files as Code Review rules via use_file_path#87zephyrq-z wants to merge 4 commits into
use_file_path#87Conversation
- Added 'rule_file' field to ProjectRuleEntry in rule.json - Implemented resolveRuleFiles to read, validate, and merge external rule files - Security: prevents path traversal (../) outside the base directory - Limits supported extensions to .md and .txt, max size 100KB - Merges contents if both 'rule' and 'rule_file' are provided - Added unit tests for security, missing files, large files, etc. - Updated README.md and README.zh-CN.md Resolves alibaba#67
| "path": "**/*mapper*.xml", | ||
| "rule": "检查 SQL 注入风险、参数错误和缺少闭合标签" | ||
| "rule": "docs/sql-rules.md", | ||
| "use_file_path": true |
There was a problem hiding this comment.
感觉需要放在顶层,不然每个子项都需要配置一遍。
| // Security check: prevent directory traversal | ||
| if !strings.HasPrefix(absFile+string(filepath.Separator), absBase) && absFile != strings.TrimSuffix(absBase, string(filepath.Separator)) { |
There was a problem hiding this comment.
Security: Symlink-based directory traversal bypass. filepath.Abs only performs lexical path cleaning — it does NOT resolve symlinks. An attacker (or a malicious repo) could place a symlink inside .opencodereview/ that points to an arbitrary file outside the base directory. The prefix check on the string path would pass because the symlink itself lives within the base dir, but os.ReadFile follows the symlink and reads the target file.
To fix this, resolve symlinks before performing the prefix check:
resolvedFile, err := filepath.EvalSymlinks(absFile)
if err != nil {
fmt.Fprintf(os.Stderr, "[WARN] Failed to resolve symlinks for rule file %s: %v\n", filePath, err)
continue
}Then use resolvedFile for both the security check and subsequent reads. You should also resolve absBase with EvalSymlinks to handle the case where the base directory itself contains symlinks.
| if !entry.UseFilePath || entry.Rule == "" { | ||
| continue | ||
| } |
There was a problem hiding this comment.
Silent skip without warning when UseFilePath is true but Rule is empty. A user who sets "use_file_path": true but forgets to specify the "rule" field will get no feedback at all, making configuration debugging difficult. Consider adding a warning here similar to the other error paths.
Suggestion:
| if !entry.UseFilePath || entry.Rule == "" { | |
| continue | |
| } | |
| if !entry.UseFilePath { | |
| continue | |
| } | |
| if entry.Rule == "" { | |
| fmt.Fprintf(os.Stderr, "[WARN] Rule entry has use_file_path=true but empty rule path, skipping\n") | |
| continue | |
| } |
…erability - Move use_file_path from per-entry to top-level ProjectRule field (simplifies config: no need to repeat flag for every rule entry) - Add filepath.EvalSymlinks to prevent symlink-based directory traversal (resolves symlinks on both file path and base directory for consistent prefix matching across platforms like macOS /var -> /private/var) - Add warning when use_file_path=true but rule path is empty - Update tests to use new top-level UseFilePath structure - Update README.md and README.zh-CN.md with new config format
|
|
||
| ```json | ||
| { | ||
| "use_file_path": true, |
There was a problem hiding this comment.
感觉这个说明会误导用户,同时存在 path 和字符串应该会报错吧,use_file_path开关应该是控制用户使用其中一种。
There was a problem hiding this comment.
另外其他语言的 readme 也需要补充下
…g in README files
Description
This PR adds support for loading Code Review rules from external
.md/.txtfiles via a top-leveluse_file_pathtoggle inrule.json.In practice, inlining complex review rules directly into
rule.jsonmakes the configuration bloated and hard to maintain. This change allows users to extract rule content into separate documents and reference them by path, keepingrule.jsonconcise and readable.Core Changes
use_file_path(boolean) onProjectRule:true, allrulefields within the samerule.jsonare treated as relative paths to external.md/.txtfiles. The file content is read and overwrites therulefield at load time.falseor omitted,rulefields continue to work as inline string rules — fully backward compatible.resolveRuleFiles()function implements the file loading logic, called fromloadGlobalRule(),loadRuleFile(), andloadProjectRule()after JSON unmarshalling.Security & Robustness
The
resolveRuleFilesfunction enforces strict safety guards:../escapes..mdand.txtfiles are accepted; other extensions are rejected with a warning.[WARN]log and leave the originalrulestring intact (no panics).Configuration Example
{ "use_file_path": true, "rules": [ { "path": "**/*mapper*.xml", "rule": "docs/sql-rules.md" }, { "path": "web/**/*.ts", "rule": "docs/frontend-rules.md" } ] }Paths are relative to the directory containing the
rule.jsonfile.Type of Change
How Has This Been Tested?
Unit tests added in
internal/config/rules/system_rules_test.go:TestResolveRuleFiles_Basic— verifies that a valid.mdfile's content correctly replaces therulefield.TestResolveRuleFiles_Security— confirms../outside.mdpath traversal is blocked and the originalrulestring is preserved.TestResolveRuleFiles_UnsupportedExtension— confirms.jsonextension is rejected.TestResolveRuleFiles_TooLarge— confirms files exceeding 100 KB are rejected.TestResolveRuleFiles_MissingFile— confirms missing files produce a warning without panicking.make testpasses locallyManual testing (describe below)
Tested with
use_file_path: truepointing to.mdfiles in.opencodereview/docs/, confirmed rules are loaded correctly. Tested with../traversal paths and verified warnings are emitted and rules are not loaded.Checklist
go fmt,go vet)Related Issues
Closes #67