Last reviewed: 2026-09-04.
Use GitHub's private vulnerability reporting: Report a vulnerability.
It is private between you and the maintainers, it does not require you to know an email address, and it keeps the report out of public view while a fix is prepared. You need a GitHub account, but the report is not visible to other users.
Please do not open a public issue for a security problem. A public issue is indexed within minutes, and for a client SDK the window between disclosure and a released fix is time in which every consumer is exposed with no way to act.
If private reporting is unavailable to you for any reason, open a normal issue saying only "security report, please advise a private channel" — with no detail, no reproduction, and no affected version — and a maintainer will follow up.
- The SDK version, the Node or browser version, and which entry point
(
B24Hook,B24Frame,B24OAuth). - What an attacker gains, concretely. "A webhook secret reaches a log sink the application did not intend" is actionable; "the logger is unsafe" is not.
- A reproduction, however rough. A failing snippet is worth more than a careful description of one.
Never include a real credential. Not a webhook URL, not an
access_token, not an application_token — advisories can become public later,
and the report itself becomes an artefact worth protecting. Redact the secret
and describe its shape instead.
We aim to acknowledge a report within a few working days. That is an intention, not a contract — this is a small team, and pretending to a fixed SLA would be worse than saying so.
After acknowledgement you can expect: confirmation of whether we reproduce it, a view on severity, and an outline of the fix. If we decide it is not a vulnerability we will say why rather than closing quietly, and you are free to disagree.
Credit in the advisory and the changelog by default; tell us if you would rather not be named.
This repository is a client library. That boundary decides where a report should go, and getting it wrong costs everyone time.
In scope — anything in this repository:
- Credential handling in the SDK: leaking a webhook secret or token into logs, error messages, serialised errors, or telemetry.
- Flaws in what the SDK does with a REST response — parsing, batch fan-out, retry and rate-limit handling.
- Anything the SDK does that lets a caller be attacked by a malicious portal response.
- The published npm package: its integrity, its dependency tree, its build.
- The documentation site in
docs/and the guidance inskills/, when a documented pattern is itself insecure. Guidance that leads a reader into a vulnerability is a vulnerability with extra steps.
Not in scope here — report these to Bitrix24 directly, through the Bitrix24 developer channels:
- Vulnerabilities in the Bitrix24 REST API, in a portal, or in the platform.
- Issues in an application built with this SDK, where the SDK behaves as documented.
- The behaviour of a Bitrix24 method itself — what it returns, who it lets you reach, what it permits.
If you are unsure which side a problem falls on, report it here and say you are unsure. Misrouting it in good faith is not a mistake worth worrying about.
| Version | Supported |
|---|---|
2.x |
Yes — security fixes land here |
1.x |
No |
1.x reached end of life with the 2.0.0 release. It receives no security
fixes.
This boundary is a policy, not an enforcement. The
1.xreleases are still published on npm and are not marked deprecated, sonpm i @bitrix24/b24jssdk@1installs one today without a warning. Marking them is a registry action rather than a repository change, so it cannot land in a pull request; it is tracked in #406. Until that happens, do not read "unsupported" as "unavailable" — check the version you actually resolved.
A 3.0.0 is in preparation. When it ships, this table changes and the change
will be announced in the release notes rather than only here.
Listed so a report can say "this defence has a hole" rather than rediscovering the defence. None of this is a guarantee — each exists because something went wrong once.
- Credentials are redacted before logging.
redactSensitiveParams()(core/http/redact.ts) stripsauth,token,secret,access_token,refresh_token,client_secret,application_token,password,sessid,keyandsignaturefrom request params — including inside a query string, and two object levels deep, which is what a batch payload needs — before they can reach a log sink. It also masks the secret segment of a Bitrix24 webhook URL,/rest/<userId>/<secret>/, which is neither a key nor akey=valuepair and so is not something the other two passes can see.AjaxError.requestInfocarries no request URL at all (#39, #40, #287). - The redactor runs over response bodies as well as request params. A portal
method can return a URL with a webhook secret in its path —
rest.deferredbatch.downloadresultdoes — so thepost/responserecord is in scope for redaction exactly aspost/sendis. SdkError.originalErroris non-enumerable. It stays readable for local debugging, but a spread,Object.keys(),JSON.stringify()or a Sentry-style capture skips it, so a raw transport error carrying a secret in itsconfigcannot leak through generic serialisation (#189).- Three lint rules guard the logging callsites.
no-credential-in-loggerrejects a URL- or credential-shaped value in a logger context;require-catch-on-logger-callkeeps a rejecting log sink from taking the process down;logger-context-must-be-objectkeeps the context argument a record, so an error does not serialise to{}. - Dependencies are audited in CI.
pnpm audit --audit-level=moderateruns on every push, for the workspace and separately for the recipe package, which has its own lockfile. Dependabot covers both. - Constant-time token comparison is documented, with the reason. See
Security patterns
for verifying an
application_tokenwithout leaking it through response timing, and for the reply-2xx-first ordering an event receiver needs.
- Redaction stops two object levels down. That depth is chosen to cover a
batch payload (
{ cmd: [{ method, params: { … } }] }); a credential nested deeper is not masked. Redact at the callsite for those. - The query-string pass only matches a bare
key=value. A bracketed or encoded key —auth[application_token]=— is not caught by the string pass, and a credential appearing after a newline inside a multi-line string value is not caught either. - The lint rules are syntax-only: they cannot see a credential through string
interpolation, so
logger.debug(`GET ${url}`)passes them. Log the bare method name.no-credential-in-loggeralso leavesauthandsessidout of its vocabulary on purpose, to avoid false positives, and relies on the runtime redactor for those two. - Redaction applies to
AjaxError. AnSdkErroryou construct yourself is not redacted — do not interpolate params, a filter, or a URL into its description. - A
filterlegitimately carries user data. It is not treated as a credential, and it is not redacted. - The path pass matches one shape, Bitrix24's own webhook URL. "A credential somewhere in a path" is not a decidable question, so a credential that some other service puts in a path is not covered.
- Response bodies are logged at
info, redacted. Redaction is not omission — a portal answer is user data. If your sink should not hold it, filter at the sink or do not wire one atinfo.
The first two are recorded as accepted residual risk in
redact.ts itself, with the reasoning.
They are listed here so a report about them arrives knowing that, rather than
being told afterwards — and so a leak that falls outside them is not dismissed
as already-known.
We prefer coordinated disclosure: a fix released, then an advisory. If you plan to publish on your own schedule, tell us the date — we would rather work to your timeline than be surprised by it.
If a report has gone unanswered long enough that you are wondering whether it was seen — a fortnight is a reasonable point to stop wondering — escalate through the Bitrix24 developer channels rather than waiting indefinitely. Read that as permission to stop being patient, not as a deadline we are promising to meet; the section above says why we do not commit to one, and it would be incoherent to commit to one here.
This document cites specific internals — the redaction key list, three lint
rules, an end-of-life date. docs-lint --strict does not reach root files, so
nothing fails when those drift. Re-read it against the source whenever
redact.ts, eslint-rules/ or the supported-version table changes, and move the
stamp above.