← 返回 Skills 市场
occupythemilkyway

Argus Pro — Code Intelligence Scanner

作者 OccupyTheMilkyWay · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ✓ 安全检测通过
29
总下载
0
收藏
0
当前安装
1
版本数
在 OpenClaw 中安装
/install argus-pro
功能描述
Argus Pro — Code Intelligence Scanner. Scan Python and JavaScript codebases with 40+ rules covering security, bugs, performance, and code quality. Get prio...
使用说明 (SKILL.md)

Argus Pro — Full Code Intelligence

Everything in Argus, plus 40+ rules, performance patterns, CI/CD integration, custom ignore paths, and trend JSON for tracking debt over time.

Pro features vs free Argus

Feature Argus (Free) Argus Pro
Rules 20 40+
Languages Python or JS Python + JS simultaneously
Performance rules ❌ ✅ N+1, blocking calls, memory leaks
CI exit code ❌ ✅ FAIL_ON_CRITICAL
Ignore paths ❌ ✅
JSON output Optional ✅ Always (CI-ready)
Finding deduplication Basic ✅ Cross-file smart dedup
Custom severity filter ✅ ✅ + per-rule override

👉 Upgrade: openclaw skills install argus-pro + key at ko-fi.com/occupythemilkyway


Step 1 — Install

pip3 install rich --break-system-packages --quiet

Step 2 — Full code scan (Pro)

import os, re, json, sys
from pathlib import Path
from datetime import date
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich import box

console = Console()

LICENSE_KEY = os.environ.get("LICENSE_KEY","").strip()
if not LICENSE_KEY:
    console.print(Panel(
        "[red bold]🔒 Argus Pro requires a license key.[/red bold]\
\
"
        "Get your key at: [bold cyan]ko-fi.com/occupythemilkyway[/bold cyan]\
\
"
        "Or use the free version: [dim]openclaw skills install argus1[/dim]",
        title="License Required", border_style="red"
    ))
    raise SystemExit(1)

SRC_PATH      = os.environ.get("SOURCE_PATH",".").strip()
LANGUAGE      = os.environ.get("LANGUAGE","auto").lower()
SEV_FILTER    = os.environ.get("SEVERITY_FILTER","all").lower()
try: MAX_FINDINGS = int(os.environ.get("MAX_FINDINGS","100"))
except: MAX_FINDINGS = 100
OUTPUT_JSON   = os.environ.get("OUTPUT_JSON","true").lower() == "true"
FAIL_CRITICAL = os.environ.get("FAIL_ON_CRITICAL","false").lower() == "true"
IGNORE_RAW    = os.environ.get("IGNORE_PATHS","")
IGNORE_PATHS  = [p.strip() for p in IGNORE_RAW.split(",") if p.strip()]
TODAY         = date.today()

src = Path(SRC_PATH)
if not src.exists():
    console.print(f"[red]❌ Path not found: {SRC_PATH}[/red]")
    raise SystemExit(1)

def detect_lang(path):
    py = len(list(path.rglob("*.py") if path.is_dir() else ([path] if str(path).endswith(".py") else [])))
    js = len(list(path.rglob("*.js") if path.is_dir() else ([path] if str(path).endswith(".js") else [])))
    return "python" if py >= js else "javascript"

lang = LANGUAGE if LANGUAGE != "auto" else detect_lang(src)

# Pro: Scan both languages if directory
SCAN_LANGS = ["python","javascript"] if src.is_dir() and LANGUAGE == "auto" else [lang]

