@canonical/pragma-cli
CLI and MCP server for Canonical's design system.
Supply chain provenance
Status for the latest visible version.
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
Accepted risks
Findings the reviewer chose to accept rather than block on.
| Source | Rule | Reason | Accepted by | When |
|---|---|---|---|---|
| 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 |
v0.27.0
10 findingsSilent 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,
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,
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,
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,
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(() => {
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(() => {
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(() => {
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(() => {
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(() => {
Package was published without Sigstore provenance. Only ~12% of npm packages have provenance, so this is common but not ideal.
v0.20.0
1 findingPackage was published without Sigstore provenance. Consider requesting the maintainer enable provenance via CI/CD.
v0.18.0
5 findingsSilent 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,
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,
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,
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,
Package was published without Sigstore provenance. Only ~12% of npm packages have provenance, so this is common but not ideal.