simple-authz 1.0.5 → 1.2.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.
@@ -1,38 +1,88 @@
1
1
  const fs = require("fs")
2
2
 
3
+ const KNOWN_KEYS = ["role", "action", "resource", "condition"]
4
+
3
5
  function parseRules(filePath) {
4
6
 
5
7
  const content = fs.readFileSync(filePath, "utf8")
6
-
7
- const lines = content.split("\n").map(l => l.trim())
8
+ const rawLines = content.split("\n")
8
9
 
9
10
  const rules = []
10
11
  let current = null
12
+ let ruleStartLine = null
13
+
14
+ rawLines.forEach((rawLine, idx) => {
11
15
 
12
- for (const line of lines) {
16
+ const lineNumber = idx + 1
17
+ const line = rawLine.trim()
13
18
 
14
- if (!line || line.startsWith("#")) continue
19
+ if (!line || line.startsWith("#")) return
15
20
 
16
21
  if (line === "rule") {
22
+
23
+ // Previously this silently overwrote `current`, discarding an
24
+ // already-open rule block that was missing its "end".
25
+ if (current) {
26
+ throw new Error(
27
+ `Policy parse error at line ${lineNumber}: found "rule" but the ` +
28
+ `rule opened at line ${ruleStartLine} is still unclosed (missing "end").`
29
+ )
30
+ }
31
+
17
32
  current = {}
33
+ ruleStartLine = lineNumber
34
+ return
18
35
  }
19
36
 
20
- else if (line === "end") {
37
+ if (line === "end") {
38
+
39
+ // Previously this pushed `current` (= null for a stray "end")
40
+ // straight into `rules`, which crashed later in validateRules
41
+ // with an unhelpful "Cannot read properties of null".
42
+ if (!current) {
43
+ throw new Error(
44
+ `Policy parse error at line ${lineNumber}: found "end" with no matching "rule".`
45
+ )
46
+ }
47
+
21
48
  rules.push(current)
22
49
  current = null
50
+ ruleStartLine = null
51
+ return
23
52
  }
24
53
 
25
- else if (current) {
54
+ if (!current) {
55
+ throw new Error(
56
+ `Policy parse error at line ${lineNumber}: "${line}" appears outside ` +
57
+ `of a rule/end block.`
58
+ )
59
+ }
26
60
 
27
- const [key, ...rest] = line.split(" ")
28
- const value = rest.join(" ")
61
+ const [key, ...rest] = line.split(" ")
62
+ const value = rest.join(" ")
29
63
 
30
- if (key === "role") current.role = value
31
- if (key === "action") current.action = value
32
- if (key === "resource") current.resource = value
33
- if (key === "condition") current.condition = value
64
+ // Previously an unrecognized key (e.g. "roel" typo'd for "role")
65
+ // was silently dropped, leaving the rule incomplete with no warning.
66
+ if (!KNOWN_KEYS.includes(key)) {
67
+ throw new Error(
68
+ `Policy parse error at line ${lineNumber}: unknown key "${key}" ` +
69
+ `(expected one of: ${KNOWN_KEYS.join(", ")}).`
70
+ )
71
+ }
34
72
 
73
+ if (!value) {
74
+ throw new Error(
75
+ `Policy parse error at line ${lineNumber}: "${key}" has no value.`
76
+ )
35
77
  }
78
+
79
+ current[key] = value
80
+ })
81
+
82
+ if (current) {
83
+ throw new Error(
84
+ `Policy parse error: rule opened at line ${ruleStartLine} was never closed with "end".`
85
+ )
36
86
  }
37
87
 
38
88
  return rules
@@ -1,3 +1,5 @@
1
+ const { compileCondition } = require("../engine/conditionCompiler")
2
+
1
3
  function validateRules(rules) {
2
4
 
3
5
  const errors = []
@@ -20,17 +22,21 @@ function validateRules(rules) {
20
22
 
21
23
  if (rule.condition) {
22
24
 
23
- const validOperators = [
24
- "==", "!=", ">", "<", ">=", "<="
25
- ]
26
-
27
- const hasOperator = validOperators.some(op =>
28
- rule.condition.includes(op)
29
- )
30
-
31
- if (!hasOperator) {
25
+ // Previously this only checked whether an operator substring
26
+ // (e.g. "==") appeared ANYWHERE in the condition text — it never
27
+ // actually verified the condition parses. A condition like
28
+ // "doc.status ==" (missing right-hand side) or one with mismatched
29
+ // AND/OR usage would pass this check and then either crash later
30
+ // deep inside the compiler with no rule number attached, or (worse)
31
+ // silently compile into a broken/always-false comparison.
32
+ //
33
+ // Actually attempting to compile it here catches real structural
34
+ // problems immediately, at the rule they belong to.
35
+ try {
36
+ compileCondition(rule.condition)
37
+ } catch (err) {
32
38
  errors.push(
33
- `Rule ${ruleNumber}: invalid condition "${rule.condition}"`
39
+ `Rule ${ruleNumber}: invalid condition "${rule.condition}" — ${err.message}`
34
40
  )
35
41
  }
36
42
  }
package/package.json CHANGED
@@ -1,36 +1,52 @@
1
- {
2
- "name": "simple-authz",
3
- "version": "1.0.5",
4
- "description": "Lightweight policy-based authorization engine for Node.js using TOON policy language",
5
- "main": "lib/index.js",
6
- "scripts": {
7
- "test": "node bin/test.js",
8
- "dev": "node lib/core/authz.js"
9
- },
10
- "keywords": [
11
- "authorization",
12
- "access-control",
13
- "auth",
14
- "policy-engine",
15
- "rbac",
16
- "abac",
17
- "permissions",
18
- "nodejs-auth",
19
- "authorization-library",
20
- "security",
21
- "policy-based-access",
22
- "authz",
23
- "role-based-access-control",
24
- "authorization-engine"
25
- ],
26
- "author": "Dhruvil",
27
- "license": "Apache-2.0",
28
- "repository": {
29
- "type": "git",
30
- "url": "https://github.com/dhruvil05/simple-authz"
31
- },
32
- "bugs": {
33
- "url": "https://github.com/dhruvil05/simple-authz/issues"
34
- },
35
- "homepage": "https://github.com/dhruvil05/simple-authz#readme"
36
- }
1
+ {
2
+ "name": "simple-authz",
3
+ "version": "1.2.0",
4
+ "description": "Lightweight policy-based authorization engine for Node.js using TOON policy language",
5
+ "main": "lib/index.js",
6
+ "scripts": {
7
+ "test": "jest",
8
+ "dev": "node lib/core/authz.js"
9
+ },
10
+ "bin": {
11
+ "simple-authz": "bin/simple-authz/index.js"
12
+ },
13
+ "dependencies": {
14
+ "chalk": "^4.1.2",
15
+ "commander": "^11.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "jest": "^30.3.0"
19
+ },
20
+ "keywords": [
21
+ "authorization",
22
+ "access-control",
23
+ "auth",
24
+ "policy-engine",
25
+ "rbac",
26
+ "abac",
27
+ "permissions",
28
+ "nodejs-auth",
29
+ "authorization-library",
30
+ "security",
31
+ "policy-based-access",
32
+ "authz",
33
+ "role-based-access-control",
34
+ "authorization-engine"
35
+ ],
36
+ "types": "types/index.d.ts",
37
+ "files": [
38
+ "lib/",
39
+ "types/",
40
+ "package.json"
41
+ ],
42
+ "author": "Dhruvil",
43
+ "license": "Apache-2.0",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/dhruvil05/simple-authz"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/dhruvil05/simple-authz/issues"
50
+ },
51
+ "homepage": "https://github.com/dhruvil05/simple-authz#readme"
52
+ }
@@ -0,0 +1,31 @@
1
+ declare module "simple-authz" {
2
+ interface User {
3
+ role?: string;
4
+ roles?: string[];
5
+ [key: string]: any;
6
+ }
7
+
8
+ interface ExplainResult {
9
+ allowed: boolean;
10
+ role?: string;
11
+ resource?: string;
12
+ action?: string;
13
+ reason: string;
14
+ condition?: any;
15
+ }
16
+
17
+ class Authz {
18
+ load(path: string): void;
19
+ can(user: User, action: string, resource: string, data?: any): boolean;
20
+ explain(
21
+ user: User,
22
+ action: string,
23
+ resource: string,
24
+ data?: any,
25
+ ): ExplainResult;
26
+ middleware(action: string, resource: string): any;
27
+ }
28
+
29
+ const authz: Authz;
30
+ export = authz;
31
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file