CodeSampleX

Ejemplo

dotenv 17.4.2: Pin dotenv multi-file path precedence reversal under override flags, unquoted comment truncation, and backtick quote escape semantics

Muestra verificada para npm dotenv 17.4.2: Pin dotenv multi-file path precedence reversal under override flags, unquoted comment truncation, and backtick…

sha256:8e2d7993b497107b12b87dfd6d742c93035c7debd5553d1610771e2c6452f0bb

Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido. Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas. MIT-0

Evidencia de ejecución

El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.

Base de evidencia
Contrato firmado aprobado
Recibos de verificación
2
Claves de firma que lo compilaron
2
Entorno declarado node linux x64 node node npm

Entornos de las ejecuciones de verificación

Entorno Contrato Etapas Ejecución
node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-16
node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18

Caso

HOW
Objetivo
Pin dotenv multi-file path precedence reversal under override flags, unquoted comment truncation, and backtick quote escape semantics
Paquetes
Símbolos
  • dotenv.configDotenv
  • dotenv.populate
  • dotenv.parse
Entorno
node
Creado
2026-08-16T06:22:15Z

Contrato

  1. assert multi-path array with override: false prioritizes the first file over subsequent files
  2. assert multi-path array with override: true silently reverses precedence causing the last file to overwrite earlier files
  3. assert unquoted # characters are silently truncated as inline comments across values and URLs
  4. assert double quotes expand escape newlines while backticks and single quotes preserve literal escape characters
  5. assert colon delimiter is parsed as a valid key-value separator alongside equal signs
  6. assert dotenv.populate returns only newly assigned keys omitting existing unmutated keys

Archivos

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • src/loader.mjs
  • test/contract.mjs

Descargar el artefacto de código fuente (tar.gz)

Código fuente

NOTES.md
# CodeSampleX Notes: dotenv Multi-Path Precedence & Parsing Semantics

## 1. Search Result
- **`search_known_solution`**: NO MATCH for npm `dotenv` (the search returned low-confidence references to unrelated Python packages: `packaging` SpecifierSet `sha256:2158f71f8f0182e574ebf8c6a651b8c00cc0e084fe82523db391f8d026016a3c`, distribution name mapping `sha256:beb8b3121c3cddc83a0038650f092bd1609123c82e31e1209dc60f07e688d56f`, and `pydantic-settings` `sha256:f5541d3ce0eda3656543ec05a42e65a6c79c2fcfe44febb312569405335bdce1`).

## 2. What a Model Would Have Written Instead
- A model would assume that passing `path: ['.env.local', '.env']` with `override: true` ensures `.env.local` overrides `.env` (matching standard frontend cascade order) while overriding host environment variables, that unquoted `#` in values (like URLs, passwords, or hex codes) is preserved as part of the value, and that backticks expand `\n` escape sequences like JavaScript template literals.

## 3. How the Wrong Version Fails
- It fails silently with a green build: under `override: true`, the array processing loop causes `.env` (the last file) to silently overwrite `.env.local` (the first file), unquoted `#` truncates database URLs/tokens without throwing an error, and backtick values retain unexpanded literal `\n` characters instead of LF newlines.
csx.json
{"case":{"caseId":"case:sha256:2c9e80cef88662eac683a7b9fbc19d74e82ef1a1598a55203adccb3a74fd2ef2","contract":["assert multi-path array with override: false prioritizes the first file over subsequent files","assert multi-path array with override: true silently reverses precedence causing the last file to overwrite earlier files","assert unquoted # characters are silently truncated as inline comments across values and URLs","assert double quotes expand escape newlines while backticks and single quotes preserve literal escape characters","assert colon delimiter is parsed as a valid key-value separator alongside equal signs","assert dotenv.populate returns only newly assigned keys omitting existing unmutated keys"],"goal":"Pin dotenv multi-file path precedence reversal under override flags, unquoted comment truncation, and backtick quote escape semantics","kind":"HOW","packages":["pkg:npm/dotenv@17.4.2"],"schemaVersion":1,"symbols":["dotenv.configDotenv","dotenv.populate","dotenv.parse"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/dotenv@17.4.2"],"schemaVersion":1,"symbols":["dotenv.configDotenv","dotenv.populate","dotenv.parse"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "npm-dotenv-version",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "npm-dotenv-version",
      "version": "1.0.0",
      "license": "ISC",
      "dependencies": {
        "dotenv": "^17.4.2"
      }
    },
    "node_modules/dotenv": {
      "version": "17.4.2",
      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz",
      "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==",
      "license": "BSD-2-Clause",
      "engines": {
        "node": ">=12"
      },
      "funding": {
        "url": "https://dotenvx.com"
      }
    }
  }
}
package.json
{
  "name": "npm-dotenv-version",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "dependencies": {
    "dotenv": "17.4.2"
  }
}
src/loader.mjs
import dotenv from 'dotenv';

