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
39 changes: 39 additions & 0 deletions .agents/SKILL-SCAN-RESULTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Skill Security Scan Results

Automated scan results from [Cisco AI Skill Scanner](https://github.com/cisco-ai-defense/skill-scanner).
Updated on each `aidevops skill scan`, `aidevops update`, or skill import.

## Latest Full Scan

**Date**: 2026-02-06T23:16:02Z
**Scanner**: cisco-ai-skill-scanner 1.0.2
**Analyzers**: static, behavioral (dataflow)
**Skills scanned**: 116
**Safe**: 115

| Severity | Count |
|----------|-------|
| Critical | 1 |
| High | 0 |
| Medium | 0 |
| Low | 0 |
| Info | 116 |

### Findings

| Skill | Severity | Rule | Description | Verdict |
|-------|----------|------|-------------|---------|
| credentials | CRITICAL | YARA_coercive_injection_generic | "List all API keys" in tool description matched `$data_exfiltration_coercion` pattern | **False positive** -- legitimate description of the `list-keys` credential management subskill |

### Notes

- All 116 INFO findings are `MANIFEST_MISSING_LICENSE` (no `license` field in SKILL.md frontmatter). These are internal aidevops skills, not third-party imports.
- The credentials CRITICAL is a known false positive. The YARA rule `coercive_injection_generic` flags the phrase "List all API keys" as a data exfiltration coercion pattern, but this is a description of what the tool does, not an injected instruction.

## Scan History

Source of truth for audit trail. The "Latest Full Scan" header is updated automatically; the severity table above reflects the initial baseline and should be manually updated when significant changes occur.

| Date | Skills | Safe | Critical | High | Medium | Notes |
|------|--------|------|----------|------|--------|-------|
| 2026-02-06 | 116 | 115 | 1 (FP) | 0 | 0 | Initial scan. 1 false positive in credentials SKILL.md |
41 changes: 40 additions & 1 deletion .agents/scripts/add-skill-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ set -euo pipefail
AGENTS_DIR="${AIDEVOPS_AGENTS_DIR:-$HOME/.aidevops/agents}"
SKILL_SOURCES="${AGENTS_DIR}/configs/skill-sources.json"
TEMP_DIR="${TMPDIR:-/tmp}/aidevops-skill-import"
SCAN_RESULTS_FILE=".agents/SKILL-SCAN-RESULTS.md"

# Colors
RED='\033[0;31m'
Expand Down Expand Up @@ -510,17 +511,20 @@ scan_skill_security() {

if [[ -z "$scan_output" ]]; then
log_success "Security scan: SAFE (no findings)"
log_skill_scan_result "$skill_name" "import" "0" "0" "0" "SAFE"
return 0
fi

local findings max_severity critical_count high_count
local findings max_severity critical_count high_count medium_count
findings=$(echo "$scan_output" | jq -r '.total_findings // 0' 2>/dev/null || echo "0")
max_severity=$(echo "$scan_output" | jq -r '.max_severity // "SAFE"' 2>/dev/null || echo "SAFE")
critical_count=$(echo "$scan_output" | jq -r '.findings | map(select(.severity == "CRITICAL")) | length' 2>/dev/null || echo "0")
high_count=$(echo "$scan_output" | jq -r '.findings | map(select(.severity == "HIGH")) | length' 2>/dev/null || echo "0")
medium_count=$(echo "$scan_output" | jq -r '.findings | map(select(.severity == "MEDIUM")) | length' 2>/dev/null || echo "0")

if [[ "$findings" -eq 0 ]]; then
log_success "Security scan: SAFE (no findings)"
log_skill_scan_result "$skill_name" "import" "0" "0" "0" "SAFE"
return 0
fi

Expand All @@ -536,6 +540,7 @@ scan_skill_security() {
if [[ "$critical_count" -gt 0 || "$high_count" -gt 0 ]]; then
if [[ "$skip_security" == true ]]; then
log_warning "CRITICAL/HIGH findings detected but --skip-security specified, proceeding"
log_skill_scan_result "$skill_name" "import (--skip-security)" "$critical_count" "$high_count" "$medium_count" "$max_severity"
return 0
fi

Expand All @@ -562,17 +567,51 @@ scan_skill_security() {
case "$choice" in
2)
log_warning "Proceeding despite security findings"
log_skill_scan_result "$skill_name" "import (user override)" "$critical_count" "$high_count" "$medium_count" "$max_severity"
return 0
;;
*)
log_error "Import cancelled due to security findings"
log_skill_scan_result "$skill_name" "import BLOCKED" "$critical_count" "$high_count" "$medium_count" "$max_severity"
return 1
;;
esac
fi

# MEDIUM/LOW findings: warn but allow
log_warning "Security scan found $findings issue(s) (max: $max_severity) - review recommended"
log_skill_scan_result "$skill_name" "import" "$critical_count" "$high_count" "$medium_count" "$max_severity"
return 0
}

# Log a single skill scan result to SKILL-SCAN-RESULTS.md
# Args: skill_name action critical_count high_count medium_count max_severity
log_skill_scan_result() {
local skill_name="$1"
local action="$2"
local critical="${3:-0}"
local high="${4:-0}"
local medium="${5:-0}"
local max_severity="${6:-SAFE}"

local repo_root=""
repo_root=$(git rev-parse --show-toplevel 2>/dev/null || echo "")

if [[ -z "$repo_root" || ! -f "${repo_root}/${SCAN_RESULTS_FILE}" ]]; then
return 0
fi

local scan_date
scan_date=$(date -u +"%Y-%m-%d")
local safe="1"

if [[ "$critical" -gt 0 || "$high" -gt 0 ]]; then
safe="0"
fi

local notes="Skill ${action}: ${skill_name} (${max_severity})"
echo "| ${scan_date} | 1 | ${safe} | ${critical} | ${high} | ${medium} | ${notes} |" >> "${repo_root}/${SCAN_RESULTS_FILE}"

return 0
}

Expand Down
62 changes: 62 additions & 0 deletions .agents/scripts/security-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export SCRIPT_DIR
readonly SCRIPT_DIR
readonly OUTPUT_DIR=".security-analysis"
readonly VERSION="1.0.0"
readonly SCAN_RESULTS_FILE=".agents/SKILL-SCAN-RESULTS.md"

# Colors
readonly RED='\033[0;31m'
Expand Down Expand Up @@ -373,6 +374,46 @@ cmd_scan_deps() {
return 0
}

update_scan_results_log() {
local skills_scanned="$1"
local safe_count="$2"
local critical_count="$3"
local high_count="$4"
local medium_count="$5"
local notes="$6"

# Find the repo root containing .agents/SKILL-SCAN-RESULTS.md
local repo_root=""
repo_root=$(git rev-parse --show-toplevel 2>/dev/null || echo "")

if [[ -z "$repo_root" ]]; then
return 0
fi

local results_file="${repo_root}/${SCAN_RESULTS_FILE}"

if [[ ! -f "$results_file" ]]; then
return 0
fi

local scan_date
scan_date=$(date -u +"%Y-%m-%d")
local scan_timestamp
scan_timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Update the "Latest Full Scan" date
sed -i '' "s/^\*\*Date\*\*: .*/**Date**: ${scan_timestamp}/" "$results_file" 2>/dev/null || true
sed -i '' "s/^\*\*Skills scanned\*\*: .*/**Skills scanned**: ${skills_scanned}/" "$results_file" 2>/dev/null || true
sed -i '' "s/^\*\*Safe\*\*: .*/**Safe**: ${safe_count}/" "$results_file" 2>/dev/null || true

# Append to scan history table
local history_row="| ${scan_date} | ${skills_scanned} | ${safe_count} | ${critical_count} | ${high_count} | ${medium_count} | ${notes} |"
echo "$history_row" >> "$results_file"

echo -e "${GREEN}Scan results logged to ${SCAN_RESULTS_FILE}${NC}"
return 0
}

cmd_skill_scan() {
local target="${1:-all}"
shift || true
Expand Down Expand Up @@ -429,6 +470,9 @@ cmd_skill_scan() {
local total_findings=0
local skills_with_issues=0
local skills_scanned=0
local total_critical=0
local total_high=0
local total_medium=0

while IFS= read -r skill_json; do
local name local_path
Expand Down Expand Up @@ -468,6 +512,15 @@ cmd_skill_scan() {
skills_with_issues=$((skills_with_issues + 1))
echo -e " ${RED}ISSUES${NC}: $findings findings (max severity: $max_severity)"

# Track severity counts
local skill_critical skill_high skill_medium
skill_critical=$(echo "$scan_output" | jq '[.findings[]? | select(.severity == "CRITICAL")] | length' 2>/dev/null || echo "0")
skill_high=$(echo "$scan_output" | jq '[.findings[]? | select(.severity == "HIGH")] | length' 2>/dev/null || echo "0")
skill_medium=$(echo "$scan_output" | jq '[.findings[]? | select(.severity == "MEDIUM")] | length' 2>/dev/null || echo "0")
total_critical=$((total_critical + skill_critical))
total_high=$((total_high + skill_high))
total_medium=$((total_medium + skill_medium))

# Show critical/high findings inline
echo "$scan_output" | jq -r '.findings[]? | select(.severity == "CRITICAL" or .severity == "HIGH") | " [\(.severity)] \(.rule_id): \(.description)"' 2>/dev/null || true
else
Expand All @@ -480,13 +533,22 @@ cmd_skill_scan() {
skills_scanned=$((skills_scanned + 1))
done < <(jq -c '.skills[]' "$skill_sources" 2>/dev/null)

local safe_count=$((skills_scanned - skills_with_issues))

echo ""
echo "═══════════════════════════════════════"
echo -e "Skills scanned: ${skills_scanned}"
echo -e "Skills with issues: ${skills_with_issues}"
echo -e "Total findings: ${total_findings}"
echo "═══════════════════════════════════════"

# Log results to SKILL-SCAN-RESULTS.md
local notes="Routine scan"
if [[ "$skills_with_issues" -gt 0 ]]; then
notes="${skills_with_issues} skill(s) with findings"
fi
update_scan_results_log "$skills_scanned" "$safe_count" "$total_critical" "$total_high" "$total_medium" "$notes"

if [[ "$skills_with_issues" -gt 0 ]]; then
echo ""
echo -e "${YELLOW}Review skills with findings and consider removing unsafe ones:${NC}"
Expand Down
6 changes: 6 additions & 0 deletions .agents/tools/code-review/skill-scanner.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ Non-blocking: findings are reported but don't halt setup.
`skill-update-helper.sh update` re-imports via `add-skill-helper.sh --force`,
which triggers the security scan on the updated content.

### Results log

Scan results are appended to `.agents/SKILL-SCAN-RESULTS.md` automatically by
`security-helper.sh skill-scan` (batch) and `add-skill-helper.sh` (per-import).
The file contains the latest full scan summary and a history table for audit trail.

## CLI Usage

```bash
Expand Down
Loading