Skip to content

Commit 346fa7b

Browse files
authored
Merge pull request #540 from brazilian-utils/claude/osps-baseline-level-2-docs
docs+ci: OSPS Baseline Levels 2–3 — security policies, DCO, MAINTAINERS.md, secret scanning, SBOM in the package, Context7
2 parents 325d577 + b02781b commit 346fa7b

13 files changed

Lines changed: 514 additions & 12 deletions

File tree

‎.github/workflows/check.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,8 @@ jobs:
5656
- name: Check llms.txt/llms-full.txt are up to date
5757
run: npm run build:llms && git diff --exit-code -- docs/llms.txt docs/llms-full.txt
5858

59+
- name: Check the VEX statements against the suppressed advisories
60+
run: npm run check:vex
61+
5962
- name: Audit dependencies
6063
run: npx --yes audit-ci@7.1.0 --high --allowlist GHSA-jmr9-qjv8-65gv GHSA-7pqw-9j4j-h8q3

‎.github/workflows/context7.yml‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Asks Context7 (https://context7.com/brazilian-utils/javascript) to re-index the documentation
2+
# whenever what it reads changes on `main`; without this it refreshes on its own schedule. The
3+
# request needs the CONTEXT7_API_KEY secret from the Context7 dashboard (Settings -> Secrets and
4+
# variables -> Actions); until it is set, the job says so and exits green.
5+
name: Context7
6+
7+
on:
8+
push:
9+
branches: [main]
10+
paths:
11+
- "docs/**"
12+
- "README.md"
13+
- "context7.json"
14+
workflow_dispatch:
15+
16+
permissions:
17+
contents: read
18+
19+
jobs:
20+
refresh:
21+
name: Refresh the Context7 index
22+
runs-on: ubuntu-latest
23+
timeout-minutes: 5
24+
env:
25+
CONTEXT7_API_KEY: ${{ secrets.CONTEXT7_API_KEY }}
26+
27+
steps:
28+
- name: Trigger the refresh
29+
run: |
30+
if [ -z "$CONTEXT7_API_KEY" ]; then
31+
echo "::notice::CONTEXT7_API_KEY is not set, skipping the Context7 refresh"
32+
exit 0
33+
fi
34+
curl --silent --show-error --fail-with-body -X POST https://context7.com/api/v1/refresh \
35+
-H "Content-Type: application/json" \
36+
-H "Authorization: Bearer $CONTEXT7_API_KEY" \
37+
-d '{"libraryName": "/brazilian-utils/javascript"}'

‎.github/workflows/release.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ jobs:
112112
echo "npm ${npm_version}"
113113
echo "${npm_version}" | awk -F. '{ exit !($1 > 11 || ($1 == 11 && $2 >= 15)) }'
114114
115+
- name: Generate the CycloneDX SBOM shipped inside the package
116+
# `files` in package.json lists brazilian-utils.cdx.json, so the tarball carries it;
117+
# --package-lock-only reads the lockfile instead of installing anything.
118+
run: npm sbom --sbom-format cyclonedx --omit dev --package-lock-only > brazilian-utils.cdx.json
119+
115120
- name: Stage on npm
116121
run: npm stage publish --provenance --access public
117122

‎.github/workflows/security.yml‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,28 @@ jobs:
5252
scan-args: |
5353
--lockfile=package-lock.json
5454
55+
secrets:
56+
name: Scan for committed secrets
57+
runs-on: ubuntu-latest
58+
timeout-minutes: 10
59+
60+
steps:
61+
- name: Checkout code
62+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
63+
with:
64+
# The scan walks the commits between the base and the head of the pull request (or of
65+
# the push), so it needs the history, not just the checked-out tree.
66+
fetch-depth: 0
67+
persist-credentials: false
68+
69+
# GitHub's push protection rejects known token formats at push time; this is the second,
70+
# tool-independent check, over the commits of the pull request or push and, on the weekly
71+
# run and on a manual one, over the whole history.
72+
- name: Run TruffleHog
73+
uses: trufflesecurity/trufflehog@f714bf454f350590f4a24c3ddb1aef02c35bf5b6 # v3.97.5
74+
with:
75+
extra_args: --results=verified,unknown
76+
5577
scorecard:
5678
name: OpenSSF Scorecard
5779
# Scorecard only analyses the default branch, so pull requests are skipped.

‎.gitignore‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ node_modules
77
dist
88
coverage
99
.stryker-tmp
10+
# Nothing in the project reads one, so a local `.env` never has a reason to be committed.
11+
.env
12+
.env.*
13+
# The SBOM is generated by the release workflow right before publishing.
14+
*.cdx.json
1015
# Every report is generated (stryker, coverage, jscpd) except the API Extractor baseline, which is
1116
# committed so that `npm run check:api` compares the public API against the reviewed one instead of
1217
# writing a new file on every run.

‎CONTRIBUTING.md‎

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,41 @@ and is invoked through the `npm` scripts below, so you don't need to install any
5353
| `npm run check:api:update` | Rewrites the committed API Extractor baseline `reports/api/brazilian-utils.api.md` from the current build; run it when a public signature changes on purpose and commit the new report. |
5454
| `npm run check:commits` | Checks the commit messages since `origin/main` with commitlint (Conventional Commits). |
5555
| `npm run check:lockfile` | Checks `package-lock.json` only resolves to the npm registry over HTTPS with integrity hashes (lockfile-lint). |
56+
| `npm run check:vex` | Checks that every advisory suppressed for `audit-ci` and OSV-Scanner has a `not_affected` statement in `openvex.json`, and nothing else does. |
5657

5758
Before opening a pull request, make sure `npm run check` and `npm run test` both pass locally. If your
5859
change touches runtime behavior, also consider running the Bun/Deno scripts above. The library is
5960
tested and must keep working on Node.js, Bun, Deno and in browsers.
6061

62+
## Architecture
63+
64+
The library is a flat collection of pure functions, so there is not much architecture to learn,
65+
but the few rules below hold everywhere:
66+
67+
- `src/<util>/<util>.ts` is one exported utility, next to its `<util>.test.ts`; `src/index.ts`
68+
re-exports it and `package.json` exposes it as a subpath import, so consumers bundle only what
69+
they call.
70+
- `src/_internals/` holds the helpers shared between utilities (check-digit arithmetic, `mod10`/
71+
`mod11`, TLV parsing for Pix, sanitizers). `src/_internals/constants/` holds the datasets
72+
(banks, municipalities, CNAE, CBO, CFOP, NCM, legal natures), generated by the `scripts/`
73+
from their official sources and refreshed by the `Update datasets` workflow through
74+
a pull request, never edited by hand.
75+
- Utilities are synchronous, stateless and side-effect free: input in, value out, no globals, no
76+
environment access, no dynamic code. The two exceptions are `getAddressInfoByCep` and
77+
`getCepInfoByAddress`, the only utilities that do I/O: they query public CEP APIs (BrasilAPI,
78+
ViaCEP and the alternatives listed in their docs) over HTTPS through
79+
`src/_internals/fetch-with-retry`, treat every response as untrusted input and never send anything
80+
but the CEP or address being looked up.
81+
- There are no runtime dependencies (see [Zero runtime dependencies](#zero-runtime-dependencies)),
82+
so the trust boundary of the published package is this repository, its build toolchain and the
83+
npm registry; [MAINTAINERS.md](MAINTAINERS.md) lists who can change what, and
84+
[SECURITY.md](SECURITY.md) how a release is verified.
85+
86+
The actors around the code are the consumers of the npm package, the contributors (pull requests
87+
from forks), the maintainers (review, merge, release approval) and the automation: GitHub Actions
88+
builds, tests and publishes, Dependabot and the `Update datasets` workflow open update pull requests, and
89+
release-please turns merged commits into releases.
90+
6191
## Adding a new utility
6292

6393
Brazilian Utils follows a consistent folder convention for every utility. To add a new one (for
@@ -248,16 +278,21 @@ export that goes missing.
248278
- Every GitHub Action is pinned to a full commit SHA with the version in a trailing comment
249279
(Dependabot updates both). Checkouts use `persist-credentials: false`.
250280
- The `Security` workflow lints the workflows themselves with
251-
[actionlint](https://github.com/rhysd/actionlint) and [zizmor](https://github.com/zizmorcore/zizmor)
252-
and scans `package-lock.json` with [OSV-Scanner](https://google.github.io/osv-scanner/); the
253-
`Check` workflow runs `audit-ci` and lockfile-lint on top. The `Security` workflow also runs the
281+
[actionlint](https://github.com/rhysd/actionlint) and [zizmor](https://github.com/zizmorcore/zizmor),
282+
scans `package-lock.json` with [OSV-Scanner](https://google.github.io/osv-scanner/) and scans the
283+
commits of every pull request (the whole history on the weekly run) for leaked secrets with
284+
[TruffleHog](https://github.com/trufflesecurity/trufflehog), next to GitHub's own secret scanning
285+
and push protection; the `Check` workflow runs `audit-ci` and lockfile-lint on top, and
286+
`npm run check:vex` keeps the advisories those two suppress accounted for in `openvex.json` (see
287+
SECURITY.md). The `Security` workflow also runs the
254288
[OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/brazilian-utils/javascript) on
255289
every push to `main` and weekly: it grades the repository configuration (pinned actions, token
256290
permissions, branch protection, code review, dependency updates, SAST) rather than the code,
257291
publishes the score and uploads the findings to the Security tab.
258-
- Every release run keeps `brazilian-utils.cdx.json` as a workflow artifact (`sbom-<tag>`), a
259-
CycloneDX SBOM of the published package generated with `npm sbom` from the release tag; releases
260-
are immutable here, so the file cannot be attached to the release itself. The package has no
292+
- Every release ships `brazilian-utils.cdx.json` inside the package, a CycloneDX SBOM generated
293+
with `npm sbom` from the release tag right before staging on npm, and keeps the same file as a
294+
workflow artifact (`sbom-<tag>`); releases are immutable here, so the file cannot be attached to
295+
the release itself. The package has no
261296
runtime dependencies, so the document describes the package itself; it exists for consumers
262297
whose supply-chain policy requires one.
263298
- Commit messages are checked with commitlint on every pull request, since release-please derives
@@ -300,6 +335,19 @@ chore(deps-dev): bump vitest
300335
Releases are cut from these commit types (see [Releasing](#releasing) below), so an accurate
301336
type/scope matters.
302337

338+
### Developer Certificate of Origin
339+
340+
Every commit must carry a `Signed-off-by` line, as in the Linux kernel: it is your statement that
341+
you wrote the change, or have the right to submit it, under the project's MIT license, per the
342+
[Developer Certificate of Origin](https://developercertificate.org). Git adds the line for you:
343+
344+
```bash
345+
git commit -s
346+
```
347+
348+
`git commit -s --amend` (or `git rebase --signoff main`) fixes a commit that is missing it. Use a
349+
real name and an e-mail address that reaches you; GitHub's `noreply` address is fine.
350+
303351
## Breaking changes
304352

305353
This library is used in production by many projects, so please do not introduce breaking changes
@@ -350,6 +398,13 @@ No local `npm login`/`npm publish` or tagging is ever needed to cut a release.
350398
6. Open a pull request against `main` using a Conventional Commit-style title. Fill in the pull
351399
request template checklist.
352400

401+
## Governance
402+
403+
[MAINTAINERS.md](MAINTAINERS.md) lists who maintains the project, what each role can do and how
404+
write access is granted and removed. In short: contributors send pull requests from forks, one
405+
maintainer reviews and merges them, the same maintainers approve releases on npm, and access grows
406+
with a track record of merged contributions, never before.
407+
353408
## Recognition
354409

355410
We use [all-contributors](https://github.com/all-contributors/all-contributors) to recognize

‎MAINTAINERS.md‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Maintainers
2+
3+
This file lists who maintains Brazilian Utils, what each role can do and how someone gets there.
4+
Everything here applies to the `brazilian-utils/javascript` repository and to the
5+
`@brazilian-utils/brazilian-utils` package on npm.
6+
7+
## Current maintainers
8+
9+
| Maintainer | GitHub | Since |
10+
| ------------------------------------------ | ---------------------------------------------- | ----- |
11+
| Hyan Mandian (project lead, npm publisher) | [@hyanmandian](https://github.com/hyanmandian) | 2018 |
12+
13+
Maintainers are the only accounts with write access to the repository and are the
14+
[code owners](.github/CODEOWNERS) of every path. The list above is the single source of truth;
15+
`.github/CODEOWNERS` and the repository collaborators on GitHub mirror it.
16+
17+
## Roles
18+
19+
**Contributor**: anyone who opens an issue, a discussion or a pull request from a fork. No standing
20+
access is needed; the CI runs the pull request with a read-only token. Contributors are credited
21+
in the README through all-contributors (see [Recognition](CONTRIBUTING.md#recognition)).
22+
23+
**Maintainer**: reviews and merges pull requests, triages issues and security reports, merges the
24+
release pull request and approves the staged version on npm with 2FA, keeps the datasets and the
25+
toolchain updated, and speaks for the project in discussions. Maintainers have the `Admin` role on
26+
the repository, are members of the `brazilian-utils` GitHub organization (which requires two-factor
27+
authentication for every member) and are publishers of the package on npm (two-factor
28+
authentication required, publishing only through the repository's trusted publisher). They hold
29+
the repository's secrets under the policy in [SECURITY.md](SECURITY.md#secrets-and-credentials).
30+
31+
**Automation**: GitHub Actions (build, tests, publish), Dependabot and release-please act with
32+
short-lived tokens scoped to each job (`permissions:` in every workflow); none of them can merge a
33+
pull request or publish a version without a maintainer's action.
34+
35+
## Becoming a maintainer
36+
37+
Write access is granted by the current maintainers, after review, to contributors with a sustained
38+
record of merged contributions (code, docs, datasets or tooling) and a track record of good
39+
judgment in reviews and discussions, typically over several months. The steps are:
40+
41+
1. An existing maintainer proposes the contributor, and the current maintainers agree.
42+
2. The contributor enables two-factor authentication on GitHub and on npm.
43+
3. They receive the lowest access that lets them do the work (`Write` on the repository first;
44+
`Admin` and the npm publisher role only when they take on releases), are added to this file and
45+
to `.github/CODEOWNERS`, and are announced in a discussion.
46+
47+
Maintainers who become inactive for a year or so, or who ask to step down, move to an
48+
**emeritus** list here and have their access removed; they are welcome back at any time through
49+
the same steps. Access is also reviewed whenever the GitHub organization or the npm package
50+
settings change.
51+
52+
## Contact
53+
54+
Use the channels in [SUPPORT.md](SUPPORT.md) for questions and [SECURITY.md](SECURITY.md) for
55+
vulnerabilities; e-mail (support@brazilian-utils.com.br) reaches the maintainers directly.

0 commit comments

Comments
 (0)