oauthlint 0.2.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,186 @@
1
+ import { extname } from 'node:path';
2
+ import { execa } from 'execa';
3
+ /**
4
+ * Raised when a git operation can't be performed — most commonly because the
5
+ * target directory is not inside a git work tree, or `git` itself is missing.
6
+ * Callers (the scan command) translate this into a clean, non-crashing error
7
+ * message rather than a stack trace.
8
+ */
9
+ export class GitError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = 'GitError';
13
+ }
14
+ }
15
+ /**
16
+ * File extensions for the languages the rule pack supports
17
+ * (javascript, typescript, python, go, java, rust). The change-set resolvers
18
+ * filter to these so we never hand Semgrep a Markdown/lockfile/binary path:
19
+ * incremental scanning only makes sense for source files semgrep can analyse,
20
+ * and skipping the rest keeps the scan fast.
21
+ *
22
+ * Kept deliberately broad within each supported language (e.g. both `.mjs`
23
+ * and `.cjs`) so a changed file is never silently dropped.
24
+ */
25
+ const SUPPORTED_EXTENSIONS = new Set([
26
+ // JavaScript / TypeScript
27
+ '.js',
28
+ '.jsx',
29
+ '.mjs',
30
+ '.cjs',
31
+ '.ts',
32
+ '.tsx',
33
+ '.mts',
34
+ '.cts',
35
+ // Python
36
+ '.py',
37
+ '.pyi',
38
+ // Go
39
+ '.go',
40
+ // Java
41
+ '.java',
42
+ // Rust
43
+ '.rs',
44
+ ]);
45
+ /** True when `file` has an extension the rule pack can actually scan. */
46
+ export function isScannableSourceFile(file) {
47
+ return SUPPORTED_EXTENSIONS.has(extname(file).toLowerCase());
48
+ }
49
+ /**
50
+ * Run `git` with the given args as an ARRAY (never a shell string), so user
51
+ * input — branch names, refs — can't be interpreted by a shell. `execa`
52
+ * spawns the process directly without `/bin/sh`, eliminating shell injection.
53
+ *
54
+ * @throws GitError when git is not installed or the command fails.
55
+ */
56
+ async function git(cwd, args) {
57
+ try {
58
+ const { stdout } = await execa('git', args, { cwd, reject: true });
59
+ return stdout;
60
+ }
61
+ catch (err) {
62
+ if (isMissingGitBinary(err)) {
63
+ throw new GitError('git is not installed or not on PATH — cannot resolve changed files.');
64
+ }
65
+ const stderr = typeof err.stderr === 'string'
66
+ ? err.stderr.trim()
67
+ : '';
68
+ throw new GitError(stderr || (err instanceof Error ? err.message : String(err)));
69
+ }
70
+ }
71
+ function isMissingGitBinary(err) {
72
+ if (typeof err !== 'object' || err === null)
73
+ return false;
74
+ const e = err;
75
+ return e.code === 'ENOENT' || e.cause?.code === 'ENOENT';
76
+ }
77
+ /**
78
+ * Throw a clear GitError unless `cwd` is inside a git work tree. Used by the
79
+ * diff/staged resolvers so "not a git repo" surfaces as a friendly message
80
+ * instead of a confusing low-level git error.
81
+ */
82
+ async function assertGitRepo(cwd) {
83
+ let inside;
84
+ try {
85
+ inside = await git(cwd, ['rev-parse', '--is-inside-work-tree']);
86
+ }
87
+ catch (err) {
88
+ if (err instanceof GitError && /not a git repository/i.test(err.message)) {
89
+ throw new GitError(`Not a git repository: ${cwd}`);
90
+ }
91
+ throw err;
92
+ }
93
+ if (inside.trim() !== 'true') {
94
+ throw new GitError(`Not a git repository: ${cwd}`);
95
+ }
96
+ }
97
+ /**
98
+ * Resolve the git ref to diff against when `--diff` is passed without a value.
99
+ * We want the merge-base with the repository's default branch so a feature
100
+ * branch only scans what it actually changed (not unrelated commits already on
101
+ * the default branch). Resolution order:
102
+ *
103
+ * 1. `origin/HEAD` — the remote's default branch symref (most accurate)
104
+ * 2. `origin/main`
105
+ * 3. `origin/master`
106
+ * 4. `HEAD` — fallback when there is no remote (diffs only the work tree)
107
+ *
108
+ * For each candidate we use its merge-base with the current HEAD so the diff
109
+ * is scoped to the branch's own changes.
110
+ */
111
+ async function resolveDefaultRef(cwd) {
112
+ for (const candidate of ['origin/HEAD', 'origin/main', 'origin/master']) {
113
+ try {
114
+ const base = (await git(cwd, ['merge-base', candidate, 'HEAD'])).trim();
115
+ if (base)
116
+ return base;
117
+ }
118
+ catch {
119
+ // candidate doesn't exist (no remote / different default) — try the next.
120
+ }
121
+ }
122
+ return 'HEAD';
123
+ }
124
+ /** Absolute repo root for `cwd`, used to turn git's repo-relative paths absolute. */
125
+ async function repoRoot(cwd) {
126
+ return (await git(cwd, ['rev-parse', '--show-toplevel'])).trim();
127
+ }
128
+ function toAbsolute(root, relPaths) {
129
+ // git emits forward-slash, repo-relative paths; join under the repo root.
130
+ return relPaths
131
+ .map((p) => p.trim())
132
+ .filter((p) => p.length > 0)
133
+ .map((p) => `${root}/${p}`);
134
+ }
135
+ /**
136
+ * Files changed versus `ref` (or the default branch's merge-base when `ref` is
137
+ * undefined), as absolute paths, filtered to scannable source files.
138
+ *
139
+ * Combines four sets so nothing in flight is missed:
140
+ * - committed changes since `ref` (`git diff --name-only <ref>...`)
141
+ * - unstaged work-tree changes (`git diff --name-only`)
142
+ * - staged-but-uncommitted changes (`git diff --name-only --cached`)
143
+ * - new, not-yet-tracked files (`git ls-files --others --exclude-standard`)
144
+ *
145
+ * `--diff-filter=ACMR` keeps Added / Copied / Modified / Renamed and drops
146
+ * Deleted files (we can't scan a file that no longer exists). Untracked files
147
+ * are picked up separately because plain `git diff` only reports tracked paths —
148
+ * a freshly written, un-`add`ed source file would otherwise be missed.
149
+ *
150
+ * @throws GitError when not in a git repo (or git is unavailable).
151
+ */
152
+ export async function resolveDiffFiles(cwd, ref) {
153
+ await assertGitRepo(cwd);
154
+ const baseRef = ref ?? (await resolveDefaultRef(cwd));
155
+ const root = await repoRoot(cwd);
156
+ const seen = new Set();
157
+ const batches = await Promise.all([
158
+ // `<ref>...` (three-dot) diffs against the merge-base of ref and HEAD,
159
+ // matching how code review compares a branch to its base.
160
+ git(cwd, ['diff', '--name-only', '--diff-filter=ACMR', `${baseRef}...`]),
161
+ git(cwd, ['diff', '--name-only', '--diff-filter=ACMR']),
162
+ git(cwd, ['diff', '--name-only', '--cached', '--diff-filter=ACMR']),
163
+ // Untracked (but not git-ignored) files — `git diff` never lists these.
164
+ git(cwd, ['ls-files', '--others', '--exclude-standard']),
165
+ ]);
166
+ for (const out of batches) {
167
+ for (const abs of toAbsolute(root, out.split('\n')))
168
+ seen.add(abs);
169
+ }
170
+ return [...seen].filter(isScannableSourceFile).sort();
171
+ }
172
+ /**
173
+ * Staged files (`git diff --name-only --cached --diff-filter=ACMR`) as absolute
174
+ * paths, filtered to scannable source files. This is the set a pre-commit hook
175
+ * cares about.
176
+ *
177
+ * @throws GitError when not in a git repo (or git is unavailable).
178
+ */
179
+ export async function resolveStagedFiles(cwd) {
180
+ await assertGitRepo(cwd);
181
+ const root = await repoRoot(cwd);
182
+ const out = await git(cwd, ['diff', '--name-only', '--cached', '--diff-filter=ACMR']);
183
+ const abs = toAbsolute(root, out.split('\n'));
184
+ return [...new Set(abs)].filter(isScannableSourceFile).sort();
185
+ }
186
+ //# sourceMappingURL=changed-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changed-files.js","sourceRoot":"","sources":["../../src/core/changed-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ export interface NotifierCache {
2
+ /** Epoch ms of the last successful registry check. */
3
+ lastCheck: number;
4
+ /** The latest version string the registry reported, or null on failure. */
5
+ latestVersion: string | null;
6
+ }
7
+ export interface UpdateNotifierOptions {
8
+ /** Currently-installed version (from package.json). */
9
+ currentVersion: string;
10
+ /** True when the command emits machine-readable output (json/sarif). */
11
+ machineReadable?: boolean;
12
+ /** Explicit opt-out (e.g. `--no-update-check` or config). */
13
+ disabled?: boolean;
14
+ /** Streams/env are injected so tests stay deterministic and offline. */
15
+ stderr?: NodeJS.WriteStream & {
16
+ isTTY?: boolean;
17
+ };
18
+ env?: NodeJS.ProcessEnv;
19
+ /** Override the cache file location (tests). */
20
+ cachePath?: string;
21
+ /** Inject the registry fetch + clock (tests). */
22
+ fetchLatest?: (name: string, timeoutMs: number) => Promise<string | null>;
23
+ now?: () => number;
24
+ }
25
+ /**
26
+ * Decide whether a notice may be shown at all, independent of versions.
27
+ * Cheap, synchronous, and the single source of truth for suppression.
28
+ */
29
+ export declare function shouldCheckForUpdate(opts: {
30
+ machineReadable?: boolean;
31
+ disabled?: boolean;
32
+ stderr?: {
33
+ isTTY?: boolean;
34
+ };
35
+ env?: NodeJS.ProcessEnv;
36
+ }): boolean;
37
+ /** Default cache location: XDG_CACHE_HOME, else ~/.cache. */
38
+ export declare function defaultCachePath(env?: NodeJS.ProcessEnv): string;
39
+ /**
40
+ * Fetch the latest published version from the npm registry.
41
+ * Hits the lightweight dist-tags endpoint, validates the response, and returns
42
+ * null on any error/timeout. Nothing from the response is ever executed.
43
+ */
44
+ export declare function fetchLatestVersion(name: string, timeoutMs?: number): Promise<string | null>;
45
+ /**
46
+ * Run the update check and, when appropriate, print a single notice to stderr.
47
+ * Designed to be awaited right before the process exits so the notice appears
48
+ * AFTER the command's normal output and never interleaves.
49
+ *
50
+ * It resolves to the notice string it printed (or null), which is handy for
51
+ * tests; production callers can ignore the return value.
52
+ */
53
+ export declare function maybeNotifyUpdate(opts: UpdateNotifierOptions): Promise<string | null>;
54
+ export declare function isValidVersion(v: string): boolean;
55
+ /**
56
+ * Compare two semver strings. Returns >0 if a>b, <0 if a<b, 0 if equal.
57
+ * Honours prerelease precedence (1.0.0-rc < 1.0.0) per the semver spec's
58
+ * common cases. Invalid input sorts as "not greater" so we never nag wrongly.
59
+ */
60
+ export declare function compareSemver(a: string, b: string): number;
61
+ //# sourceMappingURL=update-notifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-notifier.d.ts","sourceRoot":"","sources":["../../src/core/update-notifier.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,GAAG;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAClD,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1E,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,GAAG,OAAO,CAYV;AAiBD,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAG7E;AAkCD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAA2B,GACrC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsDxB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4C3F;AAgBD,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA6B1D"}
@@ -0,0 +1,267 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { request as httpsRequest } from 'node:https';
3
+ import { homedir } from 'node:os';
4
+ import { dirname, join } from 'node:path';
5
+ import pc from 'picocolors';
6
+ /**
7
+ * A minimal, dependency-free "newer version available" notifier — the same
8
+ * idea as npm/eslint, kept deliberately small so it adds no transitive
9
+ * supply-chain surface (we don't pull in the `update-notifier` tree).
10
+ *
11
+ * Hard guarantees:
12
+ * - NEVER blocks or delays the command. The check is fire-and-forget against a
13
+ * ~24h cache; the network call has a short timeout and ALL errors are
14
+ * swallowed (offline is completely silent).
15
+ * - Writes ONLY to stderr, never stdout — so `--json` / `--format sarif`
16
+ * piping is never corrupted.
17
+ * - Suppressed whenever output is machine-readable, stderr isn't a TTY, a CI
18
+ * env is detected, `NO_UPDATE_NOTIFIER` is set, or the user opted out.
19
+ */
20
+ /** Package name on the npm registry. */
21
+ const PACKAGE_NAME = 'oauthlint';
22
+ /** How long a cached registry answer is trusted before we re-check. */
23
+ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24h
24
+ /** Network timeout — short, so a slow/blocked registry never stalls the CLI. */
25
+ const NETWORK_TIMEOUT_MS = 2500;
26
+ /**
27
+ * Decide whether a notice may be shown at all, independent of versions.
28
+ * Cheap, synchronous, and the single source of truth for suppression.
29
+ */
30
+ export function shouldCheckForUpdate(opts) {
31
+ const env = opts.env ?? process.env;
32
+ if (opts.disabled)
33
+ return false;
34
+ if (opts.machineReadable)
35
+ return false;
36
+ // Explicit kill-switch honoured by the whole ecosystem.
37
+ if (env.NO_UPDATE_NOTIFIER)
38
+ return false;
39
+ // Any common CI signal — never nag automated runs.
40
+ if (isCI(env))
41
+ return false;
42
+ // Piped / redirected stderr: the notice would land in a log or get mangled.
43
+ const stderr = opts.stderr ?? process.stderr;
44
+ if (!stderr.isTTY)
45
+ return false;
46
+ return true;
47
+ }
48
+ function isCI(env) {
49
+ return Boolean(env.CI ||
50
+ env.CONTINUOUS_INTEGRATION ||
51
+ env.BUILD_NUMBER ||
52
+ env.GITHUB_ACTIONS ||
53
+ env.GITLAB_CI ||
54
+ env.CIRCLECI ||
55
+ env.TRAVIS ||
56
+ env.JENKINS_URL ||
57
+ env.TEAMCITY_VERSION ||
58
+ env.BITBUCKET_BUILD_NUMBER);
59
+ }
60
+ /** Default cache location: XDG_CACHE_HOME, else ~/.cache. */
61
+ export function defaultCachePath(env = process.env) {
62
+ const base = env.XDG_CACHE_HOME?.trim() || join(homedir(), '.cache');
63
+ return join(base, 'oauthlint', 'update-check.json');
64
+ }
65
+ async function readCache(path) {
66
+ try {
67
+ const raw = await readFile(path, 'utf8');
68
+ const parsed = JSON.parse(raw);
69
+ if (parsed &&
70
+ typeof parsed === 'object' &&
71
+ typeof parsed.lastCheck === 'number') {
72
+ const c = parsed;
73
+ const v = c.latestVersion;
74
+ // Validate the cached version: only accept null or a sane semver-ish
75
+ // string. Never trust arbitrary content (defends against a tampered file).
76
+ if (v === null || (typeof v === 'string' && isValidVersion(v))) {
77
+ return { lastCheck: c.lastCheck, latestVersion: v };
78
+ }
79
+ }
80
+ }
81
+ catch {
82
+ /* missing/corrupt cache → treat as no cache */
83
+ }
84
+ return null;
85
+ }
86
+ async function writeCache(path, cache) {
87
+ try {
88
+ await mkdir(dirname(path), { recursive: true });
89
+ await writeFile(path, JSON.stringify(cache), 'utf8');
90
+ }
91
+ catch {
92
+ /* best-effort: a read-only home must never break the command */
93
+ }
94
+ }
95
+ /**
96
+ * Fetch the latest published version from the npm registry.
97
+ * Hits the lightweight dist-tags endpoint, validates the response, and returns
98
+ * null on any error/timeout. Nothing from the response is ever executed.
99
+ */
100
+ export function fetchLatestVersion(name, timeoutMs = NETWORK_TIMEOUT_MS) {
101
+ return new Promise((resolve) => {
102
+ let settled = false;
103
+ const done = (v) => {
104
+ if (settled)
105
+ return;
106
+ settled = true;
107
+ resolve(v);
108
+ };
109
+ const req = httpsRequest({
110
+ method: 'GET',
111
+ hostname: 'registry.npmjs.org',
112
+ path: `/-/package/${encodeURIComponent(name)}/dist-tags`,
113
+ headers: { accept: 'application/json' },
114
+ timeout: timeoutMs,
115
+ }, (res) => {
116
+ if (res.statusCode !== 200) {
117
+ res.resume();
118
+ done(null);
119
+ return;
120
+ }
121
+ let body = '';
122
+ let size = 0;
123
+ res.setEncoding('utf8');
124
+ res.on('data', (chunk) => {
125
+ size += chunk.length;
126
+ // Cap the response we'll buffer — defensive against a hostile/huge body.
127
+ if (size > 64 * 1024) {
128
+ req.destroy();
129
+ done(null);
130
+ return;
131
+ }
132
+ body += chunk;
133
+ });
134
+ res.on('end', () => {
135
+ try {
136
+ const json = JSON.parse(body);
137
+ const latest = json.latest;
138
+ done(typeof latest === 'string' && isValidVersion(latest) ? latest : null);
139
+ }
140
+ catch {
141
+ done(null);
142
+ }
143
+ });
144
+ });
145
+ req.on('timeout', () => {
146
+ req.destroy();
147
+ done(null);
148
+ });
149
+ req.on('error', () => done(null));
150
+ req.end();
151
+ });
152
+ }
153
+ /**
154
+ * Run the update check and, when appropriate, print a single notice to stderr.
155
+ * Designed to be awaited right before the process exits so the notice appears
156
+ * AFTER the command's normal output and never interleaves.
157
+ *
158
+ * It resolves to the notice string it printed (or null), which is handy for
159
+ * tests; production callers can ignore the return value.
160
+ */
161
+ export async function maybeNotifyUpdate(opts) {
162
+ const env = opts.env ?? process.env;
163
+ const stderr = opts.stderr ?? process.stderr;
164
+ if (!shouldCheckForUpdate({
165
+ machineReadable: opts.machineReadable,
166
+ disabled: opts.disabled,
167
+ stderr,
168
+ env,
169
+ })) {
170
+ return null;
171
+ }
172
+ // A bad/dev version (e.g. '0.0.0') can't be meaningfully compared.
173
+ if (!isValidVersion(opts.currentVersion))
174
+ return null;
175
+ const now = opts.now ?? Date.now;
176
+ const cachePath = opts.cachePath ?? defaultCachePath(env);
177
+ const fetchLatest = opts.fetchLatest ?? fetchLatestVersion;
178
+ try {
179
+ const cache = await readCache(cachePath);
180
+ let latest = cache?.latestVersion ?? null;
181
+ const fresh = cache !== null && now() - cache.lastCheck < CHECK_INTERVAL_MS;
182
+ if (!fresh) {
183
+ // Cache is stale or absent → check the registry, then persist the answer
184
+ // (and the timestamp) so we don't hit the network again for ~24h.
185
+ latest = await fetchLatest(PACKAGE_NAME, NETWORK_TIMEOUT_MS);
186
+ await writeCache(cachePath, { lastCheck: now(), latestVersion: latest });
187
+ }
188
+ if (!latest || !isValidVersion(latest))
189
+ return null;
190
+ if (compareSemver(latest, opts.currentVersion) <= 0)
191
+ return null;
192
+ const notice = formatNotice(opts.currentVersion, latest);
193
+ stderr.write(`${notice}\n`);
194
+ return notice;
195
+ }
196
+ catch {
197
+ // Belt-and-braces: nothing here may ever surface to the user.
198
+ return null;
199
+ }
200
+ }
201
+ function formatNotice(current, latest) {
202
+ const headline = `${pc.dim('Update available')} ${pc.dim(current)} ${pc.dim('→')} ${pc.green(latest)}`;
203
+ const upgrade = `Run ${pc.cyan('npm i -g oauthlint')} ${pc.dim('(or')} ${pc.cyan('npx oauthlint@latest')}${pc.dim(')')}`;
204
+ const optOut = pc.dim('Disable: --no-update-check or NO_UPDATE_NOTIFIER=1');
205
+ return `\n${headline}\n${upgrade}\n${optOut}`;
206
+ }
207
+ // ---------------------------------------------------------------------------
208
+ // Semver: a tiny, self-contained comparator. Enough for "is latest > current",
209
+ // including basic prerelease ordering, without a dependency.
210
+ // ---------------------------------------------------------------------------
211
+ const VERSION_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
212
+ export function isValidVersion(v) {
213
+ return VERSION_RE.test(v.trim());
214
+ }
215
+ /**
216
+ * Compare two semver strings. Returns >0 if a>b, <0 if a<b, 0 if equal.
217
+ * Honours prerelease precedence (1.0.0-rc < 1.0.0) per the semver spec's
218
+ * common cases. Invalid input sorts as "not greater" so we never nag wrongly.
219
+ */
220
+ export function compareSemver(a, b) {
221
+ const pa = parseVersion(a);
222
+ const pb = parseVersion(b);
223
+ if (!pa || !pb)
224
+ return 0;
225
+ for (let i = 0; i < 3; i++) {
226
+ if (pa.main[i] !== pb.main[i])
227
+ return pa.main[i] - pb.main[i];
228
+ }
229
+ // Equal core. A version WITH a prerelease is lower than one without.
230
+ if (pa.pre.length === 0 && pb.pre.length === 0)
231
+ return 0;
232
+ if (pa.pre.length === 0)
233
+ return 1; // a is a release, b is a prerelease
234
+ if (pb.pre.length === 0)
235
+ return -1; // a is a prerelease, b is a release
236
+ const len = Math.max(pa.pre.length, pb.pre.length);
237
+ for (let i = 0; i < len; i++) {
238
+ const x = pa.pre[i];
239
+ const y = pb.pre[i];
240
+ if (x === undefined)
241
+ return -1; // shorter prerelease set has lower precedence
242
+ if (y === undefined)
243
+ return 1;
244
+ if (x === y)
245
+ continue;
246
+ const xn = /^\d+$/.test(x);
247
+ const yn = /^\d+$/.test(y);
248
+ if (xn && yn)
249
+ return Number(x) - Number(y);
250
+ if (xn)
251
+ return -1; // numeric identifiers are lower than alphanumeric
252
+ if (yn)
253
+ return 1;
254
+ return x < y ? -1 : 1; // lexical ASCII order
255
+ }
256
+ return 0;
257
+ }
258
+ function parseVersion(v) {
259
+ const m = VERSION_RE.exec(v.trim());
260
+ if (!m)
261
+ return null;
262
+ return {
263
+ main: [Number(m[1]), Number(m[2]), Number(m[3])],
264
+ pre: m[4] ? m[4].split('.') : [],
265
+ };
266
+ }
267
+ //# sourceMappingURL=update-notifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-notifier.js","sourceRoot":"","sources":["../../src/core/update-notifier.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { buildProgram } from './cli.js';
2
2
  export { runScan, type ScanCommandOptions } from './commands/scan.js';
