← 返回
未分类 Key

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...
Argus Pro — 代码智能扫描器。使用 40+ 条规则扫描 Python 与 JavaScript 代码库,覆盖安全、缺陷、性能和代码质量。获取优先级建议。
occupythemilkyway
未分类 clawhub v1.0.4 3 版本 99790.8 Key: 需要
★ 0
Stars
📥 477
下载
💾 0
安装
3
版本
#argus#ci#code-review#debugging#javascript#latest#linter#pro#python#security#static-analysis

概述

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

FeatureArgus (Free)Argus Pro
---------------------------------
Rules2040+
LanguagesPython or JSPython + JS simultaneously
Performance rules❌✅ N+1, blocking calls, memory leaks
CI exit code❌✅ FAIL_ON_CRITICAL
Ignore paths❌✅
JSON outputOptional✅ Always (CI-ready)
Finding deduplicationBasic✅ Cross-file smart dedup
Custom severity filter✅✅ + per-rule override

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


Setup

  1. Purchase your license key at ko-fi.com/s/8b27346505 ($9 one-time)
    • Or get all 5 Pro skills for $29 → ko-fi.com/s/7625accf3f (save $16)
  2. Install: openclaw skills install argus-pro
  3. Activate: set the LICENSE_KEY environment variable to the key you received
  4. Run — you're in

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

FENCE = chr(96) * 3

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]\n\n"
        "Get your key at: [bold cyan]ko-fi.com/s/8b27346505[/bold cyan]\n\n"
        "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"(?<!=)==(?!=)",                          "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) <= 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]\n"
    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']}\n"
            f"[dim]Code:[/dim] [italic]{fi['code']}[/italic]\n\n"
            f"[white]{fi['message']}[/white]\n\n"
            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}\n\n**Path:** `{SRC_PATH}`  **Files:** {len(all_files)}  **Issues:** {len(unique)}\n\n")
    f.write("## Findings\n\n| ID | Severity | Category | Lang | File:Line | Issue |\n|---|---|---|---|---|---|\n")
    for fi in unique:
        f.write(f"| {fi['id']} | {fi['severity'].upper()} | {fi['category']} | {fi['lang']} | {fi['file'][:30]}:{fi['line']} | {fi['message'][:50]} |\n")
    f.write("\n## Fix Guide (Critical + High)\n\n")
    for fi in [x for x in unique if x["severity"] in ("critical","high")]:
        f.write(f"### {fi['id']} — {fi['file']}:{fi['line']}\n**Issue:** {fi['message']}\n**Fix:** {fi['fix']}\n{FENCE}\n{fi['code']}\n{FENCE}\n\n")

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)

版本历史

共 3 个版本

  • v1.0.4 当前
    2026-05-25 16:53 安全 安全
  • v1.0.1
    2026-05-21 13:44 安全 安全
  • v1.0.0
    2026-05-11 05:13 安全 安全

安全检测

腾讯云安全 (Keen)

安全,无风险
查看报告

腾讯云安全 (Sanbu)

安全,无风险
查看报告

🔗 相关推荐

Tyche Lite — Quick Invoice Preview

occupythemilkyway
Tyche Lite —发票追踪器(免费)。可从 CSV 处理最多 5 张发票,查看付款状态,并获取友好提醒模板。作为 Ty... 的免费预览。
★ 0 📥 490

Talos Lite — Social Calendar (Free)

occupythemilkyway
Talos Lite — 社交日程(免费)。为单一平台生成1周发布计划,含3条文案。免费预览 Talos Pro 为您打造的...
★ 0 📥 567

Argus Lite — Code Scanner (Free)

occupythemilkyway
Argus Lite — 代码扫描器(免费)。对单个 Python 文件扫描前 10 大关键安全与缺陷模式。Argus Pro 功能的免费预览。
★ 0 📥 514