@fitsummehari/mergeguard 0.1.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.
- package/CHANGELOG.md +19 -0
- package/LICENSE +21 -0
- package/README.md +290 -0
- package/SECURITY.md +26 -0
- package/bin/mergeguard.js +11 -0
- package/docs/ARCHITECTURE.md +31 -0
- package/docs/CONFIGURATION.md +59 -0
- package/docs/DETECTORS.md +29 -0
- package/docs/INTEGRATIONS.md +40 -0
- package/docs/LAYA.md +82 -0
- package/docs/LIMITATIONS.md +10 -0
- package/docs/MIGRATION_FROM_V1.md +27 -0
- package/examples/github/mergeguard.yml +32 -0
- package/examples/gitlab/mergeguard.yml +15 -0
- package/package.json +62 -0
- package/scripts/laya_bridge.py +26 -0
- package/src/ci.js +58 -0
- package/src/cli.js +229 -0
- package/src/config.js +137 -0
- package/src/context.js +158 -0
- package/src/detectors/patterns.js +180 -0
- package/src/detectors/repository.js +45 -0
- package/src/doctor.js +70 -0
- package/src/git.js +269 -0
- package/src/hook.js +79 -0
- package/src/index.js +9 -0
- package/src/languages.js +29 -0
- package/src/package-meta.js +17 -0
- package/src/paths.js +33 -0
- package/src/reporters/gitlab.js +16 -0
- package/src/reporters/index.js +13 -0
- package/src/reporters/sarif.js +55 -0
- package/src/reporters/terminal.js +48 -0
- package/src/review.js +137 -0
- package/src/types.js +36 -0
- package/src/utils.js +183 -0
- package/src/verifiers/index.js +48 -0
- package/src/verifiers/jev.js +75 -0
- package/src/verifiers/laya.js +131 -0
- package/src/verifiers/offline.js +42 -0
- package/src/verifiers/questions.js +29 -0
- package/src/version.js +1 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
This project follows [Semantic Versioning](https://semver.org/). `0.1.0` is the first public beta. There is no prior public npm release of this local-first CLI.
|
|
6
|
+
|
|
7
|
+
## 0.1.0
|
|
8
|
+
|
|
9
|
+
First public beta of MergeGuard as a local-first semantic pre-push / code-change gate.
|
|
10
|
+
|
|
11
|
+
- Single review engine shared by the CLI, Git pre-push hook, GitHub Actions, GitLab CI, generic CI, and the Node API
|
|
12
|
+
- Working-tree, staged, `--base`/`--head`, and pre-push Git scopes, including first commits and first pushes against Git's empty tree
|
|
13
|
+
- High-signal detectors for security, authorization, tenant isolation, concurrency, database integrity, and related runtime risk
|
|
14
|
+
- Bounded ephemeral repository context (no persisted project database)
|
|
15
|
+
- Published as `@fitsummehari/mergeguard` (CLI binary remains `mergeguard`; unscoped `mergeguard` is a different npm package)
|
|
16
|
+
- Deterministic `offline` verifier by default (reproducible); `auto` and local Laya optional; optional Jev
|
|
17
|
+
- Terminal, JSON, SARIF, and GitLab Code Quality reporters
|
|
18
|
+
- Non-destructive, idempotent hook install/uninstall that respects `core.hooksPath`
|
|
19
|
+
- Zero runtime npm dependencies; Node.js 20+; Laya model weights are not bundled
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MergeGuard contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# MergeGuard
|
|
2
|
+
|
|
3
|
+
MergeGuard is a local-first semantic code-change gate that catches high-impact bugs before code is pushed or merged.
|
|
4
|
+
|
|
5
|
+
It reviews a Git diff — not an entire repository — for concrete runtime risks such as authorization regressions, tenant-isolation holes, check-then-write races, unsafe SQL or shell construction, and removed transaction boundaries. It is not a SaaS platform and not a style linter.
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
Git diff
|
|
9
|
+
↓
|
|
10
|
+
MergeGuard review engine
|
|
11
|
+
├── deterministic/static detectors
|
|
12
|
+
├── repository/context resolver
|
|
13
|
+
└── optional semantic verifier
|
|
14
|
+
├── local Laya
|
|
15
|
+
└── optional Jev/provider
|
|
16
|
+
↓
|
|
17
|
+
normalized findings
|
|
18
|
+
↓
|
|
19
|
+
terminal / JSON / SARIF / GitLab
|
|
20
|
+
↓
|
|
21
|
+
PASS / BLOCK
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The same engine is used by the CLI, a Git pre-push hook, GitHub Actions, GitLab CI, other CI systems, and the programmatic Node API.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -D @fitsummehari/mergeguard
|
|
28
|
+
npx mergeguard review
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Requires only **Node.js 20+** and **Git** for the default offline experience. The CLI binary is `mergeguard`; the npm package is scoped because the unscoped name is owned by a different project.
|
|
32
|
+
|
|
33
|
+
## Why it exists
|
|
34
|
+
|
|
35
|
+
Compilers, tests, and formatters do not reliably catch "this change will fail under concurrency" or "this query no longer filters by tenant." MergeGuard sits in front of `git push` and CI so those classes of defect can block the change with a normal process exit code. No MergeGuard server, database, dashboard, GitHub App, or webhook service is required.
|
|
36
|
+
|
|
37
|
+
## What it catches
|
|
38
|
+
|
|
39
|
+
High-signal, change-scoped risks, including:
|
|
40
|
+
|
|
41
|
+
- authorization / route-protection regressions
|
|
42
|
+
- tenant or ownership filter removal
|
|
43
|
+
- check-then-create and read-check-write races
|
|
44
|
+
- missing uniqueness / idempotency around creates and webhooks
|
|
45
|
+
- removed transaction boundaries and non-atomic related writes
|
|
46
|
+
- unbounded deletes or empty-filter updates
|
|
47
|
+
- SQL injection, command injection, dynamic evaluation
|
|
48
|
+
- unsafe deserialization and path traversal
|
|
49
|
+
- XSS / raw HTML sinks where the sink is visible
|
|
50
|
+
- disabled TLS verification
|
|
51
|
+
- async `forEach`, swallowed errors, N+1 query and unbounded fan-out candidates
|
|
52
|
+
- validation removal and insecure framework markers such as `@Public()` / `[AllowAnonymous]`
|
|
53
|
+
|
|
54
|
+
## What it deliberately does not catch
|
|
55
|
+
|
|
56
|
+
MergeGuard is not a style linter and will not report:
|
|
57
|
+
|
|
58
|
+
- semicolons, quotes, import order, or naming taste
|
|
59
|
+
- formatting or line length
|
|
60
|
+
- generic "consider refactoring" advice
|
|
61
|
+
- documentation nags
|
|
62
|
+
- arbitrary function-length opinions
|
|
63
|
+
- subjective AI code-review commentary
|
|
64
|
+
|
|
65
|
+
It is also not a CVE database, secret scanner, compiler, or test runner.
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install -D @fitsummehari/mergeguard
|
|
71
|
+
npx mergeguard review
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Zero runtime npm dependencies. Python is **not** required for default operation.
|
|
75
|
+
|
|
76
|
+
### One-off invocation without a local install
|
|
77
|
+
|
|
78
|
+
Do **not** run bare `npx mergeguard` before installing — npm may resolve the unrelated unscoped `mergeguard` package.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx --package=@fitsummehari/mergeguard mergeguard review
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
After a normal local install, `npx mergeguard review` is correct because npm uses the local binary.
|
|
85
|
+
|
|
86
|
+
### From this source tree
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
node bin/mergeguard.js review
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 30-second quick start
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cd your-git-repo
|
|
96
|
+
npm install -D @fitsummehari/mergeguard
|
|
97
|
+
npx mergeguard review
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
That reviews the working tree (staged, unstaged, and untracked) against `HEAD`. Exit `0` means pass. Exit `1` means a blocking finding. Exit `2` means MergeGuard or the environment failed. The default verifier is **offline** (reproducible across machines).
|
|
101
|
+
|
|
102
|
+
## Local review
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npx mergeguard review
|
|
106
|
+
npx mergeguard review --staged
|
|
107
|
+
npx mergeguard review --base origin/main
|
|
108
|
+
npx mergeguard review --base origin/main --head HEAD
|
|
109
|
+
npx mergeguard review --verifier offline
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`--no-ai` and `--verifier offline` / `--verifier deterministic` force the built-in verifier.
|
|
113
|
+
|
|
114
|
+
## Pre-push hook
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npx mergeguard hook install
|
|
118
|
+
npx mergeguard hook status
|
|
119
|
+
npx mergeguard hook uninstall
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Install is idempotent and will not overwrite an existing pre-push hook. It appends a marked MergeGuard block and respects `core.hooksPath` when set. The hook reads Git's pre-push stdin and reviews the outgoing commit range, including first pushes (diffed against Git's empty tree).
|
|
123
|
+
|
|
124
|
+
If MergeGuard blocks a push, it tells you how to bypass **only when relevant**:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
git push --no-verify
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Use CI as the enforcement layer. Local hooks are a convenience, not a security boundary.
|
|
131
|
+
|
|
132
|
+
## GitHub Actions
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx mergeguard ci github --write
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Or copy [`examples/github/mergeguard.yml`](examples/github/mergeguard.yml). Default templates install `@fitsummehari/mergeguard`, pin `--verifier offline`, and can upload SARIF. Make the job a required check if you want enforcement. Optional Laya-in-CI recipes are in [docs/LAYA.md](docs/LAYA.md).
|
|
139
|
+
|
|
140
|
+
## GitLab CI
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npx mergeguard ci gitlab --write
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Or copy [`examples/gitlab/mergeguard.yml`](examples/gitlab/mergeguard.yml). The job writes GitLab Code Quality JSON as an artifact and still uses the CLI exit code to block.
|
|
147
|
+
|
|
148
|
+
## Other CI
|
|
149
|
+
|
|
150
|
+
Any runner with Git and Node 20+ can run the same engine after installing the scoped package:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npm install -D @fitsummehari/mergeguard
|
|
154
|
+
npx mergeguard review --verifier offline --base <base> --head <head>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
That works on Jenkins, CircleCI, Azure DevOps, Bitbucket, Buildkite, and custom CI. No provider credentials are required for offline verification.
|
|
158
|
+
|
|
159
|
+
## Configuration
|
|
160
|
+
|
|
161
|
+
Zero-config defaults to the offline verifier. To pin policy:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npx mergeguard init
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
This writes `.mergeguard.yml` and will not overwrite an existing file unless you pass `--force`.
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
fail_on: high
|
|
171
|
+
|
|
172
|
+
exclude:
|
|
173
|
+
- dist/**
|
|
174
|
+
- build/**
|
|
175
|
+
- generated/**
|
|
176
|
+
- vendor/**
|
|
177
|
+
- node_modules/**
|
|
178
|
+
|
|
179
|
+
review:
|
|
180
|
+
security: true
|
|
181
|
+
correctness: true
|
|
182
|
+
concurrency: true
|
|
183
|
+
database: true
|
|
184
|
+
performance: true
|
|
185
|
+
|
|
186
|
+
verifier:
|
|
187
|
+
engine: offline
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`fail_on` may be `critical`, `high`, `medium`, `low`, `info`, or `none`. Unknown root keys produce a warning. Malformed YAML reports the file path and line. See [docs/CONFIGURATION.md](docs/CONFIGURATION.md).
|
|
191
|
+
|
|
192
|
+
`auto` is available but environment-dependent (uses Laya when installed). Prefer pinning `offline` or `laya` for team/CI reproducibility. `mergeguard doctor` shows configured vs effective verifier.
|
|
193
|
+
|
|
194
|
+
## Output formats
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npx mergeguard review --format terminal
|
|
198
|
+
npx mergeguard review --format json
|
|
199
|
+
npx mergeguard review --format sarif --output mergeguard.sarif
|
|
200
|
+
npx mergeguard review --format gitlab --output gl-code-quality-report.json
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Machine-readable formats write only the report to stdout (or `--output`). Status goes to stderr so JSON/SARIF/GitLab output stays parseable.
|
|
204
|
+
|
|
205
|
+
## Exit codes
|
|
206
|
+
|
|
207
|
+
| Code | Meaning |
|
|
208
|
+
| --- | --- |
|
|
209
|
+
| `0` | Review completed; no blocking finding |
|
|
210
|
+
| `1` | Review completed; policy blocked the change |
|
|
211
|
+
| `2` | MergeGuard, config, Git, or runtime failure |
|
|
212
|
+
|
|
213
|
+
## Optional Laya
|
|
214
|
+
|
|
215
|
+
Laya is **not** bundled with the npm package. MergeGuard works without it. Keeping model weights out of npm keeps the install small (~40 KB) and lets default CI run on Node alone.
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
python -m pip install laya
|
|
219
|
+
npx mergeguard doctor
|
|
220
|
+
npx mergeguard review --verifier laya
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`--verifier laya` exits `2` with an install hint if Laya is missing. It does **not** silently fall back. See [docs/LAYA.md](docs/LAYA.md).
|
|
224
|
+
|
|
225
|
+
MergeGuard talks to Laya through a short-lived local Python process over stdin/stdout. It does not start an HTTP server. First model use may download a checkpoint into the Hugging Face cache.
|
|
226
|
+
|
|
227
|
+
## Optional Jev
|
|
228
|
+
|
|
229
|
+
Jev is opt-in and never required for default operation.
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
export TYPESAFE_API_KEY=... # or JEV_API_KEY
|
|
233
|
+
npx mergeguard review --verifier jev
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Jev sends candidate evidence to the configured HTTP API. Secrets are read from the environment and are not printed.
|
|
237
|
+
|
|
238
|
+
## Programmatic API
|
|
239
|
+
|
|
240
|
+
```js
|
|
241
|
+
import { review } from "@fitsummehari/mergeguard";
|
|
242
|
+
|
|
243
|
+
const result = await review({
|
|
244
|
+
cwd: process.cwd(),
|
|
245
|
+
base: "origin/main",
|
|
246
|
+
head: "HEAD",
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// result.passed, result.findings, result.summary, result.metadata
|
|
250
|
+
if (!result.passed) process.exitCode = 1;
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Also exported: `reviewChangeSet`, `loadConfig`, `normalizeConfig`, `renderReport`, `toSarif`, `toGitLabCodeQuality`, `detectLaya`, `VERSION`. Everything else is internal.
|
|
254
|
+
|
|
255
|
+
## Language-support philosophy
|
|
256
|
+
|
|
257
|
+
MergeGuard **runs on any normal Git repository**. That is not the same as equal deep semantics for every language.
|
|
258
|
+
|
|
259
|
+
JavaScript, TypeScript, Python, Java, Kotlin, Go, C#, PHP, Ruby, and SQL have additional syntax-aware rules. Rust, C, C++, shell, YAML, JSON, Terraform, Dockerfiles, and mixed monorepos still get generic analysis and must not crash merely because a deeper adapter is missing.
|
|
260
|
+
|
|
261
|
+
## Limitations
|
|
262
|
+
|
|
263
|
+
- Findings are candidates plus a verifier, not proofs.
|
|
264
|
+
- Context is bounded — not a persisted project database.
|
|
265
|
+
- Huge diffs are truncated; generated/minified/binary paths are skipped.
|
|
266
|
+
- Local hooks can be bypassed with `--no-verify`.
|
|
267
|
+
- Semantic depth varies by language and framework.
|
|
268
|
+
|
|
269
|
+
See [docs/LIMITATIONS.md](docs/LIMITATIONS.md) and [docs/DETECTORS.md](docs/DETECTORS.md).
|
|
270
|
+
|
|
271
|
+
## Security and privacy
|
|
272
|
+
|
|
273
|
+
Analyzed repository text is treated as untrusted data, never as instructions or executable code. Offline mode makes no network calls. Laya mode stays on the machine aside from Laya/Hugging Face model-cache behavior. Jev mode is explicit and sends candidate context to the configured API.
|
|
274
|
+
|
|
275
|
+
Do not put credentials in `.mergeguard.yml`. See [SECURITY.md](SECURITY.md).
|
|
276
|
+
|
|
277
|
+
## Contributing
|
|
278
|
+
|
|
279
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
git clone https://github.com/FitsumMehari/MergeGuard.git
|
|
283
|
+
cd MergeGuard
|
|
284
|
+
npm test
|
|
285
|
+
npm run check
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
[MIT](LICENSE)
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
MergeGuard reviews untrusted repository text. That text is evidence only. It is never executed, never sourced as configuration code, and never treated as verifier instructions.
|
|
4
|
+
|
|
5
|
+
## Reporting a vulnerability
|
|
6
|
+
|
|
7
|
+
This project does not publish a dedicated security email. Please use [GitHub private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability) on [FitsumMehari/MergeGuard](https://github.com/FitsumMehari/MergeGuard/security/advisories/new) if the repository has that feature enabled.
|
|
8
|
+
|
|
9
|
+
Maintainers: enable **Settings → Code security → Private vulnerability reporting** so researchers have a private channel.
|
|
10
|
+
|
|
11
|
+
If private reporting is unavailable, open a GitHub issue titled "Security: please contact maintainers" with no exploit details and wait for a maintainer to reply privately.
|
|
12
|
+
|
|
13
|
+
## Privacy model
|
|
14
|
+
|
|
15
|
+
- **Offline / deterministic:** no network calls.
|
|
16
|
+
- **Laya:** local Python process. Laya/Hugging Face may download a model checkpoint into a local cache on first use.
|
|
17
|
+
- **Jev:** explicit opt-in. Candidate context is sent to the configured HTTP API. Credentials come from `TYPESAFE_API_KEY` or `JEV_API_KEY` and must not be placed in `.mergeguard.yml` or committed.
|
|
18
|
+
|
|
19
|
+
Do not include secrets in reports, fixtures, or configuration examples.
|
|
20
|
+
|
|
21
|
+
## Trust boundary
|
|
22
|
+
|
|
23
|
+
- Git filenames, diffs, and file contents from the reviewed repository are untrusted.
|
|
24
|
+
- MergeGuard must not run scripts from that repository.
|
|
25
|
+
- Analyzed paths are constrained to the repository root.
|
|
26
|
+
- Binary, oversized, and ignored files are skipped.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { main } from "../src/cli.js";
|
|
3
|
+
|
|
4
|
+
main(process.argv.slice(2)).then(
|
|
5
|
+
(code) => process.exit(code ?? 0),
|
|
6
|
+
(error) => {
|
|
7
|
+
console.error(`mergeguard: ${error?.message || error}`);
|
|
8
|
+
if (process.env.MERGEGUARD_DEBUG === "1" && error?.stack) console.error(error.stack);
|
|
9
|
+
process.exit(2);
|
|
10
|
+
},
|
|
11
|
+
);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
MergeGuard is a single-process CLI and Node library. There is no application server, database, queue, dashboard, or webhook listener.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
CLI / hook / CI / JS API
|
|
7
|
+
│
|
|
8
|
+
├─ Git scope resolver
|
|
9
|
+
│ working | staged | branch range | pre-push
|
|
10
|
+
│
|
|
11
|
+
├─ detector pipeline
|
|
12
|
+
│ pattern detectors
|
|
13
|
+
│ diff-regression detectors
|
|
14
|
+
│ repository-aware detectors
|
|
15
|
+
│
|
|
16
|
+
├─ context resolver
|
|
17
|
+
│ changed files + nearby high-signal files
|
|
18
|
+
│ manifests / schemas / auth / migrations
|
|
19
|
+
│
|
|
20
|
+
├─ verifier
|
|
21
|
+
│ offline (built-in)
|
|
22
|
+
│ Laya (local Python, optional)
|
|
23
|
+
│ Jev (remote, optional)
|
|
24
|
+
│
|
|
25
|
+
└─ reporters
|
|
26
|
+
terminal | JSON | SARIF | GitLab Code Quality
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Public library types live in `src/types.js` (`ReviewResult`, `Finding`, `ChangedFile`, severity/category). Additional detectors, language adapters, verifiers, and reporters should plug into this pipeline rather than forking it.
|
|
30
|
+
|
|
31
|
+
Repository context is resolved per review and discarded. Nothing is persisted.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
MergeGuard works with no config file. When present, it reads the first of:
|
|
4
|
+
|
|
5
|
+
- `.mergeguard.yml`
|
|
6
|
+
- `.mergeguard.yaml`
|
|
7
|
+
- `.mergeguard.json`
|
|
8
|
+
|
|
9
|
+
Or `--config PATH`. `mergeguard init` writes a conservative `.mergeguard.yml` and refuses to overwrite unless `--force` is passed.
|
|
10
|
+
|
|
11
|
+
## Defaults
|
|
12
|
+
|
|
13
|
+
| Field | Default | Notes |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `fail_on` | `high` | Also accepts `failOn`. Values: `critical`, `high`, `medium`, `low`, `info`, `none` |
|
|
16
|
+
| `verifier.engine` | `offline` | `offline` (default, reproducible), `deterministic` (alias), `auto` (Laya if installed), `laya`, `jev` |
|
|
17
|
+
| `confidence` | `0.62` | Verifier strength threshold in `0..1` |
|
|
18
|
+
| `exclude` | `node_modules/**`, `vendor/**`, `dist/**`, `build/**`, `coverage/**`, `.next/**`, `.git/**`, `**/*.min.js`, `**/*.map`, `**/generated/**` | Also accepts `ignore` / `ignore.paths` |
|
|
19
|
+
| `review.*` | all categories `true` | Disable a category with `false` |
|
|
20
|
+
| `max_files` | `300` (cap 5000) | Changed files analyzed |
|
|
21
|
+
| `max_candidates` | `120` (cap 1000) | Detector candidates kept |
|
|
22
|
+
| `context_files` | `24` (cap 100) | Nearby files loaded as evidence |
|
|
23
|
+
| `context_chars` | `18000` (cap 200000) | Evidence budget per candidate |
|
|
24
|
+
|
|
25
|
+
Unknown root keys are ignored with a warning that includes the config path.
|
|
26
|
+
|
|
27
|
+
## Example
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
fail_on: high
|
|
31
|
+
exclude:
|
|
32
|
+
- dist/**
|
|
33
|
+
- generated/**
|
|
34
|
+
review:
|
|
35
|
+
security: true
|
|
36
|
+
performance: false
|
|
37
|
+
verifier:
|
|
38
|
+
engine: offline
|
|
39
|
+
laya:
|
|
40
|
+
python: python3
|
|
41
|
+
max_len: 4096
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Environment
|
|
45
|
+
|
|
46
|
+
| Variable | Purpose |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `MERGEGUARD_PYTHON` | Python executable for Laya |
|
|
49
|
+
| `MERGEGUARD_LAYA_TIMEOUT_MS` | Laya subprocess timeout (default 180000) |
|
|
50
|
+
| `MERGEGUARD_JEV_TIMEOUT_MS` | Jev HTTP timeout (default 30000) |
|
|
51
|
+
| `TYPESAFE_API_KEY` / `JEV_API_KEY` | Jev only |
|
|
52
|
+
| `MERGEGUARD_BIN` | Pre-push hook executable override |
|
|
53
|
+
| `MERGEGUARD_REMOTE` | Remote name passed into the hook |
|
|
54
|
+
| `MERGEGUARD_DEBUG=1` | Print stack traces on CLI failures |
|
|
55
|
+
| `NO_COLOR` | Disable ANSI color |
|
|
56
|
+
|
|
57
|
+
## Limits
|
|
58
|
+
|
|
59
|
+
Tracked-file scoring for context is capped at 4000 paths. Individual file reads stop at 2 MiB and skip binary (NUL) content. Symlinks that escape the repository root are not read.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Detector coverage
|
|
2
|
+
|
|
3
|
+
MergeGuard runs only against changed content/diff regressions and then adds bounded repository context.
|
|
4
|
+
|
|
5
|
+
## High-signal categories
|
|
6
|
+
|
|
7
|
+
| Category | Examples |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Security | dynamic evaluation, SQL injection candidates, shell injection, TLS verification disabled, raw HTML, unsafe deserialization, path traversal, sensitive logging |
|
|
10
|
+
| Authorization | auth guard/policy removal, anonymous/public access introduced, token decode without visible verification |
|
|
11
|
+
| Tenant isolation | tenant/organization/owner predicates removed; repository-aware missing isolation candidates |
|
|
12
|
+
| Concurrency | check-then-create, read-check-write, webhook replay/idempotency candidates |
|
|
13
|
+
| Database | transaction removal, multi-write atomicity, broad delete/update, uniqueness removal, risky migrations |
|
|
14
|
+
| Correctness/reliability | async forEach, swallowed exceptions, unsafe retry behavior, input-validation removal, hard-coded local endpoints |
|
|
15
|
+
| Performance | N+1 query candidates, unbounded async fan-out, potentially blocking index creation |
|
|
16
|
+
|
|
17
|
+
## Language-aware rules
|
|
18
|
+
|
|
19
|
+
The generic layer runs in any text Git repository. Additional direct rules currently recognize JavaScript/TypeScript, Python, Java/Kotlin, Go, PHP, Ruby, C#, SQL and common configuration forms. C/C++, Rust, shell, Terraform, Dockerfiles and other languages still benefit from generic diff-regression, TLS/SQL/configuration, repository and migration checks, but have fewer syntax-specific rules today. `yaml.load` is ignored when `SafeLoader` is present on the same line.
|
|
20
|
+
|
|
21
|
+
## Stack/context recognition
|
|
22
|
+
|
|
23
|
+
The context resolver recognizes common signals for NestJS, Next.js, Express, Fastify, Django, FastAPI, Flask, Spring, Laravel, Symfony, Rails, ASP.NET Core, Gin/Fiber, Actix/Axum; Prisma, TypeORM, Sequelize, SQLAlchemy, Entity Framework, GORM, Hibernate, Diesel/SQLx; PostgreSQL, MySQL/MariaDB, MongoDB, SQLite and Redis.
|
|
24
|
+
|
|
25
|
+
Recognition does not imply perfect semantic parsing. It is used to select useful evidence and reduce false positives.
|
|
26
|
+
|
|
27
|
+
## What is intentionally excluded
|
|
28
|
+
|
|
29
|
+
MergeGuard does not compete with formatters/linters on formatting, naming, import order, line length or generic maintainability opinions. It also does not include a vulnerability-database service, dependency CVE feed, secret-management backend, compiler or test runner.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Integrations
|
|
2
|
+
|
|
3
|
+
All surfaces call the same `review()` engine.
|
|
4
|
+
|
|
5
|
+
Install once:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -D @fitsummehari/mergeguard
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Do not install the unscoped name `mergeguard` (different package). Do not use bare `npx mergeguard` until the scoped package is installed locally; for a one-off:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx --package=@fitsummehari/mergeguard mergeguard review
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Local
|
|
18
|
+
|
|
19
|
+
`mergeguard review` compares the working tree (including untracked files) with `HEAD`. `--staged` limits the review to the index. Default verifier is `offline`.
|
|
20
|
+
|
|
21
|
+
## Pre-push
|
|
22
|
+
|
|
23
|
+
`mergeguard hook install` adds a managed block to the pre-push hook (including `core.hooksPath`). Git supplies local/remote refs on stdin; MergeGuard reviews the outgoing range. Existing hook content is retained.
|
|
24
|
+
|
|
25
|
+
## GitHub
|
|
26
|
+
|
|
27
|
+
See [`examples/github/mergeguard.yml`](../examples/github/mergeguard.yml). Use `fetch-depth: 0` so merge-base works. Default templates pin `--verifier offline`. Optional Laya CI is in [LAYA.md](LAYA.md).
|
|
28
|
+
|
|
29
|
+
## GitLab
|
|
30
|
+
|
|
31
|
+
See [`examples/gitlab/mergeguard.yml`](../examples/gitlab/mergeguard.yml). `--format gitlab` writes Code Quality JSON. Use `artifacts: when: always` so findings upload even when the gate exits `1`.
|
|
32
|
+
|
|
33
|
+
## Jenkins / CircleCI / Azure DevOps / Bitbucket / Buildkite / custom
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install -D @fitsummehari/mergeguard
|
|
37
|
+
npx mergeguard review --verifier offline --base origin/main --head HEAD
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The contract is the exit code plus optional JSON/SARIF/GitLab output. No provider API credentials are required for offline verification.
|
package/docs/LAYA.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Optional Laya verification
|
|
2
|
+
|
|
3
|
+
Laya is **not** bundled in the MergeGuard npm package. Detectors run in Node. Laya is an optional verifier that answers narrow questions (plausible, reachable, already protected, impact, worth reporting) about those candidates.
|
|
4
|
+
|
|
5
|
+
MergeGuard works with **Node.js 20+ and Git only**. Python and Laya are extra.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python -m pip install laya
|
|
9
|
+
npx mergeguard doctor
|
|
10
|
+
npx mergeguard review --verifier laya
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
On first model-backed use, Laya may download a checkpoint into its normal local Hugging Face cache. Later runs can reuse that cache. Do not commit model files. Do not put hundreds of millions of parameters into the npm tarball.
|
|
14
|
+
|
|
15
|
+
## Reproducibility
|
|
16
|
+
|
|
17
|
+
Default for 0.1.0 is `offline` so laptop and CI agree without Python.
|
|
18
|
+
|
|
19
|
+
`auto` uses Laya when it is importable and otherwise falls back to offline. That can make environments disagree. Prefer pinning:
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
verifier:
|
|
23
|
+
engine: offline
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
or, if every environment installs Laya:
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
verifier:
|
|
30
|
+
engine: laya
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`mergeguard doctor` shows configured vs effective verifier.
|
|
34
|
+
|
|
35
|
+
## Optional GitHub Actions (Laya)
|
|
36
|
+
|
|
37
|
+
Do **not** add this to the default lightweight workflow.
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
- uses: actions/setup-node@v4
|
|
41
|
+
with:
|
|
42
|
+
node-version: 22
|
|
43
|
+
- run: npm install --save-dev @fitsummehari/mergeguard
|
|
44
|
+
- uses: actions/setup-python@v5
|
|
45
|
+
with:
|
|
46
|
+
python-version: "3.11"
|
|
47
|
+
- run: python -m pip install laya
|
|
48
|
+
- uses: actions/cache@v4
|
|
49
|
+
with:
|
|
50
|
+
path: |
|
|
51
|
+
~/.cache/huggingface
|
|
52
|
+
key: laya-hf-${{ runner.os }}
|
|
53
|
+
- run: npx mergeguard review --verifier laya --base "origin/${{ github.base_ref }}" --head "${{ github.event.pull_request.head.sha }}"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Optional GitLab CI (Laya)
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
mergeguard-laya:
|
|
60
|
+
image: node:22
|
|
61
|
+
before_script:
|
|
62
|
+
- apt-get update && apt-get install -y python3 python3-pip
|
|
63
|
+
- python3 -m pip install laya
|
|
64
|
+
- npm install --save-dev @fitsummehari/mergeguard
|
|
65
|
+
cache:
|
|
66
|
+
paths:
|
|
67
|
+
- .cache/huggingface/
|
|
68
|
+
variables:
|
|
69
|
+
HF_HOME: "$CI_PROJECT_DIR/.cache/huggingface"
|
|
70
|
+
script:
|
|
71
|
+
- npx mergeguard review --verifier laya --base "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" --head "$CI_COMMIT_SHA"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Future Node-native path
|
|
75
|
+
|
|
76
|
+
`@receptron/laya` exists on npm (0.1.x) and can load ONNX weights from a local cache. That release is too early to take as a MergeGuard runtime dependency. 0.1.0 keeps the Python subprocess bridge. A later version may optionally call:
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
MergeGuard Node → Laya ONNX/TypeScript runtime → model cached locally
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
without putting hundreds of millions of parameters inside the npm tarball.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Limitations
|
|
2
|
+
|
|
3
|
+
- MergeGuard **runs on arbitrary Git repositories**. It does **not** have equal semantic analysis for every language or framework.
|
|
4
|
+
- Detectors produce candidates. The verifier can suppress weak ones. Neither is a formal proof.
|
|
5
|
+
- Context is ephemeral and bounded. MergeGuard does not build or persist a whole-repository "project brain."
|
|
6
|
+
- Large diffs are truncated (`max_files`). Generated, minified, binary, and excluded paths are skipped.
|
|
7
|
+
- First-push and root-commit reviews use Git's empty tree. Shallow clones still need enough history for `--base` merge-base resolution.
|
|
8
|
+
- Pre-push hooks can be bypassed with `git push --no-verify`.
|
|
9
|
+
- Default verifier is `offline`. `auto` does not fail when Laya is missing and can make laptop vs CI results differ. Only `--verifier laya` (or `verifier.engine: laya`) requires Laya.
|
|
10
|
+
- Jev is optional, network-using, and credential-gated. Default operation never needs it.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Migration from the hosted 1.x shape
|
|
2
|
+
|
|
3
|
+
The previous repository modeled MergeGuard as a hosted review platform. The 2.x local-first shape removes the infrastructure layer and keeps the review capability as a CLI/library.
|
|
4
|
+
|
|
5
|
+
## Removed
|
|
6
|
+
|
|
7
|
+
- `apps/api`
|
|
8
|
+
- `apps/web`
|
|
9
|
+
- `apps/worker`
|
|
10
|
+
- database/Prisma persistence
|
|
11
|
+
- Redis/BullMQ queues
|
|
12
|
+
- webhook receivers
|
|
13
|
+
- Docker deployment topology
|
|
14
|
+
- dashboard/authentication concerns
|
|
15
|
+
- persisted repository indexing and analysis-run history
|
|
16
|
+
|
|
17
|
+
## Replaced by
|
|
18
|
+
|
|
19
|
+
- `src/git.js`: Git working/staged/range/pre-push change acquisition
|
|
20
|
+
- `src/detectors/*`: diff and repository-aware candidate detection
|
|
21
|
+
- `src/context.js`: bounded ephemeral repository context
|
|
22
|
+
- `src/verifiers/*`: deterministic, local Laya and optional Jev verifier contract
|
|
23
|
+
- `src/reporters/*`: terminal, JSON, SARIF and GitLab Code Quality
|
|
24
|
+
- `src/hook.js`: composable pre-push installation
|
|
25
|
+
- `src/review.js`: one reusable engine used by CLI and JS API
|
|
26
|
+
|
|
27
|
+
The migration intentionally does not preserve hosted analysis history because persistence is not required by the core product. If a future organization dashboard is needed, it should consume MergeGuard's JSON/SARIF output as a separate optional product rather than being required to run the analyzer.
|