registry  /  @geraldmaron/construct  /  1.4.1

@geraldmaron/construct@1.4.1

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

AI Security Review

scanned 9d ago · by lpm-firewall-ai

LPM blocks this version under the AI-agent control-surface policy. The package mutates AI-agent control surfaces during npm postinstall in a consuming project. This is install-time, unconsented staging of agent adapters and settings, even if package-aligned.

Static reason
High-risk behavior combination matched malicious policy.
Trigger
npm install of @geraldmaron/construct in a project with package.json
Impact
Can alter future Claude/Codex/OpenCode/VS Code/Cursor agent behavior for the project without an explicit construct init/install command.
Mechanism
postinstall writes project AI-agent configuration and launches adapter sync
Policy narrative
On installation, npm executes bin/construct-postinstall.mjs. For non-global consumer projects, it calls stageProjectAdapters, creates .construct launchers, runs scripts/sync-specialists.mjs --project, and appends .gitignore entries. The sync script materializes project AI-agent settings, agents, skills, and editor/agent integration files, changing the control surface for future agent sessions without an explicit user command beyond package installation.
Rationale
Source inspection confirms unconsented install-time AI-agent control-surface mutation, which meets the firewall block criterion even though the behavior is mostly documented and package-aligned. Scanner claims of credential exfiltration and embedded secrets were not substantiated as install-time malicious behavior. Product guard normalized a non-low false-positive publish_block request to warn-only suspicious.
Evidence
package.jsonbin/construct-postinstall.mjslib/install/stage-project.mjsscripts/sync-specialists.mjslib/hooks/scan-secrets.mjslib/mcp/tools/memory.mjsplatforms/opencode/sync-config.mjs<project>/.construct/run.mjs<project>/.construct/bootstrap.sh<project>/.construct/bootstrap.ps1<project>/.construct/version<project>/.construct/cache/bin<project>/.claude/settings.json<project>/.claude/agents/*.md<project>/.claude/skills/*/SKILL.md<project>/.codex/**<project>/.github/**<project>/.vscode/settings.json<project>/.gitignore

Decision evidence

public snapshot
AI called this Suspicious at 94.0% confidence as Dangerous Capability with medium false-positive risk.
Evidence for policy block
  • package.json defines postinstall: node ./bin/construct-postinstall.mjs
  • bin/construct-postinstall.mjs runs on npm install and calls stageProjectAdapters for consumer projects
  • lib/install/stage-project.mjs creates .construct files and spawns scripts/sync-specialists.mjs --project
  • scripts/sync-specialists.mjs project mode writes AI-agent control files including .claude/settings.json, .claude/agents, .codex, .github, .vscode, Cursor/OpenCode config
  • bin/construct-postinstall.mjs also appends Construct ignore patterns to the consumer .gitignore
Evidence against
  • Global install path exits with guidance and does not write user-scope AI config
  • CONSTRUCT_SKIP_POSTINSTALL=1 disables the lifecycle hook
  • lib/hooks/scan-secrets.mjs contains detector regexes, not embedded secrets
  • lib/mcp/tools/memory.mjs Rovo/Atlassian network code is user-invoked MCP search, not install-time exfiltration
  • platforms/opencode/sync-config.mjs fetches OpenRouter model metadata only when explicitly run
Behavioral surface
Source
ChildProcessCryptoDynamicRequireEnvironmentVarsEvalFilesystemNetworkShell
Supply chain
HighEntropyStringsUrlStrings
ManifestNo manifest risk signals triggered.
scanned 610 file(s), 4.14 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, 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

17 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
218' - Bootstrap a binary: ./.construct/bootstrap.sh (POSIX)\n' + L219: ' powershell -File .construct/bootstrap.ps1 (Windows)\n' L220: );
High
Shell

Package source references shell execution.

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

Package source invokes a package manager install command at runtime.

lib/deck-export-pptx.mjsView on unpkg · L1323
19L20: const require = createRequire(import.meta.url); L21: 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 · L19
lib/roles/event-bus.mjsView file
22function eventsPath() { L23: if (process.env.CONSTRUCT_ROLES_ROOT) { L24: return join(process.env.CONSTRUCT_ROLES_ROOT, 'events.jsonl'); ... L78: let entry; L79: try { entry = JSON.parse(raw[i]); } catch { continue; } L80: if (since && entry.ts < since) break;
Low
Weak Crypto

Package source references weak cryptographic algorithms.

lib/roles/event-bus.mjsView on unpkg · L22
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
22import { existsSync, mkdirSync, statSync, createWriteStream } from 'node:fs'; L23: import { spawnSync } from 'node:child_process'; L24: import { pipeline } from 'node:stream/promises'; ... L27: L28: const HF_MODEL_BASE_URL = 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main'; L29: const DEFAULT_MODEL = process.env.CONSTRUCT_WHISPER_MODEL || 'base.en'; L30: 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 · L22
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
9import path from 'node:path'; L10: import { spawnSync } from 'node:child_process'; L11: ... L18: function whichBin(name) { L19: const cmd = process.platform === 'win32' ? 'where' : 'which'; L20: const result = spawnSync(cmd, [name], { encoding: 'utf8' }); L21: if (result.status !== 0) return null; L22: return (result.stdout || '').trim().split('\n')[0] || null; L23: } L24: L25: export function resolveLibreOfficeBin(env = process.env) { L26: 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 · L9
lib/document-extract/docling-sidecar.pyView file
path = lib/document-extract/docling-sidecar.py kind = build_helper sizeBytes = 5192 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

Findings

4 Critical7 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
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/roles/event-bus.mjs
LowFilesystem
LowHigh Entropy Strings
LowUrl Strings
LowNested Archive Needs Inspectiontemplates/distribution/construct-reference.docx