Common Weakness Enumeration

CWE-215

Allowed

Insertion of Sensitive Information Into Debugging Code

Abstraction: Base · Status: Draft

The product inserts sensitive information into debugging code, which could expose this information if the debugging code is not disabled in production.

31 vulnerabilities reference this CWE, most recent first.

GHSA-XVP7-8VM8-XFXX

Vulnerability from github – Published: 2025-10-20 17:55 – Updated: 2026-06-08 23:28
VLAI
Summary
Actual Sync-server Gocardless service is logging sensitive data including bearer tokens and account numbers
Details

Summary

The GoCardless components in Actualbudget in are logging responses to STDOUT in a parsed format using console.logand console.debug (Which in this version of node is an alias for console.log). This is exposing sensitive information in log files including, but not limited to:

  • Gocardless bearer tokens.
  • Account IBAN and Bank Account numbers.
  • PII of the account holder.
  • Transaction details (Payee bank information, Recipient account numbers, Transaction IDs)...

Details

Whenever GoCardless responds to a request, the payload is printed to the debug log: https://github.com/actualbudget/actual/blob/36c40d90d2fe09eb1f25a6e2f77f6dd40638b267/packages/sync-server/src/app-gocardless/banks/integration-bank.js#L25-L27

This in turn logs the following information to Docker (all values removed here. These fields are possibly dependent on what is returned by each institution so may differ):

{
  "account": {
    "resourceId": "",
    "iban": "",
    "bban": "",
    "currency": "",
    "name": "<full legal name in the bank>",
    "product": "",
    "status": "",
    "bic": "",
    "usage": "",
    "id": "",
    "created": "",
    "last_accessed": "",
    "institution_id": "",
    "owner_name": "",
    "institution": {
      "id": "",
      "name": "",
      "bic": "",
      "transaction_total_days": "",
      "countries": [
        ""
      ],
      "logo": "",
      "max_access_valid_for_days": "",
      "supported_features": [
        "",
        "",
        ""
      ],
      "identification_codes": []
    }
  }
}

https://github.com/actualbudget/actual/blob/36c40d90d2fe09eb1f25a6e2f77f6dd40638b267/packages/sync-server/src/app-gocardless/banks/integration-bank.js#L83-L85

This is the first of the 10 transactions:

{
  "top10Transactions": [{
    "transactionId": "",
    "entryReference": "",
    "bookingDate": "",
    "valueDate": "",
    "transactionAmount": {
      "amount": "",
      "currency": ""
    },
    "creditorName": "",
    "creditorAccount": {
      "bban": ""
    },
    "debtorName": "",
    "debtorAccount": {
      "bban": ""
    },
    "remittanceInformationUnstructured": "",
    "remittanceInformationStructuredArray": [
      {"reference": "", "referenceType": ""}
    ],
    "additionalInformation": "",
    "proprietaryBankTransactionCode": "",
    "debtorAgent": "",
    "internalTransactionId": "",
    "payeeName": "",
    "date": ""
  }]
}

Additionally, in the error handling for GoCardless, there is a catch all for unclassified errors that prints the entire stack trace to the console.

https://github.com/actualbudget/actual/blob/36c40d90d2fe09eb1f25a6e2f77f6dd40638b267/packages/sync-server/src/app-gocardless/app-gocardless.js#L263-L264

Our bank was offline today for maintenance which threw a 503 error from Gocardless. The entire response payload was dumped to console, which includes the Bearer tokens for accessing GoCardless:

