Skip to content
Draft
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
2,190 changes: 1,328 additions & 862 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
"crates/api",
"crates/skills-sh",
"crates/skill",
"crates/skill-audit",
"crates/git",
"crates/json",
"crates/markdown",
Expand Down Expand Up @@ -66,6 +67,9 @@ predicates = "3.1"
# YAML parsing
serde_yaml = "0.9"

# Skill security audit — yara-x git tag (crates.io 1.14 sub-crates out of sync)
yara-x = { git = "https://github.com/VirusTotal/yara-x", tag = "v1.17.0", default-features = false, features = [ "constant-folding", "exact-atoms", "fast-regexp" ] }
Comment on lines +70 to +71

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "[1] Current manifest entry:"
rg -n '^\s*yara-x\s*=' Cargo.toml

echo
echo "[2] Current lockfile source (if present):"
rg -n -A3 -B1 'name = "yara-x"|source = "git\+https://github.com/VirusTotal/yara-x' Cargo.lock || true

echo
echo "[3] Remote commit currently referenced by tag v1.17.0:"
git ls-remote --tags https://github.com/VirusTotal/yara-x refs/tags/v1.17.0

Repository: AkaraChen/aghub

Length of output: 1076


Pin yara-x with an immutable rev in Cargo.toml

Cargo.toml uses tag = "v1.17.0", but Cargo.lock already resolves that tag to commit e0096cd41fe16be76ca5321618ac880d1894b086. Still, add rev in the manifest to make the pin explicit (defense-in-depth, and safer when Cargo.lock changes or is not present).

