opencode-gitlab-dap 1.16.6 → 1.17.0

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.
package/README.md CHANGED
@@ -279,6 +279,27 @@ Say **"bootstrap project memory"** to automatically inspect a project and build
279
279
  - **Remove an obsolete skill** — delete a skill and its associated snippet, automatically cleaning up the index entry
280
280
  - **Audit available skills** — list all published and draft skills with their descriptions to understand what automation is available for the project
281
281
 
282
+ ##### Security Audits for skills.sh Skills
283
+
284
+ When discovering or installing skills from skills.sh, the plugin automatically checks security audit results from three independent providers:
285
+
286
+ - **Gen Agent Trust Hub** — AI-powered threat detection for malware, data exfiltration, and prompt injection
287
+ - **Socket** — Supply chain security analysis for suspicious patterns and anomalies
288
+ - **Snyk** — Vulnerability scanning and risk assessment
289
+
290
+ **Behavior:**
291
+
292
+ - **Block on Fail** — if any audit provider reports a failure, installation is blocked with details and a link to review the audit findings
293
+ - **Warn on Warn** — if any provider reports a warning, the skill is installed but warnings are displayed with links to the audit details
294
+ - **Graceful degradation** — if skills.sh is unreachable or audit data is unavailable, installation proceeds with a note
295
+
296
+ **Use cases:**
297
+
298
+ - **Discover skills with security status** — `gitlab_skill_discover` shows audit results (Pass/Warn/Fail) inline for each skills.sh result, so you can assess risk before installing
299
+ - **Block installation of risky skills** — skills with audit failures from any provider are automatically blocked with a link to the full audit report
300
+ - **Review audit warnings before proceeding** — skills with warnings install successfully but display the warnings so you can make an informed decision
301
+ - **Graceful handling of unavailable audit data** — if skills.sh is unreachable, install proceeds with a note rather than failing
302
+
282
303
  ### Dynamic Refresh
283
304
 
284
305
  After enabling or disabling an agent/flow, the plugin automatically refreshes the
package/dist/index.cjs CHANGED
@@ -4052,7 +4052,16 @@ function searchSkillsSh(query) {
4052
4052
  function downloadSkillFromSkillsSh(identifier) {
4053
4053
  const tmp = (0, import_fs2.mkdtempSync)((0, import_path2.join)((0, import_os2.tmpdir)(), "skill-install-"));
4054
4054
  try {
4055
- (0, import_child_process.execSync)(`npx skills add ${JSON.stringify(identifier)} -y --copy`, {
4055
+ const atIdx = identifier.indexOf("@");
4056
+ let cmd;
4057
+ if (atIdx !== -1) {
4058
+ const repo = identifier.slice(0, atIdx);
4059
+ const skill = identifier.slice(atIdx + 1);
4060
+ cmd = `npx skills add ${JSON.stringify(repo)} --skill ${JSON.stringify(skill)} -y --copy`;
4061
+ } else {
4062
+ cmd = `npx skills add ${JSON.stringify(identifier)} -y --copy`;
4063
+ }
4064
+ (0, import_child_process.execSync)(cmd, {
4056
4065
  timeout: 6e4,
4057
4066
  cwd: tmp,
4058
4067
  encoding: "utf-8",
@@ -4105,6 +4114,73 @@ function downloadSkillFromSkillsSh(identifier) {
4105
4114
  }
4106
4115
  }
4107
4116
 
4117
+ // src/tools/skill-audit.ts
4118
+ var PROVIDER_NAMES = {
4119
+ "agent-trust-hub": "Gen Agent Trust Hub",
4120
+ socket: "Socket",
4121
+ snyk: "Snyk"
4122
+ };
4123
+ function parseIdentifierToUrl(identifier) {
4124
+ const atIdx = identifier.indexOf("@");
4125
+ if (atIdx === -1) return null;
4126
+ const ownerRepo = identifier.slice(0, atIdx);
4127
+ const skillName = identifier.slice(atIdx + 1);
4128
+ if (!ownerRepo.includes("/") || !skillName) return null;
4129
+ return `https://skills.sh/${ownerRepo}/${skillName}`;
4130
+ }
4131
+ function parseAuditHtml(html, auditUrl) {
4132
+ const secIdx = html.indexOf("Security Audits");
4133
+ if (secIdx === -1) return null;
4134
+ const section = html.slice(secIdx, secIdx + 5e3);
4135
+ const providerPattern = /href="[^"]*\/security\/(agent-trust-hub|socket|snyk)"[\s\S]*?>(Pass|Fail|Warn)</g;
4136
+ const results = [];
4137
+ let match;
4138
+ while ((match = providerPattern.exec(section)) !== null) {
4139
+ const slug = match[1];
4140
+ const status = match[2];
4141
+ results.push({
4142
+ provider: PROVIDER_NAMES[slug] ?? slug,
4143
+ status,
4144
+ detailUrl: `${auditUrl}/security/${slug}`
4145
+ });
4146
+ }
4147
+ if (results.length === 0) return null;
4148
+ return {
4149
+ results,
4150
+ hasFailure: results.some((r) => r.status === "Fail"),
4151
+ hasWarning: results.some((r) => r.status === "Warn"),
4152
+ auditUrl
4153
+ };
4154
+ }
4155
+ async function fetchSkillAudit(identifier) {
4156
+ const url = parseIdentifierToUrl(identifier);
4157
+ if (!url) return null;
4158
+ try {
4159
+ const resp = await fetch(url, {
4160
+ signal: AbortSignal.timeout(1e4),
4161
+ headers: { Accept: "text/html" }
4162
+ });
4163
+ if (!resp.ok) return null;
4164
+ const html = await resp.text();
4165
+ return parseAuditHtml(html, url);
4166
+ } catch {
4167
+ return null;
4168
+ }
4169
+ }
4170
+ function formatAuditLine(audit) {
4171
+ return audit.results.map((r) => {
4172
+ const icon = r.status === "Fail" ? "\u{1F534}" : r.status === "Warn" ? "\u{1F7E1}" : "\u{1F7E2}";
4173
+ return `${icon} ${r.provider}: ${r.status}`;
4174
+ }).join(" | ");
4175
+ }
4176
+ function formatAuditRate(audit) {
4177
+ const total = audit.results.length;
4178
+ const passed = audit.results.filter((r) => r.status === "Pass").length;
4179
+ if (passed === total) return `Security: ${passed}/${total} audits passed \u2705`;
4180
+ if (audit.hasFailure) return `Security: ${passed}/${total} audits passed \u274C`;
4181
+ return `Security: ${passed}/${total} audits passed \u26A0\uFE0F`;
4182
+ }
4183
+
4108
4184
  // src/tools/skill-tools.ts
4109
4185
  var z6 = import_plugin6.tool.schema;
4110
4186
  var PROJECT_ID_DESC2 = "Project path from git remote";
@@ -4389,14 +4465,32 @@ Install: \`gitlab_skill_install(name="${e.name}", source="group", group_id="${ar
4389
4465
  }
4390
4466
  const shResults = searchSkillsSh(args.query);
4391
4467
  if (shResults.length > 0) {
4468
+ const audits = await Promise.all(shResults.map((r) => fetchSkillAudit(r.identifier)));
4392
4469
  sections.push(
4393
4470
  `### skills.sh (${shResults.length})
4394
4471
 
4395
- ` + shResults.map(
4396
- (r) => `**${r.identifier}** (${r.installs})
4472
+ ` + shResults.map((r, i) => {
4473
+ const audit = audits[i];
4474
+ let line = `**${r.identifier}** (${r.installs})`;
4475
+ if (audit) {
4476
+ line += `
4477
+ ${formatAuditRate(audit)}`;
4478
+ line += `
4479
+ ${formatAuditLine(audit)}`;
4480
+ if (audit.hasFailure) {
4481
+ line += `
4482
+ \u26D4 BLOCKED \u2014 security audit failures detected. Review: ${audit.auditUrl}`;
4483
+ return line;
4484
+ }
4485
+ } else {
4486
+ line += `
4487
+ Security: unknown (audit data unavailable)`;
4488
+ }
4489
+ line += `
4397
4490
  ${r.url}
4398
- Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
4399
- ).join("\n\n")
4491
+ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``;
4492
+ return line;
4493
+ }).join("\n\n")
4400
4494
  );
4401
4495
  }
