Until 3.0, dist/moebius.js bundles three runtime libraries: chroma-js, nearest-color and @phun-ky/typeof. They aren't installed separately, so your npm audit can't see them; fixes to them reach you through a moebius release. Each release attaches an SBOM listing them, and their licences are in dist/THIRD_PARTY_LICENSES.txt. From 3.0 they're regular dependencies instead (#204). How dependencies are chosen, tracked and kept clear of known vulnerabilities is described under Dependencies.
Support follows the major version:
| Release | Supported |
|---|---|
2.x, the current major |
✅ |
1.x |
❌ |
- The current major is supported as a whole. Fixes are released as a new patch or minor of it, and upgrading within a major never breaks your code.
- The previous major gets fixes for high and critical vulnerabilities for 6 months after the next major is released, as a patch on its latest release. For
1.x, that period ended in November 2025 (2.0.0 was released on 5 May 2025). - Security comes before the deprecation grace period. If a vulnerability can't be fixed without breaking an API, the fix may change or remove it in a patch or minor release instead of waiting for a major. The change is kept as small as possible, and the advisory and the release notes say what changed and why.
To get a fix, upgrade to the newest release of your major.
Report vulnerabilities privately, either with GitHub's private vulnerability reporting or by email to security@phun-ky.net. Please don't open a public issue for a security problem.
- Acknowledgement: you'll get a reply within 7 days confirming the report was received.
- Assessment: the maintainer reproduces the issue and judges its severity, and may ask you for details.
- Fix: a fix is developed in a private GitHub security advisory and released as a new version. The aim is a fix within 30 days for serious issues.
- Disclosure: the advisory is published when the fix is released, with a CVE where one applies. Please keep the issue private until then.
- Credit: reporters are credited in the advisory and the release notes, unless they ask to stay anonymous.
Moebius() loads color names and returns classes that build palettes from the colors you give them.
What you can rely on:
- Network access only for color names. The only network request moebius makes is the one that loads color names in
Moebius(). PasscolorNamesas a map or an array of names, orfalse, and nothing is requested. Pass a URL, and only that URL is requested. - Loaded names are checked. A failed response throws an
Errorwith the status, a response that isn't JSON throws aTypeError, names in an unsupported shape throw aTypeError, and an empty list throws aRangeError. Options that aren't an object throw aTypeError. - No dynamic code and no storage. No
eval,new Functionor loading of code, and nothing is written to globals, storage or the file system.MoebiusSVGHelpercreates SVG elements withdocumentonly when you call it. - Releases are traceable. Every release is built and published from GitHub Actions, with signed provenance (see below).
What you can't rely on:
- The default color names source. Without
colorNames,Moebius()fetches names fromhttps://color-names.herokuapp.com/v1/, a third-party service that is deprecated, and logs a warning once. Pass your own names to avoid it; 3.0 stops using it (#184). - A timeout. The color names request has no timeout, so
Moebius()waits as long as the server does. - Checked colors. Colors are parsed by
chroma-js: an invalid color string throws itsError,nullandundefinedthrow aTypeErrorfrom inside the library, and numbers are accepted as colors (42is#00002a). Check colors that come from users before passing them in. - Unpredictable randomness. Random colors, shuffles and SVG element IDs use
Math.random, which is not cryptographically secure. Don't use them for anything secret.
Every release is published from GitHub Actions with npm trusted publishing, and carries a signed provenance attestation (Sigstore) that links it to the commit and workflow run that built it. To verify the package you've installed:
npm audit signaturesThe provenance must name the repository phun-ky/moebius and the workflow .github/workflows/publish.yml. The package's page on npm shows this under "Provenance", and npm view @phun-ky/moebius@<version> dist.attestations lists the attestations.
Each GitHub release also has a software bill of materials attached, in CycloneDX format (moebius-<version>.cdx.json), listing the libraries bundled into dist.
Choosing them. As few runtime dependencies as possible, and a new one is discussed in an issue first. It must be widely used, actively maintained and under an OSI-approved licence compatible with MIT; strong copyleft licences (GPL, AGPL) are not accepted. In 2.x, rollup bundles the runtime libraries into dist; from 3.0 they are declared in dependencies and never bundled (#204). Development dependencies follow the same rules, judged more loosely.
Obtaining them. Packages come only from the npm registry, with exact versions locked in package-lock.json and installed with npm ci in CI. GitHub Actions are pinned to commit SHAs.
Tracking them. Dependabot proposes version updates weekly, two weeks after a version is published, and security updates as soon as an advisory is published. Every pull request runs dependency review, and every release runs npm audit on runtime dependencies first. In 2.x the bundled libraries are development dependencies, so dependency review, which checks development dependencies too, is what catches them.
Fixing known vulnerabilities. Findings are checked against the GitHub Advisory Database:
| Finding | Where | What happens |
|---|---|---|
| Critical or high | Runtime or bundled dependency | Blocks merging (dependency review); from 3.0 also blocks releasing (npm audit); fixed and released within 7 days |
| Critical or high | Development dependency | Blocks merging when introduced; an existing one is fixed within 30 days |
| Moderate or low | Any dependency | Fixed with the next update, within 90 days |
| Licence outside the policy | New runtime or bundled dependency | Not merged |
Accepted exceptions. A vulnerability that can't be fixed yet and can't be exploited through this project is allowed in dependency review and listed here. Each one is removed as soon as a fix is reachable. None of them ship in the published package.
| Advisory | Package | Why it's accepted | Remove when |
|---|---|---|---|
| GHSA-vfj7-8cjw-p6xm | braces (via putout) | Every version is affected and no fix exists. Only our own glob patterns reach it. | A fixed braces version is released, or putout stops depending on it |
CodeQL analyses every pull request and every push to main. The ruleset on main blocks merging while CodeQL reports an error, or a security alert of high or critical severity. Other CodeQL alerts are fixed within 90 days, or dismissed with the reason when they are false positives. OpenSSF Scorecard runs on every push to main, with results at https://scorecard.dev/viewer/?uri=github.com/phun-ky/moebius.
The project keeps as few secrets as possible:
| Secret | Used by | For |
|---|---|---|
GH_TOKEN |
publish.yml |
Pushing release commits and tags, and GitHub releases |
CODECOV_TOKEN |
check.yml |
Uploading coverage reports |
Publishing to npm needs no secret: it uses short-lived OIDC tokens from GitHub.
- Storage: secrets are kept only as encrypted GitHub Actions secrets, never in the repository.
- Access: only the maintainer can view or change them, and they are not available to workflows run from forks.
- Rotation: at least once a year, and immediately if one may have been exposed.