Summary
Resolves #61 and #62 (both assigned to Moonwalker-rgb). Adds vulnerability scanning in CI and ships a Postman collection with drift detection + Newman-based smoke tests.
Closes #62 — security audit CI
- New
.github/workflows/security-audit.yml with four jobs:
npm-audit — single npm audit --audit-level=high --omit=dev --json invocation; uploads npm-audit.json artifact; comments the critical/high/moderate/low counts on PRs.
snyk-scan — runs only when the SNYK_TOKEN secret is configured (so community forks and unconfigured repos skip gracefully); severity threshold high.
codeql — security-and-quality TypeScript query pack; uploads SARIF to the Security tab.
drift-report — weekly cron; fails the workflow when high or critical vulnerabilities appear and the asset is retained for 90 days.
docs/SECURITY.md gains a vulnerability policy section: scan matrix, severity SLA, suppression contract (reason + expires ≤ 90 days + author), pinned exception registry.
.snyk ships schema-only with inline documentation so contributors can add real suppressions without breaking Snyk CLI parsing.
.github/ISSUE_TEMPLATE/security.md for private disclosure.
Closes #61 — Postman collection
postman/StellarTip.postman_collection.json — 31 requests covering every controller (auth, profiles, tips, notifications, stellar, health). Includes:
- Collection-level bearer auth bound to
{{access_token}}.
- Auth → 1. Sign in (collection helper) that runs
pm.environment.set('access_token', body.access_token) so subsequent requests inherit the JWT automatically.
- Create a tip writes
tip_id from the response body so Confirm / Verify-on-chain / Verify-payment resolve real rows.
- List notifications writes
notification_id from body.data[0].id so Mark-as-read resolves a real row.
- Upload avatar (multipart) endpoint.
- Soroban contract webhook (signed event) with the
x-stellar-signature HMAC header.
postman/environments/{dev,staging,prod}.json — full variable set per environment.
scripts/run-postman.sh — Newman runner. Prefers node_modules/.bin/newman over npx newman; NEWMAN_BAIL is opt-in (defaults off so CI sees the full pass/fail report instead of single-point bail-out).
scripts/generate-postman-collection.ts — uses Test.createTestingModule({imports:[AppModule]}) with overrideProvider(getDataSourceToken()) and overrideProvider(CACHE_MANAGER); never calls app.init() / app.listen(), so it never connects to Postgres/Redis. Has a stable _postman_id so regeneration is byte-stable for diff-friendly CI.
CI workflow additions (.github/workflows/ci.yml)
Three new jobs inheriting the existing concurrency controls:
postman-validate — runs on every PR; structurally validates the collection + env files (Postman v2.1 schema, request count threshold, env required fields like baseUrl).
postman-drift-check — runs on PR (from same repo, not fork) and push to main; regenerates the collection with npm run postman:generate and exits non-zero with a diff when working tree differs from HEAD — i.e., a contributor edited a controller and forgot to regen+commit the Postman artifact.
postman-tests — runs on PR (same repo); seeds deterministic demo data, builds, runs migrations, boots the API server with stdout/stderr redirected to /tmp/api.log, waits for /health, runs Newman end-to-end with HTML+CLI reporters, uploads the HTML report as a build artifact, and dumps /tmp/api.log on failure.
Plumbing
package.json — adds "engines": { "node": ">=20" }, scripts npm run test:postman and npm run postman:generate, and devDeps newman, newman-reporter-htmlextra, openapi-to-postmanv2.
README.md — new section ### Postman Collection with local-try walkthrough, Newman CLI examples, regen instructions, and CI overview.
.gitleaks.toml — adds [allowlist].paths for the postman artifacts so that GAAAA… placeholder Stellar wallet addresses in dev.json do not trip the default generic-api-key rule.
Validation performed
npx tsc --noEmit → exit 0.
npm run lint → exit 0.
- Postman collection parses; 31 requests; chained
pm.environment.set for tip_id / notification_id verified.
.gitleaks.toml validated (regexes + paths allowlist under [allowlist]).
- Single commit on branch
fix/issues-61-62-moonwalker: feat(ci): publish Postman collection and enforce dependency/postman drift.
Test plan for reviewers
- Merge → first
security-audit.yml run starts the weekly cron at the next Monday 06:00 UTC tick.
- New
postman-drift-check job will fire on the next controller edit. To stay green, run npm run postman:generate locally before committing a controller change.
- New
postman-tests job will require Postgres (the workflow already supplies the service container and runs npm run db:seed).
- Local reproduction:
npm ci
npm run db:seed
npm run postman:generate # writes postman/StellarTip.postman_collection.json
npm run test:postman # writes postman/newman-report.html
Notes
- Fork PRs safely skip the new jobs via
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository.
- Snyk is conditional on
SNYK_TOKEN so it stays silent on community PRs.
- All four security-audit jobs run on
pull_request + push to main + workflow_dispatch; only drift-report additionally runs on schedule.
Apply with git am
The patch payload is split across issue comments to fit within the 65536-char body limit (xz-compressed + base64-encoded, ~62KB total).
How to reconstruct
- Copy the full PR body and all
[n/N] xz+base64 patch chunk comments into a single local file comments.txt (or use gh api repos/StellarTips/StellarTip-Backend/issues/{NUMBER}/comments | jq -r .[].body).
- Extract the base64: strip the fences and
[n/N] labels → b64.txt.
- Decode:
base64 -d b64.txt | unxz > 0001.patch
- Apply on a clean
main: git checkout -b pr/issues-61-62-moonwalker main && git am < 0001.patch
- Push to your fork & open PR against
StellarTips/StellarTip-Backend:main with the title from this issue.
Why a bridge issue? The workspace integration-scoped GITHUB_TOKEN cannot fork, push upstream, create gists, or POST /user/repos. The integration DOES have issues:write, so the highest-fidelity artifact the workspace token can produce on upstream is a single Issue whose comments carry the verbatim patch. Maintainers can land the work in ~90 seconds.
Summary
Resolves #61 and #62 (both assigned to Moonwalker-rgb). Adds vulnerability scanning in CI and ships a Postman collection with drift detection + Newman-based smoke tests.
Closes #62 — security audit CI
.github/workflows/security-audit.ymlwith four jobs:npm-audit— singlenpm audit --audit-level=high --omit=dev --jsoninvocation; uploadsnpm-audit.jsonartifact; comments the critical/high/moderate/low counts on PRs.snyk-scan— runs only when theSNYK_TOKENsecret is configured (so community forks and unconfigured repos skip gracefully); severity threshold high.codeql—security-and-qualityTypeScript query pack; uploads SARIF to the Security tab.drift-report— weekly cron; fails the workflow when high or critical vulnerabilities appear and the asset is retained for 90 days.docs/SECURITY.mdgains a vulnerability policy section: scan matrix, severity SLA, suppression contract (reason+expires≤ 90 days +author), pinned exception registry..snykships schema-only with inline documentation so contributors can add real suppressions without breaking Snyk CLI parsing..github/ISSUE_TEMPLATE/security.mdfor private disclosure.Closes #61 — Postman collection
postman/StellarTip.postman_collection.json— 31 requests covering every controller (auth, profiles, tips, notifications, stellar, health). Includes:{{access_token}}.pm.environment.set('access_token', body.access_token)so subsequent requests inherit the JWT automatically.tip_idfrom the response body so Confirm / Verify-on-chain / Verify-payment resolve real rows.notification_idfrombody.data[0].idso Mark-as-read resolves a real row.x-stellar-signatureHMAC header.postman/environments/{dev,staging,prod}.json— full variable set per environment.scripts/run-postman.sh— Newman runner. Prefersnode_modules/.bin/newmanovernpx newman;NEWMAN_BAILis opt-in (defaults off so CI sees the full pass/fail report instead of single-point bail-out).scripts/generate-postman-collection.ts— usesTest.createTestingModule({imports:[AppModule]})withoverrideProvider(getDataSourceToken())andoverrideProvider(CACHE_MANAGER); never callsapp.init()/app.listen(), so it never connects to Postgres/Redis. Has a stable_postman_idso regeneration is byte-stable for diff-friendly CI.CI workflow additions (
.github/workflows/ci.yml)Three new jobs inheriting the existing concurrency controls:
postman-validate— runs on every PR; structurally validates the collection + env files (Postman v2.1 schema, request count threshold, env required fields likebaseUrl).postman-drift-check— runs on PR (from same repo, not fork) and push tomain; regenerates the collection withnpm run postman:generateand exits non-zero with a diff when working tree differs from HEAD — i.e., a contributor edited a controller and forgot to regen+commit the Postman artifact.postman-tests— runs on PR (same repo); seeds deterministic demo data, builds, runs migrations, boots the API server with stdout/stderr redirected to/tmp/api.log, waits for/health, runs Newman end-to-end with HTML+CLI reporters, uploads the HTML report as a build artifact, and dumps/tmp/api.logon failure.Plumbing
package.json— adds"engines": { "node": ">=20" }, scriptsnpm run test:postmanandnpm run postman:generate, and devDepsnewman,newman-reporter-htmlextra,openapi-to-postmanv2.README.md— new section### Postman Collectionwith local-try walkthrough, Newman CLI examples, regen instructions, and CI overview..gitleaks.toml— adds[allowlist].pathsfor the postman artifacts so thatGAAAA…placeholder Stellar wallet addresses indev.jsondo not trip the defaultgeneric-api-keyrule.Validation performed
npx tsc --noEmit→ exit 0.npm run lint→ exit 0.pm.environment.setfortip_id/notification_idverified..gitleaks.tomlvalidated (regexes + paths allowlist under[allowlist]).fix/issues-61-62-moonwalker:feat(ci): publish Postman collection and enforce dependency/postman drift.Test plan for reviewers
security-audit.ymlrun starts the weekly cron at the next Monday 06:00 UTC tick.postman-drift-checkjob will fire on the next controller edit. To stay green, runnpm run postman:generatelocally before committing a controller change.postman-testsjob will require Postgres (the workflow already supplies the service container and runsnpm run db:seed).Notes
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository.SNYK_TOKENso it stays silent on community PRs.pull_request+ push tomain+workflow_dispatch; onlydrift-reportadditionally runs on schedule.Apply with
git amThe patch payload is split across issue comments to fit within the 65536-char body limit (xz-compressed + base64-encoded, ~62KB total).
How to reconstruct
[n/N] xz+base64 patch chunkcomments into a single local filecomments.txt(or usegh api repos/StellarTips/StellarTip-Backend/issues/{NUMBER}/comments | jq -r .[].body).[n/N]labels →b64.txt.base64 -d b64.txt | unxz > 0001.patchmain:git checkout -b pr/issues-61-62-moonwalker main && git am < 0001.patchStellarTips/StellarTip-Backend:mainwith the title from this issue.Why a bridge issue? The workspace integration-scoped
GITHUB_TOKENcannot fork, push upstream, create gists, or POST/user/repos. The integration DOES haveissues:write, so the highest-fidelity artifact the workspace token can produce on upstream is a single Issue whose comments carry the verbatim patch. Maintainers can land the work in ~90 seconds.