jarvis-ai-assistant 0.5.0__py3-none-any.whl → 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +114 -6
- jarvis/jarvis_agent/agent_manager.py +3 -0
- jarvis/jarvis_agent/jarvis.py +45 -9
- jarvis/jarvis_agent/run_loop.py +6 -1
- jarvis/jarvis_agent/task_planner.py +219 -0
- jarvis/jarvis_c2rust/__init__.py +13 -0
- jarvis/jarvis_c2rust/cli.py +405 -0
- jarvis/jarvis_c2rust/collector.py +209 -0
- jarvis/jarvis_c2rust/library_replacer.py +933 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +1265 -0
- jarvis/jarvis_c2rust/scanner.py +1671 -0
- jarvis/jarvis_c2rust/transpiler.py +1236 -0
- jarvis/jarvis_code_agent/code_agent.py +151 -18
- jarvis/jarvis_data/config_schema.json +13 -3
- jarvis/jarvis_sec/README.md +180 -0
- jarvis/jarvis_sec/__init__.py +674 -0
- jarvis/jarvis_sec/checkers/__init__.py +33 -0
- jarvis/jarvis_sec/checkers/c_checker.py +1269 -0
- jarvis/jarvis_sec/checkers/rust_checker.py +367 -0
- jarvis/jarvis_sec/cli.py +110 -0
- jarvis/jarvis_sec/prompts.py +324 -0
- jarvis/jarvis_sec/report.py +260 -0
- jarvis/jarvis_sec/types.py +20 -0
- jarvis/jarvis_sec/workflow.py +513 -0
- jarvis/jarvis_tools/cli/main.py +1 -0
- jarvis/jarvis_tools/execute_script.py +1 -1
- jarvis/jarvis_tools/read_code.py +11 -1
- jarvis/jarvis_tools/read_symbols.py +129 -0
- jarvis/jarvis_tools/registry.py +9 -1
- jarvis/jarvis_tools/sub_agent.py +4 -3
- jarvis/jarvis_tools/sub_code_agent.py +3 -3
- jarvis/jarvis_utils/config.py +28 -6
- jarvis/jarvis_utils/git_utils.py +39 -0
- jarvis/jarvis_utils/utils.py +150 -7
- {jarvis_ai_assistant-0.5.0.dist-info → jarvis_ai_assistant-0.6.0.dist-info}/METADATA +13 -1
- {jarvis_ai_assistant-0.5.0.dist-info → jarvis_ai_assistant-0.6.0.dist-info}/RECORD +41 -22
- {jarvis_ai_assistant-0.5.0.dist-info → jarvis_ai_assistant-0.6.0.dist-info}/entry_points.txt +4 -0
- {jarvis_ai_assistant-0.5.0.dist-info → jarvis_ai_assistant-0.6.0.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.5.0.dist-info → jarvis_ai_assistant-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.5.0.dist-info → jarvis_ai_assistant-0.6.0.dist-info}/top_level.txt +0 -0
|
@@ -26,6 +26,7 @@ from jarvis.jarvis_utils.config import (
|
|
|
26
26
|
get_git_check_mode,
|
|
27
27
|
set_config,
|
|
28
28
|
get_data_dir,
|
|
29
|
+
is_plan_enabled,
|
|
29
30
|
)
|
|
30
31
|
from jarvis.jarvis_utils.git_utils import (
|
|
31
32
|
confirm_add_new_files,
|
|
@@ -40,7 +41,7 @@ from jarvis.jarvis_utils.git_utils import (
|
|
|
40
41
|
)
|
|
41
42
|
from jarvis.jarvis_utils.input import get_multiline_input, user_confirm
|
|
42
43
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
43
|
-
from jarvis.jarvis_utils.utils import get_loc_stats, init_env
|
|
44
|
+
from jarvis.jarvis_utils.utils import get_loc_stats, init_env, _acquire_single_instance_lock
|
|
44
45
|
|
|
45
46
|
app = typer.Typer(help="Jarvis 代码助手")
|
|
46
47
|
|
|
@@ -58,6 +59,8 @@ class CodeAgent:
|
|
|
58
59
|
append_tools: Optional[str] = None,
|
|
59
60
|
tool_group: Optional[str] = None,
|
|
60
61
|
non_interactive: Optional[bool] = None,
|
|
62
|
+
plan: Optional[bool] = None,
|
|
63
|
+
**kwargs,
|
|
61
64
|
):
|
|
62
65
|
self.root_dir = os.getcwd()
|
|
63
66
|
self.tool_group = tool_group
|
|
@@ -65,7 +68,6 @@ class CodeAgent:
|
|
|
65
68
|
|
|
66
69
|
# 检测 git username 和 email 是否已设置
|
|
67
70
|
self._check_git_config()
|
|
68
|
-
tool_registry = ToolRegistry() # type: ignore
|
|
69
71
|
base_tools = [
|
|
70
72
|
"execute_script",
|
|
71
73
|
"search_web",
|
|
@@ -85,7 +87,6 @@ class CodeAgent:
|
|
|
85
87
|
# 去重
|
|
86
88
|
base_tools = list(dict.fromkeys(base_tools))
|
|
87
89
|
|
|
88
|
-
tool_registry.use_tools(base_tools)
|
|
89
90
|
code_system_prompt = self._get_system_prompt()
|
|
90
91
|
# 先加载全局规则(数据目录 rules),再加载项目规则(.jarvis/rules),并拼接为单一规则块注入
|
|
91
92
|
global_rules = self._read_global_rules()
|
|
@@ -112,6 +113,8 @@ class CodeAgent:
|
|
|
112
113
|
use_methodology=False, # 禁用方法论
|
|
113
114
|
use_analysis=False, # 禁用分析
|
|
114
115
|
non_interactive=self.non_interactive,
|
|
116
|
+
plan=bool(plan) if plan is not None else is_plan_enabled(),
|
|
117
|
+
use_tools=base_tools, # 仅启用限定工具
|
|
115
118
|
)
|
|
116
119
|
|
|
117
120
|
self.agent.event_bus.subscribe(AFTER_TOOL_CALL, self._on_after_tool_call)
|
|
@@ -278,29 +281,148 @@ class CodeAgent:
|
|
|
278
281
|
return git_dir
|
|
279
282
|
|
|
280
283
|
def _update_gitignore(self, git_dir: str) -> None:
|
|
281
|
-
"""检查并更新.gitignore文件,确保忽略.jarvis
|
|
284
|
+
"""检查并更新.gitignore文件,确保忽略.jarvis目录,并追加常用语言的忽略规则(若缺失)
|
|
282
285
|
|
|
283
286
|
参数:
|
|
284
287
|
git_dir: git根目录路径
|
|
285
288
|
"""
|
|
286
|
-
|
|
287
289
|
gitignore_path = os.path.join(git_dir, ".gitignore")
|
|
288
|
-
|
|
290
|
+
|
|
291
|
+
# 常用忽略规则(按语言/场景分组)
|
|
292
|
+
sections = {
|
|
293
|
+
"General": [
|
|
294
|
+
".jarvis",
|
|
295
|
+
".DS_Store",
|
|
296
|
+
"Thumbs.db",
|
|
297
|
+
"*.log",
|
|
298
|
+
"*.tmp",
|
|
299
|
+
"*.swp",
|
|
300
|
+
"*.swo",
|
|
301
|
+
".idea/",
|
|
302
|
+
".vscode/",
|
|
303
|
+
],
|
|
304
|
+
"Python": [
|
|
305
|
+
"__pycache__/",
|
|
306
|
+
"*.py[cod]",
|
|
307
|
+
"*$py.class",
|
|
308
|
+
".Python",
|
|
309
|
+
"env/",
|
|
310
|
+
"venv/",
|
|
311
|
+
".venv/",
|
|
312
|
+
"build/",
|
|
313
|
+
"dist/",
|
|
314
|
+
"develop-eggs/",
|
|
315
|
+
"downloads/",
|
|
316
|
+
"eggs/",
|
|
317
|
+
".eggs/",
|
|
318
|
+
"lib/",
|
|
319
|
+
"lib64/",
|
|
320
|
+
"parts/",
|
|
321
|
+
"sdist/",
|
|
322
|
+
"var/",
|
|
323
|
+
"wheels/",
|
|
324
|
+
"pip-wheel-metadata/",
|
|
325
|
+
"share/python-wheels/",
|
|
326
|
+
"*.egg-info/",
|
|
327
|
+
".installed.cfg",
|
|
328
|
+
"*.egg",
|
|
329
|
+
"MANIFEST",
|
|
330
|
+
".mypy_cache/",
|
|
331
|
+
".pytest_cache/",
|
|
332
|
+
".ruff_cache/",
|
|
333
|
+
".tox/",
|
|
334
|
+
".coverage",
|
|
335
|
+
".coverage.*",
|
|
336
|
+
"htmlcov/",
|
|
337
|
+
".hypothesis/",
|
|
338
|
+
".ipynb_checkpoints",
|
|
339
|
+
".pyre/",
|
|
340
|
+
".pytype/",
|
|
341
|
+
],
|
|
342
|
+
"Rust": [
|
|
343
|
+
"target/",
|
|
344
|
+
],
|
|
345
|
+
"Node": [
|
|
346
|
+
"node_modules/",
|
|
347
|
+
"npm-debug.log*",
|
|
348
|
+
"yarn-debug.log*",
|
|
349
|
+
"yarn-error.log*",
|
|
350
|
+
"pnpm-debug.log*",
|
|
351
|
+
"lerna-debug.log*",
|
|
352
|
+
"dist/",
|
|
353
|
+
"coverage/",
|
|
354
|
+
".turbo/",
|
|
355
|
+
".next/",
|
|
356
|
+
".nuxt/",
|
|
357
|
+
"out/",
|
|
358
|
+
],
|
|
359
|
+
"Go": [
|
|
360
|
+
"bin/",
|
|
361
|
+
"vendor/",
|
|
362
|
+
"coverage.out",
|
|
363
|
+
],
|
|
364
|
+
"Java": [
|
|
365
|
+
"target/",
|
|
366
|
+
"*.class",
|
|
367
|
+
".gradle/",
|
|
368
|
+
"build/",
|
|
369
|
+
"out/",
|
|
370
|
+
],
|
|
371
|
+
"C/C++": [
|
|
372
|
+
"build/",
|
|
373
|
+
"cmake-build-*/",
|
|
374
|
+
"*.o",
|
|
375
|
+
"*.a",
|
|
376
|
+
"*.so",
|
|
377
|
+
"*.obj",
|
|
378
|
+
"*.dll",
|
|
379
|
+
"*.dylib",
|
|
380
|
+
"*.exe",
|
|
381
|
+
"*.pdb",
|
|
382
|
+
],
|
|
383
|
+
".NET": [
|
|
384
|
+
"bin/",
|
|
385
|
+
"obj/",
|
|
386
|
+
],
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
existing_content = ""
|
|
390
|
+
if os.path.exists(gitignore_path):
|
|
391
|
+
with open(gitignore_path, "r", encoding="utf-8", errors="replace") as f:
|
|
392
|
+
existing_content = f.read()
|
|
393
|
+
|
|
394
|
+
# 已存在的忽略项(去除注释与空行)
|
|
395
|
+
existing_set = set(
|
|
396
|
+
ln.strip()
|
|
397
|
+
for ln in existing_content.splitlines()
|
|
398
|
+
if ln.strip() and not ln.strip().startswith("#")
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
# 计算缺失项并准备追加内容
|
|
402
|
+
new_lines: List[str] = []
|
|
403
|
+
for name, patterns in sections.items():
|
|
404
|
+
missing = [p for p in patterns if p not in existing_set]
|
|
405
|
+
if missing:
|
|
406
|
+
new_lines.append(f"# {name}")
|
|
407
|
+
new_lines.extend(missing)
|
|
408
|
+
new_lines.append("") # 分组空行
|
|
289
409
|
|
|
290
410
|
if not os.path.exists(gitignore_path):
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
411
|
+
# 新建 .gitignore(仅包含缺失项;此处即为全部常用规则)
|
|
412
|
+
with open(gitignore_path, "w", encoding="utf-8", newline="\n") as f:
|
|
413
|
+
content_to_write = "\n".join(new_lines).rstrip()
|
|
414
|
+
if content_to_write:
|
|
415
|
+
f.write(content_to_write + "\n")
|
|
416
|
+
PrettyOutput.print("已创建 .gitignore 并添加常用忽略规则", OutputType.SUCCESS)
|
|
296
417
|
else:
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
f
|
|
303
|
-
)
|
|
418
|
+
if new_lines:
|
|
419
|
+
# 追加缺失的规则
|
|
420
|
+
with open(gitignore_path, "a", encoding="utf-8", newline="\n") as f:
|
|
421
|
+
# 若原文件不以换行结尾,先补一行
|
|
422
|
+
if existing_content and not existing_content.endswith("\n"):
|
|
423
|
+
f.write("\n")
|
|
424
|
+
f.write("\n".join(new_lines).rstrip() + "\n")
|
|
425
|
+
PrettyOutput.print("已更新 .gitignore,追加常用忽略规则", OutputType.SUCCESS)
|
|
304
426
|
|
|
305
427
|
def _handle_git_changes(self, prefix: str, suffix: str) -> None:
|
|
306
428
|
"""处理git仓库中的未提交修改"""
|
|
@@ -587,6 +709,7 @@ class CodeAgent:
|
|
|
587
709
|
返回:
|
|
588
710
|
str: 描述执行结果的输出,成功时返回None
|
|
589
711
|
"""
|
|
712
|
+
prev_dir = os.getcwd()
|
|
590
713
|
try:
|
|
591
714
|
self._init_env(prefix, suffix)
|
|
592
715
|
start_commit = get_latest_commit_hash()
|
|
@@ -644,6 +767,12 @@ class CodeAgent:
|
|
|
644
767
|
|
|
645
768
|
except RuntimeError as e:
|
|
646
769
|
return f"Error during execution: {str(e)}"
|
|
770
|
+
finally:
|
|
771
|
+
# Ensure switching back to the original working directory after CodeAgent completes
|
|
772
|
+
try:
|
|
773
|
+
os.chdir(prev_dir)
|
|
774
|
+
except Exception:
|
|
775
|
+
pass
|
|
647
776
|
|
|
648
777
|
def _on_after_tool_call(self, agent: Agent, current_response=None, need_return=None, tool_prompt=None, **kwargs) -> None:
|
|
649
778
|
"""工具调用后回调函数。"""
|
|
@@ -894,6 +1023,7 @@ def cli(
|
|
|
894
1023
|
non_interactive: bool = typer.Option(
|
|
895
1024
|
False, "-n", "--non-interactive", help="启用非交互模式:用户无法与命令交互,脚本执行超时限制为5分钟"
|
|
896
1025
|
),
|
|
1026
|
+
plan: bool = typer.Option(False, "--plan/--no-plan", help="启用或禁用任务规划(子任务拆分与汇总执行)"),
|
|
897
1027
|
) -> None:
|
|
898
1028
|
"""Jarvis主入口点。"""
|
|
899
1029
|
# CLI 标志:非交互模式(不依赖配置文件)
|
|
@@ -914,6 +1044,8 @@ def cli(
|
|
|
914
1044
|
"欢迎使用 Jarvis-CodeAgent,您的代码工程助手已准备就绪!",
|
|
915
1045
|
config_file=config_file,
|
|
916
1046
|
)
|
|
1047
|
+
# CodeAgent 单实例互斥:仅代码助手入口加锁,其他入口不受影响
|
|
1048
|
+
_acquire_single_instance_lock(lock_name="code_agent.lock")
|
|
917
1049
|
|
|
918
1050
|
# 在初始化环境后同步 CLI 选项到全局配置,避免被 init_env 覆盖
|
|
919
1051
|
try:
|
|
@@ -973,6 +1105,7 @@ def cli(
|
|
|
973
1105
|
append_tools=append_tools,
|
|
974
1106
|
tool_group=tool_group,
|
|
975
1107
|
non_interactive=non_interactive,
|
|
1108
|
+
plan=plan,
|
|
976
1109
|
)
|
|
977
1110
|
|
|
978
1111
|
# 尝试恢复会话
|
|
@@ -188,6 +188,16 @@
|
|
|
188
188
|
"description": "AI工具筛选阈值:当可用工具数量超过此值时触发AI筛选",
|
|
189
189
|
"default": 30
|
|
190
190
|
},
|
|
191
|
+
"JARVIS_PLAN_ENABLED": {
|
|
192
|
+
"type": "boolean",
|
|
193
|
+
"description": "是否默认启用任务规划。当 Agent 初始化时 plan 参数未指定,将从此配置加载。默认 false。",
|
|
194
|
+
"default": false
|
|
195
|
+
},
|
|
196
|
+
"JARVIS_PLAN_MAX_DEPTH": {
|
|
197
|
+
"type": "number",
|
|
198
|
+
"description": "任务规划的最大层数。用于限制 plan 模式的递归拆分深度。仅在启用规划时生效(通过 CLI --plan/--no-plan 控制),默认 2。",
|
|
199
|
+
"default": 2
|
|
200
|
+
},
|
|
191
201
|
"JARVIS_SCRIPT_EXECUTION_TIMEOUT": {
|
|
192
202
|
"type": "number",
|
|
193
203
|
"description": "脚本执行的超时时间(秒),仅在非交互模式下生效。",
|
|
@@ -196,7 +206,7 @@
|
|
|
196
206
|
"JARVIS_AUTO_SUMMARY_ROUNDS": {
|
|
197
207
|
"type": "number",
|
|
198
208
|
"description": "基于对话轮次的自动总结阈值(达到该轮次后自动总结并清理历史)",
|
|
199
|
-
"default":
|
|
209
|
+
"default": 50
|
|
200
210
|
},
|
|
201
211
|
"JARVIS_CONFIRM_BEFORE_APPLY_PATCH": {
|
|
202
212
|
"type": "boolean",
|
|
@@ -310,12 +320,12 @@
|
|
|
310
320
|
"JARVIS_ENABLE_GIT_JCA_SWITCH": {
|
|
311
321
|
"type": "boolean",
|
|
312
322
|
"description": "在初始化环境前检测Git仓库并提示可切换到代码开发模式(jca)",
|
|
313
|
-
"default":
|
|
323
|
+
"default": true
|
|
314
324
|
},
|
|
315
325
|
"JARVIS_ENABLE_STARTUP_CONFIG_SELECTOR": {
|
|
316
326
|
"type": "boolean",
|
|
317
327
|
"description": "在进入默认通用代理前,列出可用配置(agent/multi_agent/roles)供选择",
|
|
318
|
-
"default":
|
|
328
|
+
"default": true
|
|
319
329
|
},
|
|
320
330
|
"JARVIS_IMMEDIATE_ABORT": {
|
|
321
331
|
"type": "boolean",
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
OpenHarmony 安全演进套件(jarvis_sec)
|
|
2
|
+
================================
|
|
3
|
+
|
|
4
|
+
概览
|
|
5
|
+
- 模式:单 Agent 逐条子任务分析(先直扫拆分候选,再由 Agent 精核)
|
|
6
|
+
- 目标:在不破坏现有功能的前提下,增强稳定性与可追溯性,减少“全部解析失败”的概率
|
|
7
|
+
- 关键特性:
|
|
8
|
+
- 禁用方法论与分析(use_methodology=False, use_analysis=False),降低不确定性
|
|
9
|
+
- 按次指定模型组(--llm-group / llm_group 参数),不修改全局配置
|
|
10
|
+
- 启用摘要 need_summary=True,通过 <REPORT>…</REPORT> 输出结构化结果
|
|
11
|
+
- 进度与过程日志输出(Progress / parse-fail / no-issue / issues-found)
|
|
12
|
+
- 增量写入 JSONL 报告,长任务中可实时查看
|
|
13
|
+
- 只读约束:禁止任何写文件或破坏性命令(rm/mv/cp/echo >/sed -i/git/patch/chmod/chown 等)
|
|
14
|
+
|
|
15
|
+
CLI 使用
|
|
16
|
+
- 入口:python -m jarvis.jarvis_sec.cli agent --path ./target_project
|
|
17
|
+
- 可选参数:
|
|
18
|
+
- --languages/-l:逗号分隔的扩展名列表,例如 "c,cpp,h,hpp,rs"
|
|
19
|
+
- --llm-group/-g:本次运行使用的模型组(仅透传,不改全局)
|
|
20
|
+
- --report-file/-r:JSONL 报告输出路径(默认写入 path/.jarvis/sec/agent_issues.jsonl)
|
|
21
|
+
|
|
22
|
+
示例
|
|
23
|
+
- 最简运行:
|
|
24
|
+
python -m jarvis.jarvis_sec.cli agent -p ./path/to/project
|
|
25
|
+
|
|
26
|
+
- 指定语言与临时模型组:
|
|
27
|
+
python -m jarvis.jarvis_sec.cli agent -p ./proj -l c,cpp,h,hpp,rs -g my_llm_group
|
|
28
|
+
|
|
29
|
+
- 指定增量报告文件:
|
|
30
|
+
python -m jarvis.jarvis_sec.cli agent -p ./proj -r ./out/agent_issues.jsonl
|
|
31
|
+
|
|
32
|
+
工作流要点
|
|
33
|
+
1) 直扫(direct_scan)
|
|
34
|
+
- 使用正则/命令行辅助在本地生成候选问题(issues)与统计信息(summary)
|
|
35
|
+
- 可在无外部服务时复现与回退,保障可用性
|
|
36
|
+
|
|
37
|
+
2) 子任务拆分与单 Agent 精核
|
|
38
|
+
- 将每个候选压缩为精简上下文(language、category、pattern、file、line、evidence 等)
|
|
39
|
+
- 单 Agent 周期内:
|
|
40
|
+
- need_summary=True
|
|
41
|
+
- summary_prompt 为 _build_summary_prompt(...) 返回的提示,要求在 <REPORT>…</REPORT> 内输出 JSON 或 YAML
|
|
42
|
+
- 系统提示包含只读约束与“一次仅执行一个工具”规则
|
|
43
|
+
- 推荐工具:read_code(读取目标文件附近源码)、execute_script(只读检索,如 rg/find)
|
|
44
|
+
|
|
45
|
+
3) 解析策略
|
|
46
|
+
- 优先解析摘要(agent.generate_summary())中的 <REPORT>…</REPORT>:
|
|
47
|
+
- _try_parse_summary_report 支持 JSON 优先,失败回退 YAML(safe_load)
|
|
48
|
+
- 摘要不可解析时,直接判定 parse-fail(不会回退解析主输出)
|
|
49
|
+
|
|
50
|
+
4) 增量写入 JSONL
|
|
51
|
+
- 每个子任务如检测到 issues,立即将记录 append 到 JSONL(默认 path/.jarvis/sec/agent_issues.jsonl)
|
|
52
|
+
- 支持通过 --report-file/-r 指定其他路径
|
|
53
|
+
- 失败不会影响主流程
|
|
54
|
+
|
|
55
|
+
JSONL 记录结构
|
|
56
|
+
- 每行一个 JSON 对象,格式如下:
|
|
57
|
+
{
|
|
58
|
+
"task_id": "JARVIS-SEC-Analyzer-3",
|
|
59
|
+
"candidate": {
|
|
60
|
+
"language": "c/cpp",
|
|
61
|
+
"category": "unsafe_api",
|
|
62
|
+
"pattern": "strcpy",
|
|
63
|
+
"file": "src/foo.c",
|
|
64
|
+
"line": 120,
|
|
65
|
+
"evidence": "strcpy(dst, src);",
|
|
66
|
+
"confidence": 0.9,
|
|
67
|
+
"severity": "high"
|
|
68
|
+
},
|
|
69
|
+
"issues": [
|
|
70
|
+
{
|
|
71
|
+
"language": "c/cpp",
|
|
72
|
+
"category": "unsafe_api",
|
|
73
|
+
"pattern": "strcpy",
|
|
74
|
+
"file": "src/foo.c",
|
|
75
|
+
"line": 120,
|
|
76
|
+
"evidence": "strcpy(dst, src);",
|
|
77
|
+
"description": "使用不安全/高风险字符串API,可能导致缓冲区溢出或格式化风险。",
|
|
78
|
+
"suggestion": "替换为带边界的安全API(如 snprintf/strlcpy 等)或加入显式长度检查。",
|
|
79
|
+
"confidence": 0.9,
|
|
80
|
+
"severity": "high"
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"meta": {
|
|
84
|
+
"entry_path": "/abs/path/to/project",
|
|
85
|
+
"languages": ["c","cpp","h","hpp","rs"],
|
|
86
|
+
"source": "summary"
|
|
87
|
+
"timestamp": "2025-10-19T03:00:00Z"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
摘要 <REPORT> 结构
|
|
92
|
+
- Agent 在摘要中必须只输出以下内容(推荐 JSON,支持 YAML):
|
|
93
|
+
<REPORT>
|
|
94
|
+
{
|
|
95
|
+
"issues": [
|
|
96
|
+
{
|
|
97
|
+
"language": "c/cpp|rust",
|
|
98
|
+
"category": "unsafe_api|buffer_overflow|memory_mgmt|error_handling|unsafe_usage|concurrency|ffi",
|
|
99
|
+
"pattern": "命中的模式/关键字",
|
|
100
|
+
"file": "相对或绝对路径",
|
|
101
|
+
"line": 0,
|
|
102
|
+
"evidence": "证据代码片段(单行简化)",
|
|
103
|
+
"description": "问题说明",
|
|
104
|
+
"suggestion": "修复建议",
|
|
105
|
+
"confidence": 0.0,
|
|
106
|
+
"severity": "high|medium|low"
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
"meta": {
|
|
110
|
+
"task_id": "JARVIS-SEC-Analyzer-1",
|
|
111
|
+
"entry_path": "/abs/path",
|
|
112
|
+
"languages": ["c","cpp","h","hpp","rs"],
|
|
113
|
+
"candidate": { "...": "子任务精简信息" }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
</REPORT>
|
|
117
|
+
|
|
118
|
+
- 要求:
|
|
119
|
+
- 仅在 <REPORT> 与 </REPORT> 中输出报告,不得出现其他文本
|
|
120
|
+
- 若确认误报,返回空数组 issues: []
|
|
121
|
+
- 字段值需与实际分析一致
|
|
122
|
+
|
|
123
|
+
只读约束(强制)
|
|
124
|
+
- Agent 被要求仅做只读分析:禁止修改任何文件或执行写操作命令
|
|
125
|
+
- 禁止命令包含但不限于:rm/mv/cp/echo >、sed -i、git、patch、chmod、chown 等
|
|
126
|
+
- 推荐工具:
|
|
127
|
+
- read_code:按文件路径与行号范围读取源码(建议围绕候选行上下 50 行)
|
|
128
|
+
- execute_script:只读检索(如 rg/find/grep),避免任何写操作
|
|
129
|
+
|
|
130
|
+
日志与可观测性
|
|
131
|
+
- 进度日志:
|
|
132
|
+
- [JARVIS-SEC] Progress i/N: file:line (lang)
|
|
133
|
+
- [JARVIS-SEC] no-issue i/N: ...
|
|
134
|
+
- [JARVIS-SEC] issues-found i/N: count=k -> append report (summary)
|
|
135
|
+
- [JARVIS-SEC] parse-fail i/N: ...
|
|
136
|
+
- JSONL 写入:
|
|
137
|
+
- [JARVIS-SEC] write K issue(s) to <path>
|
|
138
|
+
|
|
139
|
+
模型组(llm_group)
|
|
140
|
+
- 通过 CLI 的 --llm-group 或 API 的 llm_group 参数传入,仅对本次调用链生效
|
|
141
|
+
- 不会覆盖全局配置(不调用 set_config)
|
|
142
|
+
|
|
143
|
+
常见问题排查
|
|
144
|
+
- 解析失败(parse-fail):
|
|
145
|
+
- 确认模型已在摘要中输出 <REPORT>…</REPORT>
|
|
146
|
+
- 优先使用 JSON;YAML 解析依赖 PyYAML(safe_load),若环境无此库将忽略 YAML 回退
|
|
147
|
+
- 注意:不会回退解析主输出;若摘要缺失或格式不合规,将直接跳过该候选
|
|
148
|
+
- JSONL 未写入:
|
|
149
|
+
- 仅当 issues 非空时追加写入
|
|
150
|
+
- 确认 --report-file 或默认目录 path/.jarvis/sec/ 可写
|
|
151
|
+
- Agent 输出为空:
|
|
152
|
+
- CLI 会回退到直扫基线(run_security_analysis_fast),仍可得到 JSON+Markdown 报告
|
|
153
|
+
|
|
154
|
+
API 概览
|
|
155
|
+
- run_with_multi_agent(entry_path, languages=None, llm_group=None, report_file=None) -> str
|
|
156
|
+
- 透传到 run_security_analysis(...),实现“直扫 + 单 Agent 逐条验证 + 聚合”
|
|
157
|
+
- run_security_analysis_fast(entry_path, languages=None, exclude_dirs=None) -> str
|
|
158
|
+
- 纯直扫基线,返回 JSON + Markdown
|
|
159
|
+
- direct_scan(entry_path, languages=None, exclude_dirs=None) -> Dict
|
|
160
|
+
- 返回结构化 issues 与 summary
|
|
161
|
+
- run_with_multi_agent(entry_path, languages=None, llm_group=None, report_file=None) -> str
|
|
162
|
+
- 透传到 run_security_analysis(...),实现“直扫 + 单 Agent 逐条验证 + 聚合”
|
|
163
|
+
- run_security_analysis_fast(entry_path, languages=None, exclude_dirs=None) -> str
|
|
164
|
+
- 纯直扫基线,返回 JSON + Markdown
|
|
165
|
+
- direct_scan(entry_path, languages=None, exclude_dirs=None) -> Dict
|
|
166
|
+
- 返回结构化 issues 与 summary
|
|
167
|
+
|
|
168
|
+
建议测试(可选)
|
|
169
|
+
- 摘要解析:
|
|
170
|
+
- _try_parse_summary_report 对 JSON/YAML/无 REPORT 的输入解析正确
|
|
171
|
+
- CLI 参数链路:
|
|
172
|
+
- --llm-group 仅透传,不改全局
|
|
173
|
+
- --report-file 写入指定路径
|
|
174
|
+
- 只读约束:
|
|
175
|
+
- 模拟 Agent 工具调用,确保拒绝写操作命令(可在提示词层面校验)
|
|
176
|
+
|
|
177
|
+
版本兼容与注意事项
|
|
178
|
+
- 本模块不修改全局模型组配置
|
|
179
|
+
- 摘要使用 JSON 优先,YAML 为回退路径(需 PyYAML)
|
|
180
|
+
- 直扫基线可在无外部服务时独立运行,便于复现与回退
|