From f50590005de48c77d3451540010e71571b3e00c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 May 2026 19:09:36 +0000 Subject: [PATCH 1/3] Initial plan From b070c3664619a7435c8bdacedd1ddfc7fafee898 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 May 2026 19:16:09 +0000 Subject: [PATCH 2/3] Add trust level, publisher, paths, policies and compatibility to actions list/info commands --- .../cli/src/commands/build-actions.test.ts | 33 ++++++++- packages/cli/src/commands/build-actions.ts | 69 ++++++++++++++++--- 2 files changed, 92 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/commands/build-actions.test.ts b/packages/cli/src/commands/build-actions.test.ts index f07df8e7..8c2d795d 100644 --- a/packages/cli/src/commands/build-actions.test.ts +++ b/packages/cli/src/commands/build-actions.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { auditWorkflowContent, createActionsCmd } from './build-actions.js'; +import { auditWorkflowContent, createActionsCmd, deriveTrustLevel } from './build-actions.js'; describe('actions command aliases', () => { const actionsCmd = createActionsCmd(); @@ -13,6 +13,37 @@ describe('actions command aliases', () => { const searchCmd = actionsCmd.commands.find((c) => c.name() === 'search'); expect(searchCmd).toBeDefined(); }); + + it('has a list subcommand', () => { + const listCmd = actionsCmd.commands.find((c) => c.name() === 'list'); + expect(listCmd).toBeDefined(); + }); +}); + +describe('deriveTrustLevel', () => { + const base = { + leastPrivilegePermissions: true, + pinThirdPartyActions: 'optional' as const, + allowPullRequestTarget: false, + defaultTimeoutMinutes: 15, + }; + + it('returns "high" when all conditions are met', () => { + expect(deriveTrustLevel({ ...base, pinThirdPartyActions: 'required' })).toBe('high'); + }); + + it('returns "medium" when least-privilege and no pull-request-target but pinning is not required', () => { + expect(deriveTrustLevel({ ...base, pinThirdPartyActions: 'optional' })).toBe('medium'); + expect(deriveTrustLevel({ ...base, pinThirdPartyActions: 'off' })).toBe('medium'); + }); + + it('returns "low" when leastPrivilegePermissions is false', () => { + expect(deriveTrustLevel({ ...base, leastPrivilegePermissions: false })).toBe('low'); + }); + + it('returns "low" when allowPullRequestTarget is true', () => { + expect(deriveTrustLevel({ ...base, allowPullRequestTarget: true })).toBe('low'); + }); }); describe('auditWorkflowContent', () => { diff --git a/packages/cli/src/commands/build-actions.ts b/packages/cli/src/commands/build-actions.ts index df9af6c9..4e0d9678 100644 --- a/packages/cli/src/commands/build-actions.ts +++ b/packages/cli/src/commands/build-actions.ts @@ -8,6 +8,7 @@ import { openPackPullRequest, planDiff, renderPack, + type ActionPackManifest, type CatalogEntry, type DiffPlan, type OpenPrOutcome, @@ -58,10 +59,27 @@ function printStatusLine(destination: string, statusKind: string, reason?: strin console.log(` ${tag} ${destination}${suffix}`); } +export type TrustLevel = 'high' | 'medium' | 'low'; + +export function deriveTrustLevel(security: ActionPackManifest['security']): TrustLevel { + if ( + security.leastPrivilegePermissions && + security.pinThirdPartyActions === 'required' && + !security.allowPullRequestTarget + ) { + return 'high'; + } + if (security.leastPrivilegePermissions && !security.allowPullRequestTarget) { + return 'medium'; + } + return 'low'; +} + function printCatalogRows(rows: Array<{ id: string; name: string; version: string; + publisher: string; categories: string[]; description: string; }>): void { @@ -71,7 +89,7 @@ function printCatalogRows(rows: Array<{ } for (const row of rows) { - console.log(`${kleur.bold(row.id)} ${kleur.dim(`v${row.version}`)}`); + console.log(`${kleur.bold(row.id)} ${kleur.dim(`v${row.version}`)} ${kleur.dim(`by ${row.publisher}`)}`); console.log(` ${row.name} — ${row.description}`); console.log(` ${kleur.dim('categories:')} ${row.categories.join(', ')}`); console.log(); @@ -82,6 +100,7 @@ async function catalogRows(query?: string): Promise> { @@ -92,12 +111,13 @@ async function catalogRows(query?: string): Promise { if (!needle) return true; - return [row.id, row.name, row.description, ...row.categories] + return [row.id, row.name, row.description, row.publisher, ...row.categories] .some((value) => value.toLowerCase().includes(needle)); }) .sort((a, b) => a.id.localeCompare(b.id)); @@ -216,20 +236,51 @@ export function createActionsCmd(): Command { .argument('', 'pack id, e.g. node-pnpm-ci') .option('--json', 'emit machine-readable JSON') .action(async (packId: string, opts: { json?: boolean }) => { - const { manifest } = await getCatalogEntry(packId); + const { manifest, packDir } = await getCatalogEntry(packId); if (opts.json) { - console.log(JSON.stringify(manifest, null, 2)); + console.log(JSON.stringify({ ...manifest, packDir, trustLevel: deriveTrustLevel(manifest.security) }, null, 2)); return; } + const trustLevel = deriveTrustLevel(manifest.security); + const trustColor = trustLevel === 'high' ? kleur.green : trustLevel === 'medium' ? kleur.yellow : kleur.red; + console.log(kleur.bold(`${manifest.name} (${manifest.id}@${manifest.version})`)); console.log(manifest.description); console.log(); - console.log(`${kleur.dim('publisher:')} ${manifest.publisher}`); - console.log(`${kleur.dim('visibility:')} ${manifest.visibility}`); - console.log(`${kleur.dim('license:')} ${manifest.license}`); - console.log(`${kleur.dim('categories:')} ${manifest.categories.join(', ')}`); - console.log(`${kleur.dim('pricing:')} ${manifest.pricing.type}`); + console.log(`${kleur.dim('publisher:')} ${manifest.publisher}`); + console.log(`${kleur.dim('visibility:')} ${manifest.visibility}`); + console.log(`${kleur.dim('license:')} ${manifest.license}`); + console.log(`${kleur.dim('categories:')} ${manifest.categories.join(', ')}`); + console.log(`${kleur.dim('pricing:')} ${manifest.pricing.type}`); + console.log(`${kleur.dim('trust level:')} ${trustColor(trustLevel)}`); + console.log(`${kleur.dim('pack path:')} ${packDir}`); + + console.log(); + console.log(kleur.bold('Compatibility')); + console.log(` ${kleur.dim('providers:')} ${manifest.compatibility.providers.join(', ')}`); + if (manifest.compatibility.languages?.length) { + console.log(` ${kleur.dim('languages:')} ${manifest.compatibility.languages.join(', ')}`); + } + if (manifest.compatibility.packageManagers?.length) { + console.log(` ${kleur.dim('package managers:')} ${manifest.compatibility.packageManagers.join(', ')}`); + } + if (manifest.compatibility.frameworks?.length) { + console.log(` ${kleur.dim('frameworks:')} ${manifest.compatibility.frameworks.join(', ')}`); + } + + console.log(); + console.log(kleur.bold('Policies')); + console.log(` ${kleur.dim('install mode:')} ${manifest.policies.installMode}`); + console.log(` ${kleur.dim('managed comment:')} ${manifest.policies.managedComment}`); + console.log(` ${kleur.dim('requires review:')} ${manifest.policies.requiresReview}`); + + console.log(); + console.log(kleur.bold('Security')); + console.log(` ${kleur.dim('least-privilege permissions:')} ${manifest.security.leastPrivilegePermissions}`); + console.log(` ${kleur.dim('pin third-party actions:')} ${manifest.security.pinThirdPartyActions}`); + console.log(` ${kleur.dim('allow pull-request-target:')} ${manifest.security.allowPullRequestTarget}`); + console.log(` ${kleur.dim('default timeout (min):')} ${manifest.security.defaultTimeoutMinutes}`); if (Object.keys(manifest.inputs).length > 0) { console.log(); From 49b244e9c02b3d51f5432a972bacb99b54f4b1b6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 May 2026 19:18:03 +0000 Subject: [PATCH 3/3] Use object lookup instead of nested ternary for trust level color --- packages/cli/src/commands/build-actions.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/build-actions.ts b/packages/cli/src/commands/build-actions.ts index 4e0d9678..2bb4b4ec 100644 --- a/packages/cli/src/commands/build-actions.ts +++ b/packages/cli/src/commands/build-actions.ts @@ -243,7 +243,12 @@ export function createActionsCmd(): Command { } const trustLevel = deriveTrustLevel(manifest.security); - const trustColor = trustLevel === 'high' ? kleur.green : trustLevel === 'medium' ? kleur.yellow : kleur.red; + const trustColors: Record string> = { + high: kleur.green, + medium: kleur.yellow, + low: kleur.red, + }; + const trustColor = trustColors[trustLevel]; console.log(kleur.bold(`${manifest.name} (${manifest.id}@${manifest.version})`)); console.log(manifest.description);