mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-15 20:15:48 +00:00
fix: dep_scanner false positive — JS import inside Python f-string (#564)
* fix: skip JS ES module imports in Python string literals during dep scan dep_scanner falsely matched JS `import mermaid from '...'` inside Python f-strings as a pip dependency, causing functional skills to be archived. Add jsFromStringRe check: if a line matching pyImportRe also contains `from '...'`/`from "..."` (JS ES module pattern), skip it. Valid Python `import X` never has `from '...'` on the same line. Closes #544 * fix: add missing stub method + additional edge case tests - Add MigrateUserDataOnMerge to stubAgentStore in context_file_interceptor_test.go (fixes CI build failure — stub was missing new AgentStore interface method) - Add TestScanFile_MultipleJSImportsInsidePythonString for broader false positive coverage --------- Co-authored-by: viettranx <viettranx@gmail.com>
This commit is contained in:
1 parent
7f897fe9f5
commit
e7afa83245
3 files changed
+101
-1
No files matched your search
@@ -120,6 +120,9 @@ var (
|
||||
nodeRequireRe = regexp.MustCompile(`require\(['"]([\w@][^'"]*)['"]\)`)
|
||||
nodeESImportRe = regexp.MustCompile(`from\s+['"]([^'"./][^'"]*?)['"]`)
|
||||
shebangRe = regexp.MustCompile(`^#!\s*/usr/bin/env\s+(\S+)`)
|
||||
// Detects JS ES module pattern: `import X from '...'` or `from '...'`.
|
||||
// Used to skip false positives when JS imports appear inside Python string literals.
|
||||
jsFromStringRe = regexp.MustCompile(`from\s+['"]`)
|
||||
)
|
||||
|
||||
func scanFile(path string, pyImports, nodeImports map[string]bool, binaries map[string]bool) {
|
||||
@@ -143,7 +146,10 @@ func scanFile(path string, pyImports, nodeImports map[string]bool, binaries map[
|
||||
for _, line := range strings.Split(content, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if m := pyImportRe.FindStringSubmatch(line); len(m) > 1 {
|
||||
pyImports[m[1]] = true
|
||||
// Skip JS ES module imports inside string literals (e.g. `import mermaid from '...'`)
|
||||
if !jsFromStringRe.MatchString(line) {
|
||||
pyImports[m[1]] = true
|
||||
}
|
||||
}
|
||||
if m := pyFromRe.FindStringSubmatch(line); len(m) > 1 {
|
||||
pyImports[m[1]] = true
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package skills
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestScanFile_JSImportInsidePythonFString(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
pyFile := filepath.Join(dir, "render.py")
|
||||
|
||||
// Python file with JS ES module import inside f-string (issue #544)
|
||||
content := `#!/usr/bin/env python3
|
||||
import sys
|
||||
import json
|
||||
|
||||
def render_html(text):
|
||||
mermaid_init = f"""
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({{ startOnLoad: true }});
|
||||
</script>
|
||||
"""
|
||||
return f"<html>{text}{mermaid_init}</html>"
|
||||
`
|
||||
if err := os.WriteFile(pyFile, []byte(content), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pyImports := make(map[string]bool)
|
||||
nodeImports := make(map[string]bool)
|
||||
binaries := make(map[string]bool)
|
||||
|
||||
scanFile(pyFile, pyImports, nodeImports, binaries)
|
||||
|
||||
// sys and json are real Python imports — should be detected
|
||||
if !pyImports["sys"] {
|
||||
t.Error("expected sys to be detected as Python import")
|
||||
}
|
||||
if !pyImports["json"] {
|
||||
t.Error("expected json to be detected as Python import")
|
||||
}
|
||||
|
||||
// mermaid is a JS import inside f-string — should NOT be detected
|
||||
if pyImports["mermaid"] {
|
||||
t.Error("FALSE POSITIVE: mermaid detected as Python import — it's a JS import inside f-string")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanFile_MultipleJSImportsInsidePythonString(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
pyFile := filepath.Join(dir, "template.py")
|
||||
|
||||
// Multiple JS imports inside a Python string + real Python imports
|
||||
content := `import os
|
||||
import subprocess
|
||||
|
||||
TEMPLATE = """
|
||||
<script type="module">
|
||||
import React from 'https://cdn.example.com/react.js';
|
||||
import lodash from 'https://cdn.example.com/lodash.js';
|
||||
</script>
|
||||
"""
|
||||
`
|
||||
if err := os.WriteFile(pyFile, []byte(content), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pyImports := make(map[string]bool)
|
||||
nodeImports := make(map[string]bool)
|
||||
binaries := make(map[string]bool)
|
||||
|
||||
scanFile(pyFile, pyImports, nodeImports, binaries)
|
||||
|
||||
// Real Python imports
|
||||
if !pyImports["os"] {
|
||||
t.Error("expected os to be detected as Python import")
|
||||
}
|
||||
if !pyImports["subprocess"] {
|
||||
t.Error("expected subprocess to be detected as Python import")
|
||||
}
|
||||
|
||||
// JS imports inside string — should NOT be detected
|
||||
if pyImports["React"] {
|
||||
t.Error("FALSE POSITIVE: React detected as Python import")
|
||||
}
|
||||
if pyImports["lodash"] {
|
||||
t.Error("FALSE POSITIVE: lodash detected as Python import")
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,9 @@ func (s *stubAgentStore) EnsureUserProfile(_ context.Context, _ uuid.UUID, _ str
|
||||
func (s *stubAgentStore) PropagateContextFile(_ context.Context, _ uuid.UUID, _ string) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (s *stubAgentStore) MigrateUserDataOnMerge(_ context.Context, _ []string, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---- Tests ----
|
||||
|
||||
|
||||
Reference in new issue
Block a user