/**
 * Loads configuration from multiple env file paths into a target environment object.
 *
 * @param {Object} params
 * @param {string[]} params.paths - Array of file paths to parse in order.
 * @param {Object} [params.targetEnv] - Destination object (defaults to an isolated object).
 * @param {boolean} [params.override=false] - Whether to override existing keys during population.
 * @param {boolean} [params.quiet=true] - Whether to suppress runtime injection logs.
 * @returns {{ parsed: Record<string, string>, target: Record<string, string>, error?: Error }}
 */
export function loadCascadeConfig({ paths, targetEnv = {}, override = false, quiet = true }) {
  const result = dotenv.configDotenv({
    path: paths,
    processEnv: targetEnv,
    override,
    quiet
  });

  return {
    parsed: result.parsed || {},
    target: targetEnv,
    error: result.error
  };
}

/**
 * Parses raw .env file string contents.
 *
 * @param {string} src - Raw env file content.
 * @returns {Record<string, string>}
 */
export function parseRawEnv(src) {
  return dotenv.parse(src);
}

/**
 * Populates a target object with parsed key-value pairs according to override settings.
 *
 * @param {Record<string, string>} target - Destination object to mutate.
 * @param {Record<string, string>} parsed - Key-value pairs to apply.
 * @param {Object} [options]
 * @param {boolean} [options.override=false] - Whether to overwrite existing keys on target.
 * @param {boolean} [options.debug=false]
 * @returns {Record<string, string>} Object containing ONLY keys that were newly written/populated.
 */
export function populateEnv(target, parsed, options = {}) {
  return dotenv.populate(target, parsed, options);
}
test/contract.mjs
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { loadCascadeConfig, parseRawEnv, populateEnv } from '../src/loader.mjs';

