← Home

@canonical/pragma-cli

CLI and MCP server for Canonical's design system.

3
Versions
GPL-3.0
License
No
Install Scripts
Missing
Provenance

Supply chain provenance

Status for the latest visible version.

No SLSA provenance npm registry signatures gitHead linked

Without SLSA provenance there is no cryptographic link between this tarball and the public source — the axios compromise (March 2026) relied on exactly this gap.

Maintainers

ilayda21frankbanhuwshimianthonydillonsteverydzamylily1011bartazjpmartinsptpetesfrenchmariadias143jmuzinamtrujedlerdabehniacanonical-organizationedisile-canonicalsteciuk-canonicalad.vlengr-aliando.gqninfa_jeonndv99goulin-canonicalimmortalcodesalvaromateoonibenjo-canonical

Accepted risks

Findings the reviewer chose to accept rather than block on.

SourceRuleReasonAccepted byWhen
semgrep semgrep:silent-process-exec AI (semgrep): Fires only in test files; spawning a local completions server for integration testing, not malicious. ai
semgrep semgrep:silent-process-exec-var AI (semgrep): Same test-file context as silent-process-exec; not a runtime concern. ai
semgrep semgrep:env-spread AI (semgrep): Pattern is in test setup/teardown to save and restore process.env; standard testing practice. ai

Versions (showing 3 of 3)

Version Deps Published
0.27.0 14 / 10
0.20.0 11 / 9
0.18.0 11 / 9

v0.27.0

10 findings
HIGH silent-process-exec: src/completions/startCompletionsServer.test.ts:32 semgrep

Silent detached process — runs invisibly in the background (reverse shells, miners) Source: https://github.com/canonical/pragma/blob/5df5709bf7cb6f2016be2f6eba8391637e2e2a0c/src/completions/startCompletionsServer.test.ts#L32 30 | const { default: querySocket } = await import("./querySocket.js"); 31 | > 32 | const server = spawn("bun", ["run", BIN_PATH, "_completions-server"], { 33 | stdio: "ignore", 34 | cwd,

HIGH silent-process-exec-var: src/completions/startCompletionsServer.test.ts:32 semgrep

Silent detached process — runs invisibly in the background (reverse shells, miners) Source: https://github.com/canonical/pragma/blob/5df5709bf7cb6f2016be2f6eba8391637e2e2a0c/src/completions/startCompletionsServer.test.ts#L32 30 | const { default: querySocket } = await import("./querySocket.js"); 31 | > 32 | const server = spawn("bun", ["run", BIN_PATH, "_completions-server"], { 33 | stdio: "ignore", 34 | cwd,

HIGH silent-process-exec: src/completions/startCompletionsServer.test.ts:56 semgrep

Silent detached process — runs invisibly in the background (reverse shells, miners) Source: https://github.com/canonical/pragma/blob/5df5709bf7cb6f2016be2f6eba8391637e2e2a0c/src/completions/startCompletionsServer.test.ts#L56 54 | const { default: querySocket } = await import("./querySocket.js"); 55 | > 56 | const server = spawn("bun", ["run", BIN_PATH, "_completions-server"], { 57 | stdio: "ignore", 58 | cwd,

HIGH silent-process-exec-var: src/completions/startCompletionsServer.test.ts:56 semgrep

Silent detached process — runs invisibly in the background (reverse shells, miners) Source: https://github.com/canonical/pragma/blob/5df5709bf7cb6f2016be2f6eba8391637e2e2a0c/src/completions/startCompletionsServer.test.ts#L56 54 | const { default: querySocket } = await import("./querySocket.js"); 55 | > 56 | const server = spawn("bun", ["run", BIN_PATH, "_completions-server"], { 57 | stdio: "ignore", 58 | cwd,

HIGH env-spread: src/domains/refs/operations/paths.test.ts:7 semgrep

Spreading entire process.env into an object — may capture all secrets Source: https://github.com/canonical/pragma/blob/5df5709bf7cb6f2016be2f6eba8391637e2e2a0c/src/domains/refs/operations/paths.test.ts#L7 5 | 6 | describe("cacheRoot", () => { > 7 | const origEnv = { ...process.env }; 8 | 9 | afterEach(() => {

HIGH env-spread: src/domains/refs/operations/paths.test.ts:38 semgrep