🔒 Suggested manifest hardening
-yara-x = { git = "https://github.com/VirusTotal/yara-x", tag = "v1.17.0", default-features = false, features = [ "constant-folding", "exact-atoms", "fast-regexp" ] }
+yara-x = { git = "https://github.com/VirusTotal/yara-x", tag = "v1.17.0", rev = "<commit-sha>", default-features = false, features = [ "constant-folding", "exact-atoms", "fast-regexp" ] }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Cargo.toml` around lines 57 - 58, The manifest currently pins yara-x by tag
("v1.17.0") only; update the Cargo.toml dependency declaration for yara-x to
include the explicit git commit SHA that Cargo.lock resolved (rev =
"e0096cd41fe16be76ca5321618ac880d1894b086") alongside the existing tag and
feature settings so the crate is immutably pinned (keep default-features = false
and the features list unchanged).


# TOML parsing
toml = "1.0"
toml_edit = "0.25"
Expand Down
1 change: 1 addition & 0 deletions crates/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ aghub-git = { path = "../git" }
aghub-inference = { path = "../inference" }
aghub-cc-plugins = { path = "../cc-plugins" }
skill = { path = "../skill" }
skill-audit = { path = "../skill-audit", features = [ "from-path" ] }
skills-sh = { path = "../skills-sh" }
rocket = { version = "0.5", features = [ "json" ] }
rocket_cors = "0.6.0"
Expand Down
13 changes: 13 additions & 0 deletions crates/api/src/bin/export-dto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ use aghub_api::dto::{
ScopeSupportDto, SkillCapabilitiesDto, SkillsPathsDto,
SubAgentCapabilitiesDto,
},
audit::{
AuditReportDto, AuditRequest, CategoryDto, ConfidenceDto, FindingDto,
FindingSourceDto, SeverityDto, VerdictDto,
},
common::ConfigSource,
credential::{CreateCredentialRequest, CredentialResponse},
inference::{
Expand Down Expand Up @@ -247,6 +251,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
export_type::<CCPluginValidateRequest>(&cfg)?;
export_type::<CCPluginValidateResponse>(&cfg)?;

export_type::<VerdictDto>(&cfg)?;
export_type::<ConfidenceDto>(&cfg)?;
export_type::<SeverityDto>(&cfg)?;
export_type::<CategoryDto>(&cfg)?;
export_type::<FindingSourceDto>(&cfg)?;
export_type::<FindingDto>(&cfg)?;
export_type::<AuditReportDto>(&cfg)?;
export_type::<AuditRequest>(&cfg)?;

write_index_file(&out_dir)?;

if disallowed_dir.exists() {
Expand Down
173 changes: 173 additions & 0 deletions crates/api/src/dto/audit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
//! DTOs mirroring `skill_audit::AuditReport` at the HTTP boundary, with ts-rs
//! bindings for the frontend. Kept separate so the skill-audit crate stays free
//! of ts-rs, matching how the other DTOs mirror core models.

use serde::{Deserialize, Serialize};
use ts_rs::TS;

use skill_audit::{
AuditReport, Category, Confidence, Finding, FindingSource, Severity,
Verdict,
};

#[derive(Debug, Serialize, TS)]
#[ts(export)]
#[serde(rename_all = "snake_case")]
pub enum VerdictDto {
Benign,
Suspicious,
Malicious,
}

#[derive(Debug, Serialize, TS)]
#[ts(export)]
#[serde(rename_all = "snake_case")]
pub enum ConfidenceDto {
Low,
Medium,
High,
}

#[derive(Debug, Serialize, TS)]
#[ts(export)]
#[serde(rename_all = "snake_case")]
pub enum SeverityDto {
Info,
Low,
Medium,
High,
Critical,
}

#[derive(Debug, Serialize, TS)]
#[ts(export)]
#[serde(rename_all = "snake_case")]
pub enum CategoryDto {
CredentialExfil,
DataExfil,
CommandInjection,
PromptInjection,
ToolChaining,
Persistence,
HostTamper,
Obfuscation,
Other,
}

#[derive(Debug, Serialize, TS)]
#[ts(export)]
#[serde(rename_all = "snake_case")]
pub enum FindingSourceDto {
Yara,
Injection,
}

#[derive(Debug, Serialize, TS)]
#[ts(export)]
pub struct FindingDto {
pub rule_id: String,
pub category: CategoryDto,
pub severity: SeverityDto,
pub file: String,
pub line: Option<u32>,
pub evidence: String,
pub source: FindingSourceDto,
}

#[derive(Debug, Serialize, TS)]
#[ts(export)]
pub struct AuditReportDto {
pub verdict: VerdictDto,
pub confidence: ConfidenceDto,
pub findings: Vec<FindingDto>,
pub summary: String,
}

/// Request to audit a skill at a local path (directory, `.skill`/`.zip`, `.md`).
#[derive(Debug, Deserialize, TS)]
#[ts(export)]
pub struct AuditRequest {
pub path: String,
}

impl From<Verdict> for VerdictDto {
fn from(v: Verdict) -> Self {
match v {
Verdict::Benign => Self::Benign,
Verdict::Suspicious => Self::Suspicious,
Verdict::Malicious => Self::Malicious,
}
}
}

impl From<Confidence> for ConfidenceDto {
fn from(c: Confidence) -> Self {
match c {
Confidence::Low => Self::Low,
Confidence::Medium => Self::Medium,
Confidence::High => Self::High,
}
}
}

impl From<Severity> for SeverityDto {
fn from(s: Severity) -> Self {
match s {
Severity::Info => Self::Info,
Severity::Low => Self::Low,
Severity::Medium => Self::Medium,
Severity::High => Self::High,
Severity::Critical => Self::Critical,
}
}
}

impl From<Category> for CategoryDto {
fn from(c: Category) -> Self {
match c {
Category::CredentialExfil => Self::CredentialExfil,
Category::DataExfil => Self::DataExfil,
Category::CommandInjection => Self::CommandInjection,
Category::PromptInjection => Self::PromptInjection,
Category::ToolChaining => Self::ToolChaining,
Category::Persistence => Self::Persistence,
Category::HostTamper => Self::HostTamper,
Category::Obfuscation => Self::Obfuscation,
Category::Other => Self::Other,
}
}
}

impl From<FindingSource> for FindingSourceDto {
fn from(s: FindingSource) -> Self {
match s {
FindingSource::Yara => Self::Yara,
FindingSource::Injection => Self::Injection,
}
}
}

impl From<Finding> for FindingDto {
fn from(f: Finding) -> Self {
Self {
rule_id: f.rule_id,
category: f.category.into(),
severity: f.severity.into(),
file: f.file,
line: f.line,
evidence: f.evidence,
source: f.source.into(),
}
}
}

impl From<AuditReport> for AuditReportDto {
fn from(r: AuditReport) -> Self {
Self {
verdict: r.verdict.into(),
confidence: r.confidence.into(),
findings: r.findings.into_iter().map(Into::into).collect(),
summary: r.summary,
}
}
}
1 change: 1 addition & 0 deletions crates/api/src/dto/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod agents;
pub mod audit;
pub mod common;
pub mod credential;
pub mod inference;
Expand Down
22 changes: 22 additions & 0 deletions crates/api/src/dto/skill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,32 @@ pub struct InstallSkillRequest {
pub scope: String,
pub project_path: Option<String>,
pub install_all: Option<bool>,
/// When true, install proceeds even if the security audit flags the skill
/// as Suspicious/Malicious (the desktop "install anyway" confirm).
#[ts(optional = nullable)]
pub force_unsafe: Option<bool>,
/// Reuse a clone cached from a prior blocked attempt (the "install anyway"
/// retry) instead of cloning the source again.
#[ts(optional = nullable)]
pub session_id: Option<String>,
}

#[derive(Debug, Serialize, TS)]
#[ts(export)]
pub struct InstallSkillResponse {
pub success: bool,
/// Security-audit report for the requested skills (the worst verdict across
/// them). Always present when at least one skill was audited, so the
/// install flow can show the result even for a Benign verdict.
#[serde(skip_serializing_if = "Option::is_none")]
pub audit: Option<crate::dto::audit::AuditReportDto>,
/// True when the audit blocked the install (`success: false`, no
/// `force_unsafe`); retry with `force_unsafe` + `session_id` to proceed.
pub audit_blocked: bool,
/// Set when blocked; pass it back with `force_unsafe` to reuse the
/// already-cloned source instead of cloning again.
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
}

/// Response for a single global skill lock entry
Expand Down Expand Up @@ -240,6 +260,8 @@ pub struct GitScanSkillEntry {
pub author: Option<String>,
pub version: Option<String>,
pub path: String,
/// Security-audit verdict for this skill (scanned in the cloned repo).
pub audit: Option<crate::dto::audit::AuditReportDto>,
}

#[derive(Debug, Serialize, TS)]
Expand Down
1 change: 1 addition & 0 deletions crates/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ fn build_rocket(
routes::inference::delete_inference_provider,
routes::skills::open_skill_folder,
routes::skills::edit_skill_folder,
routes::skills::audit_skill,
routes::skills::get_skill_content,
routes::skills::get_skill_tree,
routes::skills::get_global_skill_lock,
Expand Down
Loading
Loading