# ── Extended rule sets ────────────────────────────────────────────────────────
PYTHON_RULES = [
    # Security
    ("PY001","critical","security", r"\beval\s*\(",                              "eval() executes arbitrary code.",                         "Use ast.literal_eval() for safe literal evaluation."),
    ("PY002","critical","security", r"\bexec\s*\(",                              "exec() executes arbitrary code.",                         "Refactor to eliminate dynamic code execution."),
    ("PY003","critical","security", r"\bpickle\.loads?\s*\(",                  "pickle.load() with untrusted data enables code execution.", "Use json.loads() instead."),
    ("PY004","high","security",     r"(?i)(password|secret|api_key|token|auth_key)\s*=\s*['\"].+['\"]", "Hardcoded credential.", "Use environment variables."),
    ("PY005","high","security",     r"shell\s*=\s*True",                       "shell=True is a command-injection risk.",                  "Use list arguments: subprocess.run(['cmd','arg'])"),
    ("PY006","high","security",     r"\.execute\s*\(.*(%|\.format\(|f['\"])",  "Potential SQL injection via string formatting.",           "Use parameterised queries."),
    ("PY016","medium","security",   r"hashlib\.(md5|sha1)\s*\(",               "MD5/SHA1 are cryptographically weak.",                     "Use hashlib.sha256() or bcrypt for passwords."),
    ("PY017","high","security",     r"\brandom\.(random|randint|choice)\s*\(",  "random module not cryptographically secure.",             "Use secrets module for security-sensitive values."),
    # Bugs
    ("PY007","medium","bug",        r"def\s+\w+\s*\([^)]*=\s*\[\s*\]",        "Mutable default argument [].",                            "Use None as default; init inside function."),
    ("PY008","medium","bug",        r"def\s+\w+\s*\([^)]*=\s*\{\s*\}",        "Mutable default argument {}.",                            "Use None as default; init inside function."),
    ("PY009","medium","bug",        r"except\s*:",                             "Bare except catches SystemExit/KeyboardInterrupt.",       "Catch specific: except ValueError: or except Exception:"),
    ("PY010","medium","bug",        r"==\s*None\b|\bNone\s*==",                "Use 'is None' not '== None'.",                           "Replace with 'is None'."),
    ("PY011","medium","bug",        r"!=\s*None\b|\bNone\s*!=",                "Use 'is not None'.",                                     "Replace with 'is not None'."),
    ("PY018","medium","bug",        r"open\s*\(.+\)(?!\s*as\b)(?!.*with\b)",   "File opened without context manager.",                   "Use 'with open(...) as f:'."),
    ("PY020","medium","bug",        r"\btype\s*\(\w+\)\s*==\s*",               "type() == for type checks is fragile.",                  "Use isinstance() instead."),
    ("PY021","medium","bug",        r"except.*:\s*pass$",                      "Silent exception swallowed — bugs hidden.",               "At minimum log the exception before passing."),
    ("PY022","low","bug",           r"\bassert\s+",                            "assert statements removed by -O flag.",                  "Use explicit if + raise for runtime checks."),
    # Quality
    ("PY012","medium","quality",    r"\bprint\s*\(",                           "print() in production code.",                            "Replace with logging.debug()/logging.info()."),
    ("PY013","low","quality",       r"#\s*(TODO|FIXME|HACK|XXX|BUG)\b",       "Unresolved TODO/FIXME.",                                 "Create a tracked issue and remove the marker."),
    ("PY014","low","quality",       r"from\s+\w+\s+import\s+\*",              "Wildcard import pollutes namespace.",                     "Import only what you need."),
    ("PY015","low","quality",       r"import\s+pdb\b",                        "Debugger import left in code.",                          "Remove before committing."),
    ("PY019","low","quality",       r"lambda\s+\w+:\s*\w+\s*\(",              "Complex lambda — use a named def.",                      "Replace with a def statement."),
    # Performance (Pro only)
    ("PY030","medium","performance",r"for\s+\w+\s+in\s+range\(len\(",         "for i in range(len(x)) is slow and unidiomatic.",        "Use enumerate(x) or iterate directly."),
    ("PY031","medium","performance",r"\+\s*=\s*['\"]",                        "String concatenation in loop builds O(n²) strings.",     "Use ''.join(parts) or an f-string outside the loop."),
    ("PY032","low","performance",   r"\.keys\(\)\s*\)",                       "Iterating .keys() is redundant.",                        "Iterate the dict directly: for k in my_dict."),
    ("PY033","medium","performance",r"time\.sleep\s*\(",                      "Blocking sleep in potentially async context.",            "Use asyncio.sleep() in async functions."),
]