function runContract() {
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dotenv-csx-'));

  try {
    const baseEnvPath = path.join(tmpDir, '.env');
    const localEnvPath = path.join(tmpDir, '.env.local');

    fs.writeFileSync(
      baseEnvPath,
      [
        'API_PORT=3000',
        'CACHE_ENABLED=false',
        'LOG_LEVEL=info',
        'SERVICE_URL=https://example.com/api',
      ].join('\n')
    );

    fs.writeFileSync(
      localEnvPath,
      [
        'API_PORT=8080',
        'LOG_LEVEL=debug',
      ].join('\n')
    );

    // =========================================================================
    // 1. Multi-path cascade ordering: override=false vs override=true
    // =========================================================================

    // Case 1A: With override: false (default), the FIRST file in array sets keys;
    // subsequent files cannot overwrite earlier files in parsedAll.
    const initialTargetA = { PRESET_SYSTEM_VAR: 'from_host' };
    const resA = loadCascadeConfig({
      paths: [localEnvPath, baseEnvPath],
      targetEnv: initialTargetA,
      override: false,
    });

    assert.equal(resA.target.API_PORT, '8080', 'First file (.env.local) must win when override: false');
    assert.equal(resA.target.LOG_LEVEL, 'debug', '.env.local LOG_LEVEL must win over .env base');
    assert.equal(resA.target.CACHE_ENABLED, 'false', 'Unset keys in .env.local must fall back to .env');

    // Case 1B: If a key is already preset in targetEnv, override: false protects host env.
    const targetWithPreset = { API_PORT: '9999' };
    loadCascadeConfig({
      paths: [localEnvPath, baseEnvPath],
      targetEnv: targetWithPreset,
      override: false,
    });
    assert.equal(targetWithPreset.API_PORT, '9999', 'Existing host env must NOT be overwritten when override: false');

    // Case 1C: TRAP: When a naive caller sets override: true with [localEnv, baseEnv]
    // expecting .env.local to override .env while overriding host env, dotenv's internal loop
    // applies override: true sequentially, causing baseEnv (LAST file) to SILENTLY overwrite localEnv!
    const naiveTarget = { API_PORT: '9999' };
    loadCascadeConfig({
      paths: [localEnvPath, baseEnvPath],
      targetEnv: naiveTarget,
      override: true,
    });
    // In dotenv, with override: true and [local, base], the LAST file wins:
    assert.equal(naiveTarget.API_PORT, '3000', 'With override: true, the LAST file (.env base) silently overwrites .env.local');

    // Case 1D: The correct way to make .env.local override .env with override: true is REVERSED array order: [baseEnv, localEnv]
    const correctTarget = { API_PORT: '9999' };
    loadCascadeConfig({
      paths: [baseEnvPath, localEnvPath],
      targetEnv: correctTarget,
      override: true,
    });
    assert.equal(correctTarget.API_PORT, '8080', 'With override: true and [base, local], .env.local correctly wins and overrides host env');

    // =========================================================================
    // 2. Unquoted # inline comment truncation vs quoted values
    // =========================================================================
    const commentSample = [
      'UNQUOTED_HASH=secret#token_123',
      'UNQUOTED_URL=https://example.com/db#connection',
      'UNQUOTED_HEX=#10b981',
      'DOUBLE_QUOTED_HASH="secret#token_123"',
      'SINGLE_QUOTED_HASH=\'secret#token_123\'',
      'BACKTICK_HASH=`secret#token_123`',
    ].join('\n');

    const parsedComments = parseRawEnv(commentSample);
    // Unquoted # values are silently truncated as comments
    assert.equal(parsedComments.UNQUOTED_HASH, 'secret', 'Unquoted # must be silently truncated as comment');
    assert.equal(parsedComments.UNQUOTED_URL, 'https://example.com/db', 'Unquoted URL fragment # must be stripped');
    assert.equal(parsedComments.UNQUOTED_HEX, '', 'Unquoted hex starting with # becomes empty string');

    // Quoted values retain # intact
    assert.equal(parsedComments.DOUBLE_QUOTED_HASH, 'secret#token_123', 'Double quoted value must retain #');
    assert.equal(parsedComments.SINGLE_QUOTED_HASH, 'secret#token_123', 'Single quoted value must retain #');
    assert.equal(parsedComments.BACKTICK_HASH, 'secret#token_123', 'Backtick quoted value must retain #');

    // =========================================================================
    // 3. Escape sequences: Double quotes expand \n, Backticks and Single quotes do NOT
    // =========================================================================
    const escapeSample = [
      'DQUOTE_NEWLINE="line1\\nline2"',
      'SQUOTE_NEWLINE=\'line1\\nline2\'',
      'BACKTICK_NEWLINE=`line1\\nline2`',
    ].join('\n');

    const parsedEscapes = parseRawEnv(escapeSample);
    assert.equal(parsedEscapes.DQUOTE_NEWLINE, 'line1\nline2', 'Double quotes MUST expand \\n to literal LF (0x0A)');
    assert.equal(parsedEscapes.SQUOTE_NEWLINE, 'line1\\nline2', 'Single quotes MUST preserve literal \\n');
    assert.equal(parsedEscapes.BACKTICK_NEWLINE, 'line1\\nline2', 'Backticks MUST strip backticks but preserve literal \\n (no expansion)');

    // =========================================================================
    // 4. Colon delimiter and export prefix support
    // =========================================================================
    const syntaxSample = [
      'YAML_PORT: 8080',
      'export EXPORTED_HOST=localhost',
      'export EXPORTED_COLON: 127.0.0.1',
    ].join('\n');

    const parsedSyntax = parseRawEnv(syntaxSample);
    assert.equal(parsedSyntax.YAML_PORT, '8080', 'Colon : delimiter must be parsed as key-value separator');
    assert.equal(parsedSyntax.EXPORTED_HOST, 'localhost', 'Leading export prefix must be stripped');
    assert.equal(parsedSyntax.EXPORTED_COLON, '127.0.0.1', 'Leading export prefix with colon delimiter must work');

    // =========================================================================
    // 5. populate() return value vs target mutation semantics
    // =========================================================================
    const mutationTarget = { EXISTING_KEY: 'original' };
    const populatedReturn = populateEnv(
      mutationTarget,
      { EXISTING_KEY: 'will_be_ignored', FRESH_KEY: 'applied' },
      { override: false }
    );

    assert.equal(mutationTarget.EXISTING_KEY, 'original', 'target.EXISTING_KEY must remain unchanged');
    assert.equal(mutationTarget.FRESH_KEY, 'applied', 'target.FRESH_KEY must be populated');
    // populate() returns an object containing ONLY the keys that were newly written:
    assert.deepEqual(populatedReturn, { FRESH_KEY: 'applied' }, 'populate() must return only freshly written keys, omitting skipped existing keys');

    console.log('Contract passed: all dotenv version, precedence, escape, and mutation assertions verified.');
  } finally {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  }
}

runContract();

Seeder de origen

csx-seed