registry  /  @geraldmaron/construct  /  1.5.5

@geraldmaron/construct@1.5.5

Construct — agent orchestration layer for OpenCode, Claude Code, and other coding surfaces

AI Security Review

scanned 2h ago · by lpm-firewall-ai

LPM blocks this version under the AI-agent control-surface policy. npm postinstall mutates the consuming project's AI-agent control surfaces without an explicit user command. It stages a launcher and synchronizes bundled agent configuration into project directories.

Static reason
High-risk behavior combination matched malicious policy.; source matched previously finalized malicious package; routed for review
Trigger
Installing @geraldmaron/construct as a dependency with npm lifecycle scripts enabled.
Impact
A dependency install can alter project-local Claude/Codex/GitHub/MCP agent behavior and repository ignore rules.
Mechanism
postinstall-driven project AI-agent adapter and configuration injection
Policy narrative
On npm installation, the package executes its postinstall hook in the consumer project. The hook stages `.construct` launchers, launches `sync-specialists.mjs --project`, and adds ignore rules. The sync script targets multiple project AI-agent control surfaces, including `.claude`, `.codex`, `.github`, and `.mcp.json`, thereby changing agent behavior/configuration as a side effect of dependency installation rather than an explicit setup command.
Rationale
Source inspection confirms unconsented postinstall mutation of foreign project AI-agent control surfaces. This meets the block boundary regardless of benign-looking ancillary features and opt-out guidance.
Evidence
package.jsonbin/construct-postinstall.mjslib/install/stage-project.mjsscripts/sync-specialists.mjslib/mcp/tools/memory.mjsplatforms/opencode/sync-config.mjs<consumer-project>/.construct/run.mjs<consumer-project>/.construct/bootstrap.sh<consumer-project>/.construct/bootstrap.ps1<consumer-project>/.construct/version<consumer-project>/.construct/stage-state.json<consumer-project>/.construct/install-manifest.json<consumer-project>/.claude/<consumer-project>/.codex/<consumer-project>/.github/<consumer-project>/.mcp.json<consumer-project>/.gitignore

Decision evidence

public snapshot
AI called this Malicious at 96.0% confidence as Malware with low false-positive risk.
Evidence for policy block
  • package.json defines postinstall: node ./bin/construct-postinstall.mjs.
  • bin/construct-postinstall.mjs runs automatically for consumer project installs and calls stageProjectAdapters.
  • lib/install/stage-project.mjs copies launchers into <project>/.construct and invokes scripts/sync-specialists.mjs --project.
  • scripts/sync-specialists.mjs documents and writes project AI-agent surfaces including .claude, .codex, .github, and .mcp.json.
  • postinstall also appends Construct patterns to the consumer .gitignore and records an install manifest.
Evidence against
  • Global installs exit after printing opt-in guidance; home-directory agent setup is not performed by postinstall.
  • CONSTRUCT_SKIP_POSTINSTALL=1 disables the hook, but it is not the default.
  • No credential exfiltration was confirmed in lib/mcp/tools/memory.mjs; its memory functions are local storage/session operations.
  • Network hint for platforms/opencode/sync-config.mjs is an explicit model-sync command, not invoked by postinstall.
Behavioral surface
Source
ChildProcessCryptoDynamicRequireEnvironmentVarsEvalFilesystemNetworkShell
Supply chain
HighEntropyStringsUrlStrings
ManifestNo manifest risk signals triggered.
scanned 781 file(s), 5.59 MB of source, external domains: 127.0.0.1, api.anthropic.com, api.atlassian.com, api.github.com, api.githubcopilot.com, api.linear.app, api.openai.com, app.slack.com, astral.sh, d2lang.com, developer.1password.com, docs.docker.com, example.com, fonts.googleapis.com, generativelanguage.googleapis.com, github.com, graphviz.org, huggingface.co, json-schema.org, nodejs.org, ollama.com, opencode.ai, openrouter.ai, pandoc.org, raw.githubusercontent.com, schemas.openxmlformats.org, slack.com, www.apple.com, www.libreoffice.org, your-domain.atlassian.net

Source & flagged code

