← Home

@celilo/cli

Celilo — home lab orchestration CLI

2
Versions
MIT
License
No
Install Scripts
Missing
Provenance

Supply chain provenance

Status for the latest visible version.

No SLSA provenance npm registry signatures No source commit

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

peba2

Keywords

celilohomelaborchestrationansibleterraform

Accepted risks

Findings the reviewer chose to accept rather than block on.

SourceRuleReasonAccepted byWhen
semgrep semgrep:env-spread AI (semgrep): Used to pass process.env to subprocess (ansible-lint); standard pattern, not credential exfiltration. ai
semgrep semgrep:etc-passwd-access AI (semgrep): Fires in a test asserting that /etc/passwd path traversal is rejected — the opposite of credential harvesting. ai
semgrep semgrep:hex-decode AI (semgrep): Decoding a master key from hex in encryption/integration test code; legitimate crypto usage. ai
semgrep semgrep:shady-links-raw-ip AI (semgrep): Fires in a validator unit test accepting LAN IPs (192.168.x.x) for a Proxmox homelab URL — expected usage. ai
semgrep semgrep:api-obfuscation-reflect AI (semgrep): Reflect.get used to read a brand symbol on a hook object in tests; not evasion. ai
semgrep semgrep:base64-decode AI (semgrep): Decoding backup data after decryption in backup-restore.ts; legitimate crypto/storage pattern. ai
typosquat typosquat.levenshtein:joi AI (typosquat): Scoped package @celilo/cli for a homelab CLI; edit-distance match to 'joi' is coincidental. ai

Versions (showing 2 of 2)

Version Deps Published
0.3.13 15 / 7
0.2.0 14 / 7

v0.3.13

14 findings
HIGH env-spread: src/ansible/validation.ts:105 semgrep

Spreading entire process.env into an object — may capture all secrets 103 | // Note: ansible-lint exits with non-zero on warnings AND errors 104 | const args = ['ansible-lint', playbookPath]; > 105 | const env = { ...process.env }; 106 | 107 | // If vault password file provided, set environment variable for ansible-vault

HIGH env-spread: src/ansible/validation.ts:177 semgrep

Spreading entire process.env into an object — may capture all secrets 175 | 176 | // Run ansible-playbook --syntax-check > 177 | const env = { ...process.env }; 178 | 179 | // If vault password file provided, set environment variable for ansible-vault

HIGH env-spread: src/cli/commands/system-audit.ts:104 semgrep

Spreading entire process.env into an object — may capture all secrets 102 | // DigitalOcean) fail immediately with "no value for required 103 | // variable" — a false positive, not real drift. > 104 | env: { ...process.env, ...envVars }, 105 | }, 106 | );

HIGH env-spread: src/cli/tui/spawn.ts:84 semgrep

Spreading entire process.env into an object — may capture all secrets 82 | stderr: 'pipe', 83 | stdin: 'ignore', > 84 | env: { ...process.env }, 85 | }); 86 |

HIGH env-spread: src/config/paths.test.ts:10 semgrep

