CVE-2021-21421 (published 2021-04-01T22:15Z)

node-etsy-client is a NodeJs Etsy ReST API Client. Applications that are using node-etsy-client and reporting client error to the end user will offer api key value too This is fixed in node-etsy-client v0.3.0 and later.

Severity: MEDIUM
CVSS v2 base score: 4.0 (AV:N/AC:L/Au:S/C:P/I:N/A:N)
CVSS v3 base score: 6.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-209
cwe_name: Generation of Error Message Containing Sensitive Information
description: The product generates an error message that includes sensitive information about its environment, users, or associated data.
extended_description: The sensitive information may be valuable information on its own (such as a password), or it may be useful for launching other, more serious attacks. The error message may be created in different ways:
           
             self-generated: the source code explicitly constructs the error message and delivers it
	     externally-generated: the external environment, such as a language interpreter, handles the error and constructs its own message, whose contents are not under direct control by the programmer
	   
	 An attacker may use the contents of error messages to help launch another, more focused attack. For example, an attempt to exploit a path traversal weakness (CWE-22) might yield the full pathname of the installed application. In turn, this could be used to select the proper number of ".." sequences to navigate to the targeted file. An attack using SQL injection (CWE-89) might not initially succeed, but an error message could reveal the malformed query, which would expose query logic and possibly even passwords or other sensitive information used within the query.
         

Fix information:
repo_url: https://github.com/creharmony/node-etsy-client
commit_details: repo_url: https://github.com/creharmony/node-etsy-client
author: Brice Vandeputte
author_date: 2021-03-31 14:17:30+02:00
msg: Fix #17 do not report secret on error, add github action
repository: repo_url: https://github.com/creharmony/node-etsy-client
repo_name: creharmony/node-etsy-client
description: NodeJs Etsy Rest Client
repo_language: JavaScript
owner: creharmony
file_changes: filename: main.yml
change_type: ModificationType.ADD
diff: @@ -0,0 +1,66 @@
+# node-etsy-client continuous integration
+
+name: etsy_client_ci
+
+# Controls when the action will run.
+on:
+  # Triggers the workflow on pull request or push (only for the npmjs branch)
+  push:
+    branches: [ npmjs ]
+  pull_request:
+
+  # Allows you to run this workflow manually from the Actions tab
+  workflow_dispatch:
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+  # This workflow contains a single job called "build"
+  build:
+    # The type of runner that the job will run on
+    runs-on: ubuntu-latest
+
+    env:
+      ETSY_SHOP: fakefakefakefake
+
+    strategy:
+      matrix:
+        # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
+        # node-version: [10.x, 12.x, 14.x, 15.x]
+        node-version: [12.x]
+
+    steps:
+    - name: Checkout code
+      uses: actions/checkout@v2
+
+    - name: Use Node.js ${{ matrix.node-version }}
+      uses: actions/setup-node@v1
+      with:
+        node-version: ${{ matrix.node-version }}
+
+    - name: Cache dependencies
+      uses: actions/cache@v2
+      with:
+        path: |
+          **/node_modules
+        key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
+
+    - name: Install dependencies
+      # - run: npm ci # need package.json.lock
+      run: npm install
+
+    - name: Run ci-tests with code coverage
+      run: npm run ci-test
+
+    - name: Report coverage to the PR
+      continue-on-error: true
+      uses: romeovs/lcov-reporter-action@v0.2.16
+      with:
+        github-token: ${{ secrets.GITHUB_TOKEN }}
+        lcov-file: ./coverage/lcov.info
+
+    - name: Publish NpmJS package
+      if: github.ref == 'refs/heads/npmjs'
+      run: |
+        echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_DEPLOY_TOKEN }}" > .npmrc
+        npm whoami # rely on .npmrc
+        npm publish
\ No newline at end of file

programming_language: Markdown
filename: minor.yml
change_type: ModificationType.ADD
diff: @@ -0,0 +1,34 @@
+# WIP - node-etsy-client minor from 'main' branch to 'npmjs' branch
+name: etsy_client_minor
+on:
+  # Allows you to run this workflow manually from the Actions tab
+  workflow_dispatch:
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node-version: [12.x]
+
+    steps:
+      - name: Git checkout
+        uses: actions/checkout@v2
+        with:
+          ref: 'npmjs'
+          token: ${{ secrets.GH_ACTIONS_TOKEN }}
+
+      - name: Use Node.js ${{ matrix.node-version }}
+        uses: actions/setup-node@v1
+        with:
+          node-version: ${{ matrix.node-version }}
+
+      - name: Minor
+        run: |
+          git config --global user.email  'github-actions[bot]@users.noreply.github.com'
+          git config --global user.name 'github-actions[bot]'
+          git fetch --all
+          git checkout main
+          npm version minor
+          git branch -f npmjs
+          git push origin main npmjs --tags
\ No newline at end of file

