Install
$ agentstack add skill-xobotyi-cc-foundry-nodejs Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Dangerous shell/eval execution.
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ● Shell / process execution Used
- ● Environment & secrets Used
- ● Dynamic code execution Used
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Node.js
Respect the event loop. Every blocking operation is a scalability bug.
Node.js rewards async-first, stream-oriented code. If your Node.js code fights the event loop, it's wrong.
References
- Module system — [
${CLAUDE_SKILL_DIR}/references/modules.md]: ESM/CJS comparison tables, file extension rules,
conditional exports patterns
- Event loop — [
${CLAUDE_SKILL_DIR}/references/event-loop.md]: Phase order, execution priority, blocking
operations table, worker pool
- Streams — [
${CLAUDE_SKILL_DIR}/references/streams.md]: Stream types table, pipeline patterns, backpressure
details
- Error handling — [
${CLAUDE_SKILL_DIR}/references/errors.md]: Error categories table, global handlers,
centralized error handling
- Security — [
${CLAUDE_SKILL_DIR}/references/security.md]: Supply chain threats table, HTTP security headers,
process hardening
Module System
- Use ESM. Set
"type": "module"inpackage.json. Use.mjs/.cjsonly when mixing module systems within one
package.
- Use
node:prefix for all built-in imports:import fs from 'node:fs'. Prevents package name collision attacks
and is unambiguous.
- Import
processexplicitly.import process from 'node:process'. Never rely on theprocessglobal — explicit
imports make dependencies visible.
- Define
"exports"inpackage.jsonfor libraries. Encapsulates internals — only paths listed in"exports"are
importable by consumers.
- Imports at module top. No dynamic
import()for statically-known dependencies. - Use
import.meta.dirname/import.meta.filenameinstead of__dirname/__filename. - Use
import.meta.resolve()instead ofrequire.resolve()in ESM. - Always set
"type"explicitly inpackage.json, even in CJS packages — future-proofs the package and helps
tooling.
- Use conditional exports for dual CJS/ESM packages. Order:
types>import>require>default. Always
include "default" as fallback.
- Place
"types"first in conditional exports when publishing TypeScript declarations. - Use
#imports ("imports"inpackage.json) for clean internal paths without../../../. Supports conditional
resolution for platform-specific implementations.
- Keep
"main"alongside"exports"only for backward compatibility with old Node.js or bundlers. Never use
"main" alone for new packages.
- JSON imports in ESM require
with { type: 'json' }attribute. - Use
createRequire()fromnode:moduleonly when you mustrequire()in ESM (e.g., native addons). - CJS interop: default import from CJS always works. Named imports work if CJS uses static export patterns;
otherwise destructure the default.
require()can load synchronous ESM (no top-levelawait). For ESM with top-levelawait, use dynamic
import().
- Self-referencing: a package can import its own exports by name when
"exports"is defined.
File extension rules and ESM vs CJS comparison tables: see ${CLAUDE_SKILL_DIR}/references/modules.md.
Event Loop
Node.js uses a single-threaded event loop for JavaScript and a libuv worker pool for expensive I/O and CPU tasks.
Core Rules
- Never block the event loop. No sync I/O in servers (
readFileSync,execSync,crypto.pbkdf2Sync,
zlib.inflateSync). Sync APIs are acceptable only in CLI scripts, startup code, or build tools.
- Offload CPU-intensive work to
worker_threadsor child processes. For main-thread CPU work, partition into chunks
with setImmediate() between iterations.
- Bound input sizes. Unbounded
JSON.parse,JSON.stringify, regex, or iteration = DoS vector. A 50MB JSON string
blocks the loop for ~2 seconds.
- Avoid vulnerable regex. No nested quantifiers
(a+)*, no overlapping alternations(a|a)*, no backreferences
with repetition. Use safe-regex2, RE2, or indexOf.
- Prefer
setImmediate()over recursiveprocess.nextTick().nextTickstarves I/O if called recursively. Use
nextTick only when you must run before any I/O in the current tick (e.g., emitting events after construction before listeners attach).
- Prefer
queueMicrotask()overprocess.nextTick()for new code — it's cross-platform and web-standard. - Inside I/O callbacks,
setImmediatealways fires beforesetTimeout(fn, 0). Outside I/O, the order is
non-deterministic — do not depend on it.
- Use
AbortControllerfor cancellable timers and operations.
Phase order, execution priority, blocking operations table, and worker pool details: see ${CLAUDE_SKILL_DIR}/references/event-loop.md.
Streams
Streams process data incrementally — use them for large files, HTTP bodies, data transformation pipelines, and proxying. Do not use streams when data is already fully in memory.
Core Rules
- Use
pipeline()fromnode:stream/promisesfor stream composition. Never manual.pipe()chains — they don't
propagate errors or handle cleanup.
- Respect backpressure. Check
.write()return value; wait for'drain'event before continuing.pipeline()
handles this automatically.
- Prefer
Readable.from()for converting iterables/async iterables to streams. - Use async iteration (
for await (const chunk of stream)) as the simplest way to consume readable streams.
Backpressure is handled automatically.
- Use
readlinewithcreateInterfacefor line-by-line file processing. highWaterMarkdefaults to 16 KiB for byte streams, 16 objects for object mode. It's a hint, not a hard limit.- Object mode streams count objects (not bytes) against
highWaterMark. Enable with{ objectMode: true }. - Destroy streams explicitly when you need to abort:
stream.destroy(new Error('msg')). - Custom Readable: prefer async generators with
Readable.from()over class-based_read()implementation unless
you need fine-grained control.
- Custom Transform: implement
_transform(chunk, encoding, callback)and optionally_flush(callback)for
end-of-stream processing.
- Custom Writable: implement
_write(chunk, encoding, callback)and optionally_final(callback)for cleanup
before 'finish' event.
Stream types table and .pipe() pitfalls: see ${CLAUDE_SKILL_DIR}/references/streams.md.
Error Handling
Core Rules
- Use
async/awaitwithtry/catch. No callbacks for new code. - Always
return awaitwhen returning promises fromtryblocks — preserves full stack traces and ensurescatch
fires for rejections.
- Extend
Error. Custom errors must extendError, set acodeproperty for programmatic matching (not message
strings, which change), and set name.
- Use
error.causefor chaining:new Error("context", { cause: originalErr }). The full chain is visible via
util.inspect() and structured loggers.
- Match errors by
error.codeorinstanceof, never by message string. - Register global handlers. Always handle
process.on('unhandledRejection')andprocess.on('uncaughtException').
Log, clean up, exit. Since Node.js 15+, unhandled rejections crash the process by default.
- Never resume after
uncaughtException. The process state is unknown — log, cleanup, exit. - Subscribe to
'error'events on all EventEmitters and streams. An unhandled'error'event crashes the process.
pipeline() handles stream errors automatically.
- Handle
process.on('warning')for non-fatal process warnings (deprecations, memory leaks, experimental features). - Use centralized error handlers. Don't scatter error handling across every middleware. Use a single error handler
that maps error types to HTTP responses without leaking internals.
- Handle once: log OR throw, not both.
catch (e) { log(e); throw e }causes duplicate logging. - Never swallow errors in event handlers — always re-emit or log.
Error categories table and operational vs programmer error strategies: see ${CLAUDE_SKILL_DIR}/references/errors.md.
Process Lifecycle
- Graceful shutdown. Handle
SIGTERM/SIGINT: stop accepting connections, wait for in-flight requests (with
timeout), close DB pools, flush logs, then process.exit(0). Force-exit on timeout.
- Log to stdout/stderr. Let infrastructure (Docker, systemd) handle log routing. Use structured JSON logging (pino,
winston) in production.
- Set
NODE_ENV=productionin production. It enables framework optimizations and disables debug output. - Use
npm ciin CI/production. Nevernpm install— it ignores lockfile mismatches.
Security
Input Validation
- Validate everything from outside — request bodies, query params, headers, file uploads, environment variables from
untrusted sources. Use schema validation (zod, ajv, typebox).
- Limit request body size at the HTTP layer before parsing. Set per-content-type limits. Unbounded payloads exhaust
memory.
- Validate
Content-Lengthheader before reading the body. - Use streaming JSON parsers (
stream-json,@streamparser/json) for very large JSON payloads.
HTTP Security
- Configure server timeouts. Defaults are too permissive. Set
headersTimeout,requestTimeout,timeout,
keepAliveTimeout, maxRequestsPerSocket.
- Use security headers (via
helmetor equivalent):Strict-Transport-Security,X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Content-Security-Policy.
- Delegate TLS/gzip to reverse proxy. Node.js should not terminate TLS or compress responses in production — let
nginx/HAProxy/cloud LB handle TLS termination, compression, rate limiting, and WAF rules.
Secrets & Process Hardening
- Never hardcode secrets in source code. Use environment variables from secure vaults.
- Never commit
.envfiles — add to.gitignore. - Use
crypto.timingSafeEqual()for secret comparison (prevents timing attacks). - Use
crypto.scrypt()orcrypto.pbkdf2()(async versions) for password hashing. - Avoid shell injection. Never use
exec()with user-controlled strings. UseexecFile()orspawn()with
argument arrays.
- Never use
eval(),new Function(), or dynamicrequire()with user input. - Run as non-root in Docker — use a dedicated user.
- Limit V8 heap with
--max-old-space-sizeto prevent memory exhaustion.
Supply chain threats table, dependency auditing, and security checklist: see ${CLAUDE_SKILL_DIR}/references/security.md.
Application
When writing Node.js code:
- Apply all conventions silently — don't narrate each rule being followed.
- If an existing codebase contradicts a convention, follow the codebase and flag the divergence once.
- Prefer
node:fs/promisesover callback-basednode:fs. - Prefer
node:stream/promisesfor pipeline operations.
When reviewing Node.js code:
- Cite the specific violation and show the fix inline.
- Don't lecture or quote the rule — state what's wrong and how to fix it.
Integration
The javascript skill governs language choices; this skill governs Node.js runtime decisions. Activate typescript alongside both when working with TypeScript.
Respect the event loop. When in doubt, make it async.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: xobotyi
- Source: xobotyi/cc-foundry
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.