3
+ export { runBaseline, type BaselineCommandOptions } from './commands/baseline.js';
4
+ export { BASELINE_VERSION, DEFAULT_BASELINE_FILE, type Baseline, type BaselineEntry, type BaselineFile, BaselineNotFoundError, BaselineParseError, buildBaseline, fingerprintFindings, loadBaseline, partitionByBaseline, serialiseBaseline, } from './core/baseline.js';
3
5
  export { runList, type ListOptions } from './commands/list.js';
4
6
  export { runInit, type InitOptions } from './commands/init.js';
5
7
  export { runDoctor, type DoctorOptions } from './commands/doctor.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EACL,WAAW,EACX,eAAe,EACf,cAAc,EACd,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,eAAe,EACf,OAAO,EACP,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EACL,WAAW,EACX,eAAe,EACf,cAAc,EACd,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,eAAe,EACf,OAAO,EACP,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  export { buildProgram } from './cli.js';
2
2
  export { runScan } from './commands/scan.js';
3
+ export { runBaseline } from './commands/baseline.js';
4
+ export { BASELINE_VERSION, DEFAULT_BASELINE_FILE, BaselineNotFoundError, BaselineParseError, buildBaseline, fingerprintFindings, loadBaseline, partitionByBaseline, serialiseBaseline, } from './core/baseline.js';
3
5
  export { runList } from './commands/list.js';
4
6
  export { runInit } from './commands/init.js';
5
7
  export { runDoctor } from './commands/doctor.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAA2B,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAoB,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAoB,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAsB,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAwB,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EACL,WAAW,EACX,eAAe,EACf,cAAc,EACd,YAAY,GACb,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,OAAO,EAA2B,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,WAAW,EAA+B,MAAM,wBAAwB,CAAC;AAClF,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EAIrB,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAoB,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAoB,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAsB,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAwB,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EACL,WAAW,EACX,eAAe,EACf,cAAc,EACd,YAAY,GACb,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauthlint",
3
- "version": "0.2.4",
3
+ "version": "0.4.0",
4
4
  "description": "Catch the OAuth/OIDC/JWT anti-patterns AI coding tools systematically produce. CLI wrapper around the oauthlint-rules Semgrep rule pack.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -47,7 +47,7 @@
47
47
  "picocolors": "1.1.1",
48
48
  "yaml": "2.9.0",
49
49
  "zod": "3.24.1",
50
- "oauthlint-rules": "0.2.2"
50
+ "oauthlint-rules": "0.2.4"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/node": "22.10.5",