JS_RULES = [
    ("JS001","high","quality",      r"\bvar\s+",                               "var is function-scoped and hoisted.",                    "Use const or let."),
    ("JS002","critical","security", r"\.innerHTML\s*=",                        "innerHTML is an XSS vector.",                           "Use textContent or DOMPurify."),
    ("JS003","critical","security", r"\beval\s*\(",                            "eval() executes arbitrary JS.",                          "Eliminate eval() usage."),
    ("JS004","high","security",     r"(?i)(api_key|apikey|api_secret|access_token|password)\s*=\s*['\"].+['\"]", "Hardcoded credential.", "Use environment variables."),
    ("JS005","medium","bug",        r"==\s*null\b(?!\=)|null\s*==(?!\=)",      "Loose null check also matches undefined.",               "Use === null explicitly."),
    ("JS006","medium","bug",        r"(?\x3C!=)==(?!=)",                          "Loose equality (==) causes type coercion bugs.",         "Use strict equality (===)."),
    ("JS007","medium","quality",    r"\bconsole\.(log|warn|error|debug)\s*\(", "console.log left in production.",                        "Remove or replace with a logging library."),
    ("JS008","medium","security",   r"document\.write\s*\(",                   "document.write() is an XSS risk.",                      "Use DOM manipulation methods instead."),
    ("JS009","low","quality",       r"//\s*(TODO|FIXME|HACK|XXX|BUG)\b",      "Unresolved TODO/FIXME.",                                "Create a tracked issue."),
    ("JS010","high","security",     r"__proto__\s*=",                          "Prototype pollution pattern.",                          "Validate object keys exclude __proto__."),
    ("JS011","medium","bug",        r"setTimeout\s*\(\s*['\"]",                "setTimeout with string calls eval internally.",          "Pass a function: setTimeout(() => fn(), ms)"),
    ("JS012","low","quality",       r"function\s+\w+\s*\([^)]{60,}\)",        "Too many parameters.",                                  "Use an options object instead."),
    # Performance (Pro only)
    ("JS030","medium","performance",r"\.forEach\s*\(.*\.push\s*\(",           "forEach+push is slower than Array.map().",               "Replace with map() for transformations."),
    ("JS031","medium","performance",r"document\.querySelector\s*\(.*\bfor\b", "DOM query inside a loop — expensive.",                  "Cache the element outside the loop."),
    ("JS032","low","performance",   r"JSON\.parse\s*\(JSON\.stringify\s*\(",  "JSON.parse(JSON.stringify()) for deep clone is slow.",   "Use structuredClone() in modern environments."),
]

SEV_ORDER = {"critical":0,"high":1,"medium":2,"low":3}
SEV_FILTER_LEVEL = {"all":3,"low":3,"medium":2,"high":1,"critical":0}.get(SEV_FILTER,3)

def should_skip(filepath, ignore_paths):
    path_str = str(filepath)
    return any(ig in path_str for ig in ignore_paths)

all_findings = []
all_files    = []

for scan_lang in SCAN_LANGS:
    ext   = "*.py" if scan_lang == "python" else "*.js"
    rules = PYTHON_RULES if scan_lang == "python" else JS_RULES
    files = [f for f in (src.rglob(ext) if src.is_dir() else [src]) if not should_skip(f, IGNORE_PATHS)]
    all_files.extend(files)
    for filepath in files:
        try:
            source = filepath.read_text(encoding="utf-8", errors="replace")
            for lineno, line in enumerate(source.splitlines(), 1):
                for rule_id, sev, category, pattern, message, fix in rules:
                    if re.search(pattern, line):
                        if SEV_ORDER.get(sev,3) \x3C= SEV_FILTER_LEVEL:
                            all_findings.append({
                                "id": rule_id, "severity": sev, "category": category,
                                "lang": scan_lang,
                                "file": str(filepath.relative_to(src) if src.is_dir() else filepath),
                                "line": lineno, "code": line.strip()[:80],
                                "message": message, "fix": fix,
                            })
        except Exception as e:
            console.print(f"[dim]Skipping {filepath}: {e}[/dim]")

