Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/agent-sec-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,25 +213,36 @@ Output example:
### Verification Flow

1. Load trusted public keys from `skill/scripts/asset-verify/trusted-keys/*.asc`
2. Verify the GPG signature (`.skill.sig`) of `Manifest.json` in each skill directory
2. Verify the GPG signature (`.skill-meta/.skill.sig`) of `.skill-meta/Manifest.json` in each skill directory
3. Validate SHA-256 hashes of all files listed in the Manifest

### Error Codes

| Code | Meaning |
|------|---------|
| 0 | Passed |
| 10 | Missing `.skill.sig` |
| 11 | Missing `Manifest.json` |
| 10 | Missing `.skill-meta/.skill.sig` |
| 11 | Missing `.skill-meta/Manifest.json` |
| 12 | Invalid signature |
| 13 | Hash mismatch |

### Sign a Skill
### Sign Skills (Self-Deployment Quick Start)

When deploying from source, skills are unsigned by default. Sign them so Phase 2 passes:

```bash
sign-skill.sh <skill-directory>
# 1. One-time: generate GPG key + export public key
tools/sign-skill.sh --init

# 2. Batch-sign all skills
tools/sign-skill.sh --batch /usr/share/anolisa/skills --force

# 3. Verify
python3 skill/scripts/asset-verify/verifier.py
```

For the complete guide (manual key management, custom skills, CI/CD, troubleshooting), see **[Skill Signing Guide](tools/SIGNING_GUIDE.md)**.

## Audit Log

All security events are logged to `/var/log/agent-sec/violations.log`:
Expand Down
21 changes: 16 additions & 5 deletions src/agent-sec-core/README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,25 +213,36 @@ python3 skill/scripts/sandbox/sandbox_policy.py --cwd "$PWD" "git status"
### 校验流程

1. 加载受信公钥(`skill/scripts/asset-verify/trusted-keys/*.asc`)
2. 验证 Skill 目录中 `Manifest.json` 的 GPG 签名(`.skill.sig`)
2. 验证 Skill 目录中 `.skill-meta/Manifest.json` 的 GPG 签名(`.skill-meta/.skill.sig`)
3. 校验 Manifest 中所有文件的 SHA-256 哈希

### 错误码

| 码 | 含义 |
|----|------|
| 0 | 通过 |
| 10 | 缺失 `.skill.sig` |
| 11 | 缺失 `Manifest.json` |
| 10 | 缺失 `.skill-meta/.skill.sig` |
| 11 | 缺失 `.skill-meta/Manifest.json` |
| 12 | 签名无效 |
| 13 | 哈希不匹配 |

### 签名技能
### Skill 签名(自行部署快速开始)

通过源码部署时,skill 默认未签名。签名后 Phase 2 才能通过:

```bash
sign-skill.sh <技能目录>
# 1. 一次性初始化:生成 GPG 密钥 + 导出公钥
tools/sign-skill.sh --init

# 2. 批量签名所有 skill
tools/sign-skill.sh --batch /usr/share/anolisa/skills --force

# 3. 验证
python3 skill/scripts/asset-verify/verifier.py
```

完整指南(手动密钥管理、自定义 skill、CI/CD、问题排查)请参见 **[Skill 签名指南](tools/SIGNING_GUIDE_CN.md)**。

## 审计日志

所有安全事件记录至 `/var/log/agent-sec/violations.log`:
Expand Down
4 changes: 2 additions & 2 deletions src/agent-sec-core/skill/scripts/asset-verify/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ class ErrSigMissing(SkillVerifyError):
code = 10

def __init__(self, skill_name):
super().__init__(f"ERR_SIG_MISSING: Missing .skill.sig in '{skill_name}'")
super().__init__(f"ERR_SIG_MISSING: Missing .skill-meta/.skill.sig in '{skill_name}'")


class ErrManifestMissing(SkillVerifyError):
code = 11

def __init__(self, skill_name):
super().__init__(
f"ERR_MANIFEST_MISSING: Missing Manifest.json in '{skill_name}'"
f"ERR_MANIFEST_MISSING: Missing .skill-meta/Manifest.json in '{skill_name}'"
)


Expand Down
Empty file.
12 changes: 8 additions & 4 deletions src/agent-sec-core/skill/scripts/asset-verify/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@
DEFAULT_CONFIG = SCRIPT_DIR / "config.conf"
DEFAULT_TRUSTED_KEYS_DIR = SCRIPT_DIR / "trusted-keys"

# Check if system gpg is available
GPG_BIN = shutil.which("gpg")
# Check if system gpg is available (prefer 'gpg', fall back to 'gpg2' on RHEL/Alinux)
GPG_BIN = shutil.which("gpg") or shutil.which("gpg2")

# Hidden directory inside each skill that holds signing artifacts
SIGNING_DIR = ".skill-meta"


def load_config(config_path: Path) -> dict:
Expand Down Expand Up @@ -199,8 +202,9 @@ def verify_manifest_hashes(skill_dir: str, manifest: dict, skill_name: str) -> N
def verify_skill(skill_dir: str, trusted_keys: list) -> tuple:
"""Verify a single skill directory"""
skill_name = os.path.basename(skill_dir)
manifest_path = os.path.join(skill_dir, "Manifest.json")
sig_path = os.path.join(skill_dir, ".skill.sig")
signing_dir = os.path.join(skill_dir, SIGNING_DIR)
manifest_path = os.path.join(signing_dir, "Manifest.json")
sig_path = os.path.join(signing_dir, ".skill.sig")

if not os.path.exists(manifest_path):
raise ErrManifestMissing(skill_name)
Expand Down
Loading
Loading