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"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…