4402
4496
  if (sections.length === 0) {
@@ -4419,6 +4513,14 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
4419
4513
  const projectScope = resolveScope2(args);
4420
4514
  const targetPrefix = args.draft ? DRAFTS_PREFIX : SKILLS_PREFIX;
4421
4515
  if (args.source === "skills.sh") {
4516
+ const audit = await fetchSkillAudit(args.name);
4517
+ if (audit?.hasFailure) {
4518
+ const failures = audit.results.filter((r) => r.status === "Fail").map((r) => ` \u{1F534} ${r.provider}: Fail (${r.detailUrl})`).join("\n");
4519
+ return `\u26D4 Installation BLOCKED \u2014 security audit failure(s) detected:
4520
+ ${failures}
4521
+
4522
+ Review the audit details before proceeding: ${audit.auditUrl}`;
4523
+ }
4422
4524
  const downloaded = downloadSkillFromSkillsSh(args.name);
4423
4525
  if (!downloaded) {
4424
4526
  return `Failed to download skill "${args.name}" from skills.sh.`;
@@ -4470,7 +4572,19 @@ Install: \`gitlab_skill_install(name="${r.identifier}", source="skills.sh")\``
4470
4572
  }
4471
4573
  const parts = [`${wikiCount} wiki page(s)`];
4472
4574
  if (hasBundle) parts.push(`${scriptFiles.length} bundled script(s)`);
4473
- return `Installed skill "${downloaded.name}" from skills.sh. ${parts.join(", ")}. Use gitlab_skill_setup to extract scripts.`;
4575
+ let result = `Installed skill "${downloaded.name}" from skills.sh. ${parts.join(", ")}. Use gitlab_skill_setup to extract scripts.`;
4576
+ if (audit?.hasWarning) {
4577
+ const warnings = audit.results.filter((r) => r.status === "Warn").map((r) => ` \u26A0\uFE0F ${r.provider}: Warn (${r.detailUrl})`).join("\n");
4578
+ result += `
4579
+
4580
+ \u26A0\uFE0F Security audit warnings:
4581
+ ${warnings}`;
4582
+ } else if (!audit) {
4583
+ result += `
4584
+
4585
+ (security audit data unavailable \u2014 skills.sh may be unreachable)`;
4586
+ }
4587
+ return result;
4474
4588
  } catch (err) {
4475
4589
  return `Error installing from skills.sh: ${err.message}`;
4476
4590
  }