Spreading entire process.env into an object — may capture all secrets 8 | beforeEach(() => { 9 | // Save original environment > 10 | originalEnv = { ...process.env }; 11 | _originalPlatform = process.platform; 12 | });

HIGH etc-passwd-access: src/hooks/executor.test.ts:97 semgrep

Accessing /etc/passwd or /etc/shadow — credential harvesting on Linux 95 | test('rejects path that escapes module directory', () => { 96 | expect(() => { > 97 | resolveHookScript('/modules/namecheap', '../../etc/passwd'); 98 | }).toThrow('Hook script path escapes module directory'); 99 | });

HIGH etc-passwd-access: src/hooks/executor.test.ts:264 semgrep

Accessing /etc/passwd or /etc/shadow — credential harvesting on Linux 262 | 263 | const definition: HookDefinition = { > 264 | script: '../../etc/passwd', 265 | }; 266 |

HIGH etc-passwd-access: src/manifest/validate.test.ts:394 semgrep

Accessing /etc/passwd or /etc/shadow — credential harvesting on Linux 392 | script: build.sh 393 | artifacts: > 394 | - ../../../etc/passwd 395 | `; 396 |

HIGH env-spread: src/module/packaging/build.ts:248 semgrep

Spreading entire process.env into an object — may capture all secrets 246 | // variable expansion would strip shell-only bash variables like 247 | // $STAGE before bash ever sees them. > 248 | const buildEnv = { ...process.env, CELILO_MODULE_SOURCE_DIR: sourceDir }; 249 | try { 250 | if (manifest.build.command) {

HIGH env-spread: src/services/build-stream.ts:37 semgrep

Spreading entire process.env into an object — may capture all secrets 35 | const child = spawn(command, args, { 36 | cwd, > 37 | env: { ...process.env, ...env }, 38 | stdio: [stdin ? 'pipe' : 'ignore', 'pipe', 'pipe'], 39 | shell: true,

HIGH env-spread: src/test-utils/cli-context.ts:191 semgrep

Spreading entire process.env into an object — may capture all secrets 189 | 190 | this.process = spawn('bun', ['run', this.cliPath], { > 191 | env: { 192 | ...process.env, 193 | ...this.env,

HIGH etc-passwd-access: src/utils/shell.test.ts:286 semgrep

Accessing /etc/passwd or /etc/shadow — credential harvesting on Linux 284 | 285 | test('accepts path traversal (..)', () => { > 286 | expect(() => validatePath('../../etc/passwd')).not.toThrow(); 287 | }); 288 |

HIGH etc-passwd-access: src/utils/shell.ts:122 semgrep

Accessing /etc/passwd or /etc/shadow — credential harvesting on Linux 120 | * validatePath('/tmp/test') // OK 121 | * validatePath('') // throws: empty path > 122 | * validatePath('../../../etc/passwd') // OK (relative paths allowed) 123 | * ``` 124 | */

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.2.0

14 findings
HIGH env-spread: src/ansible/validation.ts:105 semgrep

Spreading entire process.env into an object — may capture all secrets 103 | // Note: ansible-lint exits with non-zero on warnings AND errors 104 | const args = ['ansible-lint', playbookPath]; > 105 | const env = { ...process.env }; 106 | 107 | // If vault password file provided, set environment variable for ansible-vault

HIGH env-spread: src/ansible/validation.ts:177 semgrep

Spreading entire process.env into an object — may capture all secrets 175 | 176 | // Run ansible-playbook --syntax-check > 177 | const env = { ...process.env }; 178 | 179 | // If vault password file provided, set environment variable for ansible-vault

HIGH env-spread: src/cli/commands/system-audit.ts:104 semgrep

Spreading entire process.env into an object — may capture all secrets 102 | // DigitalOcean) fail immediately with "no value for required 103 | // variable" — a false positive, not real drift. > 104 | env: { ...process.env, ...envVars }, 105 | }, 106 | );

HIGH env-spread: src/cli/tui/spawn.ts:84 semgrep

Spreading entire process.env into an object — may capture all secrets 82 | stderr: 'pipe', 83 | stdin: 'ignore', > 84 | env: { ...process.env }, 85 | }); 86 |

HIGH env-spread: src/config/paths.test.ts:10 semgrep

Spreading entire process.env into an object — may capture all secrets 8 | beforeEach(() => { 9 | // Save original environment > 10 | originalEnv = { ...process.env }; 11 | _originalPlatform = process.platform; 12 | });

HIGH etc-passwd-access: src/hooks/executor.test.ts:97 semgrep

Accessing /etc/passwd or /etc/shadow — credential harvesting on Linux 95 | test('rejects path that escapes module directory', () => { 96 | expect(() => { > 97 | resolveHookScript('/modules/namecheap', '../../etc/passwd'); 98 | }).toThrow('Hook script path escapes module directory'); 99 | });

HIGH etc-passwd-access: src/hooks/executor.test.ts:264 semgrep

Accessing /etc/passwd or /etc/shadow — credential harvesting on Linux 262 | 263 | const definition: HookDefinition = { > 264 | script: '../../etc/passwd', 265 | }; 266 |

HIGH etc-passwd-access: src/manifest/validate.test.ts:394 semgrep

Accessing /etc/passwd or /etc/shadow — credential harvesting on Linux 392 | script: build.sh 393 | artifacts: > 394 | - ../../../etc/passwd 395 | `; 396 |

HIGH env-spread: src/module/packaging/build.ts:248 semgrep

Spreading entire process.env into an object — may capture all secrets 246 | // variable expansion would strip shell-only bash variables like 247 | // $STAGE before bash ever sees them. > 248 | const buildEnv = { ...process.env, CELILO_MODULE_SOURCE_DIR: sourceDir }; 249 | try { 250 | if (manifest.build.command) {

HIGH env-spread: src/services/build-stream.ts:37 semgrep

Spreading entire process.env into an object — may capture all secrets 35 | const child = spawn(command, args, { 36 | cwd, > 37 | env: { ...process.env, ...env }, 38 | stdio: [stdin ? 'pipe' : 'ignore', 'pipe', 'pipe'], 39 | shell: true,

HIGH env-spread: src/test-utils/cli-context.ts:191 semgrep

Spreading entire process.env into an object — may capture all secrets 189 | 190 | this.process = spawn('bun', ['run', this.cliPath], { > 191 | env: { 192 | ...process.env, 193 | ...this.env,

HIGH etc-passwd-access: src/utils/shell.test.ts:286 semgrep

Accessing /etc/passwd or /etc/shadow — credential harvesting on Linux 284 | 285 | test('accepts path traversal (..)', () => { > 286 | expect(() => validatePath('../../etc/passwd')).not.toThrow(); 287 | }); 288 |

HIGH etc-passwd-access: src/utils/shell.ts:122 semgrep

Accessing /etc/passwd or /etc/shadow — credential harvesting on Linux 120 | * validatePath('/tmp/test') // OK 121 | * validatePath('') // throws: empty path > 122 | * validatePath('../../../etc/passwd') // OK (relative paths allowed) 123 | * ``` 124 | */

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.