Skip to content
Open
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
41 changes: 41 additions & 0 deletions .agents/skills/security-status-report/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
name: security-status-report
description: Generates a security status report based on docs/threats.json by spinning up sub-agents for each threat to compute a quality score.
---

# Task
Generate a security status report by evaluating threats listed in `docs/threats.json`.

# Workflow

1. Complete the TODO in `.agents/skills/security-status-report/scripts/template_dispatch.py`, meeting the following reqiurements:
a. Iterate over each threat from `docs/threats.json` to produce a list of invocations that matches your tool for invoking sub-agents.
b. Each invocation must address a single threat, use the below prompt, and instruct the sub-agent to output the correct schema.
2. Execute the script using `run_command` from the repository root, specifying `.agents/scratch/security-status-report/subagents.json` as the output file argument (`python3 .agents/skills/security-status-report/scripts/template_dispatch.py .agents/scratch/security-status-report/subagents.json`).
3. Read `.agents/scratch/security-status-report/subagents.json` and copy its exact JSON array into the `Subagents` array of your `invoke_subagent` tool. DO NOT manually craft or bypass the invocations. Run ALL generated sub-agents concurrently in a single tool call.
4. Wait for all sub-agents to complete.
5. Run `python3 .agents/skills/security-status-report/scripts/compile_report.py docs/threats.json .agents/scratch/security-status-report .agents/scratch/security-status-report/final.json` to produce the final report.
6. Run `python3 .agents/skills/security-status-report/scripts/render_chart.py .agents/scratch/security-status-report/final.json .agents/scratch/security-status-report/chart.png` to render a bar chart of the scores.

## Prompt template for each sub-agent that should be used in the script

You are a security reviewer evaluating the following specific threat:

{INSERT EACH THREAT JSON VERBATIM HERE}

- Focus on this threat only.
- Review the entire repo.
- Produce a gut-feel "quality score" based on the current security posture of the repo with respect to that threat.
- Output your results using the following schema, by writing them to `.agents/scratch/security-status-report/{threat_id}.json`,
where `{threat_id}` matches the id in the threat json you were initially provided.

```json
{
"threat_id": "<threat_id from input>",
"threat": "<threat text from input>",
"quality": <Decimal between 0 (no effective mitigation) and 1 (perfectly mitigated).>,
"strengths": "<Specific positive code/design mechanisms responsible for the score.>",
"weaknesses": "<Specific negative code/design mechanisms responsible for the score.>",
"citations": ["<repo-relative/path/to/file1.go>"]
}
```
45 changes: 45 additions & 0 deletions .agents/skills/security-status-report/scripts/compile_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
import json
import os

def compile_report(threats_json_path, results_dir, output_path):
with open(threats_json_path, 'r') as f:
threats = json.load(f)

final_report = []
for t in threats:
threat_id = t.get("threat_id", "unknown")
result_file = os.path.join(results_dir, f"{threat_id}.json")

if os.path.exists(result_file):
try:
with open(result_file, 'r') as rf:
res = json.load(rf)
t.update(res)
except Exception as e:
t.update({"error": f"Failed to parse agent JSON: {e}"})
else:
t.update({"error": "The evaluation sub-agent timed out or failed to produce a valid JSON."})
final_report.append(t)

os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, 'w') as f:
json.dump(final_report, f, indent=2)
print(f"Report compiled successfully to {output_path}")

if __name__ == '__main__':
compile_report(sys.argv[1], sys.argv[2], sys.argv[3])
90 changes: 90 additions & 0 deletions .agents/skills/security-status-report/scripts/render_chart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
import json
import os

# Set backend to non-interactive before importing pyplot
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

def render_chart(final_report_path, output_png_path):
with open(final_report_path, 'r') as f:
data = json.load(f)

# Sort data by threat_id (e.g. T-01, T-02, ...)
def sort_key(item):
tid = item.get("threat_id", "")
if tid.startswith("T-") and tid[2:].isdigit():
return int(tid[2:])
return tid

sorted_data = sorted(data, key=sort_key)

threat_ids = [item.get("threat_id", f"T-{i+1:02d}") for i, item in enumerate(sorted_data)]

scores = []
colors = []
errors = []

for item in sorted_data:
raw_score = item.get("quality", 0.0)
is_error = "error" in item or raw_score in ("Error", "Timeout/Missing")
errors.append(is_error)

try:
score = float(raw_score) if not is_error else 0.0
except (ValueError, TypeError):
score = 0.0
score = max(0.0, min(1.0, score))
scores.append(score)

if is_error:
colors.append("#dc2626") # Red for error / timeout
elif score >= 0.8:
colors.append("#16a34a") # Green
elif score >= 0.5:
colors.append("#d97706") # Orange
else:
colors.append("#dc2626") # Red

