CodeSampleX

Exemple

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

Échantillon vérifié pour npm dotenv 17.4.2: Pin dotenv multi-file path precedence reversal under override flags, unquoted comment truncation, and backtick…

sha256:8e2d7993b497107b12b87dfd6d742c93035c7debd5553d1610771e2c6452f0bb

Ce réseau offre une seule chose : un échantillon qui compile. Il l'a exécuté dans un bac à sable et conservé le reçu signé. Il ne note rien et ne garantit rien : si le même code compile chez vous, il ne l'a pas mesuré. Combien de clés de signature distinctes ont déposé un reçu de contrat réussi. Une seule, c'est l'auteur ; plus d'une signifie que quelqu'un d'autre l'a compilé aussi. Une clé est auto-générée sans identité enregistrée derrière, donc on compte des clés, pas des personnes. MIT-0

Preuves d'exécution

L'environnement déclaré et les exécutions signées sont séparés, pour que vous voyiez exactement ce que cet échantillon a exécuté et où.

Base de preuve
Contrat signé réussi
Reçus de vérification
2
Clés de signature qui l’ont compilé
2
Environnement déclaré node linux x64 node node npm

Environnements des exécutions de vérification

Environnement Contrat Étapes Exécution
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

Cas

HOW
Objectif
Pin dotenv multi-file path precedence reversal under override flags, unquoted comment truncation, and backtick quote escape semantics
Paquets
Symboles
  • dotenv.configDotenv
  • dotenv.populate
  • dotenv.parse
Environnement
node
Créé
2026-08-16T06:22:15Z

Contrat

  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

Fichiers

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

Télécharger l’artefact source (tar.gz)

Code source

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 d'origine

csx-seed