Something went wrong ServiceError: Institution service unavailable
    at handleGoCardlessError (file:///app/src/app-gocardless/services/gocardless-service.js:59:13)
    at Object.getTransactions (file:///app/src/app-gocardless/services/gocardless-service.js:530:7)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Object.getNormalizedTransactions (file:///app/src/app-gocardless/services/gocardless-service.js:267:26)
    at async file:///app/src/app-gocardless/app-gocardless.js:186:13 {
  details: h [AxiosError]: Request failed with status code 503
      at te (file:///app/node_modules/nordigen-node/dist/index.esm.js:13:914)
      at IncomingMessage.<anonymous> (file:///app/node_modules/nordigen-node/dist/index.esm.js:17:16315)
      at IncomingMessage.emit (node:events:529:35)
      at endReadableNT (node:internal/streams/readable:1400:12)
      at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
    code: 'ERR_BAD_RESPONSE',
    config: {
      transitional: {
        silentJSONParsing: true,
        forcedJSONParsing: true,
        clarifyTimeoutError: false
      },
      adapter: [ 'xhr', 'http' ],
      transformRequest: [ [Function (anonymous)] ],
      transformResponse: [ [Function (anonymous)] ],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      env: {
        FormData: [Function: _] {
          LINE_BREAK: '\r\n',
          DEFAULT_CONTENT_TYPE: 'application/octet-stream'
        },
        Blob: [class Blob]
      },
      validateStatus: [Function: validateStatus],
      headers: T [AxiosHeaders] {
        Accept: 'application/json',
        'Content-Type': 'application/json',
        'User-Agent': 'Nordigen-Node-v2',
        'Authorization': 'Bearer eyJ0eXAi... (the full token is in the response)',
        'Accept-Encoding': 'gzip, compress, deflate, br'
      },
      method: 'get',
      url: URL {
        href: 'https://bankaccountdata.gocardless.com/api/v2/accounts/<Account id Was Here>?date_from=2024-12-22',
        origin: 'https://bankaccountdata.gocardless.com',
        protocol: 'https:',
        username: '',
        password: '',
        host: 'bankaccountdata.gocardless.com',
        hostname: 'bankaccountdata.gocardless.com',
        port: '',
        pathname: '/api/v2/accounts/<Account id Was Here>/transactions',
        search: '?date_from=2024-12-22',
        searchParams: URLSearchParams { 'date_from' => '2024-12-22' },
        hash: ''
      },
      data: undefined
    },

And quite a few pages more.

PoC

  • Setup an Actualbudget server inside of Docker. In this instance I was using the Docker Compose script posted in the repository: https://github.com/actualbudget/actual/blob/master/packages/sync-server/docker-compose.yml
  • Link a gocardless account to Actualbudget and sync a bank account
  • Observe in the container using docker logs actual-actual_server-1 -f that sensitive details are logged to the console and ingested by docker.

Impact

Information disclosure. The services are available both on-premises and in environments that are not under the control of the end user, such as third-party providers who offer this application as a managed solution.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 25.10.0"
      },
      "package": {
        "ecosystem": "npm",
        "name": "@actual-app/sync-server"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "25.11.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-209",
      "CWE-215",
      "CWE-219"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-10-20T17:55:59Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\nThe GoCardless components in Actualbudget in are logging responses to STDOUT in a parsed format using `console.log`and `console.debug` (Which in this version of node is an alias for `console.log`). This is exposing sensitive information in log files including, but not limited to:\n\n- Gocardless bearer tokens.\n- Account IBAN and Bank Account numbers.\n- PII of the account holder.\n- Transaction details (Payee bank information, Recipient account numbers, Transaction IDs)...\n\n### Details\n\nWhenever GoCardless responds to a request, the payload is printed to the debug log: \nhttps://github.com/actualbudget/actual/blob/36c40d90d2fe09eb1f25a6e2f77f6dd40638b267/packages/sync-server/src/app-gocardless/banks/integration-bank.js#L25-L27\n\nThis in turn logs the following information to Docker (all values removed here. These fields are possibly dependent on what is returned by each institution so may differ):\n\n```json\n{\n  \"account\": {\n    \"resourceId\": \"\",\n    \"iban\": \"\",\n    \"bban\": \"\",\n    \"currency\": \"\",\n    \"name\": \"\u003cfull legal name in the bank\u003e\",\n    \"product\": \"\",\n    \"status\": \"\",\n    \"bic\": \"\",\n    \"usage\": \"\",\n    \"id\": \"\",\n    \"created\": \"\",\n    \"last_accessed\": \"\",\n    \"institution_id\": \"\",\n    \"owner_name\": \"\",\n    \"institution\": {\n      \"id\": \"\",\n      \"name\": \"\",\n      \"bic\": \"\",\n      \"transaction_total_days\": \"\",\n      \"countries\": [\n        \"\"\n      ],\n      \"logo\": \"\",\n      \"max_access_valid_for_days\": \"\",\n      \"supported_features\": [\n        \"\",\n        \"\",\n        \"\"\n      ],\n      \"identification_codes\": []\n    }\n  }\n}\n```\n\nhttps://github.com/actualbudget/actual/blob/36c40d90d2fe09eb1f25a6e2f77f6dd40638b267/packages/sync-server/src/app-gocardless/banks/integration-bank.js#L83-L85\n\nThis is the first of the 10 transactions:\n```json\n{\n  \"top10Transactions\": [{\n    \"transactionId\": \"\",\n    \"entryReference\": \"\",\n    \"bookingDate\": \"\",\n    \"valueDate\": \"\",\n    \"transactionAmount\": {\n      \"amount\": \"\",\n      \"currency\": \"\"\n    },\n    \"creditorName\": \"\",\n    \"creditorAccount\": {\n      \"bban\": \"\"\n    },\n    \"debtorName\": \"\",\n    \"debtorAccount\": {\n      \"bban\": \"\"\n    },\n    \"remittanceInformationUnstructured\": \"\",\n    \"remittanceInformationStructuredArray\": [\n      {\"reference\": \"\", \"referenceType\": \"\"}\n    ],\n    \"additionalInformation\": \"\",\n    \"proprietaryBankTransactionCode\": \"\",\n    \"debtorAgent\": \"\",\n    \"internalTransactionId\": \"\",\n    \"payeeName\": \"\",\n    \"date\": \"\"\n  }]\n}\n```\n\nAdditionally, in the error handling for GoCardless, there is a catch all for unclassified errors that prints the entire stack trace to the console.\n\nhttps://github.com/actualbudget/actual/blob/36c40d90d2fe09eb1f25a6e2f77f6dd40638b267/packages/sync-server/src/app-gocardless/app-gocardless.js#L263-L264\n\nOur bank was offline today for maintenance which threw a 503 error from Gocardless. The entire response payload was dumped to console, which includes the Bearer tokens for accessing GoCardless:\n\n```java\nSomething went wrong ServiceError: Institution service unavailable\n    at handleGoCardlessError (file:///app/src/app-gocardless/services/gocardless-service.js:59:13)\n    at Object.getTransactions (file:///app/src/app-gocardless/services/gocardless-service.js:530:7)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async Object.getNormalizedTransactions (file:///app/src/app-gocardless/services/gocardless-service.js:267:26)\n    at async file:///app/src/app-gocardless/app-gocardless.js:186:13 {\n  details: h [AxiosError]: Request failed with status code 503\n      at te (file:///app/node_modules/nordigen-node/dist/index.esm.js:13:914)\n      at IncomingMessage.\u003canonymous\u003e (file:///app/node_modules/nordigen-node/dist/index.esm.js:17:16315)\n      at IncomingMessage.emit (node:events:529:35)\n      at endReadableNT (node:internal/streams/readable:1400:12)\n      at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {\n    code: \u0027ERR_BAD_RESPONSE\u0027,\n    config: {\n      transitional: {\n        silentJSONParsing: true,\n        forcedJSONParsing: true,\n        clarifyTimeoutError: false\n      },\n      adapter: [ \u0027xhr\u0027, \u0027http\u0027 ],\n      transformRequest: [ [Function (anonymous)] ],\n      transformResponse: [ [Function (anonymous)] ],\n      timeout: 0,\n      xsrfCookieName: \u0027XSRF-TOKEN\u0027,\n      xsrfHeaderName: \u0027X-XSRF-TOKEN\u0027,\n      maxContentLength: -1,\n      maxBodyLength: -1,\n      env: {\n        FormData: [Function: _] {\n          LINE_BREAK: \u0027\\r\\n\u0027,\n          DEFAULT_CONTENT_TYPE: \u0027application/octet-stream\u0027\n        },\n        Blob: [class Blob]\n      },\n      validateStatus: [Function: validateStatus],\n      headers: T [AxiosHeaders] {\n        Accept: \u0027application/json\u0027,\n        \u0027Content-Type\u0027: \u0027application/json\u0027,\n        \u0027User-Agent\u0027: \u0027Nordigen-Node-v2\u0027,\n        \u0027Authorization\u0027: \u0027Bearer eyJ0eXAi... (the full token is in the response)\u0027,\n        \u0027Accept-Encoding\u0027: \u0027gzip, compress, deflate, br\u0027\n      },\n      method: \u0027get\u0027,\n      url: URL {\n        href: \u0027https://bankaccountdata.gocardless.com/api/v2/accounts/\u003cAccount id Was Here\u003e?date_from=2024-12-22\u0027,\n        origin: \u0027https://bankaccountdata.gocardless.com\u0027,\n        protocol: \u0027https:\u0027,\n        username: \u0027\u0027,\n        password: \u0027\u0027,\n        host: \u0027bankaccountdata.gocardless.com\u0027,\n        hostname: \u0027bankaccountdata.gocardless.com\u0027,\n        port: \u0027\u0027,\n        pathname: \u0027/api/v2/accounts/\u003cAccount id Was Here\u003e/transactions\u0027,\n        search: \u0027?date_from=2024-12-22\u0027,\n        searchParams: URLSearchParams { \u0027date_from\u0027 =\u003e \u00272024-12-22\u0027 },\n        hash: \u0027\u0027\n      },\n      data: undefined\n    },\n```\nAnd quite a few pages more.\n\n### PoC\n- Setup an Actualbudget server inside of Docker. In this instance I was using the Docker Compose script posted in the repository: https://github.com/actualbudget/actual/blob/master/packages/sync-server/docker-compose.yml\n- Link a gocardless account to Actualbudget and sync a bank account\n- Observe in the container using `docker logs actual-actual_server-1 -f`  that sensitive details are logged to the console and ingested by docker. \n\n### Impact\nInformation disclosure. The services are available both on-premises and in environments that are not under the control of the end user, such as third-party providers who offer this application as a managed solution.",
  "id": "GHSA-xvp7-8vm8-xfxx",
  "modified": "2026-06-08T23:28:41Z",
  "published": "2025-10-20T17:55:59Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/actualbudget/actual/security/advisories/GHSA-xvp7-8vm8-xfxx"
    },
    {
      "type": "WEB",
      "url": "https://github.com/actualbudget/actual/commit/97482a082d502887ef22514b93e35e4c67f4f30f"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/actualbudget/actual"
    },
    {
      "type": "WEB",
      "url": "https://github.com/actualbudget/actual/blob/36c40d90d2fe09eb1f25a6e2f77f6dd40638b267/packages/sync-server/src/app-gocardless/app-gocardless.js#L263-L264"
    },
    {
      "type": "WEB",
      "url": "https://github.com/actualbudget/actual/blob/36c40d90d2fe09eb1f25a6e2f77f6dd40638b267/packages/sync-server/src/app-gocardless/banks/integration-bank.js#L25-L27"
    },
    {
      "type": "WEB",
      "url": "https://github.com/actualbudget/actual/blob/36c40d90d2fe09eb1f25a6e2f77f6dd40638b267/packages/sync-server/src/app-gocardless/banks/integration-bank.js#L83-L85"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Actual Sync-server Gocardless service is logging sensitive data including bearer tokens and account numbers"
}

Mitigation
Implementation

Do not leave debug statements that could be executed in the source code. Ensure that all debug information is eradicated before releasing the software.

Mitigation MIT-46
Architecture and Design

Strategy: Separation of Privilege

  • Compartmentalize the system to have "safe" areas where trust boundaries can be unambiguously drawn. Do not allow sensitive data to go outside of the trust boundary and always be careful when interfacing with a compartment outside of the safe area.
  • Ensure that appropriate compartmentalization is built into the system design, and the compartmentalization allows for and reinforces privilege separation functionality. Architects and designers should rely on the principle of least privilege to decide the appropriate time to use privileges and the time to drop privileges.

No CAPEC attack patterns related to this CWE.