unguard 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.
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const binDir = path.dirname(fileURLToPath(import.meta.url));
8
+ const packageRoot = path.resolve(binDir, "..");
9
+ const configPath = path.join(packageRoot, "sgconfig.yml");
10
+ const localBinDir = path.join(packageRoot, "node_modules", ".bin");
11
+
12
+ const rawArgs = process.argv.slice(2);
13
+ const firstArg = rawArgs[0];
14
+
15
+ if (firstArg === "-h" || firstArg === "--help") {
16
+ console.log(
17
+ "unguard: data-shape AST checker\n\nUsage:\n unguard scan [paths...] [ast-grep scan options]\n unguard scan [paths...] --strict\n unguard [paths...] [ast-grep scan options]\n\nExamples:\n unguard scan src\n unguard scan src --filter no-loose-null-check\n unguard scan src --strict\n unguard src --strict\n",
18
+ );
19
+ process.exit(0);
20
+ }
21
+
22
+ const isScanCommand = firstArg === "scan";
23
+ const userArgs = isScanCommand ? rawArgs.slice(1) : rawArgs;
24
+
25
+ const forwardedArgs = [];
26
+ let strictMode = false;
27
+ for (const arg of userArgs) {
28
+ if (arg === "--strict") {
29
+ strictMode = true;
30
+ continue;
31
+ }
32
+ forwardedArgs.push(arg);
33
+ }
34
+
35
+ const scanArgs = ["scan", "--config", configPath, ...forwardedArgs];
36
+ if (strictMode) {
37
+ scanArgs.push("--error");
38
+ }
39
+
40
+ const astGrepBinary = process.platform === "win32" ? "ast-grep.cmd" : "ast-grep";
41
+ const envPath = process.env.PATH
42
+ ? `${localBinDir}${path.delimiter}${process.env.PATH}`
43
+ : localBinDir;
44
+
45
+ const result = spawnSync(astGrepBinary, scanArgs, {
46
+ stdio: "inherit",
47
+ env: {
48
+ ...process.env,
49
+ PATH: envPath,
50
+ },
51
+ });
52
+
53
+ if (result.error) {
54
+ if (result.error.code === "ENOENT") {
55
+ console.error(
56
+ "ast-grep binary not found. Run `npm install` in this package or install @ast-grep/cli globally.",
57
+ );
58
+ }
59
+ console.error(result.error.message);
60
+ process.exit(1);
61
+ }
62
+
63
+ process.exit(result.status ?? 0);
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "unguard",
3
+ "version": "0.1.0",
4
+ "description": "Prove data-shape guarantees with ast-grep rule packs",
5
+ "author": "TwoAbove",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/TwoAbove/unguard.git"
10
+ },
11
+ "homepage": "https://github.com/TwoAbove/unguard#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/TwoAbove/unguard/issues"
14
+ },
15
+ "type": "module",
16
+ "bin": {
17
+ "unguard": "./bin/unguard.mjs"
18
+ },
19
+ "files": ["bin", "rules", "sgconfig.yml"],
20
+ "scripts": {
21
+ "scan": "node ./bin/unguard.mjs scan .",
22
+ "scan:strict": "node ./bin/unguard.mjs scan . --strict",
23
+ "test:rules": "ast-grep test -c sgconfig.yml",
24
+ "test:rules:update": "ast-grep test -c sgconfig.yml -U",
25
+ "prepublishOnly": "npm run test:rules"
26
+ },
27
+ "dependencies": {
28
+ "@ast-grep/cli": "^0.39.4"
29
+ },
30
+ "engines": {
31
+ "node": ">=18"
32
+ }
33
+ }
@@ -0,0 +1,8 @@
1
+ id: no-loose-null-check
2
+ language: TypeScript
3
+ severity: warning
4
+ message: Replace loose null checks with explicit upstream guarantees or strict checks
5
+ rule:
6
+ any:
7
+ - pattern: $VALUE == null
8
+ - pattern: $VALUE != null
@@ -0,0 +1,8 @@
1
+ id: no-nested-existence-guard
2
+ language: TypeScript
3
+ severity: warning
4
+ message: Nested existence guards indicate missing boundary proof; enforce shape upstream
5
+ rule:
6
+ any:
7
+ - pattern: if ($OBJ && $OBJ.$PROP) { $$$BODY }
8
+ - pattern: if ($OBJ && $OBJ.$PROP) $STMT
@@ -0,0 +1,9 @@
1
+ id: no-optional-chain-fallback
2
+ language: TypeScript
3
+ severity: warning
4
+ message: Remove optional-chain fallback and prove this shape upstream at the boundary
5
+ rule:
6
+ pattern: $VALUE ?? $DEFAULT
7
+ constraints:
8
+ VALUE:
9
+ regex: \?\.
@@ -0,0 +1,12 @@
1
+ id: no-property-nullish-fallback
2
+ language: TypeScript
3
+ severity: warning
4
+ message: Property nullish fallback hides upstream guarantees; validate earlier or throw
5
+ rule:
6
+ pattern: $VALUE ?? $DEFAULT
7
+ constraints:
8
+ VALUE:
9
+ all:
10
+ - kind: member_expression
11
+ - not:
12
+ regex: \?\.
package/sgconfig.yml ADDED
@@ -0,0 +1,4 @@
1
+ ruleDirs:
2
+ - rules
3
+ testConfigs:
4
+ - testDir: rule-tests