# Smart dedup
seen = set()
unique = []
for f in all_findings:
    key = (f["id"], f["file"], f["line"])
    if key not in seen:
        seen.add(key)
        unique.append(f)
unique.sort(key=lambda f: (SEV_ORDER.get(f["severity"],3), f["file"], f["line"]))
display = unique[:MAX_FINDINGS]

SEV_COLOUR = {"critical":"red","high":"orange3","medium":"yellow","low":"dim"}

console.print()
console.print(Panel.fit(
    f"[bold red]🐛👁️⚡ Argus Pro — Code Intelligence Scanner[/bold red]\
"
    f"Scanning [yellow]{len(all_files)}[/yellow] file(s)  |  Languages: [cyan]{', '.join(SCAN_LANGS)}[/cyan]  |  Rules: [white]{len(PYTHON_RULES)+len(JS_RULES) if len(SCAN_LANGS)>1 else (len(PYTHON_RULES) if lang=='python' else len(JS_RULES))}[/white]",
    border_style="red"
))

if not unique:
    console.print(Panel("[green]✅ No issues found — clean codebase![/green]", border_style="green"))
else:
    suffix = f" — showing {len(display)} of {len(unique)}" if len(unique) > MAX_FINDINGS else f" — {len(unique)} total"
    tbl = Table(title=f"🔍 Findings{suffix}", box=box.ROUNDED, border_style="red", show_lines=True)
    tbl.add_column("ID",        width=7,  style="dim")
    tbl.add_column("Sev",       width=9)
    tbl.add_column("Cat",       width=12, style="cyan")
    tbl.add_column("Lang",      width=5,  style="magenta")
    tbl.add_column("File:Line", width=30, style="yellow")
    tbl.add_column("Issue",     width=42)
    for fi in display:
        sc  = SEV_COLOUR.get(fi["severity"],"white")
        loc = f"{fi['file'][-26:]}:{fi['line']}" if len(fi["file"])>26 else f"{fi['file']}:{fi['line']}"
        tbl.add_row(fi["id"],f"[{sc}]{fi['severity'].upper()}[/{sc}]",fi["category"],fi["lang"][:2].upper(),loc,fi["message"][:40])
    console.print(tbl)

    for fi in [f for f in display if f["severity"] in ("critical","high")][:5]:
        sc = SEV_COLOUR.get(fi["severity"],"white")
        console.print(Panel(
            f"[dim]File:[/dim] {fi['file']}:{fi['line']}\
"
            f"[dim]Code:[/dim] [italic]{fi['code']}[/italic]\
\
"
            f"[white]{fi['message']}[/white]\
\
"
            f"[green]Fix:[/green] {fi['fix']}",
            title=f"[{sc}][bold]{fi['severity'].upper()}[/bold][/{sc}] — {fi['id']}",
            border_style=sc
        ))

    sev_counts = {s: sum(1 for f in unique if f["severity"]==s) for s in ("critical","high","medium","low")}
    console.print()
    console.print(Panel(
        f"Files: [yellow]{len(all_files)}[/yellow]  Issues: [red]{len(unique)}[/red]  "
        + "  ".join(f"[{SEV_COLOUR[s]}]{s.title()}: {sev_counts[s]}[/{SEV_COLOUR[s]}]" for s in ("critical","high","medium","low") if sev_counts[s]),
        title="Summary", border_style="cyan"
    ))

# Save
report_file = f"argus_pro_report_{TODAY}.md"
json_file   = f"argus_pro_report_{TODAY}.json"

with open(report_file,"w",encoding="utf-8") as f:
    f.write(f"# 🐛 Argus Pro Report — {TODAY}\
\
**Path:** `{SRC_PATH}`  **Files:** {len(all_files)}  **Issues:** {len(unique)}\
\
")
    f.write("## Findings\
\
| ID | Severity | Category | Lang | File:Line | Issue |\
|---|---|---|---|---|---|\
")
    for fi in unique:
        f.write(f"| {fi['id']} | {fi['severity'].upper()} | {fi['category']} | {fi['lang']} | {fi['file'][:30]}:{fi['line']} | {fi['message'][:50]} |\
")
    f.write("\
## Fix Guide (Critical + High)\
\
")
    for fi in [x for x in unique if x["severity"] in ("critical","high")]:
        f.write(f"### {fi['id']} — {fi['file']}:{fi['line']}\
**Issue:** {fi['message']}\
**Fix:** {fi['fix']}\
```\
{fi['code']}\
```\
\
")

