permission-gap-detector 0.0.0-stage → 1.0.2

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 ADDED
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ ## 1.0.2
4
+
5
+ - Reject control characters in route paths before rendering reports.
6
+ - Accept UTF-8 BOM JSON files and literal filenames after `--`.
7
+ - Add command-specific help with documented exit codes.
8
+ - Expand regression coverage to 27 tests and include a clean route example.
9
+
10
+
11
+ ## 1.0.1
12
+
13
+ - Keep the distribution focused on source code, usage examples, and package documentation.
14
+ - Simplify the README and published file list.
15
+
16
+ ## 1.0.0
17
+
18
+ - Add missing and invalid permission checks.
19
+ - Add duplicate and conflicting policy detection.
20
+ - Add summary counts and declaration coverage.
21
+ - Add CommonJS API and CLI with JSON output and exit codes.
22
+ - Add 20 automated tests and usage examples.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aditya030207
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 CHANGED
@@ -1,3 +1,236 @@
1
- # Temporary Holding Version
1
+ # Permission Gap Detector
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A small, dependency-free CommonJS Node.js package that checks **declared API route permissions**.
4
+
5
+ ## Problem statement
6
+
7
+ While building an Express REST API, a developer can forget to specify who should access a route. A route list makes these decisions visible. This package finds missing or inconsistent declarations so they can be reviewed.
8
+
9
+ It does not inspect Express middleware or prove that an application is secure.
10
+
11
+ ## Features
12
+
13
+ - Detect missing permissions and unsupported permission values.
14
+ - Support `public`, `authenticated`, and `admin` policies.
15
+ - Detect repeated method/path pairs and conflicting valid policies.
16
+ - Return structured findings and permission declaration coverage.
17
+ - Read route lists from JSON using a simple CLI.
18
+
19
+ ## Installation
20
+
21
+ Requires Node.js 20 or newer and npm. No external dependencies.
22
+
23
+ Install from npm:
24
+
25
+ ```bash
26
+ npm install permission-gap-detector
27
+ ```
28
+
29
+ Alternatively, install directly from GitHub:
30
+
31
+ ```bash
32
+ npm install github:Aditya030207/npm-package-project
33
+ ```
34
+
35
+ Or run `npm pack` in this repository and install the resulting archive in another project:
36
+
37
+ ```bash
38
+ npm install /absolute/path/to/permission-gap-detector-1.0.2.tgz
39
+ ```
40
+
41
+ ## Basic usage
42
+
43
+ ```js
44
+ const { auditRoutes } = require('permission-gap-detector');
45
+
46
+ const routes = [
47
+ { method: 'GET', path: '/products', access: 'public' },
48
+ { method: 'DELETE', path: '/products/:id' }
49
+ ];
50
+
51
+ const result = auditRoutes(routes);
52
+ console.log(result.summary);
53
+ console.log(result.findings.missingPermissions);
54
+ // [{ index: 1, method: 'DELETE', path: '/products/:id' }]
55
+ ```
56
+
57
+ Maintain this route list alongside your Express routes. The package does not automatically discover routes.
58
+
59
+ ## Route format and validation
60
+
61
+ Input is an array of ordinary route objects. Each object needs:
62
+
63
+ | Field | Accepted values |
64
+ | --- | --- |
65
+ | `method` | GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, CONNECT, TRACE; case-insensitive |
66
+ | `path` | A string starting with `/`, without whitespace or control characters |
67
+ | `access` | Exactly `public`, `authenticated`, or `admin` |
68
+
69
+ `public` allows anyone; `authenticated` requires a logged-in user; `admin` requires administrator access. These are declarations, not enforcement.
70
+
71
+ Absent, undefined, null, empty, and whitespace-only access values are missing. Other unsupported values are invalid permissions. Invalid route objects, methods, and paths are reported as invalid routes and excluded from other checks. Additional fields are ignored. Methods are normalized to uppercase without changing the input. Paths are compared exactly: `/users`, `/Users`, `/users/`, and parameter names are distinct.
72
+
73
+ ## API
74
+
75
+ All four functions accept a route array and leave it unchanged. A non-array argument throws `TypeError`. Invalid entries inside an array are reported by `auditRoutes` instead of throwing.
76
+
77
+ ### `auditRoutes(routes)`
78
+
79
+ Returns `{ summary, findings, hasIssues }`.
80
+
81
+ `summary` contains:
82
+
83
+ | Field | Meaning |
84
+ | --- | --- |
85
+ | `totalRoutes` | All submitted definitions, including invalid entries |
86
+ | `validPermissions` | Structurally valid definitions with a supported access value |
87
+ | `missingPermissions` | Valid route structures with missing access |
88
+ | `invalidPermissions` | Valid route structures with unsupported access |
89
+ | `invalidRoutes` | Invalid route structures |
90
+ | `duplicateRoutes` | Extra definitions beyond the first in each repeated method/path group |
91
+ | `conflictingRoutes` | Method/path groups with at least two distinct supported policies |
92
+ | `coveragePercentage` | Valid permissions divided by total definitions, multiplied by 100, rounded to one decimal |
93
+
94
+ The four permission/validation categories sum to `totalRoutes`. An empty array has 0% coverage because no declarations were checked. Duplicates and conflicts overlap with these categories and must not be added to them. Three copies of one route count as two duplicates. A conflicting group counts once. Valid policies on duplicates still count toward declaration coverage: **100% coverage can coexist with conflicts and does not mean secure**.
95
+
96
+ `findings` contains five arrays:
97
+
98
+ - `missingPermissions`: `{ index, method, path }`.
99
+ - `invalidPermissions`: `{ index, method, path, access }`.
100
+ - `invalidRoutes`: `{ index, message }`.
101
+ - `duplicateRoutes`: `{ method, path, indices, extraDefinitions }`.
102
+ - `conflictingPermissions`: `{ method, path, indices, permissions }`.
103
+
104
+ Indices are zero-based positions in the original array. Conflict indices include all definitions in that group; `permissions` lists its distinct supported values. Missing and invalid policies do not themselves create conflicts. `hasIssues` is true when any findings array is nonempty.
105
+
106
+ ### `findMissingPermissions(routes)`
107
+
108
+ Returns the `missingPermissions` findings array.
109
+
110
+ ### `findDuplicateRoutes(routes)`
111
+
112
+ Returns the `duplicateRoutes` findings array, one object per repeated group.
113
+
114
+ ### `findConflictingPermissions(routes)`
115
+
116
+ Returns the `conflictingPermissions` findings array, one object per conflicting group.
117
+
118
+ The three convenience functions return only their named findings. Use `auditRoutes` to see validation errors as well.
119
+
120
+ ## CLI usage
121
+
122
+ Run directly from npm:
123
+
124
+ ```bash
125
+ npx permission-gap-detector check routes.json
126
+ ```
127
+
128
+ After installation into a project:
129
+
130
+ ```bash
131
+ npx --no-install permission-gap check routes.json
132
+ npx --no-install permission-gap check routes.json --json
133
+ ```
134
+
135
+ For a direct shell command, globally install the package with `npm install -g permission-gap-detector` then run `permission-gap check routes.json`. Both binary names point to the same CLI.
136
+
137
+ From this repository:
138
+
139
+ ```bash
140
+ node bin/cli.js check examples/routes.json
141
+ node bin/cli.js check examples/routes.json --json
142
+ node bin/cli.js --help
143
+ node bin/cli.js check --help
144
+ node bin/cli.js --version
145
+ ```
146
+
147
+ UTF-8 files may begin with a byte order mark (BOM). Files must contain a JSON array, not an object wrapping the array. Paths are relative to the current working directory; quote paths containing spaces. Use `permission-gap check --json -- -routes.json` for a filename beginning with a dash; options must come before `--`. Exit codes: **0** = no findings, **1** = audit findings, **2** = invalid command, unreadable file, malformed JSON, or non-array input. Human-readable failures go to stderr. With `--json`, failures are `{ "error": "message" }` on stdout.
148
+
149
+ ## Example output
150
+
151
+ Running the included six-definition example produces:
152
+
153
+ ```text
154
+ ==================================
155
+ PERMISSION GAP DETECTOR
156
+ ==================================
157
+
158
+ Total Routes Checked: 6
159
+ Valid Permissions: 5
160
+ Missing Permissions: 1
161
+ Invalid Permissions: 0
162
+ Invalid Route Definitions: 0
163
+ Duplicate Routes (extra definitions): 2
164
+ Conflicting Policies (route groups): 1
165
+
166
+ Permission Coverage: 83.3%
167
+
168
+ Issues Found:
169
+ [WARNING] DELETE /products/:id: Missing permission declaration (index 2).
170
+ [WARNING] GET /products: Duplicate route definition (indices 0, 3).
171
+ [WARNING] GET /admin: Duplicate route definition (indices 4, 5).
172
+ [ERROR] GET /admin: Conflicting permissions: admin, public.
173
+
174
+ ==================================
175
+ Audit Complete
176
+ ==================================
177
+ ```
178
+
179
+ The example intentionally has issues and exits with code 1.
180
+
181
+ For a complete set of consistent declarations, run:
182
+
183
+ ```bash
184
+ node bin/cli.js check examples/clean-routes.json
185
+ ```
186
+
187
+ This example uses all three supported permissions, reports 100% declaration coverage with no findings, and exits with code 0. Compare it with `examples/routes.json` to see how missing and conflicting declarations change the result. Neither result verifies middleware enforcement.
188
+
189
+ ## Development and testing
190
+
191
+ ```bash
192
+ git clone https://github.com/Aditya030207/npm-package-project.git
193
+ cd npm-package-project
194
+ npm ci
195
+ npm test
196
+ node examples/basic.js
197
+ npm pack --dry-run
198
+ ```
199
+
200
+ The 27 automated tests use `node:test` and strict assertions. They cover valid policies, input validation, missing permissions, duplicates, conflicts, counting, immutability, reports, and CLI behavior with real temporary files and child processes.
201
+
202
+ ## Source structure
203
+
204
+ ```text
205
+ npm-package-project/
206
+ ├── src/
207
+ │ ├── index.js # Four public exports
208
+ │ ├── auditRoutes.js # Combine checks and calculate summary
209
+ │ ├── checkPermissions.js # Validate input and access policies
210
+ │ ├── checkDuplicates.js # Group routes and find conflicts
211
+ │ └── generateReport.js # Format readable terminal output
212
+ ├── bin/cli.js # Arguments, JSON file reading, exit codes
213
+ ├── tests/package.test.js # API and CLI tests
214
+ ├── examples/
215
+ │ ├── basic.js
216
+ │ ├── routes.json
217
+ │ └── clean-routes.json
218
+ ├── package.json
219
+ ├── package-lock.json
220
+ ├── README.md
221
+ ├── CHANGELOG.md
222
+ ├── LICENSE
223
+ └── .gitignore
224
+ ```
225
+
226
+ ## Limitations
227
+
228
+ This audits only supplied route declarations. It does not scan source code, discover Express routes, execute middleware, validate tokens, enforce access rules, or assess application security. It does not infer route equivalence, inherited policies, Express mount paths, or wildcard matching. A public policy on a sensitive route is only detected as a conflict if another definition declares a different valid policy. Keep route declarations synchronized with the actual application.
229
+
230
+ ## Contributing
231
+
232
+ Open an issue describing a problem or propose a small change. Add a meaningful test for changed behavior and run `npm test`. Keep the project simple and dependency-free.
233
+
234
+ ## License
235
+
236
+ MIT. See [LICENSE](LICENSE).
package/bin/cli.js ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('node:fs');
3
+ const { auditRoutes } = require('../src');
4
+ const generateReport = require('../src/generateReport');
5
+ const { version } = require('../package.json');
6
+ const args = process.argv.slice(2);
7
+ // Arguments after -- are literal filenames, even when they begin with a dash.
8
+ const separator = args.indexOf('--');
9
+ const options = separator === -1 ? args : args.slice(0, separator);
10
+ const filenames = separator === -1 ? [] : args.slice(separator + 1);
11
+ const json = options.includes('--json');
12
+ const usage = 'Usage: permission-gap check <routes.json> [--json]\n permission-gap check [--json] -- <filename>\nOptions: --help, --version, --json\nExit codes: 0 = no findings, 1 = audit findings, 2 = input error';
13
+
14
+ try {
15
+ if ((args.length === 1 && args[0] === '--help') ||
16
+ (args.length === 2 && args[0] === 'check' && args[1] === '--help')) {
17
+ console.log(usage);
18
+ } else if (args.length === 1 && args[0] === '--version') {
19
+ console.log(version);
20
+ } else {
21
+ const positional = [...options.filter(arg => arg !== '--json'), ...filenames];
22
+ const unknownOption = options.some(arg => arg.startsWith('-') && arg !== '--json');
23
+ if (positional.length !== 2 || positional[0] !== 'check' || unknownOption || options.filter(arg => arg === '--json').length > 1) {
24
+ throw new Error(usage);
25
+ }
26
+ let content;
27
+ try { content = fs.readFileSync(positional[1], 'utf8'); }
28
+ catch { throw new Error(`Cannot read file: ${positional[1]}`); }
29
+ let routes;
30
+ try { routes = JSON.parse(content.replace(/^\uFEFF/, '')); }
31
+ catch { throw new Error(`Invalid JSON in file: ${positional[1]}`); }
32
+ const result = auditRoutes(routes);
33
+ console.log(json ? JSON.stringify(result, null, 2) : generateReport(result));
34
+ process.exitCode = result.hasIssues ? 1 : 0;
35
+ }
36
+ } catch (error) {
37
+ if (json) console.log(JSON.stringify({ error: error.message }));
38
+ else console.error(`Error: ${error.message}`);
39
+ process.exitCode = 2;
40
+ }
@@ -0,0 +1,3 @@
1
+ const { auditRoutes } = require('../src');
2
+ const routes = require('./routes.json');
3
+ console.log(JSON.stringify(auditRoutes(routes), null, 2));
@@ -0,0 +1,17 @@
1
+ [
2
+ {
3
+ "method": "GET",
4
+ "path": "/products",
5
+ "access": "public"
6
+ },
7
+ {
8
+ "method": "POST",
9
+ "path": "/products",
10
+ "access": "authenticated"
11
+ },
12
+ {
13
+ "method": "DELETE",
14
+ "path": "/products/:id",
15
+ "access": "admin"
16
+ }
17
+ ]
@@ -0,0 +1,31 @@
1
+ [
2
+ {
3
+ "method": "GET",
4
+ "path": "/products",
5
+ "access": "public"
6
+ },
7
+ {
8
+ "method": "POST",
9
+ "path": "/products",
10
+ "access": "authenticated"
11
+ },
12
+ {
13
+ "method": "DELETE",
14
+ "path": "/products/:id"
15
+ },
16
+ {
17
+ "method": "GET",
18
+ "path": "/products",
19
+ "access": "public"
20
+ },
21
+ {
22
+ "method": "GET",
23
+ "path": "/admin",
24
+ "access": "admin"
25
+ },
26
+ {
27
+ "method": "GET",
28
+ "path": "/admin",
29
+ "access": "public"
30
+ }
31
+ ]
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "permission-gap-detector",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.2",
4
+ "description": "Check declared API route permissions for missing policies, duplicates, and conflicts.",
5
+ "main": "src/index.js",
6
+ "type": "commonjs",
7
+ "bin": {
8
+ "permission-gap": "bin/cli.js",
9
+ "permission-gap-detector": "bin/cli.js"
10
+ },
11
+ "files": [
12
+ "src/",
13
+ "bin/",
14
+ "examples/",
15
+ "CHANGELOG.md"
16
+ ],
17
+ "scripts": {
18
+ "test": "node --test",
19
+ "prepublishOnly": "npm test"
20
+ },
21
+ "keywords": [
22
+ "permissions",
23
+ "routes",
24
+ "express",
25
+ "audit",
26
+ "cli"
27
+ ],
28
+ "author": "Aditya030207",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/Aditya030207/npm-package-project.git"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/Aditya030207/npm-package-project/issues"
36
+ },
37
+ "homepage": "https://github.com/Aditya030207/npm-package-project#readme",
38
+ "engines": {
39
+ "node": ">=20"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public",
43
+ "registry": "https://registry.npmjs.org/"
44
+ }
45
+ }
@@ -0,0 +1,25 @@
1
+ const { validateRoutes, checkPermissions } = require('./checkPermissions');
2
+ const checkDuplicates = require('./checkDuplicates');
3
+
4
+ function auditRoutes(routes) {
5
+ const { validRoutes, invalidRoutes } = validateRoutes(routes);
6
+ const { validPermissions, missingPermissions, invalidPermissions } = checkPermissions(validRoutes);
7
+ const { duplicateRoutes, conflictingPermissions } = checkDuplicates(validRoutes);
8
+ const findings = { missingPermissions, invalidPermissions, invalidRoutes, duplicateRoutes, conflictingPermissions };
9
+ return {
10
+ summary: {
11
+ totalRoutes: routes.length,
12
+ validPermissions,
13
+ missingPermissions: missingPermissions.length,
14
+ invalidPermissions: invalidPermissions.length,
15
+ invalidRoutes: invalidRoutes.length,
16
+ duplicateRoutes: duplicateRoutes.reduce((sum, group) => sum + group.extraDefinitions, 0),
17
+ conflictingRoutes: conflictingPermissions.length,
18
+ coveragePercentage: routes.length === 0 ? 0 : Math.round(validPermissions / routes.length * 1000) / 10
19
+ },
20
+ findings,
21
+ hasIssues: Object.values(findings).some(items => items.length > 0)
22
+ };
23
+ }
24
+
25
+ module.exports = auditRoutes;
@@ -0,0 +1,26 @@
1
+ const { ACCESS_TYPES } = require('./checkPermissions');
2
+
3
+ function checkDuplicates(routes) {
4
+ const groups = new Map();
5
+ for (const route of routes) {
6
+ const key = `${route.method} ${route.path}`;
7
+ if (!groups.has(key)) groups.set(key, []);
8
+ groups.get(key).push(route);
9
+ }
10
+ const duplicateRoutes = [];
11
+ const conflictingPermissions = [];
12
+ for (const group of groups.values()) {
13
+ if (group.length < 2) continue;
14
+ const { method, path } = group[0];
15
+ const indices = group.map(route => route.index);
16
+ duplicateRoutes.push({ method, path, indices, extraDefinitions: group.length - 1 });
17
+ // Only supported policies can conflict; other values have their own findings.
18
+ const permissions = [...new Set(group.map(route => route.access).filter(access => ACCESS_TYPES.includes(access)))];
19
+ if (permissions.length > 1) {
20
+ conflictingPermissions.push({ method, path, indices, permissions });
21
+ }
22
+ }
23
+ return { duplicateRoutes, conflictingPermissions };
24
+ }
25
+
26
+ module.exports = checkDuplicates;
@@ -0,0 +1,38 @@
1
+ const ACCESS_TYPES = ['public', 'authenticated', 'admin'];
2
+ const METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'CONNECT', 'TRACE'];
3
+
4
+ function validateRoutes(routes) {
5
+ if (!Array.isArray(routes)) throw new TypeError('Routes must be an array.');
6
+ const validRoutes = [];
7
+ const invalidRoutes = [];
8
+ for (let index = 0; index < routes.length; index++) {
9
+ const route = routes[index];
10
+ if (!route || typeof route !== 'object' || Array.isArray(route) ||
11
+ typeof route.method !== 'string' || !METHODS.includes(route.method.toUpperCase()) ||
12
+ typeof route.path !== 'string' || !route.path.startsWith('/') || /[\s\x00-\x1f\x7f-\x9f]/.test(route.path)) {
13
+ invalidRoutes.push({ index, message: 'Expected an object with a supported HTTP method and a path starting with / without whitespace or control characters.' });
14
+ continue;
15
+ }
16
+ validRoutes.push({ index, method: route.method.toUpperCase(), path: route.path, access: route.access });
17
+ }
18
+ return { validRoutes, invalidRoutes };
19
+ }
20
+
21
+ function checkPermissions(routes) {
22
+ const missingPermissions = [];
23
+ const invalidPermissions = [];
24
+ let validPermissions = 0;
25
+ for (const route of routes) {
26
+ const { index, method, path, access } = route;
27
+ if (access === undefined || access === null || (typeof access === 'string' && access.trim() === '')) {
28
+ missingPermissions.push({ index, method, path });
29
+ } else if (!ACCESS_TYPES.includes(access)) {
30
+ invalidPermissions.push({ index, method, path, access });
31
+ } else {
32
+ validPermissions++;
33
+ }
34
+ }
35
+ return { validPermissions, missingPermissions, invalidPermissions };
36
+ }
37
+
38
+ module.exports = { validateRoutes, checkPermissions, ACCESS_TYPES };
@@ -0,0 +1,21 @@
1
+ function generateReport(result) {
2
+ const { summary: s, findings: f } = result;
3
+ const lines = [
4
+ '==================================', ' PERMISSION GAP DETECTOR', '==================================', '',
5
+ `Total Routes Checked: ${s.totalRoutes}`, `Valid Permissions: ${s.validPermissions}`,
6
+ `Missing Permissions: ${s.missingPermissions}`, `Invalid Permissions: ${s.invalidPermissions}`,
7
+ `Invalid Route Definitions: ${s.invalidRoutes}`, `Duplicate Routes (extra definitions): ${s.duplicateRoutes}`,
8
+ `Conflicting Policies (route groups): ${s.conflictingRoutes}`, '',
9
+ `Permission Coverage: ${s.coveragePercentage}%`, '', 'Issues Found:'
10
+ ];
11
+ for (const route of f.missingPermissions) lines.push(`[WARNING] ${route.method} ${route.path}: Missing permission declaration (index ${route.index}).`);
12
+ for (const route of f.invalidPermissions) lines.push(`[ERROR] ${route.method} ${route.path}: Invalid permission; use public, authenticated, or admin (index ${route.index}).`);
13
+ for (const route of f.invalidRoutes) lines.push(`[ERROR] Index ${route.index}: ${route.message}`);
14
+ for (const route of f.duplicateRoutes) lines.push(`[WARNING] ${route.method} ${route.path}: Duplicate route definition (indices ${route.indices.join(', ')}).`);
15
+ for (const route of f.conflictingPermissions) lines.push(`[ERROR] ${route.method} ${route.path}: Conflicting permissions: ${route.permissions.join(', ')}.`);
16
+ if (!result.hasIssues) lines.push('No issues found.');
17
+ lines.push('', '==================================', 'Audit Complete', '==================================');
18
+ return lines.join('\n');
19
+ }
20
+
21
+ module.exports = generateReport;
package/src/index.js ADDED
@@ -0,0 +1,15 @@
1
+ const auditRoutes = require('./auditRoutes');
2
+
3
+ function findMissingPermissions(routes) {
4
+ return auditRoutes(routes).findings.missingPermissions;
5
+ }
6
+
7
+ function findDuplicateRoutes(routes) {
8
+ return auditRoutes(routes).findings.duplicateRoutes;
9
+ }
10
+
11
+ function findConflictingPermissions(routes) {
12
+ return auditRoutes(routes).findings.conflictingPermissions;
13
+ }
14
+
15
+ module.exports = { auditRoutes, findMissingPermissions, findDuplicateRoutes, findConflictingPermissions };