22 flagged · loading source
package.jsonView file
scripts.postinstall = node ./bin/construct-postinstall.mjs
High
Install Time Lifecycle Scripts

Package defines install-time lifecycle scripts.

package.jsonView on unpkg
lib/hooks/scan-secrets.mjsView file
47patternName = private_key_rsa severity = critical line = 47 matchedText = { name: .../ },
Critical
Critical Secret

Package contains a critical-looking secret pattern.

lib/hooks/scan-secrets.mjsView on unpkg · L47
47patternName = private_key_rsa severity = critical line = 47 matchedText = { name: .../ },
Critical
Secret Pattern

RSA private key in lib/hooks/scan-secrets.mjs

lib/hooks/scan-secrets.mjsView on unpkg · L47
51patternName = private_key_openssh severity = critical line = 51 matchedText = { name: .../ },
Critical
Secret Pattern

OpenSSH private key in lib/hooks/scan-secrets.mjs

lib/hooks/scan-secrets.mjsView on unpkg · L51
platforms/opencode/sync-config.mjsView file
6import fs from "node:fs"; L7: import { spawnSync } from "node:child_process"; L8: import { readOpenCodeConfig, writeOpenCodeConfig, findOpenCodeConfigPath } from "../../lib/opencode-config.mjs";
High
Child Process

Package source references child process execution.

platforms/opencode/sync-config.mjsView on unpkg · L6
templates/distribution/run.mjsView file
221' - Bootstrap a binary: ./.construct/bootstrap.sh (POSIX)\n' + L222: ' powershell -File .construct/bootstrap.ps1 (Windows)\n' L223: );
High
Shell

Package source references shell execution.