fig, ax = plt.subplots(figsize=(14, 6))
ax.bar(threat_ids, scores, color=colors, width=0.6)

# Annotate errors with vertical "ERROR" text above x-axis
for i, err in enumerate(errors):
if err:
ax.text(i, 0.02, "ERROR", rotation=90, ha='center', va='bottom', fontsize=8, fontweight='bold', color='#dc2626')

ax.set_ylim(0.0, 1.0)
ax.set_ylabel("Quality Score (0.0 - 1.0)", fontsize=12, fontweight='bold')
ax.set_xlabel("Threat ID", fontsize=12, fontweight='bold')
ax.set_title("Substrate Security Threat Posture Scores", fontsize=16, fontweight='bold', pad=15)

# Standard matplotlib way to rotate tick labels cleanly
plt.xticks(rotation=90, fontsize=9, fontweight='bold')
ax.grid(axis='y', linestyle='--', alpha=0.5)

plt.tight_layout()
os.makedirs(os.path.dirname(output_png_path), exist_ok=True)
plt.savefig(output_png_path, dpi=150)
plt.close()
print(f"Chart rendered successfully to {output_png_path}")

if __name__ == '__main__':
report_file = sys.argv[1] if len(sys.argv) > 1 else ".agents/scratch/security-status-report/final.json"
output_png = sys.argv[2] if len(sys.argv) > 2 else ".agents/scratch/security-status-report/chart.png"
render_chart(report_file, output_png)
66 changes: 66 additions & 0 deletions .agents/skills/security-status-report/scripts/template_dispatch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Template script for formatting threats into agent dispatch specifications.
The main agent should complete this script to output a valid JSON array
matching the 'Subagents' parameter of its invoke_subagent tool.
"""
import sys
import json
import os

def generate_dispatch_payload(output_path):
with open('docs/threats.json', 'r') as f:
threats = json.load(f)

subagents = []

for t in threats:
prompt = f'''You are a security reviewer evaluating the following specific threat:

{json.dumps(t, indent=2)}

- Focus on this threat only.
- Review the entire repo.
- Produce a gut-feel "quality score" based on the current security posture of the repo with respect to that threat.
- Output your results using the following schema, by writing them to .agents/scratch/security-status-report/{t["threat_id"]}.json,
where {t["threat_id"]} matches the id in the threat json you were initially provided.

```json
{{
"threat_id": "<threat_id from input>",
"threat": "<threat text from input>",
"quality": <Decimal between 0 (no effective mitigation) and 1 (perfectly mitigated).>,
"strengths": "<Specific positive code/design mechanisms responsible for the score.>",
"weaknesses": "<Specific negative code/design mechanisms responsible for the score.>",
"citations": ["<repo-relative/path/to/file1.go>"]
}}
```'''
# TODO: Agent, update this part to ensure it matches the correct schema for you to invoke sub-agents via a tool call.
subagents.append({
"Prompt": prompt,
"Role": "Security Reviewer",
"TypeName": "self",
"Workspace": "inherit"
})

os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, 'w') as f:
json.dump(subagents, f, indent=2)
print(f"Dispatch payload written successfully to {output_path}")

if __name__ == '__main__':
output_path = sys.argv[1] if len(sys.argv) > 1 else ".agents/scratch/security-status-report/subagents.json"
generate_dispatch_payload(output_path)
20 changes: 20 additions & 0 deletions .agents/skills/skill-review/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: skill-review
description: Reviews agent skill definitions for token efficiency, clarity, and standardized formatting.
---
# Task
Review agent skill definitions (`SKILL.md` files) to enforce maximum token efficiency, eliminate conversational fluff, and ensure standardized formatting without losing functional fidelity.

# Checks
## 1. Structure & Headers
- **Standardization**: Require a top-level `# Task` section and `# Checks` (or `# Workflow`) section.
- **Header Conciseness**: Flag overly verbose headers (e.g., `## Focus Areas & Deterministic Checks`). Require terse headers.

## 2. Prose & Verbosity
- **Persona Elimination**: Flag conversational AI persona setups (e.g., "You are an expert..."). Replace with imperative tasks.
- **Conversational Fluff**: Flag transitional phrases, unnecessary adverbs, and filler words.
- **Imperative Voice**: Require bullet points to start with strong, punchy verbs (`Flag...`, `Require...`, `Ensure...`).

## 3. Formatting
- **Density**: Ensure rules are condensed into terse, single-sentence commands where possible.
- **Fidelity**: Ensure no deterministic rules, checks, or domain knowledge are lost during token optimization.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ Thumbs.db

# Stray local build outputs (go build ./tools/... without -o)
/validate-image-cache

# Substrate agent workspace scratchpads
.agents/scratch/
Loading
Loading