with open(json_file,"w",encoding="utf-8") as f:
    json.dump({"date":str(TODAY),"path":SRC_PATH,"files":len(all_files),"findings":unique},f,indent=2)

console.print(Panel(f"[green]✅ Done![/green]  [cyan]{report_file}[/cyan]  |  [cyan]{json_file}[/cyan]",border_style="green"))

if FAIL_CRITICAL and any(f["severity"]=="critical" for f in unique):
    console.print("[red]CI: Exiting with code 1 — critical findings detected.[/red]")
    sys.exit(1)
安全使用建议
This skill looks coherent for local Python/JavaScript static analysis. Before installing, use a virtual environment for the dependency install, protect the LICENSE_KEY, set SOURCE_PATH and IGNORE_PATHS to avoid scanning unintended files, and keep any JSON scan reports private.
功能分析
Type: OpenClaw Skill Name: argus-pro Version: 1.0.0 The Argus Pro skill is a static analysis tool designed to scan Python and JavaScript codebases for security vulnerabilities, bugs, and performance issues using predefined regex patterns. The script (SKILL.md) operates locally, reading files from a user-specified path and generating Markdown and JSON reports without any evidence of data exfiltration, unauthorized network communication, or malicious execution logic.
能力标签
requires-sensitive-credentials
能力评估
Purpose & Capability
The requested capabilities fit the stated purpose of scanning Python and JavaScript code, including recursive source scanning and JSON reporting, but scan outputs may contain sensitive code or security details.
Instruction Scope
The visible instructions are aimed at installing a display dependency and running a local scanner; no goal-hijacking, hidden role changes, or deceptive agent-control instructions were shown.
Install Mechanism
There is no install spec; setup is a Markdown instruction to install an unpinned PyPI package using pip with --break-system-packages. This is visible and purpose-related, but users should prefer a virtual environment.
Credentials
Default scanning starts at SOURCE_PATH='.' and can recursively inspect local Python and JavaScript files. This is expected for a code scanner, but users should set SOURCE_PATH and IGNORE_PATHS deliberately.
Persistence & Privilege
The skill requires a LICENSE_KEY and defaults to JSON output/trend-style reporting, but the shown artifacts do not show background persistence, credential exfiltration, or autonomous account mutation.
如何使用
  1. 确保已安装 OpenClaw(本地或 Docker 部署)
  2. 在对话框中输入安装命令:/install argus-pro
  3. 安装完成后,直接呼叫该 Skill 的名称或使用 /argus-pro 触发
  4. 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
版本历史
v1.0.0
Initial Pro release: 40+ rules, performance patterns, CI/CD integration, custom ignore paths, JSON output
元数据
Slug argus-pro
版本 1.0.0
许可证 MIT-0
累计安装 0
当前安装数 0
历史版本数 1
常见问题

Argus Pro — Code Intelligence Scanner 是什么?

Argus Pro — Code Intelligence Scanner. Scan Python and JavaScript codebases with 40+ rules covering security, bugs, performance, and code quality. Get prio... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 29 次。

如何安装 Argus Pro — Code Intelligence Scanner?

在 OpenClaw 或 Claude Code 对话框中运行命令「/install argus-pro」即可一键安装,无需额外配置。

Argus Pro — Code Intelligence Scanner 是免费的吗?

是的,Argus Pro — Code Intelligence Scanner 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。

Argus Pro — Code Intelligence Scanner 支持哪些平台?

Argus Pro — Code Intelligence Scanner 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。

谁开发了 Argus Pro — Code Intelligence Scanner?

由 OccupyTheMilkyWay(@occupythemilkyway)开发并维护,当前版本 v1.0.0。

💬 留言讨论