templates/distribution/run.mjsView on unpkg · L221
lib/deck-export-pptx.mjsView file
1345missing: ['pptxgenjs'], L1346: message: 'Install pptxgenjs to enable PPTX export (`npm install pptxgenjs` in the Construct package).', L1347: }; ... L1350: const payload = Buffer.from(JSON.stringify(opts)).toString('base64'); L1351: const child = spawnSync(process.execPath, [ L1352: '--input-type=module',
High
Runtime Package Install

Package source invokes a package manager install command at runtime.

lib/deck-export-pptx.mjsView on unpkg · L1345
matchType = normalized_sha256 matchedPackage = @geraldmaron/construct@1.5.4 matchedPath = lib/deck-export-pptx.mjs matchedIdentity = npm:QGdlcmFsZG1hcm9uL2NvbnN0cnVjdA:1.5.4 similarity = 1.000 summary = normalized source hash matched finalized malicious source
High
Known Malware Source Similarity

Source file is highly similar to a previously finalized malicious package; route for source-aware review.

lib/deck-export-pptx.mjsView on unpkg
20L21: const require = createRequire(import.meta.url); L22: const MODULE_URL = pathToFileURL(fileURLToPath(import.meta.url)).href;
Medium
Dynamic Require

Package source references dynamic require/import behavior.

lib/deck-export-pptx.mjsView on unpkg · L20
lib/document-assets.mjsView file
21import crypto from 'node:crypto'; L22: import { spawnSync } from 'node:child_process'; L23: L24: const REMOTE_REF = /^(?:https?:|data:)/i; L25: const HTTP_REF = /^https?:/i; ... L38: } catch { return null; } L39: const script = 'const u=process.argv[1],d=process.argv[2];fetch(u).then(r=>{if(!r.ok)process.exit(2);return r.arrayBuffer();}).then(b=>{require("node:fs").writeFileSync(d,Buffer.fr... L40: const res = spawnSync(process.execPath, ['-e', script, url, dest], { timeout: 20000 }); ... L114: L115: export function buildAssetManifest(doc, { baseDir = process.cwd() } = {}) { L116: const blocks = (doc?.sections || []).flatMap((section) => section.blocks || []);
Low
Weak Crypto

Package source references weak cryptographic algorithms.

lib/document-assets.mjsView on unpkg · L21
lib/embed/supervision.mjsView file
9* Linux — systemd user unit at ~/.config/systemd/user/construct-embed.service L10: * Windows — Task Scheduler (schtasks) entry named "Construct.Embed" L11: * ... L21: import path from 'node:path'; L22: import { spawnSync } from 'node:child_process'; L23: ... L25: L26: const HOME = os.homedir(); L27: const PLATFORM = process.platform; L28: const NODE_BIN = process.execPath; ... L34: const result = spawnSync('which', ['construct'], { encoding: 'utf8' }); L35: return result.status === 0 ? result.stdout.trim() : 'construct';
Medium
Install Persistence

Source writes installer persistence such as shell profile or service configuration.

lib/embed/supervision.mjsView on unpkg · L9
lib/runtime/whisper-bootstrap.mjsView file
23import { existsSync, mkdirSync, statSync, createWriteStream } from 'node:fs'; L24: import { spawnSync } from 'node:child_process'; L25: import { pipeline } from 'node:stream/promises'; ... L30: L31: const HF_MODEL_BASE_URL = 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main'; L32: const DEFAULT_MODEL = process.env.CONSTRUCT_WHISPER_MODEL || 'base.en'; L33: const KNOWN_MODELS = new Set(['tiny', 'tiny.en', 'base', 'base.en', 'small', 'small.en', 'medium', 'medium.en', 'large-v3', 'large-v3-turbo']);
High
Same File Env Network Execution

A single source file combines environment access, network access, and code or shell execution; review context before blocking.

lib/runtime/whisper-bootstrap.mjsView on unpkg · L23
lib/mcp/tools/memory.mjsView file
14export async function memorySearch(args) { L15: const cwd = args.cwd ? resolve(String(args.cwd)) : process.cwd(); L16: const project = args.project || null; ... L28: try { L29: const hybrid = await buildHybridSearchResultsAsync(cwd, query, { limit, env: process.env }); L30: hybridResults = (hybrid.results || []).map((r) => ({ ... L177: const cloudId = process.env.ATLASSIAN_CLOUD_ID || 'hashicorp.atlassian.net'; L178: const res = await fetch(`https://api.atlassian.com/rovo/v1/search`, { L179: method: 'POST', ... L183: }, L184: body: JSON.stringify({ query, limit: topK }), L185: });
Critical
Credential Exfiltration

Source appears to send environment or credential material to an external endpoint.

lib/mcp/tools/memory.mjsView on unpkg · L14
lib/libreoffice-export.mjsView file
10import path from 'node:path'; L11: import { spawnSync } from 'node:child_process'; L12: ... L19: function whichBin(name, env) { L20: const cmd = process.platform === 'win32' ? 'where' : 'which'; L21: const result = spawnSync(cmd, [name], { encoding: 'utf8', env }); L22: if (result.status !== 0) return null; L23: return (result.stdout || '').trim().split('\n')[0] || null; L24: } L25: L26: export function resolveLibreOfficeBin(env = process.env, { existsSyncFn = fs.existsSync } = {}) { L27: const fromEnv = (env.CONSTRUCT_LIBREOFFICE_BIN || env.SOFFICE_BIN || '').trim();
High
Sandbox Evasion Gated Capability

Source gates dangerous network, credential, or execution behavior behind CI, host, platform, time, or geo fingerprint checks.

lib/libreoffice-export.mjsView on unpkg · L10
lib/document-extract/docling-sidecar.pyView file
path = lib/document-extract/docling-sidecar.py kind = build_helper sizeBytes = 6406 magicHex = [redacted]
Medium
Ships Build Helper

Package ships non-JavaScript build or shell helper files.

lib/document-extract/docling-sidecar.pyView on unpkg
templates/distribution/construct-reference.docxView file
path = [redacted]-reference.docx kind = high_entropy_blob sizeBytes = 11466 magicHex = [redacted]
High
Ships High Entropy Blob

Package ships high-entropy non-source blobs.

templates/distribution/construct-reference.docxView on unpkg
path = [redacted]-reference.docx kind = compressed_blob sizeBytes = 11466 magicHex = [redacted]
Medium
Ships Compressed Blob

Package ships compressed or archive-like blobs.

templates/distribution/construct-reference.docxView on unpkg
path = [redacted]-reference.docx kind = nested_archive_needs_inspection sizeBytes = 11466 magicHex = [redacted]
Low
Nested Archive Needs Inspection

Package ships a nested archive or MCP bundle that was inventoried but not recursively analyzed.

templates/distribution/construct-reference.docxView on unpkg
lib/doctor/watchers/mcp-protocol.mjsView file
matchType = normalized_sha256 matchedPackage = @geraldmaron/construct@1.5.4 matchedPath = lib/doctor/watchers/mcp-protocol.mjs matchedIdentity = npm:QGdlcmFsZG1hcm9uL2NvbnN0cnVjdA:1.5.4 similarity = 1.000 summary = normalized source hash matched finalized malicious source
High
Known Malware Source Similarity

Source file is highly similar to a previously finalized malicious package; route for source-aware review.

lib/doctor/watchers/mcp-protocol.mjsView on unpkg
lib/init-unified.mjsView file
matchType = normalized_sha256 matchedPackage = @geraldmaron/construct@1.5.4 matchedPath = lib/init-unified.mjs matchedIdentity = npm:QGdlcmFsZG1hcm9uL2NvbnN0cnVjdA:1.5.4 similarity = 1.000 summary = normalized source hash matched finalized malicious source
High
Known Malware Source Similarity

Source file is highly similar to a previously finalized malicious package; route for source-aware review.

lib/init-unified.mjsView on unpkg
lib/mcp/memory-bridge.mjsView file
matchType = normalized_sha256 matchedPackage = @geraldmaron/construct@1.5.4 matchedPath = lib/mcp/memory-bridge.mjs matchedIdentity = npm:QGdlcmFsZG1hcm9uL2NvbnN0cnVjdA:1.5.4 similarity = 1.000 summary = normalized source hash matched finalized malicious source
High
Known Malware Source Similarity

Source file is highly similar to a previously finalized malicious package; route for source-aware review.

lib/mcp/memory-bridge.mjsView on unpkg
lib/mcp/tools/telemetry.mjsView file
matchType = normalized_sha256 matchedPackage = @geraldmaron/construct@1.5.4 matchedPath = lib/mcp/tools/telemetry.mjs matchedIdentity = npm:QGdlcmFsZG1hcm9uL2NvbnN0cnVjdA:1.5.4 similarity = 1.000 summary = normalized source hash matched finalized malicious source
High
Known Malware Source Similarity

Source file is highly similar to a previously finalized malicious package; route for source-aware review.

lib/mcp/tools/telemetry.mjsView on unpkg

Findings

4 Critical12 High7 Medium7 Low
CriticalCritical Secretlib/hooks/scan-secrets.mjs
CriticalCredential Exfiltrationlib/mcp/tools/memory.mjs
CriticalSecret Patternlib/hooks/scan-secrets.mjs
CriticalSecret Patternlib/hooks/scan-secrets.mjs
HighInstall Time Lifecycle Scriptspackage.json
HighChild Processplatforms/opencode/sync-config.mjs
HighShelltemplates/distribution/run.mjs
HighSame File Env Network Executionlib/runtime/whisper-bootstrap.mjs
HighSandbox Evasion Gated Capabilitylib/libreoffice-export.mjs
HighRuntime Package Installlib/deck-export-pptx.mjs
HighShips High Entropy Blobtemplates/distribution/construct-reference.docx
HighKnown Malware Source Similaritylib/deck-export-pptx.mjs
HighKnown Malware Source Similaritylib/doctor/watchers/mcp-protocol.mjs
HighKnown Malware Source Similaritylib/init-unified.mjs
HighKnown Malware Source Similaritylib/mcp/memory-bridge.mjs
HighKnown Malware Source Similaritylib/mcp/tools/telemetry.mjs
MediumDynamic Requirelib/deck-export-pptx.mjs
MediumNetwork
MediumEnvironment Vars
MediumInstall Persistencelib/embed/supervision.mjs
MediumShips Build Helperlib/document-extract/docling-sidecar.py
MediumShips Compressed Blobtemplates/distribution/construct-reference.docx
MediumStructural Risk Force Deep Review
LowScripts Present
LowEval
LowWeak Cryptolib/document-assets.mjs
LowFilesystem
LowHigh Entropy Strings
LowUrl Strings
LowNested Archive Needs Inspectiontemplates/distribution/construct-reference.docx