programming_language: Batchfile
filename: patch.yml
change_type: ModificationType.ADD
diff: @@ -0,0 +1,34 @@
+# WIP - node-etsy-client patch from 'main' branch to 'npmjs' branch
+name: etsy_client_patch
+on:
+  # Allows you to run this workflow manually from the Actions tab
+  workflow_dispatch:
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node-version: [12.x]
+
+    steps:
+      - name: Git checkout
+        uses: actions/checkout@v2
+        with:
+          ref: 'npmjs'
+          token: ${{ secrets.GH_ACTIONS_TOKEN }}
+
+      - name: Use Node.js ${{ matrix.node-version }}
+        uses: actions/setup-node@v1
+        with:
+          node-version: ${{ matrix.node-version }}
+
+      - name: Patch
+        run: |
+          git config --global user.email  'github-actions[bot]@users.noreply.github.com'
+          git config --global user.name 'github-actions[bot]'
+          git fetch --all
+          git checkout main
+          npm version patch
+          git branch -f npmjs
+          git push origin main npmjs --tags
\ No newline at end of file

programming_language: Batchfile
filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -60,6 +60,9 @@ You're not a dev ? just submit an issue (bug, improvements, questions). Or else:
 git clone https://github.com/creharmony/node-etsy-client.git
 cd node-etsy-client
 npm install
+# play test without etsy endpoint
+npm run test
+# play test with etsy endpoint
 . ./env/initEnv.example.sh
 npm run test
 ```

programming_language: Markdown
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -5,7 +5,9 @@
   "main": "./src/EtsyClient.js",
   "types": "./src/EtsyClient.d.ts",
   "scripts": {
-    "test": "mocha tests/*.test.js"
+    "test": "mocha tests/*.test.js",
+    "cover": "nyc --reporter text --reporter cobertura --reporter html --reporter=lcov --lines 66 mocha --exit --unhandled-rejections=strict tests/*.test.js",
+    "ci-test": "echo linux ci-test&& nyc --reporter text --reporter cobertura --reporter html --reporter=lcov --lines 66 mocha --exit --unhandled-rejections=strict tests/*.test.js --timeout 5000"
   },
   "private": false,
   "author": "Boly38 <boly380@gmail.com>",
@@ -35,6 +37,7 @@
     "chai": "^4.2.0",
     "mocha": "^8.2.1",
     "node-fetch": "^2.6.1",
+    "nyc": "^15.1.0",
     "query-string": "^6.13.7",
     "winston": "^3.3.3"
   },

programming_language: JavaScript
filename: EtsyClient.js
change_type: ModificationType.MODIFY
diff: @@ -93,10 +93,28 @@ class EtsyClient {
          const getQueryString = queryString.stringify(this.getOptions(options));
          fetch(`${this.apiUrl}${endpoint}?${getQueryString}`)
            .then(response => EtsyClient._response(response, resolve, reject))
-           .catch(reject);
+           .catch((fetchError) => {
+             var secureError = {};
+             this.secureErrorAttribute(secureError, fetchError, "message");
+             this.secureErrorAttribute(secureError, fetchError, "reason");
+             this.secureErrorAttribute(secureError, fetchError, "type");
+             this.secureErrorAttribute(secureError, fetchError, "errno");
+             this.secureErrorAttribute(secureError, fetchError, "code");
+             reject(secureError);
+           });
      });
   }
 
+  secureErrorAttribute(secureError, sourceError, attribute) {
+    if (!Object.keys(sourceError).includes(attribute)) {
+      return;
+    }
+    secureError[attribute] = this.secureAttributeValue(sourceError[attribute]);
+  }
+
+  secureAttributeValue(value) {
+    return (value === null || value === undefined) ? null : value.replace(new RegExp(this.apiKey,'g'), "**hidden**");
+  }
 
   getOptions(options) {
     var merged = options ? options : {};

programming_language: TypeScript
filename: unauthenticated_client.test.js
change_type: ModificationType.MODIFY
diff: @@ -2,6 +2,8 @@ const assert = require('assert').strict;
 const expect = require('chai').expect
 const EtsyClient = require('../src/EtsyClient.js');
 
+const FAKE_API_KEY = "ultraSecretRightHere";
+
 if (!process.env.ETSY_API_KEY) {
 
   describe("Test Unauthenticated EtsyClient", function() {
@@ -10,6 +12,17 @@ if (!process.env.ETSY_API_KEY) {
         expect(function () { new EtsyClient() } ).to.throw('apiKey is required');
     });
 
+    it("should not report api key in error case", async function() {
+      const client = new EtsyClient({
+        apiKey:FAKE_API_KEY,
+        apiUrl:"https://IAmNotEtsyEndpoint.com"
+      });
+      const shops = await client.findAllShops()
+        .catch((getShopsError) => {
+          expect(""+getShopsError).to.not.include(FAKE_API_KEY);
+        })
+    });
+
   });
 
 }
\ No newline at end of file

programming_language: TypeScript