Spreading entire process.env into an object — may capture all secrets Source: https://github.com/canonical/pragma/blob/5df5709bf7cb6f2016be2f6eba8391637e2e2a0c/src/domains/refs/operations/paths.test.ts#L38 36 | 37 | describe("globalConfigDir", () => { > 38 | const origEnv = { ...process.env }; 39 | 40 | afterEach(() => {

HIGH env-spread: src/domains/refs/operations/paths.test.ts:56 semgrep

Spreading entire process.env into an object — may capture all secrets Source: https://github.com/canonical/pragma/blob/5df5709bf7cb6f2016be2f6eba8391637e2e2a0c/src/domains/refs/operations/paths.test.ts#L56 54 | 55 | describe("gitCacheDir", () => { > 56 | const origEnv = { ...process.env }; 57 | 58 | beforeEach(() => {

HIGH env-spread: src/domains/refs/operations/readGlobalRefs.test.ts:9 semgrep

Spreading entire process.env into an object — may capture all secrets Source: https://github.com/canonical/pragma/blob/5df5709bf7cb6f2016be2f6eba8391637e2e2a0c/src/domains/refs/operations/readGlobalRefs.test.ts#L9 7 | describe("readGlobalRefs", () => { 8 | let tmpDir: string; > 9 | const origEnv = { ...process.env }; 10 | 11 | beforeEach(() => {

HIGH env-spread: src/domains/refs/operations/updateRefs.test.ts:12 semgrep

Spreading entire process.env into an object — may capture all secrets Source: https://github.com/canonical/pragma/blob/5df5709bf7cb6f2016be2f6eba8391637e2e2a0c/src/domains/refs/operations/updateRefs.test.ts#L12 10 | let projectDir: string; 11 | let bareRepo: string; > 12 | const origEnv = { ...process.env }; 13 | 14 | beforeEach(() => {

LOW No provenance attestation provenance

Package was published without Sigstore provenance. Only ~12% of npm packages have provenance, so this is common but not ideal.

v0.20.0

1 finding
LOW No provenance attestation provenance

Package was published without Sigstore provenance. Consider requesting the maintainer enable provenance via CI/CD.

v0.18.0

5 findings
HIGH silent-process-exec: src/completions/startCompletionsServer.test.ts:32 semgrep

Silent detached process — runs invisibly in the background (reverse shells, miners) Source: https://github.com/canonical/pragma/blob/e5434d05421090cb77c227ccaa6025983c9a292c/src/completions/startCompletionsServer.test.ts#L32 30 | const { default: querySocket } = await import("./querySocket.js"); 31 | > 32 | const server = spawn("bun", ["run", BIN_PATH, "_completions-server"], { 33 | stdio: "ignore", 34 | cwd,

HIGH silent-process-exec-var: src/completions/startCompletionsServer.test.ts:32 semgrep

Silent detached process — runs invisibly in the background (reverse shells, miners) Source: https://github.com/canonical/pragma/blob/e5434d05421090cb77c227ccaa6025983c9a292c/src/completions/startCompletionsServer.test.ts#L32 30 | const { default: querySocket } = await import("./querySocket.js"); 31 | > 32 | const server = spawn("bun", ["run", BIN_PATH, "_completions-server"], { 33 | stdio: "ignore", 34 | cwd,

HIGH silent-process-exec: src/completions/startCompletionsServer.test.ts:56 semgrep

Silent detached process — runs invisibly in the background (reverse shells, miners) Source: https://github.com/canonical/pragma/blob/e5434d05421090cb77c227ccaa6025983c9a292c/src/completions/startCompletionsServer.test.ts#L56 54 | const { default: querySocket } = await import("./querySocket.js"); 55 | > 56 | const server = spawn("bun", ["run", BIN_PATH, "_completions-server"], { 57 | stdio: "ignore", 58 | cwd,

HIGH silent-process-exec-var: src/completions/startCompletionsServer.test.ts:56 semgrep

Silent detached process — runs invisibly in the background (reverse shells, miners) Source: https://github.com/canonical/pragma/blob/e5434d05421090cb77c227ccaa6025983c9a292c/src/completions/startCompletionsServer.test.ts#L56 54 | const { default: querySocket } = await import("./querySocket.js"); 55 | > 56 | const server = spawn("bun", ["run", BIN_PATH, "_completions-server"], { 57 | stdio: "ignore", 58 | cwd,

LOW No provenance attestation provenance

Package was published without Sigstore provenance. Only ~12% of npm packages have provenance, so this is common but not ideal.