block-no-verify 1.0.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/LICENSE +21 -0
- package/README.md +119 -0
- package/dist/check-command.d.ts +9 -0
- package/dist/check-command.d.ts.map +1 -0
- package/dist/check-command.js +23 -0
- package/dist/check-command.js.map +1 -0
- package/dist/check-result.d.ts +13 -0
- package/dist/check-result.d.ts.map +1 -0
- package/dist/check-result.js +2 -0
- package/dist/check-result.js.map +1 -0
- package/dist/cli.d.ts +32 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +75 -0
- package/dist/cli.js.map +1 -0
- package/dist/detect-git-command.d.ts +6 -0
- package/dist/detect-git-command.d.ts.map +1 -0
- package/dist/detect-git-command.js +39 -0
- package/dist/detect-git-command.js.map +1 -0
- package/dist/exit-codes.d.ts +12 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +12 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/git-command.d.ts +6 -0
- package/dist/git-command.d.ts.map +1 -0
- package/dist/git-command.js +2 -0
- package/dist/git-command.js.map +1 -0
- package/dist/has-no-verify-flag.d.ts +6 -0
- package/dist/has-no-verify-flag.d.ts.map +1 -0
- package/dist/has-no-verify-flag.js +22 -0
- package/dist/has-no-verify-flag.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
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,119 @@
|
|
|
1
|
+
# block-no-verify
|
|
2
|
+
|
|
3
|
+
A security tool that blocks the `--no-verify` flag in git commands. Designed to prevent AI agents from bypassing git hooks.
|
|
4
|
+
|
|
5
|
+
## Why?
|
|
6
|
+
|
|
7
|
+
When using AI coding assistants like Claude Code, you might have git hooks (pre-commit, pre-push) that enforce code quality, run tests, or perform security checks. The `--no-verify` flag allows bypassing these hooks, which could allow AI agents to skip important validations.
|
|
8
|
+
|
|
9
|
+
This package provides a CLI that can be used as a Claude Code hook to block any git commands that include `--no-verify`.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g block-no-verify
|
|
15
|
+
# or
|
|
16
|
+
pnpm add -g block-no-verify
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage with Claude Code
|
|
20
|
+
|
|
21
|
+
Add this to your `.claude/settings.json`:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"hooks": {
|
|
26
|
+
"PreToolUse": [
|
|
27
|
+
{
|
|
28
|
+
"matcher": "Bash",
|
|
29
|
+
"hooks": [
|
|
30
|
+
{
|
|
31
|
+
"type": "command",
|
|
32
|
+
"command": "block-no-verify"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This will block any Bash commands that contain `--no-verify` with supported git commands.
|
|
42
|
+
|
|
43
|
+
## Supported Git Commands
|
|
44
|
+
|
|
45
|
+
The following git commands are monitored for `--no-verify`:
|
|
46
|
+
|
|
47
|
+
- `git commit`
|
|
48
|
+
- `git push`
|
|
49
|
+
- `git merge`
|
|
50
|
+
- `git cherry-pick`
|
|
51
|
+
- `git rebase`
|
|
52
|
+
- `git am`
|
|
53
|
+
|
|
54
|
+
## Behavior
|
|
55
|
+
|
|
56
|
+
| Command | Blocked? | Notes |
|
|
57
|
+
| ------------------------ | -------- | --------------------------------------------- |
|
|
58
|
+
| `git commit --no-verify` | Yes | |
|
|
59
|
+
| `git commit -n` | Yes | `-n` is shorthand for `--no-verify` in commit |
|
|
60
|
+
| `git push --no-verify` | Yes | |
|
|
61
|
+
| `git push -n` | No | `-n` means `--dry-run` in push |
|
|
62
|
+
| `git merge --no-verify` | Yes | |
|
|
63
|
+
| `git merge -n` | No | `-n` means `--no-commit` in merge |
|
|
64
|
+
| `git commit -m "msg"` | No | No `--no-verify` flag |
|
|
65
|
+
|
|
66
|
+
## Exit Codes
|
|
67
|
+
|
|
68
|
+
- `0` - Command is allowed
|
|
69
|
+
- `2` - Command is blocked (contains `--no-verify`)
|
|
70
|
+
- `1` - An error occurred
|
|
71
|
+
|
|
72
|
+
## Programmatic Usage
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import {
|
|
76
|
+
checkCommand,
|
|
77
|
+
detectGitCommand,
|
|
78
|
+
hasNoVerifyFlag,
|
|
79
|
+
} from 'block-no-verify'
|
|
80
|
+
|
|
81
|
+
// Check if a command should be blocked
|
|
82
|
+
const result = checkCommand('git commit --no-verify -m "test"')
|
|
83
|
+
console.log(result.blocked) // true
|
|
84
|
+
console.log(result.reason) // "BLOCKED: --no-verify flag is not allowed..."
|
|
85
|
+
|
|
86
|
+
// Detect git command type
|
|
87
|
+
const cmd = detectGitCommand('git push origin main')
|
|
88
|
+
console.log(cmd) // "push"
|
|
89
|
+
|
|
90
|
+
// Check for no-verify flag
|
|
91
|
+
const hasFlag = hasNoVerifyFlag('git commit -n -m "test"', 'commit')
|
|
92
|
+
console.log(hasFlag) // true
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Development
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Install dependencies
|
|
99
|
+
pnpm install
|
|
100
|
+
|
|
101
|
+
# Build
|
|
102
|
+
pnpm build
|
|
103
|
+
|
|
104
|
+
# Run tests
|
|
105
|
+
pnpm test
|
|
106
|
+
|
|
107
|
+
# Run tests with coverage
|
|
108
|
+
pnpm test:coverage
|
|
109
|
+
|
|
110
|
+
# Lint
|
|
111
|
+
pnpm lint
|
|
112
|
+
|
|
113
|
+
# Format
|
|
114
|
+
pnpm format
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CheckResult } from './check-result.js';
|
|
2
|
+
/**
|
|
3
|
+
* Checks a command input for --no-verify flag usage
|
|
4
|
+
*
|
|
5
|
+
* @param input - The command input to check (typically from stdin in Claude Code hooks)
|
|
6
|
+
* @returns CheckResult indicating whether the command should be blocked
|
|
7
|
+
*/
|
|
8
|
+
export declare function checkCommand(input: string): CheckResult;
|
|
9
|
+
//# sourceMappingURL=check-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-command.d.ts","sourceRoot":"","sources":["../src/check-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAIpD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAgBvD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { detectGitCommand } from './detect-git-command.js';
|
|
2
|
+
import { hasNoVerifyFlag } from './has-no-verify-flag.js';
|
|
3
|
+
/**
|
|
4
|
+
* Checks a command input for --no-verify flag usage
|
|
5
|
+
*
|
|
6
|
+
* @param input - The command input to check (typically from stdin in Claude Code hooks)
|
|
7
|
+
* @returns CheckResult indicating whether the command should be blocked
|
|
8
|
+
*/
|
|
9
|
+
export function checkCommand(input) {
|
|
10
|
+
const gitCommand = detectGitCommand(input);
|
|
11
|
+
if (!gitCommand) {
|
|
12
|
+
return { blocked: false };
|
|
13
|
+
}
|
|
14
|
+
if (hasNoVerifyFlag(input, gitCommand)) {
|
|
15
|
+
return {
|
|
16
|
+
blocked: true,
|
|
17
|
+
reason: `BLOCKED: --no-verify flag is not allowed with git ${gitCommand}. Git hooks must not be bypassed.`,
|
|
18
|
+
gitCommand,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return { blocked: false, gitCommand };
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=check-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-command.js","sourceRoot":"","sources":["../src/check-command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAEzD;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;IAE1C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;IAC3B,CAAC;IAED,IAAI,eAAe,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC;QACvC,OAAO;YACL,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,qDAAqD,UAAU,mCAAmC;YAC1G,UAAU;SACX,CAAA;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAA;AACvC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { GitCommand } from './git-command.js';
|
|
2
|
+
/**
|
|
3
|
+
* Result of checking a command for --no-verify flag
|
|
4
|
+
*/
|
|
5
|
+
export interface CheckResult {
|
|
6
|
+
/** Whether the command is blocked */
|
|
7
|
+
blocked: boolean;
|
|
8
|
+
/** The reason the command was blocked, if any */
|
|
9
|
+
reason?: string;
|
|
10
|
+
/** The detected git command, if any */
|
|
11
|
+
gitCommand?: GitCommand;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=check-result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-result.d.ts","sourceRoot":"","sources":["../src/check-result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAA;IAChB,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,uCAAuC;IACvC,UAAU,CAAC,EAAE,UAAU,CAAA;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-result.js","sourceRoot":"","sources":["../src/check-result.ts"],"names":[],"mappings":""}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* block-no-verify CLI
|
|
4
|
+
*
|
|
5
|
+
* Usage in Claude Code hooks:
|
|
6
|
+
*
|
|
7
|
+
* In your .claude/settings.json:
|
|
8
|
+
* ```json
|
|
9
|
+
* {
|
|
10
|
+
* "hooks": {
|
|
11
|
+
* "PreToolUse": [
|
|
12
|
+
* {
|
|
13
|
+
* "matcher": "Bash",
|
|
14
|
+
* "hooks": [
|
|
15
|
+
* {
|
|
16
|
+
* "type": "command",
|
|
17
|
+
* "command": "block-no-verify"
|
|
18
|
+
* }
|
|
19
|
+
* ]
|
|
20
|
+
* }
|
|
21
|
+
* ]
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* The CLI reads the command from stdin and exits with:
|
|
27
|
+
* - 0 if the command is allowed
|
|
28
|
+
* - 2 if the command is blocked (contains --no-verify)
|
|
29
|
+
* - 1 if an error occurred
|
|
30
|
+
*/
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* block-no-verify CLI
|
|
4
|
+
*
|
|
5
|
+
* Usage in Claude Code hooks:
|
|
6
|
+
*
|
|
7
|
+
* In your .claude/settings.json:
|
|
8
|
+
* ```json
|
|
9
|
+
* {
|
|
10
|
+
* "hooks": {
|
|
11
|
+
* "PreToolUse": [
|
|
12
|
+
* {
|
|
13
|
+
* "matcher": "Bash",
|
|
14
|
+
* "hooks": [
|
|
15
|
+
* {
|
|
16
|
+
* "type": "command",
|
|
17
|
+
* "command": "block-no-verify"
|
|
18
|
+
* }
|
|
19
|
+
* ]
|
|
20
|
+
* }
|
|
21
|
+
* ]
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* The CLI reads the command from stdin and exits with:
|
|
27
|
+
* - 0 if the command is allowed
|
|
28
|
+
* - 2 if the command is blocked (contains --no-verify)
|
|
29
|
+
* - 1 if an error occurred
|
|
30
|
+
*/
|
|
31
|
+
import { checkCommand, EXIT_CODES } from './index.js';
|
|
32
|
+
async function readStdin() {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
let data = '';
|
|
35
|
+
// Set encoding to utf8
|
|
36
|
+
process.stdin.setEncoding('utf8');
|
|
37
|
+
// Handle data chunks
|
|
38
|
+
process.stdin.on('data', chunk => {
|
|
39
|
+
data += chunk;
|
|
40
|
+
});
|
|
41
|
+
// Resolve when stdin ends
|
|
42
|
+
process.stdin.on('end', () => {
|
|
43
|
+
resolve(data);
|
|
44
|
+
});
|
|
45
|
+
// Handle errors
|
|
46
|
+
process.stdin.on('error', err => {
|
|
47
|
+
reject(err);
|
|
48
|
+
});
|
|
49
|
+
// If stdin is empty/not piped, resolve with empty string after a short timeout
|
|
50
|
+
if (process.stdin.isTTY) {
|
|
51
|
+
resolve('');
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function main() {
|
|
56
|
+
try {
|
|
57
|
+
const input = await readStdin();
|
|
58
|
+
if (!input.trim()) {
|
|
59
|
+
// No input provided, allow by default
|
|
60
|
+
process.exit(EXIT_CODES.ALLOWED);
|
|
61
|
+
}
|
|
62
|
+
const result = checkCommand(input);
|
|
63
|
+
if (result.blocked) {
|
|
64
|
+
console.error(result.reason);
|
|
65
|
+
process.exit(EXIT_CODES.BLOCKED);
|
|
66
|
+
}
|
|
67
|
+
process.exit(EXIT_CODES.ALLOWED);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error('Error:', error instanceof Error ? error.message : String(error));
|
|
71
|
+
process.exit(EXIT_CODES.ERROR);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
main();
|
|
75
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAErD,KAAK,UAAU,SAAS;IACtB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,IAAI,GAAG,EAAE,CAAA;QAEb,uBAAuB;QACvB,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QAEjC,qBAAqB;QACrB,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE;YAC/B,IAAI,IAAI,KAAK,CAAA;QACf,CAAC,CAAC,CAAA;QAEF,0BAA0B;QAC1B,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YAC3B,OAAO,CAAC,IAAI,CAAC,CAAA;QACf,CAAC,CAAC,CAAA;QAEF,gBAAgB;QAChB,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;YAC9B,MAAM,CAAC,GAAG,CAAC,CAAA;QACb,CAAC,CAAC,CAAA;QAEF,+EAA+E;QAC/E,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACxB,OAAO,CAAC,EAAE,CAAC,CAAA;QACb,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAA;QAE/B,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAClB,sCAAsC;YACtC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;QAClC,CAAC;QAED,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;QAElC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC5B,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;QAClC,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,QAAQ,EACR,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAA;QACD,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;IAChC,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-git-command.d.ts","sourceRoot":"","sources":["../src/detect-git-command.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAmCjE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { GIT_COMMANDS_WITH_NO_VERIFY } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Checks if the input contains a git command
|
|
4
|
+
*/
|
|
5
|
+
export function detectGitCommand(input) {
|
|
6
|
+
for (const cmd of GIT_COMMANDS_WITH_NO_VERIFY) {
|
|
7
|
+
// Match patterns like: git commit, git -C /path commit
|
|
8
|
+
// Using string concatenation and simple includes/match to avoid non-literal RegExp
|
|
9
|
+
const gitIndex = input.indexOf('git');
|
|
10
|
+
if (gitIndex === -1)
|
|
11
|
+
continue;
|
|
12
|
+
const cmdIndex = input.indexOf(cmd, gitIndex);
|
|
13
|
+
if (cmdIndex === -1)
|
|
14
|
+
continue;
|
|
15
|
+
// Verify 'git' is a word boundary (not part of another word)
|
|
16
|
+
const beforeGit = gitIndex > 0 ? input[gitIndex - 1] : ' ';
|
|
17
|
+
const afterGit = input[gitIndex + 3] || ' ';
|
|
18
|
+
if (!/\s|^$/.test(beforeGit) && beforeGit !== ';' && beforeGit !== '&') {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (!/\s/.test(afterGit))
|
|
22
|
+
continue;
|
|
23
|
+
// Verify the command is a word boundary
|
|
24
|
+
const beforeCmd = cmdIndex > 0 ? input[cmdIndex - 1] : ' ';
|
|
25
|
+
const afterCmd = input[cmdIndex + cmd.length] || ' ';
|
|
26
|
+
if (!/\s/.test(beforeCmd))
|
|
27
|
+
continue;
|
|
28
|
+
if (!/\s|$/.test(afterCmd) && afterCmd !== ';' && afterCmd !== '&') {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
// Make sure there's no pipe or semicolon between git and cmd that would indicate separate commands
|
|
32
|
+
const between = input.slice(gitIndex + 3, cmdIndex);
|
|
33
|
+
if (/[;|]/.test(between))
|
|
34
|
+
continue;
|
|
35
|
+
return cmd;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=detect-git-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-git-command.js","sourceRoot":"","sources":["../src/detect-git-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AAGxD;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,KAAK,MAAM,GAAG,IAAI,2BAA2B,EAAE,CAAC;QAC9C,uDAAuD;QACvD,mFAAmF;QACnF,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QACrC,IAAI,QAAQ,KAAK,CAAC,CAAC;YAAE,SAAQ;QAE7B,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;QAC7C,IAAI,QAAQ,KAAK,CAAC,CAAC;YAAE,SAAQ;QAE7B,6DAA6D;QAC7D,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;QAC1D,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,IAAI,GAAG,CAAA;QAE3C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YACvE,SAAQ;QACV,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,SAAQ;QAElC,wCAAwC;QACxC,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;QAC1D,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAA;QAEpD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,SAAQ;QACnC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,QAAQ,KAAK,GAAG,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;YACnE,SAAQ;QACV,CAAC;QAED,mGAAmG;QACnG,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAA;QACnD,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,SAAQ;QAElC,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exit codes for the CLI
|
|
3
|
+
*/
|
|
4
|
+
export declare const EXIT_CODES: {
|
|
5
|
+
/** Command is allowed to proceed */
|
|
6
|
+
readonly ALLOWED: 0;
|
|
7
|
+
/** Command is blocked */
|
|
8
|
+
readonly BLOCKED: 2;
|
|
9
|
+
/** An error occurred */
|
|
10
|
+
readonly ERROR: 1;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=exit-codes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exit-codes.d.ts","sourceRoot":"","sources":["../src/exit-codes.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,UAAU;IACrB,oCAAoC;;IAEpC,yBAAyB;;IAEzB,wBAAwB;;CAEhB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exit-codes.js","sourceRoot":"","sources":["../src/exit-codes.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,oCAAoC;IACpC,OAAO,EAAE,CAAC;IACV,yBAAyB;IACzB,OAAO,EAAE,CAAC;IACV,wBAAwB;IACxB,KAAK,EAAE,CAAC;CACA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-command.d.ts","sourceRoot":"","sources":["../src/git-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AAExD;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-command.js","sourceRoot":"","sources":["../src/git-command.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GitCommand } from './git-command.js';
|
|
2
|
+
/**
|
|
3
|
+
* Checks if the input contains a --no-verify flag for a specific git command
|
|
4
|
+
*/
|
|
5
|
+
export declare function hasNoVerifyFlag(input: string, command: GitCommand): boolean;
|
|
6
|
+
//# sourceMappingURL=has-no-verify-flag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"has-no-verify-flag.d.ts","sourceRoot":"","sources":["../src/has-no-verify-flag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAoB3E"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if the input contains a --no-verify flag for a specific git command
|
|
3
|
+
*/
|
|
4
|
+
export function hasNoVerifyFlag(input, command) {
|
|
5
|
+
// Check for --no-verify
|
|
6
|
+
if (/--no-verify\b/.test(input)) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
// For commit, -n is a shorthand for --no-verify
|
|
10
|
+
// For other commands, -n might mean something else, so we need to be careful
|
|
11
|
+
if (command === 'commit') {
|
|
12
|
+
// Match -n as a standalone flag or at the start of combined flags
|
|
13
|
+
if (/\s-n(?:\s|$)/.test(input) || /\s-n[a-zA-Z]/.test(input)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// For push, -n is --dry-run, not --no-verify
|
|
18
|
+
// For merge, -n is --no-commit, not --no-verify
|
|
19
|
+
// So we only block --no-verify (long form) for these
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=has-no-verify-flag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"has-no-verify-flag.js","sourceRoot":"","sources":["../src/has-no-verify-flag.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,OAAmB;IAChE,wBAAwB;IACxB,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,gDAAgD;IAChD,6EAA6E;IAC7E,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,kEAAkE;QAClE,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7D,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,gDAAgD;IAChD,qDAAqD;IAErD,OAAO,KAAK,CAAA;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* block-no-verify
|
|
3
|
+
*
|
|
4
|
+
* A security tool that blocks the --no-verify flag in git commands.
|
|
5
|
+
* Designed to prevent AI agents from bypassing git hooks.
|
|
6
|
+
*/
|
|
7
|
+
export { GIT_COMMANDS_WITH_NO_VERIFY } from './types.js';
|
|
8
|
+
export type { GitCommand } from './git-command.js';
|
|
9
|
+
export type { CheckResult } from './check-result.js';
|
|
10
|
+
export { EXIT_CODES } from './exit-codes.js';
|
|
11
|
+
export { detectGitCommand } from './detect-git-command.js';
|
|
12
|
+
export { hasNoVerifyFlag } from './has-no-verify-flag.js';
|
|
13
|
+
export { checkCommand } from './check-command.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AACxD,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* block-no-verify
|
|
3
|
+
*
|
|
4
|
+
* A security tool that blocks the --no-verify flag in git commands.
|
|
5
|
+
* Designed to prevent AI agents from bypassing git hooks.
|
|
6
|
+
*/
|
|
7
|
+
export { GIT_COMMANDS_WITH_NO_VERIFY } from './types.js';
|
|
8
|
+
export { EXIT_CODES } from './exit-codes.js';
|
|
9
|
+
export { detectGitCommand } from './detect-git-command.js';
|
|
10
|
+
export { hasNoVerifyFlag } from './has-no-verify-flag.js';
|
|
11
|
+
export { checkCommand } from './check-command.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AAGxD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,2BAA2B,qEAO9B,CAAA"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,QAAQ;IACR,MAAM;IACN,OAAO;IACP,aAAa;IACb,QAAQ;IACR,IAAI;CACI,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "block-no-verify",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool to block --no-verify flag in git commands. Prevents AI agents from bypassing git hooks.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"block-no-verify": "./dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"git",
|
|
22
|
+
"no-verify",
|
|
23
|
+
"hooks",
|
|
24
|
+
"cli",
|
|
25
|
+
"security",
|
|
26
|
+
"ai-safety",
|
|
27
|
+
"claude-code",
|
|
28
|
+
"agent-governance"
|
|
29
|
+
],
|
|
30
|
+
"author": "",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20.0.0"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/tupe12334/block-no-verify.git"
|
|
41
|
+
},
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/tupe12334/block-no-verify/issues"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/tupe12334/block-no-verify#readme",
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@commitlint/cli": "^20.3.1",
|
|
48
|
+
"@commitlint/config-conventional": "^20.3.1",
|
|
49
|
+
"@cspell/dict-he": "^4.0.5",
|
|
50
|
+
"@types/node": "^25.0.3",
|
|
51
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
52
|
+
"cspell": "^9.4.0",
|
|
53
|
+
"eslint": "^9.39.2",
|
|
54
|
+
"eslint-config-agent": "^1.9.0",
|
|
55
|
+
"eslint-config-publishable-package-json": "^1.0.0",
|
|
56
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
57
|
+
"husky": "^9.1.7",
|
|
58
|
+
"knip": "^5.80.2",
|
|
59
|
+
"lint-staged": "^16.2.7",
|
|
60
|
+
"prettier": "^3.7.4",
|
|
61
|
+
"release-it": "^19.2.3",
|
|
62
|
+
"typescript": "^5.9.3",
|
|
63
|
+
"vitest": "^4.0.16"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsc",
|
|
67
|
+
"dev": "tsc --watch",
|
|
68
|
+
"test": "vitest",
|
|
69
|
+
"test:watch": "vitest --watch",
|
|
70
|
+
"test:coverage": "vitest --coverage",
|
|
71
|
+
"lint": "eslint .",
|
|
72
|
+
"lint:fix": "eslint . --fix",
|
|
73
|
+
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,md}\"",
|
|
74
|
+
"format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,md}\"",
|
|
75
|
+
"spell": "cspell lint '**/*.{ts,js,md,json}' --gitignore",
|
|
76
|
+
"spell:check": "cspell lint '**/*.{ts,js,md,json}' --gitignore",
|
|
77
|
+
"knip": "knip",
|
|
78
|
+
"release": "release-it"
|
|
79
|
+
}
|
|
80
|
+
}
|